summaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/Kconfig4
-rw-r--r--drivers/base/Makefile2
-rw-r--r--drivers/base/attribute_container.c8
-rw-r--r--drivers/base/base.h9
-rw-r--r--drivers/base/bus.c28
-rw-r--r--drivers/base/class.c112
-rw-r--r--drivers/base/core.c200
-rw-r--r--drivers/base/firmware_class.c22
-rw-r--r--drivers/base/hypervisor.c19
-rw-r--r--drivers/base/init.c1
-rw-r--r--drivers/base/isa.c180
-rw-r--r--drivers/base/platform.c35
-rw-r--r--drivers/base/power/Makefile4
-rw-r--r--drivers/base/power/resume.c4
-rw-r--r--drivers/base/power/suspend.c17
-rw-r--r--drivers/base/power/trace.c228
-rw-r--r--drivers/base/sys.c51
17 files changed, 855 insertions, 69 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index f0eff3dac58..80502dc6ed6 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -38,3 +38,7 @@ config DEBUG_DRIVER
If you are unsure about this, say N here.
endmenu
+
+config SYS_HYPERVISOR
+ bool
+ default n
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index e99471d3232..b539e5e75b5 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -5,10 +5,12 @@ obj-y := core.o sys.o bus.o dd.o \
cpu.o firmware.o init.o map.o dmapool.o \
attribute_container.o transport_class.o
obj-y += power/
+obj-$(CONFIG_ISA) += isa.o
obj-$(CONFIG_FW_LOADER) += firmware_class.o
obj-$(CONFIG_NUMA) += node.o
obj-$(CONFIG_MEMORY_HOTPLUG) += memory.o
obj-$(CONFIG_SMP) += topology.o
+obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
ifeq ($(CONFIG_DEBUG_DRIVER),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c
index 2a7d7ae83e1..22220733f76 100644
--- a/drivers/base/attribute_container.c
+++ b/drivers/base/attribute_container.c
@@ -236,7 +236,6 @@ attribute_container_remove_device(struct device *dev,
}
up(&attribute_container_mutex);
}
-EXPORT_SYMBOL_GPL(attribute_container_remove_device);
/**
* attribute_container_device_trigger - execute a trigger for each matching classdev
@@ -276,7 +275,6 @@ attribute_container_device_trigger(struct device *dev,
}
up(&attribute_container_mutex);
}
-EXPORT_SYMBOL_GPL(attribute_container_device_trigger);
/**
* attribute_container_trigger - trigger a function for each matching container
@@ -304,7 +302,6 @@ attribute_container_trigger(struct device *dev,
}
up(&attribute_container_mutex);
}
-EXPORT_SYMBOL_GPL(attribute_container_trigger);
/**
* attribute_container_add_attrs - add attributes
@@ -333,7 +330,6 @@ attribute_container_add_attrs(struct class_device *classdev)
return 0;
}
-EXPORT_SYMBOL_GPL(attribute_container_add_attrs);
/**
* attribute_container_add_class_device - same function as class_device_add
@@ -352,7 +348,6 @@ attribute_container_add_class_device(struct class_device *classdev)
return error;
return attribute_container_add_attrs(classdev);
}
-EXPORT_SYMBOL_GPL(attribute_container_add_class_device);
/**
* attribute_container_add_class_device_adapter - simple adapter for triggers
@@ -367,7 +362,6 @@ attribute_container_add_class_device_adapter(struct attribute_container *cont,
{
return attribute_container_add_class_device(classdev);
}
-EXPORT_SYMBOL_GPL(attribute_container_add_class_device_adapter);
/**
* attribute_container_remove_attrs - remove any attribute files
@@ -389,7 +383,6 @@ attribute_container_remove_attrs(struct class_device *classdev)
for (i = 0; attrs[i]; i++)
class_device_remove_file(classdev, attrs[i]);
}
-EXPORT_SYMBOL_GPL(attribute_container_remove_attrs);
/**
* attribute_container_class_device_del - equivalent of class_device_del
@@ -405,7 +398,6 @@ attribute_container_class_device_del(struct class_device *classdev)
attribute_container_remove_attrs(classdev);
class_device_del(classdev);
}
-EXPORT_SYMBOL_GPL(attribute_container_class_device_del);
/**
* attribute_container_find_class_device - find the corresponding class_device
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 5735b38582d..c3b8dc98b8a 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -5,13 +5,21 @@ extern int devices_init(void);
extern int buses_init(void);
extern int classes_init(void);
extern int firmware_init(void);
+#ifdef CONFIG_SYS_HYPERVISOR
+extern int hypervisor_init(void);
+#else
+static inline int hypervisor_init(void) { return 0; }
+#endif
extern int platform_bus_init(void);
extern int system_bus_init(void);
extern int cpu_dev_init(void);
extern int attribute_container_init(void);
extern int bus_add_device(struct device * dev);
+extern void bus_attach_device(struct device * dev);
extern void bus_remove_device(struct device * dev);
+extern struct bus_type *get_bus(struct bus_type * bus);
+extern void put_bus(struct bus_type * bus);
extern int bus_add_driver(struct device_driver *);
extern void bus_remove_driver(struct device_driver *);
@@ -34,4 +42,5 @@ struct class_device_attribute *to_class_dev_attr(struct attribute *_attr)
return container_of(_attr, struct class_device_attribute, attr);
}
+extern char *make_class_name(const char *name, struct kobject *kobj);
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 76656acd00d..050d86d0b87 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -362,8 +362,7 @@ static void device_remove_attrs(struct bus_type * bus, struct device * dev)
* @dev: device being added
*
* - Add the device to its bus's list of devices.
- * - Try to attach to driver.
- * - Create link to device's physical location.
+ * - Create link to device's bus.
*/
int bus_add_device(struct device * dev)
{
@@ -372,11 +371,10 @@ int bus_add_device(struct device * dev)
if (bus) {
pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id);
- device_attach(dev);
- klist_add_tail(&dev->knode_bus, &bus->klist_devices);
error = device_add_attrs(bus, dev);
if (!error) {
sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id);
+ sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "subsystem");
sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "bus");
}
}
@@ -384,6 +382,22 @@ int bus_add_device(struct device * dev)
}
/**
+ * bus_attach_device - add device to bus
+ * @dev: device tried to attach to a driver
+ *
+ * - Try to attach to driver.
+ */
+void bus_attach_device(struct device * dev)
+{
+ struct bus_type * bus = dev->bus;
+
+ if (bus) {
+ device_attach(dev);
+ klist_add_tail(&dev->knode_bus, &bus->klist_devices);
+ }
+}
+
+/**
* bus_remove_device - remove device from bus
* @dev: device to be removed
*
@@ -395,6 +409,7 @@ int bus_add_device(struct device * dev)
void bus_remove_device(struct device * dev)
{
if (dev->bus) {
+ sysfs_remove_link(&dev->kobj, "subsystem");
sysfs_remove_link(&dev->kobj, "bus");
sysfs_remove_link(&dev->bus->devices.kobj, dev->bus_id);
device_remove_attrs(dev->bus, dev);
@@ -732,14 +747,9 @@ EXPORT_SYMBOL_GPL(bus_for_each_dev);
EXPORT_SYMBOL_GPL(bus_find_device);
EXPORT_SYMBOL_GPL(bus_for_each_drv);
-EXPORT_SYMBOL_GPL(bus_add_device);
-EXPORT_SYMBOL_GPL(bus_remove_device);
EXPORT_SYMBOL_GPL(bus_register);
EXPORT_SYMBOL_GPL(bus_unregister);
EXPORT_SYMBOL_GPL(bus_rescan_devices);
-EXPORT_SYMBOL_GPL(get_bus);
-EXPORT_SYMBOL_GPL(put_bus);
-EXPORT_SYMBOL_GPL(find_bus);
EXPORT_SYMBOL_GPL(bus_create_file);
EXPORT_SYMBOL_GPL(bus_remove_file);
diff --git a/drivers/base/class.c b/drivers/base/class.c
index b1ea4df85c7..9aa12746026 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -91,14 +91,14 @@ void class_remove_file(struct class * cls, const struct class_attribute * attr)
sysfs_remove_file(&cls->subsys.kset.kobj, &attr->attr);
}
-struct class * class_get(struct class * cls)
+static struct class *class_get(struct class *cls)
{
if (cls)
return container_of(subsys_get(&cls->subsys), struct class, subsys);
return NULL;
}
-void class_put(struct class * cls)
+static void class_put(struct class * cls)
{
if (cls)
subsys_put(&cls->subsys);
@@ -142,6 +142,7 @@ int class_register(struct class * cls)
pr_debug("device class '%s': registering\n", cls->name);
INIT_LIST_HEAD(&cls->children);
+ INIT_LIST_HEAD(&cls->devices);
INIT_LIST_HEAD(&cls->interfaces);
init_MUTEX(&cls->sem);
error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name);
@@ -504,22 +505,21 @@ void class_device_initialize(struct class_device *class_dev)
INIT_LIST_HEAD(&class_dev->node);
}
-static char *make_class_name(struct class_device *class_dev)
+char *make_class_name(const char *name, struct kobject *kobj)
{
- char *name;
+ char *class_name;
int size;
- size = strlen(class_dev->class->name) +
- strlen(kobject_name(&class_dev->kobj)) + 2;
+ size = strlen(name) + strlen(kobject_name(kobj)) + 2;
- name = kmalloc(size, GFP_KERNEL);
- if (!name)
+ class_name = kmalloc(size, GFP_KERNEL);
+ if (!class_name)
return ERR_PTR(-ENOMEM);
- strcpy(name, class_dev->class->name);
- strcat(name, ":");
- strcat(name, kobject_name(&class_dev->kobj));
- return name;
+ strcpy(class_name, name);
+ strcat(class_name, ":");
+ strcat(class_name, kobject_name(kobj));
+ return class_name;
}
int class_device_add(struct class_device *class_dev)
@@ -535,18 +535,22 @@ int class_device_add(struct class_device *class_dev)
return -EINVAL;
if (!strlen(class_dev->class_id))
- goto register_done;
+ goto out1;
parent_class = class_get(class_dev->class);
if (!parent_class)
- goto register_done;
+ goto out1;
+
parent_class_dev = class_device_get(class_dev->parent);
pr_debug("CLASS: registering class device: ID = '%s'\n",
class_dev->class_id);
/* first, register with generic layer. */
- kobject_set_name(&class_dev->kobj, "%s", class_dev->class_id);
+ error = kobject_set_name(&class_dev->kobj, "%s", class_dev->class_id);
+ if (error)
+ goto out2;
+
if (parent_class_dev)
class_dev->kobj.parent = &parent_class_dev->kobj;
else
@@ -554,41 +558,58 @@ int class_device_add(struct class_device *class_dev)
error = kobject_add(&class_dev->kobj);
if (error)
- goto register_done;
+ goto out2;
/* add the needed attributes to this device */
+ sysfs_create_link(&class_dev->kobj, &parent_class->subsys.kset.kobj, "subsystem");
class_dev->uevent_attr.attr.name = "uevent";
class_dev->uevent_attr.attr.mode = S_IWUSR;
class_dev->uevent_attr.attr.owner = parent_class->owner;
class_dev->uevent_attr.store = store_uevent;
- class_device_create_file(class_dev, &class_dev->uevent_attr);
+ error = class_device_create_file(class_dev, &class_dev->uevent_attr);
+ if (error)
+ goto out3;
if (MAJOR(class_dev->devt)) {
struct class_device_attribute *attr;
attr = kzalloc(sizeof(*attr), GFP_KERNEL);
if (!attr) {
error = -ENOMEM;
- kobject_del(&class_dev->kobj);
- goto register_done;
+ goto out4;
}
attr->attr.name = "dev";
attr->attr.mode = S_IRUGO;
attr->attr.owner = parent_class->owner;
attr->show = show_dev;
- class_device_create_file(class_dev, attr);
+ error = class_device_create_file(class_dev, attr);
+ if (error) {
+ kfree(attr);
+ goto out4;
+ }
+
class_dev->devt_attr = attr;
}
- class_device_add_attrs(class_dev);
+ error = class_device_add_attrs(class_dev);
+ if (error)
+ goto out5;
+
if (class_dev->dev) {
- class_name = make_class_name(class_dev);
- sysfs_create_link(&class_dev->kobj,
- &class_dev->dev->kobj, "device");
- sysfs_create_link(&class_dev->dev->kobj, &class_dev->kobj,
- class_name);
+ class_name = make_class_name(class_dev->class->name,
+ &class_dev->kobj);
+ error = sysfs_create_link(&class_dev->kobj,
+ &class_dev->dev->kobj, "device");
+ if (error)
+ goto out6;
+ error = sysfs_create_link(&class_dev->dev->kobj, &class_dev->kobj,
+ class_name);
+ if (error)
+ goto out7;
}
- class_device_add_groups(class_dev);
+ error = class_device_add_groups(class_dev);
+ if (error)
+ goto out8;
kobject_uevent(&class_dev->kobj, KOBJ_ADD);
@@ -601,11 +622,28 @@ int class_device_add(struct class_device *class_dev)
}
up(&parent_class->sem);
- register_done:
- if (error) {
- class_put(parent_class);
+ goto out1;
+
+ out8:
+ if (class_dev->dev)
+ sysfs_remove_link(&class_dev->kobj, class_name);
+ out7:
+ if (class_dev->dev)
+ sysfs_remove_link(&class_dev->kobj, "device");
+ out6:
+ class_device_remove_attrs(class_dev);
+ out5:
+ if (class_dev->devt_attr)
+ class_device_remove_file(class_dev, class_dev->devt_attr);
+ out4:
+ class_device_remove_file(class_dev, &class_dev->uevent_attr);
+ out3:
+ kobject_del(&class_dev->kobj);
+ out2:
+ if(parent_class_dev)
class_device_put(parent_class_dev);
- }
+ class_put(parent_class);
+ out1:
class_device_put(class_dev);
kfree(class_name);
return error;
@@ -695,10 +733,12 @@ void class_device_del(struct class_device *class_dev)
}
if (class_dev->dev) {
- class_name = make_class_name(class_dev);
+ class_name = make_class_name(class_dev->class->name,
+ &class_dev->kobj);
sysfs_remove_link(&class_dev->kobj, "device");
sysfs_remove_link(&class_dev->dev->kobj, class_name);
}
+ sysfs_remove_link(&class_dev->kobj, "subsystem");
class_device_remove_file(class_dev, &class_dev->uevent_attr);
if (class_dev->devt_attr)
class_device_remove_file(class_dev, class_dev->devt_attr);
@@ -760,14 +800,16 @@ int class_device_rename(struct class_device *class_dev, char *new_name)
new_name);
if (class_dev->dev)
- old_class_name = make_class_name(class_dev);
+ old_class_name = make_class_name(class_dev->class->name,
+ &class_dev->kobj);
strlcpy(class_dev->class_id, new_name, KOBJ_NAME_LEN);
error = kobject_rename(&class_dev->kobj, new_name);
if (class_dev->dev) {
- new_class_name = make_class_name(class_dev);
+ new_class_name = make_class_name(class_dev->class->name,
+ &class_dev->kobj);
sysfs_create_link(&class_dev->dev->kobj, &class_dev->kobj,
new_class_name);
sysfs_remove_link(&class_dev->dev->kobj, old_class_name);
@@ -858,8 +900,6 @@ EXPORT_SYMBOL_GPL(class_create_file);
EXPORT_SYMBOL_GPL(class_remove_file);
EXPORT_SYMBOL_GPL(class_register);
EXPORT_SYMBOL_GPL(class_unregister);
-EXPORT_SYMBOL_GPL(class_get);
-EXPORT_SYMBOL_GPL(class_put);
EXPORT_SYMBOL_GPL(class_create);
EXPORT_SYMBOL_GPL(class_destroy);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 6b355bd7816..27c2176895d 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/kdev_t.h>
#include <asm/semaphore.h>
@@ -28,6 +29,22 @@ int (*platform_notify_remove)(struct device * dev) = NULL;
* sysfs bindings for devices.
*/
+/**
+ * dev_driver_string - Return a device's driver name, if at all possible
+ * @dev: struct device to get the name of
+ *
+ * Will return the device's driver's name if it is bound to a device. If
+ * the device is not bound to a device, it will return the name of the bus
+ * it is attached to. If it is not attached to a bus either, an empty
+ * string will be returned.
+ */
+const char *dev_driver_string(struct device *dev)
+{
+ return dev->driver ? dev->driver->name :
+ (dev->bus ? dev->bus->name : "");
+}
+EXPORT_SYMBOL_GPL(dev_driver_string);
+
#define to_dev(obj) container_of(obj, struct device, kobj)
#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
@@ -98,6 +115,8 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj)
struct device *dev = to_dev(kobj);
if (dev->bus)
return 1;
+ if (dev->class)
+ return 1;
}
return 0;
}
@@ -106,7 +125,11 @@ static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj)
{
struct device *dev = to_dev(kobj);
- return dev->bus->name;
+ if (dev->bus)
+ return dev->bus->name;
+ if (dev->class)
+ return dev->class->name;
+ return NULL;
}
static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp,
@@ -117,6 +140,16 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp,
int length = 0;
int retval = 0;
+ /* add the major/minor if present */
+ if (MAJOR(dev->devt)) {
+ add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "MAJOR=%u", MAJOR(dev->devt));
+ add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "MINOR=%u", MINOR(dev->devt));
+ }
+
/* add bus name of physical device */
if (dev->bus)
add_uevent_var(envp, num_envp, &i,
@@ -161,6 +194,12 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
return count;
}
+static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return print_dev_t(buf, dev->devt);
+}
+
/*
* devices_subsys - structure to be registered with kobject core.
*/
@@ -231,6 +270,7 @@ void device_initialize(struct device *dev)
klist_init(&dev->klist_children, klist_children_get,
klist_children_put);
INIT_LIST_HEAD(&dev->dma_pools);
+ INIT_LIST_HEAD(&dev->node);
init_MUTEX(&dev->sem);
device_init_wakeup(dev, 0);
}
@@ -249,6 +289,7 @@ void device_initialize(struct device *dev)
int device_add(struct device *dev)
{
struct device *parent = NULL;
+ char *class_name = NULL;
int error = -EINVAL;
dev = get_device(dev);
@@ -274,23 +315,69 @@ int device_add(struct device *dev)
dev->uevent_attr.store = store_uevent;
device_create_file(dev, &dev->uevent_attr);
- kobject_uevent(&dev->kobj, KOBJ_ADD);
+ if (MAJOR(dev->devt)) {
+ struct device_attribute *attr;
+ attr = kzalloc(sizeof(*attr), GFP_KERNEL);
+ if (!attr) {
+ error = -ENOMEM;
+ goto PMError;
+ }
+ attr->attr.name = "dev";
+ attr->attr.mode = S_IRUGO;
+ if (dev->driver)
+ attr->attr.owner = dev->driver->owner;
+ attr->show = show_dev;
+ error = device_create_file(dev, attr);
+ if (error) {
+ kfree(attr);
+ goto attrError;
+ }
+
+ dev->devt_attr = attr;
+ }
+
+ if (dev->class) {
+ sysfs_create_link(&dev->kobj, &dev->class->subsys.kset.kobj,
+ "subsystem");
+ sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj,
+ dev->bus_id);
+
+ sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");
+ class_name = make_class_name(dev->class->name, &dev->kobj);
+ sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name);
+ }
+
if ((error = device_pm_add(dev)))
goto PMError;
if ((error = bus_add_device(dev)))
goto BusError;
+ kobject_uevent(&dev->kobj, KOBJ_ADD);
+ bus_attach_device(dev);
if (parent)
klist_add_tail(&dev->knode_parent, &parent->klist_children);
+ if (dev->class) {
+ /* tie the class to the device */
+ down(&dev->class->sem);
+ list_add_tail(&dev->node, &dev->class->devices);
+ up(&dev->class->sem);
+ }
+
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
Done:
+ kfree(class_name);
put_device(dev);
return error;
BusError:
device_pm_remove(dev);
PMError:
+ if (dev->devt_attr) {
+ device_remove_file(dev, dev->devt_attr);
+ kfree(dev->devt_attr);
+ }
+ attrError:
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
kobject_del(&dev->kobj);
Error:
@@ -362,9 +449,23 @@ void put_device(struct device * dev)
void device_del(struct device * dev)
{
struct device * parent = dev->parent;
+ char *class_name = NULL;
if (parent)
klist_del(&dev->knode_parent);
+ if (dev->devt_attr)
+ device_remove_file(dev, dev->devt_attr);
+ if (dev->class) {
+ sysfs_remove_link(&dev->kobj, "subsystem");
+ sysfs_remove_link(&dev->class->subsys.kset.kobj, dev->bus_id);
+ class_name = make_class_name(dev->class->name, &dev->kobj);
+ sysfs_remove_link(&dev->kobj, "device");
+ sysfs_remove_link(&dev->parent->kobj, class_name);
+ kfree(class_name);
+ down(&dev->class->sem);
+ list_del_init(&dev->node);
+ up(&dev->class->sem);
+ }
device_remove_file(dev, &dev->uevent_attr);
/* Notify the platform of the removal, in case they
@@ -449,3 +550,98 @@ EXPORT_SYMBOL_GPL(put_device);
EXPORT_SYMBOL_GPL(device_create_file);
EXPORT_SYMBOL_GPL(device_remove_file);
+
+
+static void device_create_release(struct device *dev)
+{
+ pr_debug("%s called for %s\n", __FUNCTION__, dev->bus_id);
+ kfree(dev);
+}
+
+/**
+ * device_create - creates a device and registers it with sysfs
+ * @cs: pointer to the struct class that this device should be registered to.
+ * @parent: pointer to the parent struct device of this new device, if any.
+ * @dev: the dev_t for the char device to be added.
+ * @fmt: string for the class device's name
+ *
+ * This function can be used by char device classes. A struct
+ * device will be created in sysfs, registered to the specified
+ * class.
+ * A "dev" file will be created, showing the dev_t for the device, if
+ * the dev_t is not 0,0.
+ * If a pointer to a parent struct device is passed in, the newly
+ * created struct device will be a child of that device in sysfs. The
+ * pointer to the struct device will be returned from the call. Any
+ * further sysfs files that might be required can be created using this
+ * pointer.
+ *
+ * Note: the struct class passed to this function must have previously
+ * been created with a call to class_create().
+ */
+struct device *device_create(struct class *class, struct device *parent,
+ dev_t devt, char *fmt, ...)
+{
+ va_list args;
+ struct device *dev = NULL;
+ int retval = -ENODEV;
+
+ if (class == NULL || IS_ERR(class))
+ goto error;
+ if (parent == NULL) {
+ printk(KERN_WARNING "%s does not work yet for NULL parents\n", __FUNCTION__);
+ goto error;
+ }
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ dev->devt = devt;
+ dev->class = class;
+ dev->parent = parent;
+ dev->release = device_create_release;
+
+ va_start(args, fmt);
+ vsnprintf(dev->bus_id, BUS_ID_SIZE, fmt, args);
+ va_end(args);
+ retval = device_register(dev);
+ if (retval)
+ goto error;
+
+ return dev;
+
+error:
+ kfree(dev);
+ return ERR_PTR(retval);
+}
+EXPORT_SYMBOL_GPL(device_create);
+
+/**
+ * device_destroy - removes a device that was created with device_create()
+ * @class: the pointer to the struct class that this device was registered * with.
+ * @dev: the dev_t of the device that was previously registered.
+ *
+ * This call unregisters and cleans up a class device that was created with a
+ * call to class_device_create()
+ */
+void device_destroy(struct class *class, dev_t devt)
+{
+ struct device *dev = NULL;
+ struct device *dev_tmp;
+
+ down(&class->sem);
+ list_for_each_entry(dev_tmp, &class->devices, node) {
+ if (dev_tmp->devt == devt) {
+ dev = dev_tmp;
+ break;
+ }
+ }
+ up(&class->sem);
+
+ if (dev)
+ device_unregister(dev);
+}
+EXPORT_SYMBOL_GPL(device_destroy);
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 0c99ae6a340..5d6c011183f 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -15,7 +15,7 @@
#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
-#include <asm/semaphore.h>
+#include <linux/mutex.h>
#include <linux/firmware.h>
#include "base.h"
@@ -36,7 +36,7 @@ static int loading_timeout = 10; /* In seconds */
/* fw_lock could be moved to 'struct firmware_priv' but since it is just
* guarding for corner cases a global lock should be OK */
-static DECLARE_MUTEX(fw_lock);
+static DEFINE_MUTEX(fw_lock);
struct firmware_priv {
char fw_id[FIRMWARE_NAME_MAX];
@@ -142,9 +142,9 @@ firmware_loading_store(struct class_device *class_dev,
switch (loading) {
case 1:
- down(&fw_lock);
+ mutex_lock(&fw_lock);
if (!fw_priv->fw) {
- up(&fw_lock);
+ mutex_unlock(&fw_lock);
break;
}
vfree(fw_priv->fw->data);
@@ -152,7 +152,7 @@ firmware_loading_store(struct class_device *class_dev,
fw_priv->fw->size = 0;
fw_priv->alloc_size = 0;
set_bit(FW_STATUS_LOADING, &fw_priv->status);
- up(&fw_lock);
+ mutex_unlock(&fw_lock);
break;
case 0:
if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
@@ -185,7 +185,7 @@ firmware_data_read(struct kobject *kobj,
struct firmware *fw;
ssize_t ret_count = count;
- down(&fw_lock);
+ mutex_lock(&fw_lock);
fw = fw_priv->fw;
if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
ret_count = -ENODEV;
@@ -200,7 +200,7 @@ firmware_data_read(struct kobject *kobj,
memcpy(buffer, fw->data + offset, ret_count);
out:
- up(&fw_lock);
+ mutex_unlock(&fw_lock);
return ret_count;
}
@@ -253,7 +253,7 @@ firmware_data_write(struct kobject *kobj,
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
- down(&fw_lock);
+ mutex_lock(&fw_lock);
fw = fw_priv->fw;
if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
retval = -ENODEV;
@@ -268,7 +268,7 @@ firmware_data_write(struct kobject *kobj,
fw->size = max_t(size_t, offset + count, fw->size);
retval = count;
out:
- up(&fw_lock);
+ mutex_unlock(&fw_lock);
return retval;
}
@@ -436,14 +436,14 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
} else
wait_for_completion(&fw_priv->completion);
- down(&fw_lock);
+ mutex_lock(&fw_lock);
if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status)) {
retval = -ENOENT;
release_firmware(fw_priv->fw);
*firmware_p = NULL;
}
fw_priv->fw = NULL;
- up(&fw_lock);
+ mutex_unlock(&fw_lock);
class_device_unregister(class_dev);
goto out;
diff --git a/drivers/base/hypervisor.c b/drivers/base/hypervisor.c
new file mode 100644
index 00000000000..0c85e9d6a44
--- /dev/null
+++ b/drivers/base/hypervisor.c
@@ -0,0 +1,19 @@
+/*
+ * hypervisor.c - /sys/hypervisor subsystem.
+ *
+ * This file is released under the GPLv2
+ *
+ */
+
+#include <linux/kobject.h>
+#include <linux/device.h>
+
+#include "base.h"
+
+decl_subsys(hypervisor, NULL, NULL);
+EXPORT_SYMBOL_GPL(hypervisor_subsys);
+
+int __init hypervisor_init(void)
+{
+ return subsystem_register(&hypervisor_subsys);
+}
diff --git a/drivers/base/init.c b/drivers/base/init.c
index c648914b9cd..37138154f9e 100644
--- a/drivers/base/init.c
+++ b/drivers/base/init.c
@@ -27,6 +27,7 @@ void __init driver_init(void)
buses_init();
classes_init();
firmware_init();
+ hypervisor_init();
/* These are also core pieces, but must come after the
* core core pieces.
diff --git a/drivers/base/isa.c b/drivers/base/isa.c
new file mode 100644
index 00000000000..d2222397a40
--- /dev/null
+++ b/drivers/base/isa.c
@@ -0,0 +1,180 @@
+/*
+ * ISA bus.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/isa.h>
+
+static struct device isa_bus = {
+ .bus_id = "isa"
+};
+
+struct isa_dev {
+ struct device dev;
+ struct device *next;
+ unsigned int id;
+};
+
+#define to_isa_dev(x) container_of((x), struct isa_dev, dev)
+
+static int isa_bus_match(struct device *dev, struct device_driver *driver)
+{
+ struct isa_driver *isa_driver = to_isa_driver(driver);
+
+ if (dev->platform_data == isa_driver) {
+ if (!isa_driver->match ||
+ isa_driver->match(dev, to_isa_dev(dev)->id))
+ return 1;
+ dev->platform_data = NULL;
+ }
+ return 0;
+}
+
+static int isa_bus_probe(struct device *dev)
+{
+ struct isa_driver *isa_driver = dev->platform_data;
+
+ if (isa_driver->probe)
+ return isa_driver->probe(dev, to_isa_dev(dev)->id);
+
+ return 0;
+}
+
+static int isa_bus_remove(struct device *dev)
+{
+ struct isa_driver *isa_driver = dev->platform_data;
+
+ if (isa_driver->remove)
+ return isa_driver->remove(dev, to_isa_dev(dev)->id);
+
+ return 0;
+}
+
+static void isa_bus_shutdown(struct device *dev)
+{
+ struct isa_driver *isa_driver = dev->platform_data;
+
+ if (isa_driver->shutdown)
+ isa_driver->shutdown(dev, to_isa_dev(dev)->id);
+}
+
+static int isa_bus_suspend(struct device *dev, pm_message_t state)
+{
+ struct isa_driver *isa_driver = dev->platform_data;
+
+ if (isa_driver->suspend)
+ return isa_driver->suspend(dev, to_isa_dev(dev)->id, state);
+
+ return 0;
+}
+
+static int isa_bus_resume(struct device *dev)
+{
+ struct isa_driver *isa_driver = dev->platform_data;
+
+ if (isa_driver->resume)
+ return isa_driver->resume(dev, to_isa_dev(dev)->id);
+
+ return 0;
+}
+
+static struct bus_type isa_bus_type = {
+ .name = "isa",
+ .match = isa_bus_match,
+ .probe = isa_bus_probe,
+ .remove = isa_bus_remove,
+ .shutdown = isa_bus_shutdown,
+ .suspend = isa_bus_suspend,
+ .resume = isa_bus_resume
+};
+
+static void isa_dev_release(struct device *dev)
+{
+ kfree(to_isa_dev(dev));
+}
+
+void isa_unregister_driver(struct isa_driver *isa_driver)
+{
+ struct device *dev = isa_driver->devices;
+
+ while (dev) {
+ struct device *tmp = to_isa_dev(dev)->next;
+ device_unregister(dev);
+ dev = tmp;
+ }
+ driver_unregister(&isa_driver->driver);
+}
+EXPORT_SYMBOL_GPL(isa_unregister_driver);
+
+int isa_register_driver(struct isa_driver *isa_driver, unsigned int ndev)
+{
+ int error;
+ unsigned int id;
+
+ isa_driver->driver.bus = &isa_bus_type;
+ isa_driver->devices = NULL;
+
+ error = driver_register(&isa_driver->driver);
+ if (error)
+ return error;
+
+ for (id = 0; id < ndev; id++) {
+ struct isa_dev *isa_dev;
+
+ isa_dev = kzalloc(sizeof *isa_dev, GFP_KERNEL);
+ if (!isa_dev) {
+ error = -ENOMEM;
+ break;
+ }
+
+ isa_dev->dev.parent = &isa_bus;
+ isa_dev->dev.bus = &isa_bus_type;
+
+ snprintf(isa_dev->dev.bus_id, BUS_ID_SIZE, "%s.%u",
+ isa_driver->driver.name, id);
+
+ isa_dev->dev.platform_data = isa_driver;
+ isa_dev->dev.release = isa_dev_release;
+ isa_dev->id = id;
+
+ error = device_register(&isa_dev->dev);
+ if (error) {
+ put_device(&isa_dev->dev);
+ break;
+ }
+
+ if (isa_dev->dev.platform_data) {
+ isa_dev->next = isa_driver->devices;
+ isa_driver->devices = &isa_dev->dev;
+ } else
+ device_unregister(&isa_dev->dev);
+ }
+
+ if (!error && !isa_driver->devices)
+ error = -ENODEV;
+
+ if (error)
+ isa_unregister_driver(isa_driver);
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(isa_register_driver);
+
+static int __init isa_bus_init(void)
+{
+ int error;
+
+ error = bus_register(&isa_bus_type);
+ if (!error) {
+ error = device_register(&isa_bus);
+ if (error)
+ bus_unregister(&isa_bus_type);
+ }
+ return error;
+}
+
+device_initcall(isa_bus_init);
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 83f5c5984d1..2b8755db76c 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -275,7 +275,7 @@ int platform_device_add(struct platform_device *pdev)
pr_debug("Registering platform device '%s'. Parent at %s\n",
pdev->dev.bus_id, pdev->dev.parent->bus_id);
- ret = device_register(&pdev->dev);
+ ret = device_add(&pdev->dev);
if (ret == 0)
return ret;
@@ -452,6 +452,37 @@ void platform_driver_unregister(struct platform_driver *drv)
EXPORT_SYMBOL_GPL(platform_driver_unregister);
+/* modalias support enables more hands-off userspace setup:
+ * (a) environment variable lets new-style hotplug events work once system is
+ * fully running: "modprobe $MODALIAS"
+ * (b) sysfs attribute lets new-style coldplug recover from hotplug events
+ * mishandled before system is fully running: "modprobe $(cat modalias)"
+ */
+static ssize_t
+modalias_show(struct device *dev, struct device_attribute *a, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int len = snprintf(buf, PAGE_SIZE, "%s\n", pdev->name);
+
+ return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+}
+
+static struct device_attribute platform_dev_attrs[] = {
+ __ATTR_RO(modalias),
+ __ATTR_NULL,
+};
+
+static int platform_uevent(struct device *dev, char **envp, int num_envp,
+ char *buffer, int buffer_size)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ envp[0] = buffer;
+ snprintf(buffer, buffer_size, "MODALIAS=%s", pdev->name);
+ return 0;
+}
+
+
/**
* platform_match - bind platform device to platform driver.
* @dev: device.
@@ -496,7 +527,9 @@ static int platform_resume(struct device * dev)
struct bus_type platform_bus_type = {
.name = "platform",
+ .dev_attrs = platform_dev_attrs,
.match = platform_match,
+ .uevent = platform_uevent,
.suspend = platform_suspend,
.resume = platform_resume,
};
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
index c0219ad94ac..91f230939c1 100644
--- a/drivers/base/power/Makefile
+++ b/drivers/base/power/Makefile
@@ -1,6 +1,10 @@
obj-y := shutdown.o
obj-$(CONFIG_PM) += main.o suspend.o resume.o runtime.o sysfs.o
+obj-$(CONFIG_PM_TRACE) += trace.o
ifeq ($(CONFIG_DEBUG_DRIVER),y)
EXTRA_CFLAGS += -DDEBUG
endif
+ifeq ($(CONFIG_PM_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c
index 317edbf0fec..520679ce53a 100644
--- a/drivers/base/power/resume.c
+++ b/drivers/base/power/resume.c
@@ -9,6 +9,7 @@
*/
#include <linux/device.h>
+#include <linux/resume-trace.h>
#include "../base.h"
#include "power.h"
@@ -23,6 +24,8 @@ int resume_device(struct device * dev)
{
int error = 0;
+ TRACE_DEVICE(dev);
+ TRACE_RESUME(0);
down(&dev->sem);
if (dev->power.pm_parent
&& dev->power.pm_parent->power.power_state.event) {
@@ -36,6 +39,7 @@ int resume_device(struct device * dev)
error = dev->bus->resume(dev);
}
up(&dev->sem);
+ TRACE_RESUME(error);
return error;
}
diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c
index 2a769cc6f5f..1a1fe43a305 100644
--- a/drivers/base/power/suspend.c
+++ b/drivers/base/power/suspend.c
@@ -29,6 +29,15 @@
* lists. This way, the ancestors will be accessed before their descendents.
*/
+static inline char *suspend_verb(u32 event)
+{
+ switch (event) {
+ case PM_EVENT_SUSPEND: return "suspend";
+ case PM_EVENT_FREEZE: return "freeze";
+ default: return "(unknown suspend event)";
+ }
+}
+
/**
* suspend_device - Save state of one device.
@@ -57,7 +66,13 @@ int suspend_device(struct device * dev, pm_message_t state)
dev->power.prev_state = dev->power.power_state;
if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
- dev_dbg(dev, "suspending\n");
+ dev_dbg(dev, "%s%s\n",
+ suspend_verb(state.event),
+ ((state.event == PM_EVENT_SUSPEND)
+ && device_may_wakeup(dev))
+ ? ", may wakeup"
+ : ""
+ );
error = dev->bus->suspend(dev, state);
suspend_report_result(dev->bus->suspend, error);
}
diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c
new file mode 100644
index 00000000000..a9ab30fefff
--- /dev/null
+++ b/drivers/base/power/trace.c
@@ -0,0 +1,228 @@
+/*
+ * drivers/base/power/trace.c
+ *
+ * Copyright (C) 2006 Linus Torvalds
+ *
+ * Trace facility for suspend/resume problems, when none of the
+ * devices may be working.
+ */
+
+#include <linux/resume-trace.h>
+#include <linux/rtc.h>
+
+#include <asm/rtc.h>
+
+#include "power.h"
+
+/*
+ * Horrid, horrid, horrid.
+ *
+ * It turns out that the _only_ piece of hardware that actually
+ * keeps its value across a hard boot (and, more importantly, the
+ * POST init sequence) is literally the realtime clock.
+ *
+ * Never mind that an RTC chip has 114 bytes (and often a whole
+ * other bank of an additional 128 bytes) of nice SRAM that is
+ * _designed_ to keep data - the POST will clear it. So we literally
+ * can just use the few bytes of actual time data, which means that
+ * we're really limited.
+ *
+ * It means, for example, that we can't use the seconds at all
+ * (since the time between the hang and the boot might be more
+ * than a minute), and we'd better not depend on the low bits of
+ * the minutes either.
+ *
+ * There are the wday fields etc, but I wouldn't guarantee those
+ * are dependable either. And if the date isn't valid, either the
+ * hw or POST will do strange things.
+ *
+ * So we're left with:
+ * - year: 0-99
+ * - month: 0-11
+ * - day-of-month: 1-28
+ * - hour: 0-23
+ * - min: (0-30)*2
+ *
+ * Giving us a total range of 0-16128000 (0xf61800), ie less
+ * than 24 bits of actual data we can save across reboots.
+ *
+ * And if your box can't boot in less than three minutes,
+ * you're screwed.
+ *
+ * Now, almost 24 bits of data is pitifully small, so we need
+ * to be pretty dense if we want to use it for anything nice.
+ * What we do is that instead of saving off nice readable info,
+ * we save off _hashes_ of information that we can hopefully
+ * regenerate after the reboot.
+ *
+ * In particular, this means that we might be unlucky, and hit
+ * a case where we have a hash collision, and we end up not
+ * being able to tell for certain exactly which case happened.
+ * But that's hopefully unlikely.
+ *
+ * What we do is to take the bits we can fit, and split them
+ * into three parts (16*997*1009 = 16095568), and use the values
+ * for:
+ * - 0-15: user-settable
+ * - 0-996: file + line number
+ * - 0-1008: device
+ */
+#define USERHASH (16)
+#define FILEHASH (997)
+#define DEVHASH (1009)
+
+#define DEVSEED (7919)
+
+static unsigned int dev_hash_value;
+
+static int set_magic_time(unsigned int user, unsigned int file, unsigned int device)
+{
+ unsigned int n = user + USERHASH*(file + FILEHASH*device);
+
+ // June 7th, 2006
+ static struct rtc_time time = {
+ .tm_sec = 0,
+ .tm_min = 0,
+ .tm_hour = 0,
+ .tm_mday = 7,
+ .tm_mon = 5, // June - counting from zero
+ .tm_year = 106,
+ .tm_wday = 3,
+ .tm_yday = 160,
+ .tm_isdst = 1
+ };
+
+ time.tm_year = (n % 100);
+ n /= 100;
+ time.tm_mon = (n % 12);
+ n /= 12;
+ time.tm_mday = (n % 28) + 1;
+ n /= 28;
+ time.tm_hour = (n % 24);
+ n /= 24;
+ time.tm_min = (n % 20) * 3;
+ n /= 20;
+ set_rtc_time(&time);
+ return n ? -1 : 0;
+}
+
+static unsigned int read_magic_time(void)
+{
+ struct rtc_time time;
+ unsigned int val;
+
+ get_rtc_time(&time);
+ printk("Time: %2d:%02d:%02d Date: %02d/%02d/%02d\n",
+ time.tm_hour, time.tm_min, time.tm_sec,
+ time.tm_mon, time.tm_mday, time.tm_year);
+ val = time.tm_year; /* 100 years */
+ if (val > 100)
+ val -= 100;
+ val += time.tm_mon * 100; /* 12 months */
+ val += (time.tm_mday-1) * 100 * 12; /* 28 month-days */
+ val += time.tm_hour * 100 * 12 * 28; /* 24 hours */
+ val += (time.tm_min / 3) * 100 * 12 * 28 * 24; /* 20 3-minute intervals */
+ return val;
+}
+
+/*
+ * This is just the sdbm hash function with a user-supplied
+ * seed and final size parameter.
+ */
+static unsigned int hash_string(unsigned int seed, const char *data, unsigned int mod)
+{
+ unsigned char c;
+ while ((c = *data++) != 0) {
+ seed = (seed << 16) + (seed << 6) - seed + c;
+ }
+ return seed % mod;
+}
+
+void set_trace_device(struct device *dev)
+{
+ dev_hash_value = hash_string(DEVSEED, dev->bus_id, DEVHASH);
+}
+
+/*
+ * We could just take the "tracedata" index into the .tracedata
+ * section instead. Generating a hash of the data gives us a
+ * chance to work across kernel versions, and perhaps more
+ * importantly it also gives us valid/invalid check (ie we will
+ * likely not give totally bogus reports - if the hash matches,
+ * it's not any guarantee, but it's a high _likelihood_ that
+ * the match is valid).
+ */
+void generate_resume_trace(void *tracedata, unsigned int user)
+{
+ unsigned short lineno = *(unsigned short *)tracedata;
+ const char *file = *(const char **)(tracedata + 2);
+ unsigned int user_hash_value, file_hash_value;
+
+ user_hash_value = user % USERHASH;
+ file_hash_value = hash_string(lineno, file, FILEHASH);
+ set_magic_time(user_hash_value, file_hash_value, dev_hash_value);
+}
+
+extern char __tracedata_start, __tracedata_end;
+static int show_file_hash(unsigned int value)
+{
+ int match;
+ char *tracedata;
+
+ match = 0;
+ for (tracedata = &__tracedata_start ; tracedata < &__tracedata_end ; tracedata += 6) {
+ unsigned short lineno = *(unsigned short *)tracedata;
+ const char *file = *(const char **)(tracedata + 2);
+ unsigned int hash = hash_string(lineno, file, FILEHASH);
+ if (hash != value)
+ continue;
+ printk(" hash matches %s:%u\n", file, lineno);
+ match++;
+ }
+ return match;
+}
+
+static int show_dev_hash(unsigned int value)
+{
+ int match = 0;
+ struct list_head * entry = dpm_active.prev;
+
+ while (entry != &dpm_active) {
+ struct device * dev = to_device(entry);
+ unsigned int hash = hash_string(DEVSEED, dev->bus_id, DEVHASH);
+ if (hash == value) {
+ printk(" hash matches device %s\n", dev->bus_id);
+ match++;
+ }
+ entry = entry->prev;
+ }
+ return match;
+}
+
+static unsigned int hash_value_early_read;
+
+static int early_resume_init(void)
+{
+ hash_value_early_read = read_magic_time();
+ return 0;
+}
+
+static int late_resume_init(void)
+{
+ unsigned int val = hash_value_early_read;
+ unsigned int user, file, dev;
+
+ user = val % USERHASH;
+ val = val / USERHASH;
+ file = val % FILEHASH;
+ val = val / FILEHASH;
+ dev = val /* % DEVHASH */;
+
+ printk(" Magic number: %d:%d:%d\n", user, file, dev);
+ show_file_hash(file);
+ show_dev_hash(dev);
+ return 0;
+}
+
+core_initcall(early_resume_init);
+late_initcall(late_resume_init);
diff --git a/drivers/base/sys.c b/drivers/base/sys.c
index 6fc23ab127b..6858178b3af 100644
--- a/drivers/base/sys.c
+++ b/drivers/base/sys.c
@@ -80,10 +80,59 @@ void sysdev_remove_file(struct sys_device * s, struct sysdev_attribute * a)
EXPORT_SYMBOL_GPL(sysdev_create_file);
EXPORT_SYMBOL_GPL(sysdev_remove_file);
+#define to_sysdev_class(k) container_of(k, struct sysdev_class, kset.kobj)
+#define to_sysdev_class_attr(a) container_of(a, \
+ struct sysdev_class_attribute, attr)
+
+static ssize_t sysdev_class_show(struct kobject *kobj, struct attribute *attr,
+ char *buffer)
+{
+ struct sysdev_class * class = to_sysdev_class(kobj);
+ struct sysdev_class_attribute *class_attr = to_sysdev_class_attr(attr);
+
+ if (class_attr->show)
+ return class_attr->show(class, buffer);
+ return -EIO;
+}
+
+static ssize_t sysdev_class_store(struct kobject *kobj, struct attribute *attr,
+ const char *buffer, size_t count)
+{
+ struct sysdev_class * class = to_sysdev_class(kobj);
+ struct sysdev_class_attribute * class_attr = to_sysdev_class_attr(attr);
+
+ if (class_attr->store)
+ return class_attr->store(class, buffer, count);
+ return -EIO;
+}
+
+static struct sysfs_ops sysfs_class_ops = {
+ .show = sysdev_class_show,
+ .store = sysdev_class_store,
+};
+
+static struct kobj_type ktype_sysdev_class = {
+ .sysfs_ops = &sysfs_class_ops,
+};
+
+int sysdev_class_create_file(struct sysdev_class *c,
+ struct sysdev_class_attribute *a)
+{
+ return sysfs_create_file(&c->kset.kobj, &a->attr);
+}
+EXPORT_SYMBOL_GPL(sysdev_class_create_file);
+
+void sysdev_class_remove_file(struct sysdev_class *c,
+ struct sysdev_class_attribute *a)
+{
+ sysfs_remove_file(&c->kset.kobj, &a->attr);
+}
+EXPORT_SYMBOL_GPL(sysdev_class_remove_file);
+
/*
* declare system_subsys
*/
-static decl_subsys(system, &ktype_sysdev, NULL);
+static decl_subsys(system, &ktype_sysdev_class, NULL);
int sysdev_class_register(struct sysdev_class * cls)
{