diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/attribute_container.c | 2 | ||||
-rw-r--r-- | drivers/base/base.h | 12 | ||||
-rw-r--r-- | drivers/base/class.c | 150 | ||||
-rw-r--r-- | drivers/base/core.c | 21 | ||||
-rw-r--r-- | drivers/base/cpu.c | 1 | ||||
-rw-r--r-- | drivers/base/driver.c | 3 | ||||
-rw-r--r-- | drivers/base/firmware.c | 3 | ||||
-rw-r--r-- | drivers/base/init.c | 10 | ||||
-rw-r--r-- | drivers/base/platform.c | 22 | ||||
-rw-r--r-- | drivers/base/power/sysfs.c | 73 |
10 files changed, 222 insertions, 75 deletions
diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c index 6b2eb6f39b4d..2a7d7ae83e1e 100644 --- a/drivers/base/attribute_container.c +++ b/drivers/base/attribute_container.c @@ -19,6 +19,8 @@ #include <linux/list.h> #include <linux/module.h> +#include "base.h" + /* This is a private structure used to tie the classdev and the * container .. it should never be visible outside this file */ struct internal_container { diff --git a/drivers/base/base.h b/drivers/base/base.h index 783752b68a9a..e3b548d46cff 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -1,3 +1,15 @@ + +/* initialisation functions */ + +extern int devices_init(void); +extern int buses_init(void); +extern int classes_init(void); +extern int firmware_init(void); +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_remove_device(struct device * dev); diff --git a/drivers/base/class.c b/drivers/base/class.c index ce23dc8c18c5..c3e569730afe 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -99,7 +99,8 @@ struct class * class_get(struct class * cls) void class_put(struct class * cls) { - subsys_put(&cls->subsys); + if (cls) + subsys_put(&cls->subsys); } @@ -165,14 +166,25 @@ void class_unregister(struct class * cls) static void class_create_release(struct class *cls) { + pr_debug("%s called for %s\n", __FUNCTION__, cls->name); kfree(cls); } static void class_device_create_release(struct class_device *class_dev) { + pr_debug("%s called for %s\n", __FUNCTION__, class_dev->class_id); kfree(class_dev); } +/* needed to allow these devices to have parent class devices */ +static int class_device_create_hotplug(struct class_device *class_dev, + char **envp, int num_envp, + char *buffer, int buffer_size) +{ + pr_debug("%s called for %s\n", __FUNCTION__, class_dev->class_id); + return 0; +} + /** * class_create - create a struct class structure * @owner: pointer to the module that is to "own" this struct class @@ -301,10 +313,12 @@ static void class_dev_release(struct kobject * kobj) kfree(cd->devt_attr); cd->devt_attr = NULL; - if (cls->release) + if (cd->release) + cd->release(cd); + else if (cls->release) cls->release(cd); else { - printk(KERN_ERR "Device class '%s' does not have a release() function, " + printk(KERN_ERR "Class Device '%s' does not have a release() function, " "it is broken and must be fixed.\n", cd->class_id); WARN_ON(1); @@ -382,14 +396,18 @@ static int class_hotplug(struct kset *kset, struct kobject *kobj, char **envp, buffer = &buffer[length]; buffer_size -= length; - if (class_dev->class->hotplug) { - /* have the bus specific function add its stuff */ - retval = class_dev->class->hotplug (class_dev, envp, num_envp, - buffer, buffer_size); - if (retval) { - pr_debug ("%s - hotplug() returned %d\n", - __FUNCTION__, retval); - } + if (class_dev->hotplug) { + /* have the class device specific function add its stuff */ + retval = class_dev->hotplug(class_dev, envp, num_envp, + buffer, buffer_size); + if (retval) + pr_debug("class_dev->hotplug() returned %d\n", retval); + } else if (class_dev->class->hotplug) { + /* have the class specific function add its stuff */ + retval = class_dev->class->hotplug(class_dev, envp, num_envp, + buffer, buffer_size); + if (retval) + pr_debug("class->hotplug() returned %d\n", retval); } return retval; @@ -442,6 +460,13 @@ static ssize_t show_dev(struct class_device *class_dev, char *buf) return print_dev_t(buf, class_dev->devt); } +static ssize_t store_uevent(struct class_device *class_dev, + const char *buf, size_t count) +{ + kobject_hotplug(&class_dev->kobj, KOBJ_ADD); + return count; +} + void class_device_initialize(struct class_device *class_dev) { kobj_set_kset_s(class_dev, class_obj_subsys); @@ -469,34 +494,45 @@ static char *make_class_name(struct class_device *class_dev) int class_device_add(struct class_device *class_dev) { - struct class * parent = NULL; - struct class_interface * class_intf; + struct class *parent_class = NULL; + struct class_device *parent_class_dev = NULL; + struct class_interface *class_intf; char *class_name = NULL; - int error; + int error = -EINVAL; class_dev = class_device_get(class_dev); if (!class_dev) return -EINVAL; - if (!strlen(class_dev->class_id)) { - error = -EINVAL; + if (!strlen(class_dev->class_id)) goto register_done; - } - parent = class_get(class_dev->class); + parent_class = class_get(class_dev->class); + if (!parent_class) + goto register_done; + 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); - if (parent) - class_dev->kobj.parent = &parent->subsys.kset.kobj; + if (parent_class_dev) + class_dev->kobj.parent = &parent_class_dev->kobj; + else + class_dev->kobj.parent = &parent_class->subsys.kset.kobj; - if ((error = kobject_add(&class_dev->kobj))) + error = kobject_add(&class_dev->kobj); + if (error) goto register_done; /* add the needed attributes to this device */ + 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); + if (MAJOR(class_dev->devt)) { struct class_device_attribute *attr; attr = kzalloc(sizeof(*attr), GFP_KERNEL); @@ -505,12 +541,10 @@ int class_device_add(struct class_device *class_dev) kobject_del(&class_dev->kobj); goto register_done; } - attr->attr.name = "dev"; attr->attr.mode = S_IRUGO; - attr->attr.owner = parent->owner; + attr->attr.owner = parent_class->owner; attr->show = show_dev; - attr->store = NULL; class_device_create_file(class_dev, attr); class_dev->devt_attr = attr; } @@ -524,20 +558,23 @@ int class_device_add(struct class_device *class_dev) class_name); } + kobject_hotplug(&class_dev->kobj, KOBJ_ADD); + /* notify any interfaces this device is now here */ - if (parent) { - down(&parent->sem); - list_add_tail(&class_dev->node, &parent->children); - list_for_each_entry(class_intf, &parent->interfaces, node) + if (parent_class) { + down(&parent_class->sem); + list_add_tail(&class_dev->node, &parent_class->children); + list_for_each_entry(class_intf, &parent_class->interfaces, node) if (class_intf->add) - class_intf->add(class_dev); - up(&parent->sem); + class_intf->add(class_dev, class_intf); + up(&parent_class->sem); } - kobject_hotplug(&class_dev->kobj, KOBJ_ADD); register_done: - if (error && parent) - class_put(parent); + if (error) { + class_put(parent_class); + class_device_put(parent_class_dev); + } class_device_put(class_dev); kfree(class_name); return error; @@ -552,21 +589,28 @@ int class_device_register(struct class_device *class_dev) /** * class_device_create - creates a class 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 class_device of this new device, if any. * @dev: the dev_t for the char device to be added. * @device: a pointer to a struct device that is assiociated with this class device. * @fmt: string for the class device's name * * This function can be used by char device classes. A struct * class_device will be created in sysfs, registered to the specified - * class. A "dev" file will be created, showing the dev_t for the - * device. The pointer to the struct class_device will be returned from - * the call. Any further sysfs files that might be required can be - * created using this pointer. + * 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 class_device is passed in, the newly + * created struct class_device will be a child of that device in sysfs. + * The pointer to the struct class_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 class_device *class_device_create(struct class *cls, dev_t devt, +struct class_device *class_device_create(struct class *cls, + struct class_device *parent, + dev_t devt, struct device *device, char *fmt, ...) { va_list args; @@ -585,6 +629,9 @@ struct class_device *class_device_create(struct class *cls, dev_t devt, class_dev->devt = devt; class_dev->dev = device; class_dev->class = cls; + class_dev->parent = parent; + class_dev->release = class_device_create_release; + class_dev->hotplug = class_device_create_hotplug; va_start(args, fmt); vsnprintf(class_dev->class_id, BUS_ID_SIZE, fmt, args); @@ -602,17 +649,18 @@ error: void class_device_del(struct class_device *class_dev) { - struct class * parent = class_dev->class; - struct class_interface * class_intf; + struct class *parent_class = class_dev->class; + struct class_device *parent_device = class_dev->parent; + struct class_interface *class_intf; char *class_name = NULL; - if (parent) { - down(&parent->sem); + if (parent_class) { + down(&parent_class->sem); list_del_init(&class_dev->node); - list_for_each_entry(class_intf, &parent->interfaces, node) + list_for_each_entry(class_intf, &parent_class->interfaces, node) if (class_intf->remove) - class_intf->remove(class_dev); - up(&parent->sem); + class_intf->remove(class_dev, class_intf); + up(&parent_class->sem); } if (class_dev->dev) { @@ -620,6 +668,7 @@ void class_device_del(struct class_device *class_dev) sysfs_remove_link(&class_dev->kobj, "device"); sysfs_remove_link(&class_dev->dev->kobj, class_name); } + 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); class_device_remove_attrs(class_dev); @@ -627,8 +676,8 @@ void class_device_del(struct class_device *class_dev) kobject_hotplug(&class_dev->kobj, KOBJ_REMOVE); kobject_del(&class_dev->kobj); - if (parent) - class_put(parent); + class_device_put(parent_device); + class_put(parent_class); kfree(class_name); } @@ -708,7 +757,8 @@ struct class_device * class_device_get(struct class_device *class_dev) void class_device_put(struct class_device *class_dev) { - kobject_put(&class_dev->kobj); + if (class_dev) + kobject_put(&class_dev->kobj); } @@ -728,7 +778,7 @@ int class_interface_register(struct class_interface *class_intf) list_add_tail(&class_intf->node, &parent->interfaces); if (class_intf->add) { list_for_each_entry(class_dev, &parent->children, node) - class_intf->add(class_dev); + class_intf->add(class_dev, class_intf); } up(&parent->sem); @@ -747,7 +797,7 @@ void class_interface_unregister(struct class_interface *class_intf) list_del_init(&class_intf->node); if (class_intf->remove) { list_for_each_entry(class_dev, &parent->children, node) - class_intf->remove(class_dev); + class_intf->remove(class_dev, class_intf); } up(&parent->sem); diff --git a/drivers/base/core.c b/drivers/base/core.c index 6ab73f5c799a..8615b42b517a 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -154,6 +154,13 @@ static struct kset_hotplug_ops device_hotplug_ops = { .hotplug = dev_hotplug, }; +static ssize_t store_uevent(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + kobject_hotplug(&dev->kobj, KOBJ_ADD); + return count; +} + /** * device_subsys - structure to be registered with kobject core. */ @@ -225,6 +232,7 @@ void device_initialize(struct device *dev) klist_children_put); INIT_LIST_HEAD(&dev->dma_pools); init_MUTEX(&dev->sem); + device_init_wakeup(dev, 0); } /** @@ -258,6 +266,14 @@ int device_add(struct device *dev) if ((error = kobject_add(&dev->kobj))) goto Error; + + dev->uevent_attr.attr.name = "uevent"; + dev->uevent_attr.attr.mode = S_IWUSR; + if (dev->driver) + dev->uevent_attr.attr.owner = dev->driver->owner; + dev->uevent_attr.store = store_uevent; + device_create_file(dev, &dev->uevent_attr); + kobject_hotplug(&dev->kobj, KOBJ_ADD); if ((error = device_pm_add(dev))) goto PMError; @@ -349,6 +365,7 @@ void device_del(struct device * dev) if (parent) klist_del(&dev->knode_parent); + device_remove_file(dev, &dev->uevent_attr); /* Notify the platform of the removal, in case they * need to do anything... @@ -390,11 +407,11 @@ static struct device * next_device(struct klist_iter * i) /** * device_for_each_child - device child iterator. - * @dev: parent struct device. + * @parent: parent struct device. * @data: data for the callback. * @fn: function to be called for each device. * - * Iterate over @dev's child devices, and call @fn for each, + * Iterate over @parent's child devices, and call @fn for each, * passing it @data. * * We check the return of @fn each time. If it returns anything diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index b79badd0f158..081c927b1ed8 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -9,6 +9,7 @@ #include <linux/topology.h> #include <linux/device.h> +#include "base.h" struct sysdev_class cpu_sysdev_class = { set_kset_name("cpu"), diff --git a/drivers/base/driver.c b/drivers/base/driver.c index ef3fe513e398..161f3a390d90 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -28,6 +28,7 @@ static struct device * next_device(struct klist_iter * i) /** * driver_for_each_device - Iterator for devices bound to a driver. * @drv: Driver we're iterating. + * @start: Device to begin with * @data: Data to pass to the callback. * @fn: Function to call for each device. * @@ -57,7 +58,7 @@ EXPORT_SYMBOL_GPL(driver_for_each_device); /** * driver_find_device - device iterator for locating a particular device. - * @driver: The device's driver + * @drv: The device's driver * @start: Device to begin with * @data: Data to pass to match function * @match: Callback function to check device diff --git a/drivers/base/firmware.c b/drivers/base/firmware.c index 88ab044932f2..cb1b98ae0d58 100644 --- a/drivers/base/firmware.c +++ b/drivers/base/firmware.c @@ -11,6 +11,9 @@ #include <linux/kobject.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/device.h> + +#include "base.h" static decl_subsys(firmware, NULL, NULL); diff --git a/drivers/base/init.c b/drivers/base/init.c index a76ae5a221f3..84e604e25c4f 100644 --- a/drivers/base/init.c +++ b/drivers/base/init.c @@ -10,14 +10,8 @@ #include <linux/device.h> #include <linux/init.h> -extern int devices_init(void); -extern int buses_init(void); -extern int classes_init(void); -extern int firmware_init(void); -extern int platform_bus_init(void); -extern int system_bus_init(void); -extern int cpu_dev_init(void); -extern int attribute_container_init(void); +#include "base.h" + /** * driver_init - initialize driver model. * diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 361e204209eb..75ce8711bca5 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -17,6 +17,8 @@ #include <linux/bootmem.h> #include <linux/err.h> +#include "base.h" + struct device platform_bus = { .bus_id = "platform", }; @@ -279,13 +281,9 @@ static int platform_suspend(struct device * dev, pm_message_t state) { int ret = 0; - if (dev->driver && dev->driver->suspend) { - ret = dev->driver->suspend(dev, state, SUSPEND_DISABLE); - if (ret == 0) - ret = dev->driver->suspend(dev, state, SUSPEND_SAVE_STATE); - if (ret == 0) - ret = dev->driver->suspend(dev, state, SUSPEND_POWER_DOWN); - } + if (dev->driver && dev->driver->suspend) + ret = dev->driver->suspend(dev, state); + return ret; } @@ -293,13 +291,9 @@ static int platform_resume(struct device * dev) { int ret = 0; - if (dev->driver && dev->driver->resume) { - ret = dev->driver->resume(dev, RESUME_POWER_ON); - if (ret == 0) - ret = dev->driver->resume(dev, RESUME_RESTORE_STATE); - if (ret == 0) - ret = dev->driver->resume(dev, RESUME_ENABLE); - } + if (dev->driver && dev->driver->resume) + ret = dev->driver->resume(dev); + return ret; } diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 8d04fb435c17..89c57875f3e5 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -48,8 +48,81 @@ static ssize_t state_store(struct device * dev, struct device_attribute *attr, c static DEVICE_ATTR(state, 0644, state_show, state_store); +/* + * wakeup - Report/change current wakeup option for device + * + * Some devices support "wakeup" events, which are hardware signals + * used to activate devices from suspended or low power states. Such + * devices have one of three values for the sysfs power/wakeup file: + * + * + "enabled\n" to issue the events; + * + "disabled\n" not to do so; or + * + "\n" for temporary or permanent inability to issue wakeup. + * + * (For example, unconfigured USB devices can't issue wakeups.) + * + * Familiar examples of devices that can issue wakeup events include + * keyboards and mice (both PS2 and USB styles), power buttons, modems, + * "Wake-On-LAN" Ethernet links, GPIO lines, and more. Some events + * will wake the entire system from a suspend state; others may just + * wake up the device (if the system as a whole is already active). + * Some wakeup events use normal IRQ lines; other use special out + * of band signaling. + * + * It is the responsibility of device drivers to enable (or disable) + * wakeup signaling as part of changing device power states, respecting + * the policy choices provided through the driver model. + * + * Devices may not be able to generate wakeup events from all power + * states. Also, the events may be ignored in some configurations; + * for example, they might need help from other devices that aren't + * active, or which may have wakeup disabled. Some drivers rely on + * wakeup events internally (unless they are disabled), keeping + * their hardware in low power modes whenever they're unused. This + * saves runtime power, without requiring system-wide sleep states. + */ + +static const char enabled[] = "enabled"; +static const char disabled[] = "disabled"; + +static ssize_t +wake_show(struct device * dev, struct device_attribute *attr, char * buf) +{ + return sprintf(buf, "%s\n", device_can_wakeup(dev) + ? (device_may_wakeup(dev) ? enabled : disabled) + : ""); +} + +static ssize_t +wake_store(struct device * dev, struct device_attribute *attr, + const char * buf, size_t n) +{ + char *cp; + int len = n; + + if (!device_can_wakeup(dev)) + return -EINVAL; + + cp = memchr(buf, '\n', n); + if (cp) + len = cp - buf; + if (len == sizeof enabled - 1 + && strncmp(buf, enabled, sizeof enabled - 1) == 0) + device_set_wakeup_enable(dev, 1); + else if (len == sizeof disabled - 1 + && strncmp(buf, disabled, sizeof disabled - 1) == 0) + device_set_wakeup_enable(dev, 0); + else + return -EINVAL; + return n; +} + +static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); + + static struct attribute * power_attrs[] = { &dev_attr_state.attr, + &dev_attr_wakeup.attr, NULL, }; static struct attribute_group pm_attr_group = { |