diff --git a/Documentation/filesystems/sysfs.txt b/Documentation/filesystems/sysfs.txt index dc276598a65a..c8bce82ddcac 100644 --- a/Documentation/filesystems/sysfs.txt +++ b/Documentation/filesystems/sysfs.txt @@ -90,7 +90,7 @@ void device_remove_file(struct device *, struct device_attribute *); It also defines this helper for defining device attributes: -#define DEVICE_ATTR(_name,_mode,_show,_store) \ +#define DEVICE_ATTR(_name, _mode, _show, _store) \ struct device_attribute dev_attr_##_name = { \ .attr = {.name = __stringify(_name) , .mode = _mode }, \ .show = _show, \ @@ -99,14 +99,14 @@ struct device_attribute dev_attr_##_name = { \ For example, declaring -static DEVICE_ATTR(foo,0644,show_foo,store_foo); +static DEVICE_ATTR(foo, S_IWUSR | S_IRUGO, show_foo, store_foo); is equivalent to doing: static struct device_attribute dev_attr_foo = { .attr = { .name = "foo", - .mode = 0644, + .mode = S_IWUSR | S_IRUGO, }, .show = show_foo, .store = store_foo, @@ -121,8 +121,8 @@ set of sysfs operations for forwarding read and write calls to the show and store methods of the attribute owners. struct sysfs_ops { - ssize_t (*show)(struct kobject *, struct attribute *,char *); - ssize_t (*store)(struct kobject *,struct attribute *,const char *); + ssize_t (*show)(struct kobject *, struct attribute *, char *); + ssize_t (*store)(struct kobject *, struct attribute *, const char *); }; [ Subsystems should have already defined a struct kobj_type as a @@ -137,7 +137,7 @@ calls the associated methods. To illustrate: -#define to_dev_attr(_attr) container_of(_attr,struct device_attribute,attr) +#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr) #define to_dev(d) container_of(d, struct device, kobj) static ssize_t @@ -148,7 +148,7 @@ dev_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) ssize_t ret = 0; if (dev_attr->show) - ret = dev_attr->show(dev,buf); + ret = dev_attr->show(dev, buf); return ret; } @@ -216,16 +216,16 @@ A very simple (and naive) implementation of a device attribute is: static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf,"%s\n",dev->name); + return snprintf(buf, PAGE_SIZE, "%s\n", dev->name); } static ssize_t store_name(struct device * dev, const char * buf) { - sscanf(buf,"%20s",dev->name); - return strlen(buf); + sscanf(buf, "%20s", dev->name); + return strnlen(buf, PAGE_SIZE); } -static DEVICE_ATTR(name,S_IRUGO,show_name,store_name); +static DEVICE_ATTR(name, S_IRUGO, show_name, store_name); (Note that the real implementation doesn't allow userspace to set the @@ -290,7 +290,7 @@ struct device_attribute { Declaring: -DEVICE_ATTR(_name,_str,_mode,_show,_store); +DEVICE_ATTR(_name, _str, _mode, _show, _store); Creation/Removal: @@ -310,7 +310,7 @@ struct bus_attribute { Declaring: -BUS_ATTR(_name,_mode,_show,_store) +BUS_ATTR(_name, _mode, _show, _store) Creation/Removal: @@ -331,7 +331,7 @@ struct driver_attribute { Declaring: -DRIVER_ATTR(_name,_mode,_show,_store) +DRIVER_ATTR(_name, _mode, _show, _store) Creation/Removal: diff --git a/drivers/base/bus.c b/drivers/base/bus.c index ab53832d57e5..17e96698410e 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -156,7 +156,9 @@ static ssize_t driver_unbind(struct device_driver *drv, device_release_driver(dev); err = count; } - return err; + if (err) + return err; + return count; } static DRIVER_ATTR(unbind, S_IWUSR, NULL, driver_unbind); @@ -358,7 +360,7 @@ 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(&bus->klist_devices, &dev->knode_bus); + 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); @@ -446,7 +448,7 @@ int bus_add_driver(struct device_driver * drv) } driver_attach(drv); - klist_add_tail(&bus->klist_drivers, &drv->knode_bus); + klist_add_tail(&drv->knode_bus, &bus->klist_drivers); module_add_driver(drv->owner, drv); driver_add_attrs(bus, drv); diff --git a/drivers/base/class.c b/drivers/base/class.c index 0154a1623b21..d164c32a97ad 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -299,10 +299,8 @@ static void class_dev_release(struct kobject * kobj) pr_debug("device class '%s': release.\n", cd->class_id); - if (cd->devt_attr) { - kfree(cd->devt_attr); - cd->devt_attr = NULL; - } + kfree(cd->devt_attr); + cd->devt_attr = NULL; if (cls->release) cls->release(cd); @@ -452,10 +450,29 @@ 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 *name; + int size; + + size = strlen(class_dev->class->name) + + strlen(kobject_name(&class_dev->kobj)) + 2; + + name = kmalloc(size, GFP_KERNEL); + if (!name) + return ERR_PTR(-ENOMEM); + + strcpy(name, class_dev->class->name); + strcat(name, ":"); + strcat(name, kobject_name(&class_dev->kobj)); + return name; +} + int class_device_add(struct class_device *class_dev) { struct class * parent = NULL; struct class_interface * class_intf; + char *class_name = NULL; int error; class_dev = class_device_get(class_dev); @@ -500,9 +517,13 @@ int class_device_add(struct class_device *class_dev) } class_device_add_attrs(class_dev); - if (class_dev->dev) + 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); + } /* notify any interfaces this device is now here */ if (parent) { @@ -519,6 +540,7 @@ int class_device_add(struct class_device *class_dev) if (error && parent) class_put(parent); class_device_put(class_dev); + kfree(class_name); return error; } @@ -584,6 +606,7 @@ void class_device_del(struct class_device *class_dev) { struct class * parent = class_dev->class; struct class_interface * class_intf; + char *class_name = NULL; if (parent) { down(&parent->sem); @@ -594,8 +617,11 @@ void class_device_del(struct class_device *class_dev) up(&parent->sem); } - if (class_dev->dev) + if (class_dev->dev) { + class_name = make_class_name(class_dev); sysfs_remove_link(&class_dev->kobj, "device"); + sysfs_remove_link(&class_dev->dev->kobj, class_name); + } if (class_dev->devt_attr) class_device_remove_file(class_dev, class_dev->devt_attr); class_device_remove_attrs(class_dev); @@ -605,6 +631,7 @@ void class_device_del(struct class_device *class_dev) if (parent) class_put(parent); + kfree(class_name); } void class_device_unregister(struct class_device *class_dev) diff --git a/drivers/base/core.c b/drivers/base/core.c index efe03a024a5b..c8a33df00761 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -249,7 +249,7 @@ int device_add(struct device *dev) if ((error = bus_add_device(dev))) goto BusError; if (parent) - klist_add_tail(&parent->klist_children, &dev->knode_parent); + klist_add_tail(&dev->knode_parent, &parent->klist_children); /* notify platform of device entry */ if (platform_notify) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 16323f9cbff0..d5bbce38282f 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -42,7 +42,7 @@ void device_bind_driver(struct device * dev) { pr_debug("bound device '%s' to driver '%s'\n", dev->bus_id, dev->driver->name); - klist_add_tail(&dev->driver->klist_devices, &dev->knode_driver); + klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices); sysfs_create_link(&dev->driver->kobj, &dev->kobj, kobject_name(&dev->kobj)); sysfs_create_link(&dev->kobj, &dev->driver->kobj, "driver"); diff --git a/drivers/base/sys.c b/drivers/base/sys.c index 214b96435409..3431eb6004c3 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -288,6 +288,27 @@ void sysdev_shutdown(void) up(&sysdev_drivers_lock); } +static void __sysdev_resume(struct sys_device *dev) +{ + struct sysdev_class *cls = dev->cls; + struct sysdev_driver *drv; + + /* First, call the class-specific one */ + if (cls->resume) + cls->resume(dev); + + /* Call auxillary drivers next. */ + list_for_each_entry(drv, &cls->drivers, entry) { + if (drv->resume) + drv->resume(dev); + } + + /* Call global drivers. */ + list_for_each_entry(drv, &sysdev_drivers, entry) { + if (drv->resume) + drv->resume(dev); + } +} /** * sysdev_suspend - Suspend all system devices. @@ -305,38 +326,93 @@ void sysdev_shutdown(void) int sysdev_suspend(pm_message_t state) { struct sysdev_class * cls; + struct sys_device *sysdev, *err_dev; + struct sysdev_driver *drv, *err_drv; + int ret; pr_debug("Suspending System Devices\n"); list_for_each_entry_reverse(cls, &system_subsys.kset.list, kset.kobj.entry) { - struct sys_device * sysdev; pr_debug("Suspending type '%s':\n", kobject_name(&cls->kset.kobj)); list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { - struct sysdev_driver * drv; pr_debug(" %s\n", kobject_name(&sysdev->kobj)); /* Call global drivers first. */ list_for_each_entry(drv, &sysdev_drivers, entry) { - if (drv->suspend) - drv->suspend(sysdev, state); + if (drv->suspend) { + ret = drv->suspend(sysdev, state); + if (ret) + goto gbl_driver; + } } /* Call auxillary drivers next. */ list_for_each_entry(drv, &cls->drivers, entry) { - if (drv->suspend) - drv->suspend(sysdev, state); + if (drv->suspend) { + ret = drv->suspend(sysdev, state); + if (ret) + goto aux_driver; + } } /* Now call the generic one */ - if (cls->suspend) - cls->suspend(sysdev, state); + if (cls->suspend) { + ret = cls->suspend(sysdev, state); + if (ret) + goto cls_driver; + } } } return 0; + /* resume current sysdev */ +cls_driver: + drv = NULL; + printk(KERN_ERR "Class suspend failed for %s\n", + kobject_name(&sysdev->kobj)); + +aux_driver: + if (drv) + printk(KERN_ERR "Class driver suspend failed for %s\n", + kobject_name(&sysdev->kobj)); + list_for_each_entry(err_drv, &cls->drivers, entry) { + if (err_drv == drv) + break; + if (err_drv->resume) + err_drv->resume(sysdev); + } + drv = NULL; + +gbl_driver: + if (drv) + printk(KERN_ERR "sysdev driver suspend failed for %s\n", + kobject_name(&sysdev->kobj)); + list_for_each_entry(err_drv, &sysdev_drivers, entry) { + if (err_drv == drv) + break; + if (err_drv->resume) + err_drv->resume(sysdev); + } + /* resume other sysdevs in current class */ + list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) { + if (err_dev == sysdev) + break; + pr_debug(" %s\n", kobject_name(&err_dev->kobj)); + __sysdev_resume(err_dev); + } + + /* resume other classes */ + list_for_each_entry_continue(cls, &system_subsys.kset.list, + kset.kobj.entry) { + list_for_each_entry(err_dev, &cls->kset.list, kobj.entry) { + pr_debug(" %s\n", kobject_name(&err_dev->kobj)); + __sysdev_resume(err_dev); + } + } + return ret; } @@ -362,25 +438,9 @@ int sysdev_resume(void) kobject_name(&cls->kset.kobj)); list_for_each_entry(sysdev, &cls->kset.list, kobj.entry) { - struct sysdev_driver * drv; pr_debug(" %s\n", kobject_name(&sysdev->kobj)); - /* First, call the class-specific one */ - if (cls->resume) - cls->resume(sysdev); - - /* Call auxillary drivers next. */ - list_for_each_entry(drv, &cls->drivers, entry) { - if (drv->resume) - drv->resume(sysdev); - } - - /* Call global drivers. */ - list_for_each_entry(drv, &sysdev_drivers, entry) { - if (drv->resume) - drv->resume(sysdev); - } - + __sysdev_resume(sysdev); } } return 0; diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index f0c1084b840f..888dad5eef34 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -493,6 +493,8 @@ static struct floppy_struct user_params[N_DRIVE]; static sector_t floppy_sizes[256]; +static char floppy_device_name[] = "floppy"; + /* * The driver is trying to determine the correct media format * while probing is set. rw_interrupt() clears it after a @@ -4191,18 +4193,24 @@ static int __init floppy_setup(char *str) static int have_no_fdc = -ENODEV; +static ssize_t floppy_cmos_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *p; + int drive; + + p = container_of(dev, struct platform_device,dev); + drive = p->id; + return sprintf(buf, "%X\n", UDP->cmos); +} +DEVICE_ATTR(cmos,S_IRUGO,floppy_cmos_show,NULL); + static void floppy_device_release(struct device *dev) { complete(&device_release); } -static struct platform_device floppy_device = { - .name = "floppy", - .id = 0, - .dev = { - .release = floppy_device_release, - } -}; +static struct platform_device floppy_device[N_DRIVE]; static struct kobject *floppy_find(dev_t dev, int *part, void *data) { @@ -4370,20 +4378,26 @@ static int __init floppy_init(void) goto out_flush_work; } - err = platform_device_register(&floppy_device); - if (err) - goto out_flush_work; - for (drive = 0; drive < N_DRIVE; drive++) { if (!(allowed_drive_mask & (1 << drive))) continue; if (fdc_state[FDC(drive)].version == FDC_NONE) continue; + + floppy_device[drive].name = floppy_device_name; + floppy_device[drive].id = drive; + floppy_device[drive].dev.release = floppy_device_release; + + err = platform_device_register(&floppy_device[drive]); + if (err) + goto out_flush_work; + + device_create_file(&floppy_device[drive].dev,&dev_attr_cmos); /* to be cleaned up... */ disks[drive]->private_data = (void *)(long)drive; disks[drive]->queue = floppy_queue; disks[drive]->flags |= GENHD_FL_REMOVABLE; - disks[drive]->driverfs_dev = &floppy_device.dev; + disks[drive]->driverfs_dev = &floppy_device[drive].dev; add_disk(disks[drive]); } @@ -4603,10 +4617,11 @@ void cleanup_module(void) fdc_state[FDC(drive)].version != FDC_NONE) { del_gendisk(disks[drive]); unregister_devfs_entries(drive); + device_remove_file(&floppy_device[drive].dev, &dev_attr_cmos); + platform_device_unregister(&floppy_device[drive]); } put_disk(disks[drive]); } - platform_device_unregister(&floppy_device); devfs_remove("floppy"); del_timer_sync(&fd_timeout); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 79422a3b07bc..9f44e83c6a69 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -782,7 +782,7 @@ static int usb_register_bus(struct usb_bus *bus) return -E2BIG; } - bus->class_dev = class_device_create(usb_host_class, MKDEV(0,0), bus->controller, "usb%d", busnum); + bus->class_dev = class_device_create(usb_host_class, MKDEV(0,0), bus->controller, "usb_host%d", busnum); if (IS_ERR(bus->class_dev)) { clear_bit(busnum, busmap.busmap); up(&usb_bus_list_lock); diff --git a/include/linux/klist.h b/include/linux/klist.h index eebf5e5696ec..c4d1fae4dd89 100644 --- a/include/linux/klist.h +++ b/include/linux/klist.h @@ -9,6 +9,9 @@ * This file is rleased under the GPL v2. */ +#ifndef _LINUX_KLIST_H +#define _LINUX_KLIST_H + #include #include #include @@ -31,8 +34,8 @@ struct klist_node { struct completion n_removed; }; -extern void klist_add_tail(struct klist * k, struct klist_node * n); -extern void klist_add_head(struct klist * k, struct klist_node * n); +extern void klist_add_tail(struct klist_node * n, struct klist * k); +extern void klist_add_head(struct klist_node * n, struct klist * k); extern void klist_del(struct klist_node * n); extern void klist_remove(struct klist_node * n); @@ -53,3 +56,4 @@ extern void klist_iter_init_node(struct klist * k, struct klist_iter * i, extern void klist_iter_exit(struct klist_iter * i); extern struct klist_node * klist_next(struct klist_iter * i); +#endif diff --git a/lib/klist.c b/lib/klist.c index 738ab810160a..a70c836c5c4c 100644 --- a/lib/klist.c +++ b/lib/klist.c @@ -79,11 +79,11 @@ static void klist_node_init(struct klist * k, struct klist_node * n) /** * klist_add_head - Initialize a klist_node and add it to front. - * @k: klist it's going on. * @n: node we're adding. + * @k: klist it's going on. */ -void klist_add_head(struct klist * k, struct klist_node * n) +void klist_add_head(struct klist_node * n, struct klist * k) { klist_node_init(k, n); add_head(k, n); @@ -94,11 +94,11 @@ EXPORT_SYMBOL_GPL(klist_add_head); /** * klist_add_tail - Initialize a klist_node and add it to back. - * @k: klist it's going on. * @n: node we're adding. + * @k: klist it's going on. */ -void klist_add_tail(struct klist * k, struct klist_node * n) +void klist_add_tail(struct klist_node * n, struct klist * k) { klist_node_init(k, n); add_tail(k, n);