diff options
| author | Greg Kroah-Hartman <greg@kroah.com> | 2004-05-14 01:44:08 -0700 |
|---|---|---|
| committer | Greg Kroah-Hartman <greg@kroah.com> | 2004-05-14 01:44:08 -0700 |
| commit | 3004c70f9036bbb74ea2842995edc8a21de9106e (patch) | |
| tree | be89c6ef4881d7749556bc63fb2fdc82d09ecadd | |
| parent | 141baf80514374935d084c12f2f1e426e9cea5a0 (diff) | |
| parent | 51c0c34c8f0ac464c97aaef4754e7dc163dd586e (diff) | |
Merge kroah.com:/home/greg/linux/BK/bleed-2.6
into kroah.com:/home/greg/linux/BK/driver-2.6
31 files changed, 861 insertions, 80 deletions
diff --git a/drivers/base/bus.c b/drivers/base/bus.c index ebb0ec31fa41..b40af6a72afe 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -451,7 +451,11 @@ int bus_add_driver(struct device_driver * drv) if (bus) { pr_debug("bus %s: add driver %s\n",bus->name,drv->name); - kobject_set_name(&drv->kobj,drv->name); + error = kobject_set_name(&drv->kobj,drv->name); + if (error) { + put_bus(bus); + return error; + } drv->kobj.kset = &bus->drivers; if ((error = kobject_register(&drv->kobj))) { put_bus(bus); @@ -555,21 +559,39 @@ struct bus_type * find_bus(char * name) */ int bus_register(struct bus_type * bus) { - kobject_set_name(&bus->subsys.kset.kobj,bus->name); + int retval; + + retval = kobject_set_name(&bus->subsys.kset.kobj,bus->name); + if (retval) + goto out; + subsys_set_kset(bus,bus_subsys); - subsystem_register(&bus->subsys); + retval = subsystem_register(&bus->subsys); + if (retval) + goto out; kobject_set_name(&bus->devices.kobj, "devices"); bus->devices.subsys = &bus->subsys; - kset_register(&bus->devices); + retval = kset_register(&bus->devices); + if (retval) + goto bus_devices_fail; kobject_set_name(&bus->drivers.kobj, "drivers"); bus->drivers.subsys = &bus->subsys; bus->drivers.ktype = &ktype_driver; - kset_register(&bus->drivers); + retval = kset_register(&bus->drivers); + if (retval) + goto bus_drivers_fail; pr_debug("bus type '%s' registered\n",bus->name); return 0; + +bus_drivers_fail: + kset_unregister(&bus->devices); +bus_devices_fail: + subsystem_unregister(&bus->subsys); +out: + return retval; } diff --git a/drivers/base/class.c b/drivers/base/class.c index b9fdce488e2c..ed13d9de0d1d 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -361,6 +361,8 @@ void class_device_unregister(struct class_device *class_dev) int class_device_rename(struct class_device *class_dev, char *new_name) { + int error = 0; + class_dev = class_device_get(class_dev); if (!class_dev) return -EINVAL; @@ -370,11 +372,11 @@ int class_device_rename(struct class_device *class_dev, char *new_name) strlcpy(class_dev->class_id, new_name, KOBJ_NAME_LEN); - kobject_rename(&class_dev->kobj, new_name); + error = kobject_rename(&class_dev->kobj, new_name); class_device_put(class_dev); - return 0; + return error; } struct class_device * class_device_get(struct class_device *class_dev) diff --git a/drivers/base/map.c b/drivers/base/map.c index 1effe3a303ee..aab93fc2a31b 100644 --- a/drivers/base/map.c +++ b/drivers/base/map.c @@ -138,6 +138,13 @@ struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL); struct probe *base = kmalloc(sizeof(struct probe), GFP_KERNEL); int i; + + if ((p == NULL) || (base == NULL)) { + kfree(p); + kfree(base); + return NULL; + } + memset(base, 0, sizeof(struct probe)); base->dev = 1; base->range = ~0; diff --git a/drivers/base/platform.c b/drivers/base/platform.c index e55900732f29..0d75909a4538 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -57,8 +57,9 @@ void platform_device_unregister(struct platform_device * pdev) * type of device, like "pci" or "floppy", and <instance> is the * enumerated instance of the device, like '0' or '42'. * Driver IDs are simply "<name>". - * So, extract the <name> from the device, and compare it against - * the name of the driver. Return whether they match or not. + * So, extract the <name> from the platform_device structure, + * and compare it against the name of the driver. Return whether + * they match or not. */ static int platform_match(struct device * dev, struct device_driver * drv) diff --git a/drivers/base/sys.c b/drivers/base/sys.c index 420cb3cf616f..b0d55a930855 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -180,8 +180,11 @@ int sysdev_register(struct sys_device * sysdev) /* But make sure we point to the right type for sysfs translation */ sysdev->kobj.ktype = &ktype_sysdev; - kobject_set_name(&sysdev->kobj,"%s%d", + error = kobject_set_name(&sysdev->kobj,"%s%d", kobject_name(&cls->kset.kobj),sysdev->id); + if (error) + return error; + pr_debug("Registering sys device '%s'\n",kobject_name(&sysdev->kobj)); /* Register the object */ diff --git a/drivers/block/paride/pg.c b/drivers/block/paride/pg.c index 506f982e5518..5ccc106fc89d 100644 --- a/drivers/block/paride/pg.c +++ b/drivers/block/paride/pg.c @@ -161,6 +161,7 @@ enum {D_PRT, D_PRO, D_UNI, D_MOD, D_SLV, D_DLY}; #include <linux/slab.h> #include <linux/mtio.h> #include <linux/pg.h> +#include <linux/device.h> #include <asm/uaccess.h> @@ -240,6 +241,8 @@ static int pg_identify(struct pg *dev, int log); static char pg_scratch[512]; /* scratch block buffer */ +static struct class_simple *pg_class; + /* kernel glue structures */ static struct file_operations pg_fops = { @@ -658,15 +661,19 @@ static ssize_t pg_read(struct file *filp, char *buf, size_t count, loff_t *ppos) static int __init pg_init(void) { - int unit; + int unit, err = 0; - if (disable) - return -1; + if (disable){ + err = -1; + goto out; + } pg_init_units(); - if (pg_detect()) - return -1; + if (pg_detect()) { + err = -1; + goto out; + } if (register_chrdev(major, name, &pg_fops)) { printk("pg_init: unable to get major number %d\n", major); @@ -675,18 +682,37 @@ static int __init pg_init(void) if (dev->present) pi_release(dev->pi); } - return -1; + err = -1; + goto out; + } + pg_class = class_simple_create(THIS_MODULE, "pg"); + if (IS_ERR(pg_class)) { + err = PTR_ERR(pg_class); + goto out_chrdev; } devfs_mk_dir("pg"); for (unit = 0; unit < PG_UNITS; unit++) { struct pg *dev = &devices[unit]; if (dev->present) { - devfs_mk_cdev(MKDEV(major, unit), + class_simple_device_add(pg_class, MKDEV(major, unit), + NULL, "pg%u", unit); + err = devfs_mk_cdev(MKDEV(major, unit), S_IFCHR | S_IRUSR | S_IWUSR, "pg/%u", unit); + if (err) + goto out_class; } } - return 0; + err = 0; + goto out; + +out_class: + class_simple_device_remove(MKDEV(major, unit)); + class_simple_destroy(pg_class); +out_chrdev: + unregister_chrdev(major, "pg"); +out: + return err; } static void __exit pg_exit(void) @@ -695,10 +721,12 @@ static void __exit pg_exit(void) for (unit = 0; unit < PG_UNITS; unit++) { struct pg *dev = &devices[unit]; - if (dev->present) + if (dev->present) { + class_simple_device_remove(MKDEV(major, unit)); devfs_remove("pg/%u", unit); + } } - + class_simple_destroy(pg_class); devfs_remove("pg"); unregister_chrdev(major, name); diff --git a/drivers/block/paride/pt.c b/drivers/block/paride/pt.c index 7db4fc7584c0..81c4690cf4c4 100644 --- a/drivers/block/paride/pt.c +++ b/drivers/block/paride/pt.c @@ -145,6 +145,7 @@ static int (*drives[4])[6] = {&drive0, &drive1, &drive2, &drive3}; #include <linux/delay.h> #include <linux/slab.h> #include <linux/mtio.h> +#include <linux/device.h> #include <asm/uaccess.h> @@ -260,6 +261,9 @@ static struct file_operations pt_fops = { .release = pt_release, }; +/* sysfs class support */ +static struct class_simple *pt_class; + static inline int status_reg(struct pi_adapter *pi) { return pi_read_regr(pi, 1, 6); @@ -959,33 +963,62 @@ static ssize_t pt_write(struct file *filp, const char *buf, size_t count, loff_t static int __init pt_init(void) { - int unit; + int unit, err = 0; - if (disable) - return -1; + if (disable) { + err = -1; + goto out; + } - if (pt_detect()) - return -1; + if (pt_detect()) { + err = -1; + goto out; + } if (register_chrdev(major, name, &pt_fops)) { printk("pt_init: unable to get major number %d\n", major); for (unit = 0; unit < PT_UNITS; unit++) if (pt[unit].present) pi_release(pt[unit].pi); - return -1; + err = -1; + goto out; + } + pt_class = class_simple_create(THIS_MODULE, "pt"); + if (IS_ERR(pt_class)) { + err = PTR_ERR(pt_class); + goto out_chrdev; } devfs_mk_dir("pt"); for (unit = 0; unit < PT_UNITS; unit++) if (pt[unit].present) { - devfs_mk_cdev(MKDEV(major, unit), + class_simple_device_add(pt_class, MKDEV(major, unit), + NULL, "pt%d", unit); + err = devfs_mk_cdev(MKDEV(major, unit), S_IFCHR | S_IRUSR | S_IWUSR, "pt/%d", unit); - devfs_mk_cdev(MKDEV(major, unit + 128), + if (err) { + class_simple_device_remove(MKDEV(major, unit)); + goto out_class; + } + class_simple_device_add(pt_class, MKDEV(major, unit + 128), + NULL, "pt%dn", unit); + err = devfs_mk_cdev(MKDEV(major, unit + 128), S_IFCHR | S_IRUSR | S_IWUSR, "pt/%dn", unit); + if (err) { + class_simple_device_remove(MKDEV(major, unit + 128)); + goto out_class; + } } - return 0; + goto out; + +out_class: + class_simple_destroy(pt_class); +out_chrdev: + unregister_chrdev(major, "pt"); +out: + return err; } static void __exit pt_exit(void) @@ -993,9 +1026,12 @@ static void __exit pt_exit(void) int unit; for (unit = 0; unit < PT_UNITS; unit++) if (pt[unit].present) { + class_simple_device_remove(MKDEV(major, unit)); devfs_remove("pt/%d", unit); + class_simple_device_remove(MKDEV(major, unit + 128)); devfs_remove("pt/%dn", unit); } + class_simple_destroy(pt_class); devfs_remove("pt"); unregister_chrdev(major, name); for (unit = 0; unit < PT_UNITS; unit++) diff --git a/drivers/char/ip2main.c b/drivers/char/ip2main.c index 043cff46fb74..d5c18535bbfa 100644 --- a/drivers/char/ip2main.c +++ b/drivers/char/ip2main.c @@ -99,6 +99,7 @@ #include <linux/slab.h> #include <linux/major.h> #include <linux/wait.h> +#include <linux/device.h> #include <linux/tty.h> #include <linux/tty_flip.h> @@ -301,6 +302,9 @@ static int iindx; static char rirqs[IP2_MAX_BOARDS]; static int Valid_Irqs[] = { 3, 4, 5, 7, 10, 11, 12, 15, 0}; +/* for sysfs class support */ +static struct class_simple *ip2_class; + // Some functions to keep track of what irq's we have static int __init @@ -411,7 +415,9 @@ cleanup_module(void) iiResetDelay( i2BoardPtrTable[i] ); /* free io addresses and Tibet */ release_region( ip2config.addr[i], 8 ); + class_simple_device_remove(MKDEV(IP2_IPL_MAJOR, 4 * i)); devfs_remove("ip2/ipl%d", i); + class_simple_device_remove(MKDEV(IP2_IPL_MAJOR, 4 * i + 1)); devfs_remove("ip2/stat%d", i); } /* Disable and remove interrupt handler. */ @@ -420,6 +426,7 @@ cleanup_module(void) clear_requested_irq( ip2config.irq[i]); } } + class_simple_destroy(ip2_class); devfs_remove("ip2"); if ( ( err = tty_unregister_driver ( ip2_tty_driver ) ) ) { printk(KERN_ERR "IP2: failed to unregister tty driver (%d)\n", err); @@ -494,7 +501,7 @@ int ip2_loadmain(int *iop, int *irqp, unsigned char *firmware, int firmsize) { int i, j, box; - int err; + int err = 0; int status = 0; static int loaded; i2eBordStrPtr pB = NULL; @@ -683,7 +690,14 @@ ip2_loadmain(int *iop, int *irqp, unsigned char *firmware, int firmsize) /* Register the IPL driver. */ if ( ( err = register_chrdev ( IP2_IPL_MAJOR, pcIpl, &ip2_ipl ) ) ) { printk(KERN_ERR "IP2: failed to register IPL device (%d)\n", err ); - } else + } else { + /* create the sysfs class */ + ip2_class = class_simple_create(THIS_MODULE, "ip2"); + if (IS_ERR(ip2_class)) { + err = PTR_ERR(ip2_class); + goto out_chrdev; + } + } /* Register the read_procmem thing */ if (!create_proc_info_entry("ip2mem",0,&proc_root,ip2_read_procmem)) { printk(KERN_ERR "IP2: failed to register read_procmem\n"); @@ -700,13 +714,27 @@ ip2_loadmain(int *iop, int *irqp, unsigned char *firmware, int firmsize) } if ( NULL != ( pB = i2BoardPtrTable[i] ) ) { - devfs_mk_cdev(MKDEV(IP2_IPL_MAJOR, 4 * i), + class_simple_device_add(ip2_class, MKDEV(IP2_IPL_MAJOR, + 4 * i), NULL, "ipl%d", i); + err = devfs_mk_cdev(MKDEV(IP2_IPL_MAJOR, 4 * i), S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, "ip2/ipl%d", i); + if (err) { + class_simple_device_remove(MKDEV(IP2_IPL_MAJOR, + 4 * i)); + goto out_class; + } - devfs_mk_cdev(MKDEV(IP2_IPL_MAJOR, 4 * i + 1), + class_simple_device_add(ip2_class, MKDEV(IP2_IPL_MAJOR, + 4 * i + 1), NULL, "stat%d", i); + err = devfs_mk_cdev(MKDEV(IP2_IPL_MAJOR, 4 * i + 1), S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, "ip2/stat%d", i); + if (err) { + class_simple_device_remove(MKDEV(IP2_IPL_MAJOR, + 4 * i + 1)); + goto out_class; + } for ( box = 0; box < ABS_MAX_BOXES; ++box ) { @@ -759,8 +787,14 @@ retry: } } ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_RETURN, 0 ); - - return 0; + goto out; + +out_class: + class_simple_destroy(ip2_class); +out_chrdev: + unregister_chrdev(IP2_IPL_MAJOR, "ip2"); +out: + return err; } EXPORT_SYMBOL(ip2_loadmain); diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 525905c07435..56ae1f09db3a 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -59,6 +59,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/sched.h> +#include <linux/device.h> #include <linux/devfs_fs_kernel.h> #include <linux/ioctl.h> #include <linux/parport.h> @@ -739,6 +740,8 @@ static unsigned int pp_poll (struct file * file, poll_table * wait) return mask; } +static struct class_simple *ppdev_class; + static struct file_operations pp_fops = { .owner = THIS_MODULE, .llseek = no_llseek, @@ -750,23 +753,59 @@ static struct file_operations pp_fops = { .release = pp_release, }; +static void pp_attach(struct parport *port) +{ + class_simple_device_add(ppdev_class, MKDEV(PP_MAJOR, port->number), + NULL, "parport%d", port->number); +} + +static void pp_detach(struct parport *port) +{ + class_simple_device_remove(MKDEV(PP_MAJOR, port->number)); +} + +static struct parport_driver pp_driver = { + .name = CHRDEV, + .attach = pp_attach, + .detach = pp_detach, +}; + static int __init ppdev_init (void) { - int i; + int i, err = 0; if (register_chrdev (PP_MAJOR, CHRDEV, &pp_fops)) { printk (KERN_WARNING CHRDEV ": unable to get major %d\n", PP_MAJOR); return -EIO; } + ppdev_class = class_simple_create(THIS_MODULE, CHRDEV); + if (IS_ERR(ppdev_class)) { + err = PTR_ERR(ppdev_class); + goto out_chrdev; + } devfs_mk_dir("parports"); for (i = 0; i < PARPORT_MAX; i++) { devfs_mk_cdev(MKDEV(PP_MAJOR, i), S_IFCHR | S_IRUGO | S_IWUGO, "parports/%d", i); } + if (parport_register_driver(&pp_driver)) { + printk (KERN_WARNING CHRDEV ": unable to register with parport\n"); + goto out_class; + } printk (KERN_INFO PP_VERSION "\n"); - return 0; + goto out; + +out_class: + for (i = 0; i < PARPORT_MAX; i++) + devfs_remove("parports/%d", i); + devfs_remove("parports"); + class_simple_destroy(ppdev_class); +out_chrdev: + unregister_chrdev(PP_MAJOR, CHRDEV); +out: + return err; } static void __exit ppdev_cleanup (void) @@ -775,7 +814,9 @@ static void __exit ppdev_cleanup (void) /* Clean up all parport stuff */ for (i = 0; i < PARPORT_MAX; i++) devfs_remove("parports/%d", i); + parport_unregister_driver(&pp_driver); devfs_remove("parports"); + class_simple_destroy(ppdev_class); unregister_chrdev (PP_MAJOR, CHRDEV); } diff --git a/drivers/char/tipar.c b/drivers/char/tipar.c index 3632e28648a5..8cbf15bb51ac 100644 --- a/drivers/char/tipar.c +++ b/drivers/char/tipar.c @@ -58,6 +58,7 @@ #include <asm/bitops.h> #include <linux/devfs_fs_kernel.h> /* DevFs support */ #include <linux/parport.h> /* Our code depend on parport */ +#include <linux/device.h> /* * TI definitions @@ -92,6 +93,8 @@ static int timeout = TIMAXTIME; /* timeout in tenth of seconds */ static unsigned int tp_count; /* tipar count */ static unsigned long opened; /* opened devices */ +static struct class_simple *tipar_class; + /* --- macros for parport access -------------------------------------- */ #define r_dtr(x) (parport_read_data(table[(x)].dev->port)) @@ -424,18 +427,26 @@ tipar_setup(char *str) static int tipar_register(int nr, struct parport *port) { + int err = 0; + /* Register our module into parport */ table[nr].dev = parport_register_device(port, "tipar", NULL, NULL, NULL, 0, (void *) &table[nr]); - if (table[nr].dev == NULL) - return 1; + if (table[nr].dev == NULL) { + err = 1; + goto out; + } + class_simple_device_add(tipar_class, MKDEV(TIPAR_MAJOR, TIPAR_MINOR + nr), + NULL, "par%d", nr); /* Use devfs, tree: /dev/ticables/par/[0..2] */ - devfs_mk_cdev(MKDEV(TIPAR_MAJOR, TIPAR_MINOR + nr), + err = devfs_mk_cdev(MKDEV(TIPAR_MAJOR, TIPAR_MINOR + nr), S_IFCHR | S_IRUGO | S_IWUGO, "ticables/par/%d", nr); + if (err) + goto out_class; /* Display informations */ printk(KERN_INFO "tipar%d: using %s (%s).\n", nr, port->name, @@ -447,7 +458,14 @@ tipar_register(int nr, struct parport *port) else printk("tipar%d: link cable not found.\n", nr); - return 0; + err = 0; + goto out; + +out_class: + class_simple_device_remove(MKDEV(TIPAR_MAJOR, TIPAR_MINOR + nr)); + class_simple_destroy(tipar_class); +out: + return err; } static void @@ -477,23 +495,38 @@ static struct parport_driver tipar_driver = { int __init tipar_init_module(void) { + int err = 0; + printk("tipar: parallel link cable driver, version %s\n", DRIVER_VERSION); if (register_chrdev(TIPAR_MAJOR, "tipar", &tipar_fops)) { printk("tipar: unable to get major %d\n", TIPAR_MAJOR); - return -EIO; + err = -EIO; + goto out; } /* Use devfs with tree: /dev/ticables/par/[0..2] */ devfs_mk_dir("ticables/par"); + tipar_class = class_simple_create(THIS_MODULE, "ticables"); + if (IS_ERR(tipar_class)) { + err = PTR_ERR(tipar_class); + goto out_chrdev; + } if (parport_register_driver(&tipar_driver)) { printk("tipar: unable to register with parport\n"); - return -EIO; + err = -EIO; + goto out; } - return 0; + err = 0; + goto out; + +out_chrdev: + unregister_chrdev(TIPAR_MAJOR, "tipar"); +out: + return err; } void __exit @@ -510,8 +543,10 @@ tipar_cleanup_module(void) if (table[i].dev == NULL) continue; parport_unregister_device(table[i].dev); + class_simple_device_remove(MKDEV(TIPAR_MAJOR, i)); devfs_remove("ticables/par/%d", i); } + class_simple_destroy(tipar_class); devfs_remove("ticables/par"); printk("tipar: module unloaded !\n"); diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 597bf5f04e6d..83575861bd21 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -34,4 +34,12 @@ config EFI_VARS Subsequent efibootmgr releases may be found at: http://linux.dell.com/efibootmgr +config SMBIOS + tristate "BIOS SMBIOS table access driver." + help + Say Y or M here if you want to enable access to the SMBIOS table + via driverfs. It exposes /sys/firmware/smbios/ subdirectory tree + containing a binary dump of the SMBIOS table header as well as the SMBIOS + table. + endmenu diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index ca7745addd5c..694725046da7 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_EDD) += edd.o obj-$(CONFIG_EFI_VARS) += efivars.o +obj-$(CONFIG_SMBIOS) += smbios.o diff --git a/drivers/firmware/smbios.c b/drivers/firmware/smbios.c new file mode 100644 index 000000000000..583ab0939c08 --- /dev/null +++ b/drivers/firmware/smbios.c @@ -0,0 +1,248 @@ +/* + * linux/drivers/firmware/smbios.c + * Copyright (C) 2004 Dell Inc. + * by Michael Brown <Michael_E_Brown@dell.com> + * vim:noet:ts=8:sw=8:filetype=c:textwidth=80: + * + * BIOS SMBIOS Table access + * conformant to DMTF SMBIOS definition + * at http://www.dmtf.org/standards/smbios + * + * This code takes information provided by SMBIOS tables + * and presents it in sysfs as: + * /sys/firmware/smbios + * |--> /table_entry_point + * |--> /table + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2.0 as published by + * the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include "smbios.h" + +MODULE_AUTHOR("Michael Brown <Michael_E_Brown@Dell.com>"); +MODULE_DESCRIPTION("sysfs interface to SMBIOS information"); +MODULE_LICENSE("GPL"); + +#define SMBIOS_VERSION "1.0 2004-04-19" + +struct smbios_device { + struct smbios_table_entry_point table_eps; + unsigned int smbios_table_real_length; +}; + +/* there shall be only one */ +static struct smbios_device the_smbios_device; + +#define to_smbios_device(obj) container_of(obj,struct smbios_device,kobj) + +/* don't currently have any "normal" attributes, so we don't need a way to + * show them. */ +static struct sysfs_ops smbios_attr_ops = { }; + +static __init int +checksum_eps(struct smbios_table_entry_point *table_eps) +{ + u8 *p = (u8 *)table_eps; + u8 checksum = 0; + int i=0; + for (i=0; i < table_eps->eps_length && i < sizeof(*table_eps); ++i) { + checksum += p[i]; + } + return( checksum == 0 ); +} + +static __init int +find_table_entry_point(struct smbios_device *sdev) +{ + struct smbios_table_entry_point *table_eps = &(sdev->table_eps); + u32 fp = 0xF0000; + while (fp < 0xFFFFF) { + isa_memcpy_fromio(table_eps, fp, sizeof(*table_eps)); + if (memcmp(table_eps->anchor, "_SM_", 4)==0 && + checksum_eps(table_eps)) { + return 0; + } + fp += 16; + } + + printk(KERN_INFO "SMBIOS table entry point not found in " + "0xF0000 - 0xFFFFF\n"); + return -ENODEV; +} + +static __init int +find_table_max_address(struct smbios_device *sdev) +{ + /* break out on one of three conditions: + * -- hit table_eps.table_length + * -- hit number of items that table claims we have + * -- hit structure type 127 + */ + + u8 *buf = ioremap(sdev->table_eps.table_address, + sdev->table_eps.table_length); + u8 *ptr = buf; + int count = 0, keep_going = 1; + int max_count = sdev->table_eps.table_num_structs; + int max_length = sdev->table_eps.table_length; + while(keep_going && ((ptr - buf) <= max_length) && count < max_count){ + if( ptr[0] == 0x7F ) /* ptr[0] is type */ + keep_going = 0; + + ptr += ptr[1]; /* ptr[1] is length, skip structure */ + /* skip strings at end of structure */ + while((ptr-buf) < max_length && (ptr[0] || ptr[1])) + ++ptr; + + /* string area ends in double-null. skip it. */ + ptr += 2; + ++count; + } + sdev->smbios_table_real_length = (ptr - buf); + iounmap(buf); + + if( count != max_count ) + printk(KERN_INFO "Warning: SMBIOS table structure count" + " does not match count specified in the" + " table entry point.\n" + " Table entry point count: %d\n" + " Actual count: %d\n", + max_count, count ); + + if(keep_going != 0) + printk(KERN_INFO "Warning: SMBIOS table does not end with a" + " structure type 127. This may indicate a" + " truncated table."); + + if(sdev->smbios_table_real_length != max_length) + printk(KERN_INFO "Warning: BIOS specified SMBIOS table length" + " does not match calculated length.\n" + " BIOS specified: %d\n" + " calculated length: %d\n", + max_length, sdev->smbios_table_real_length); + + return sdev->smbios_table_real_length; +} + +static ssize_t +smbios_read_table_entry_point(struct kobject *kobj, char *buffer, + loff_t pos, size_t size) +{ + struct smbios_device *sdev = &the_smbios_device; + const char *p = (const char *)&(sdev->table_eps); + unsigned int count = + size > sizeof(sdev->table_eps) ? + sizeof(sdev->table_eps) : size; + memcpy( buffer, p, count ); + return count; +} + +static ssize_t +smbios_read_table(struct kobject *kobj, char *buffer, + loff_t pos, size_t size) +{ + struct smbios_device *sdev = &the_smbios_device; + u8 *buf; + unsigned int count = sdev->smbios_table_real_length - pos; + int i = 0; + count = count < size ? count : size; + + if (pos > sdev->smbios_table_real_length) + return 0; + + buf = ioremap(sdev->table_eps.table_address, sdev->smbios_table_real_length); + if (buf == NULL) + return -ENXIO; + + /* memcpy( buffer, buf+pos, count ); */ + for (i = 0; i < count; ++i) { + buffer[i] = readb( buf+pos+i ); + } + + iounmap(buf); + + return count; +} + +static struct bin_attribute tep_attr = { + .attr = {.name = "table_entry_point", .owner = THIS_MODULE, .mode = 0444}, + .size = sizeof(struct smbios_table_entry_point), + .read = smbios_read_table_entry_point, + /* not writeable */ +}; + +static struct bin_attribute table_attr = { + .attr = { .name = "table", .owner = THIS_MODULE, .mode = 0444 }, + /* size set later, we don't know it here. */ + .read = smbios_read_table, + /* not writeable */ +}; + +/* no default attributes yet. */ +static struct attribute * def_attrs[] = { NULL, }; + +static struct kobj_type ktype_smbios = { + .sysfs_ops = &smbios_attr_ops, + .default_attrs = def_attrs, + /* statically allocated, no release method necessary */ +}; + +static decl_subsys(smbios,&ktype_smbios,NULL); + +static void smbios_device_unregister(void) +{ + sysfs_remove_bin_file(&smbios_subsys.kset.kobj, &tep_attr ); + sysfs_remove_bin_file(&smbios_subsys.kset.kobj, &table_attr ); +} + +static void __init smbios_device_register(void) +{ + sysfs_create_bin_file(&smbios_subsys.kset.kobj, &tep_attr ); + sysfs_create_bin_file(&smbios_subsys.kset.kobj, &table_attr ); +} + +static int __init +smbios_init(void) +{ + int rc=0; + + printk(KERN_INFO "SMBIOS facility v%s\n", SMBIOS_VERSION ); + + rc = find_table_entry_point(&the_smbios_device); + if (rc) + return rc; + + table_attr.size = find_table_max_address(&the_smbios_device); + + rc = firmware_register(&smbios_subsys); + if (rc) + return rc; + + smbios_device_register(); + + return rc; +} + +static void __exit +smbios_exit(void) +{ + smbios_device_unregister(); + firmware_unregister(&smbios_subsys); +} + +late_initcall(smbios_init); +module_exit(smbios_exit); diff --git a/drivers/firmware/smbios.h b/drivers/firmware/smbios.h new file mode 100644 index 000000000000..7d56a0a2f4cf --- /dev/null +++ b/drivers/firmware/smbios.h @@ -0,0 +1,53 @@ +/* + * linux/drivers/firmware/smbios.c + * Copyright (C) 2002, 2003, 2004 Dell Inc. + * by Michael Brown <Michael_E_Brown@dell.com> + * vim:noet:ts=8:sw=8:filetype=c:textwidth=80: + * + * BIOS SMBIOS Table access + * conformant to DMTF SMBIOS definition + * at http://www.dmtf.org/standards/smbios + * + * This code takes information provided by SMBIOS tables + * and presents it in sysfs. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2.0 as published by + * the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _LINUX_SMBIOS_H +#define _LINUX_SMBIOS_H + +#include <linux/types.h> + +struct smbios_table_entry_point { + u8 anchor[4]; + u8 checksum; + u8 eps_length; + u8 major_ver; + u8 minor_ver; + u16 max_struct_size; + u8 revision; + u8 formatted_area[5]; + u8 dmi_anchor[5]; + u8 intermediate_checksum; + u16 table_length; + u32 table_address; + u16 table_num_structs; + u8 smbios_bcd_revision; +} __attribute__ ((packed)); + +struct smbios_structure_header { + u8 type; + u8 length; + u16 handle; +} __attribute__ ((packed)); + +#endif /* _LINUX_SMBIOS_H */ diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index f100c194be08..5916de82fbe8 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -10,7 +10,7 @@ * * To do: * - * - /proc/adb to list the devices and infos + * - /sys/bus/adb to list the devices and infos * - more /dev/adb to allow userland to receive the * flow of auto-polling datas from a given device. * - move bus probe to a kernel thread @@ -23,7 +23,6 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/fs.h> -#include <linux/devfs_fs_kernel.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/smp_lock.h> @@ -36,6 +35,7 @@ #include <linux/delay.h> #include <linux/spinlock.h> #include <linux/completion.h> +#include <linux/device.h> #include <asm/uaccess.h> #include <asm/semaphore.h> #ifdef CONFIG_PPC @@ -75,6 +75,8 @@ static struct adb_driver *adb_driver_list[] = { NULL }; +static struct class_simple *adb_dev_class; + struct adb_driver *adb_controller; struct notifier_block *adb_client_list = NULL; static int adb_got_sleep; @@ -883,6 +885,7 @@ out: } static struct file_operations adb_fops = { + .owner = THIS_MODULE, .llseek = no_llseek, .read = adb_read, .write = adb_write, @@ -893,9 +896,13 @@ static struct file_operations adb_fops = { static void adbdev_init(void) { - if (register_chrdev(ADB_MAJOR, "adb", &adb_fops)) + if (register_chrdev(ADB_MAJOR, "adb", &adb_fops)) { printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR); - else - devfs_mk_cdev(MKDEV(ADB_MAJOR, 0), - S_IFCHR | S_IRUSR | S_IWUSR, "adb"); + return; + } + adb_dev_class = class_simple_create(THIS_MODULE, "adb"); + if (IS_ERR(adb_dev_class)) { + return; + } + class_simple_device_add(adb_dev_class, MKDEV(ADB_MAJOR, 0), NULL, "adb"); } diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 9fb3e65244f5..fdd0be1a8d5a 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -6,7 +6,7 @@ menu "Misc devices" config IBM_ASM tristate "Device driver for IBM RSA service processor" - depends on X86 + depends on X86 && EXPERIMENTAL default n ---help--- This option enables device driver support for in-band access to the @@ -20,6 +20,12 @@ config IBM_ASM this feature serial driver support (CONFIG_SERIAL_8250) must be enabled. + WARNING: This software may not be supported or function + correctly on your IBM server. Please consult the IBM ServerProven + website http://www.pc.ibm/ww/eserver/xseries/serverproven for + information on the specific driver level and support statement + for your IBM server. + If unsure, say N. diff --git a/drivers/misc/ibmasm/module.c b/drivers/misc/ibmasm/module.c index 190349fbb36e..ee29ced8784b 100644 --- a/drivers/misc/ibmasm/module.c +++ b/drivers/misc/ibmasm/module.c @@ -126,6 +126,13 @@ static int __init ibmasm_init_one(struct pci_dev *pdev, const struct pci_device_ ibmasm_register_uart(sp); + dev_printk(KERN_DEBUG, &pdev->dev, "WARNING: This software may not be supported or function\n"); + dev_printk(KERN_DEBUG, &pdev->dev, "correctly on your IBM server. Please consult the IBM\n"); + dev_printk(KERN_DEBUG, &pdev->dev, "ServerProven website\n"); + dev_printk(KERN_DEBUG, &pdev->dev, "http://www.pc.ibm.com/ww/eserver/xseries/serverproven\n"); + dev_printk(KERN_DEBUG, &pdev->dev, "for information on the specific driver level and support\n"); + dev_printk(KERN_DEBUG, &pdev->dev, "statement for your IBM server.\n"); + return 0; error_send_message: diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c index 00d0b751e827..a4d0452c3c97 100644 --- a/drivers/net/wan/cosa.c +++ b/drivers/net/wan/cosa.c @@ -93,6 +93,7 @@ #include <linux/netdevice.h> #include <linux/spinlock.h> #include <linux/smp_lock.h> +#include <linux/device.h> #undef COSA_SLOW_IO /* for testing purposes only */ #undef REALLY_SLOW_IO @@ -233,6 +234,9 @@ static int dma[MAX_CARDS+1]; /* IRQ can be safely autoprobed */ static int irq[MAX_CARDS+1] = { -1, -1, -1, -1, -1, -1, 0, }; +/* for class stuff*/ +static struct class_simple *cosa_class; + #ifdef MODULE MODULE_PARM(io, "1-" __MODULE_STRING(MAX_CARDS) "i"); MODULE_PARM_DESC(io, "The I/O bases of the COSA or SRP cards"); @@ -359,7 +363,7 @@ static void debug_status_out(struct cosa_data *cosa, int status); static int __init cosa_init(void) { - int i; + int i, err = 0; printk(KERN_INFO "cosa v1.08 (c) 1997-2000 Jan Kasprzak <kas@fi.muni.cz>\n"); #ifdef CONFIG_SMP @@ -369,12 +373,14 @@ static int __init cosa_init(void) if (register_chrdev(cosa_major, "cosa", &cosa_fops)) { printk(KERN_WARNING "cosa: unable to get major %d\n", cosa_major); - return -EIO; + err = -EIO; + goto out; } } else { if (!(cosa_major=register_chrdev(0, "cosa", &cosa_fops))) { printk(KERN_WARNING "cosa: unable to register chardev\n"); - return -EIO; + err = -EIO; + goto out; } } for (i=0; i<MAX_CARDS; i++) @@ -384,15 +390,33 @@ static int __init cosa_init(void) if (!nr_cards) { printk(KERN_WARNING "cosa: no devices found.\n"); unregister_chrdev(cosa_major, "cosa"); - return -ENODEV; + err = -ENODEV; + goto out; } devfs_mk_dir("cosa"); + cosa_class = class_simple_create(THIS_MODULE, "cosa"); + if (IS_ERR(cosa_class)) { + err = PTR_ERR(cosa_class); + goto out_chrdev; + } for (i=0; i<nr_cards; i++) { - devfs_mk_cdev(MKDEV(cosa_major, i), + class_simple_device_add(cosa_class, MKDEV(cosa_major, i), + NULL, "cosa%d", i); + err = devfs_mk_cdev(MKDEV(cosa_major, i), S_IFCHR|S_IRUSR|S_IWUSR, "cosa/%d", i); + if (err) { + class_simple_device_remove(MKDEV(cosa_major, i)); + goto out_chrdev; + } } - return 0; + err = 0; + goto out; + +out_chrdev: + unregister_chrdev(cosa_major, "cosa"); +out: + return err; } module_init(cosa_init); @@ -402,8 +426,11 @@ static void __exit cosa_exit(void) int i; printk(KERN_INFO "Unloading the cosa module\n"); - for (i=0; i<nr_cards; i++) + for (i=0; i<nr_cards; i++) { + class_simple_device_remove(MKDEV(cosa_major, i)); devfs_remove("cosa/%d", i); + } + class_simple_destroy(cosa_class); devfs_remove("cosa"); for (cosa=cosa_cards; nr_cards--; cosa++) { /* Clean up the per-channel data */ diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index 6b4c28844f3f..d455725102fb 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c @@ -94,7 +94,7 @@ static ssize_t write(struct file * file, const char __user * userbuf, static int open(struct inode * inode, struct file * file) { - struct kobject * kobj = kobject_get(file->f_dentry->d_parent->d_fsdata); + struct kobject *kobj = sysfs_get_kobject(file->f_dentry->d_parent); struct bin_attribute * attr = file->f_dentry->d_fsdata; int error = -EINVAL; diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index add2045e8c25..91b06a20cd76 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -154,24 +154,33 @@ restart: dput(dentry); } -void sysfs_rename_dir(struct kobject * kobj, const char *new_name) +int sysfs_rename_dir(struct kobject * kobj, const char *new_name) { + int error = 0; struct dentry * new_dentry, * parent; if (!strcmp(kobject_name(kobj), new_name)) - return; + return -EINVAL; if (!kobj->parent) - return; + return -EINVAL; parent = kobj->parent->dentry; down(&parent->d_inode->i_sem); new_dentry = sysfs_get_dentry(parent, new_name); - d_move(kobj->dentry, new_dentry); - kobject_set_name(kobj,new_name); + if (!IS_ERR(new_dentry)) { + if (!new_dentry->d_inode) { + error = kobject_set_name(kobj,new_name); + if (!error) + d_move(kobj->dentry, new_dentry); + } + dput(new_dentry); + } up(&parent->d_inode->i_sem); + + return error; } EXPORT_SYMBOL(sysfs_create_dir); diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index cb1f3d9c7bf7..47c98d0224ef 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -238,7 +238,7 @@ sysfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t static int check_perm(struct inode * inode, struct file * file) { - struct kobject * kobj = kobject_get(file->f_dentry->d_parent->d_fsdata); + struct kobject *kobj = sysfs_get_kobject(file->f_dentry->d_parent); struct attribute * attr = file->f_dentry->d_fsdata; struct sysfs_buffer * buffer; struct sysfs_ops * ops = NULL; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 96e390a7b99f..5976cc1aede0 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -11,3 +11,16 @@ extern void sysfs_hash_and_remove(struct dentry * dir, const char * name); extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **); extern void sysfs_remove_subdir(struct dentry *); + + +static inline struct kobject *sysfs_get_kobject(struct dentry *dentry) +{ + struct kobject * kobj = NULL; + + spin_lock(&dentry->d_lock); + if (!d_unhashed(dentry)) + kobj = kobject_get(dentry->d_fsdata); + spin_unlock(&dentry->d_lock); + + return kobj; +} diff --git a/include/linux/device.h b/include/linux/device.h index 9bc07b556eea..bd05cdfb326e 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -400,7 +400,7 @@ extern void firmware_unregister(struct subsystem *); /* debugging and troubleshooting/diagnostic helpers. */ #define dev_printk(level, dev, format, arg...) \ - printk(level "%s %s: " format , (dev)->driver->name , (dev)->bus_id , ## arg) + printk(level "%s %s: " format , (dev)->driver ? (dev)->driver->name : "" , (dev)->bus_id , ## arg) #ifdef DEBUG #define dev_dbg(dev, format, arg...) \ diff --git a/include/linux/kobject.h b/include/linux/kobject.h index f32f39b4cce6..6360d225884c 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -48,7 +48,7 @@ extern void kobject_cleanup(struct kobject *); extern int kobject_add(struct kobject *); extern void kobject_del(struct kobject *); -extern void kobject_rename(struct kobject *, char *new_name); +extern int kobject_rename(struct kobject *, char *new_name); extern int kobject_register(struct kobject *); extern void kobject_unregister(struct kobject *); diff --git a/include/linux/module.h b/include/linux/module.h index 0a86652fb1cb..9a3eb2f1f95d 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -16,6 +16,8 @@ #include <linux/kmod.h> #include <linux/elf.h> #include <linux/stringify.h> +#include <linux/kobject.h> +#include <linux/moduleparam.h> #include <asm/local.h> #include <asm/module.h> @@ -207,6 +209,23 @@ enum module_state MODULE_STATE_GOING, }; +/* sysfs stuff */ +struct module_attribute +{ + struct attribute attr; + struct kernel_param *param; +}; + +struct module_kobject +{ + /* Everyone should have one of these. */ + struct kobject kobj; + + /* We always have refcnt, we may have others from module_param(). */ + unsigned int num_attributes; + struct module_attribute attr[0]; +}; + struct module { enum module_state state; @@ -217,6 +236,9 @@ struct module /* Unique handle for this module */ char name[MODULE_NAME_LEN]; + /* Sysfs stuff. */ + struct module_kobject *mkobj; + /* Exported symbols */ const struct kernel_symbol *syms; unsigned int num_syms; @@ -267,6 +289,9 @@ struct module /* Destruction function. */ void (*exit)(void); + + /* Fake kernel param for refcnt. */ + struct kernel_param refcnt_param; #endif #ifdef CONFIG_KALLSYMS diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h index e9d6a16d3fef..f63de16cdf54 100644 --- a/include/linux/moduleparam.h +++ b/include/linux/moduleparam.h @@ -50,7 +50,7 @@ struct kparam_array not there, read bits mean it's readable, write bits mean it's writable. */ #define __module_param_call(prefix, name, set, get, arg, perm) \ - static char __param_str_##name[] __initdata = prefix #name; \ + static char __param_str_##name[] = prefix #name; \ static struct kernel_param const __param_##name \ __attribute_used__ \ __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \ @@ -71,7 +71,7 @@ struct kparam_array /* Actually copy string: maxlen param is usually sizeof(string). */ #define module_param_string(name, string, len, perm) \ - static struct kparam_string __param_string_##name __initdata \ + static struct kparam_string __param_string_##name \ = { len, string }; \ module_param_call(name, param_set_copystring, param_get_charp, \ &__param_string_##name, perm) diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index de2083939b74..31f1c8b4428d 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -44,7 +44,7 @@ sysfs_create_dir(struct kobject *); extern void sysfs_remove_dir(struct kobject *); -extern void +extern int sysfs_rename_dir(struct kobject *, const char *new_name); extern int diff --git a/kernel/module.c b/kernel/module.c index 2ddd9697afcb..1781faa9a132 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -379,6 +379,22 @@ static inline void percpu_modcopy(void *pcpudst, const void *src, } #endif /* CONFIG_SMP */ +static int add_attribute(struct module *mod, struct kernel_param *kp) +{ + struct module_attribute *a; + int retval; + + a = &mod->mkobj->attr[mod->mkobj->num_attributes]; + a->attr.name = (char *)kp->name; + a->attr.owner = mod; + a->attr.mode = kp->perm; + a->param = kp; + retval = sysfs_create_file(&mod->mkobj->kobj, &a->attr); + if (!retval) + mod->mkobj->num_attributes++; + return retval; +} + #ifdef CONFIG_MODULE_UNLOAD /* Init the unload section of the module. */ static void module_unload_init(struct module *mod) @@ -663,6 +679,23 @@ void symbol_put_addr(void *addr) } EXPORT_SYMBOL_GPL(symbol_put_addr); +static int refcnt_get_fn(char *buffer, struct kernel_param *kp) +{ + struct module *mod = container_of(kp, struct module, refcnt_param); + + /* sysfs holds one reference. */ + return sprintf(buffer, "%u", module_refcount(mod)-1); +} + +static inline int sysfs_unload_setup(struct module *mod) +{ + mod->refcnt_param.name = "refcnt"; + mod->refcnt_param.perm = 0444; + mod->refcnt_param.get = refcnt_get_fn; + + return add_attribute(mod, &mod->refcnt_param); +} + #else /* !CONFIG_MODULE_UNLOAD */ static void print_unload_info(struct seq_file *m, struct module *mod) { @@ -689,6 +722,10 @@ sys_delete_module(const char *name_user, unsigned int flags) return -ENOSYS; } +static inline int sysfs_unload_setup(struct module *mod) +{ + return 0; +} #endif /* CONFIG_MODULE_UNLOAD */ #ifdef CONFIG_OBSOLETE_MODPARM @@ -944,6 +981,116 @@ static unsigned long resolve_symbol(Elf_Shdr *sechdrs, return ret; } +#define to_module_attr(n) container_of(n, struct module_attribute, attr); + +static ssize_t module_attr_show(struct kobject *kobj, + struct attribute *attr, + char *buf) +{ + int count; + struct module_attribute *attribute = to_module_attr(attr); + + if (!attribute->param->get) + return -EPERM; + + count = attribute->param->get(buf, attribute->param); + if (count > 0) { + strcat(buf, "\n"); + ++count; + } + return count; +} + +/* sysfs always hands a nul-terminated string in buf. We rely on that. */ +static ssize_t module_attr_store(struct kobject *kobj, + struct attribute *attr, + const char *buf, size_t len) +{ + int err; + struct module_attribute *attribute = to_module_attr(attr); + + if (!attribute->param->set) + return -EPERM; + + err = attribute->param->set(buf, attribute->param); + if (!err) + return len; + return err; +} + +static struct sysfs_ops module_sysfs_ops = { + .show = module_attr_show, + .store = module_attr_store, +}; + +static void module_kobj_release(struct kobject *kobj) +{ + kfree(container_of(kobj, struct module_kobject, kobj)); +} + +static struct kobj_type module_ktype = { + .sysfs_ops = &module_sysfs_ops, + .release = &module_kobj_release, +}; +static decl_subsys(module, &module_ktype, NULL); + +static int mod_sysfs_setup(struct module *mod, + struct kernel_param *kparam, + unsigned int num_params) +{ + unsigned int i; + int err; + + /* We overallocate: not every param is in sysfs, and maybe no refcnt */ + mod->mkobj = kmalloc(sizeof(*mod->mkobj) + + sizeof(mod->mkobj->attr[0]) * (num_params+1), + GFP_KERNEL); + if (!mod->mkobj) + return -ENOMEM; + + memset(&mod->mkobj->kobj, 0, sizeof(mod->mkobj->kobj)); + err = kobject_set_name(&mod->mkobj->kobj, mod->name); + if (err) + goto out; + kobj_set_kset_s(mod->mkobj, module_subsys); + err = kobject_register(&mod->mkobj->kobj); + if (err) + goto out; + + mod->mkobj->num_attributes = 0; + + for (i = 0; i < num_params; i++) { + if (kparam[i].perm) { + err = add_attribute(mod, &kparam[i]); + if (err) + goto out_unreg; + } + } + err = sysfs_unload_setup(mod); + if (err) + goto out_unreg; + return 0; + +out_unreg: + for (i = 0; i < mod->mkobj->num_attributes; i++) + sysfs_remove_file(&mod->mkobj->kobj,&mod->mkobj->attr[i].attr); + /* Calls module_kobj_release */ + kobject_unregister(&mod->mkobj->kobj); + return err; +out: + kfree(mod->mkobj); + return err; +} + +static void mod_kobject_remove(struct module *mod) +{ + unsigned int i; + for (i = 0; i < mod->mkobj->num_attributes; i++) + sysfs_remove_file(&mod->mkobj->kobj,&mod->mkobj->attr[i].attr); + /* Calls module_kobj_release */ + kobject_unregister(&mod->mkobj->kobj); +} + /* Free a module, remove from lists, etc (must hold module mutex). */ static void free_module(struct module *mod) { @@ -952,6 +1099,8 @@ static void free_module(struct module *mod) list_del(&mod->list); spin_unlock_irq(&modlist_lock); + mod_kobject_remove(mod); + /* Arch-specific cleanup. */ module_arch_cleanup(mod); @@ -1556,6 +1705,11 @@ static struct module *load_module(void __user *umod, / sizeof(struct kernel_param), NULL); } + err = mod_sysfs_setup(mod, + (struct kernel_param *) + sechdrs[setupindex].sh_addr, + sechdrs[setupindex].sh_size + / sizeof(struct kernel_param)); if (err < 0) goto arch_cleanup; @@ -1891,3 +2045,9 @@ struct module *module_text_address(unsigned long addr) void struct_module(struct module *mod) { return; } EXPORT_SYMBOL(struct_module); #endif + +static int __init modules_init(void) +{ + return subsystem_register(&module_subsys); +} +__initcall(modules_init); diff --git a/kernel/params.c b/kernel/params.c index 59667bce9ce0..5f38ee74a637 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -161,7 +161,7 @@ int parse_args(const char *name, \ if (!val) return -EINVAL; \ l = strtolfn(val, &endp, 0); \ - if (endp == val || *endp || ((type)l != l)) \ + if (endp == val || ((type)l != l)) \ return -EINVAL; \ *((type *)kp->arg) = l; \ return 0; \ diff --git a/lib/kobject.c b/lib/kobject.c index 5e8d1ed70716..5c2ade5739be 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -385,13 +385,17 @@ EXPORT_SYMBOL(kobject_set_name); * @new_name: object's new name */ -void kobject_rename(struct kobject * kobj, char *new_name) +int kobject_rename(struct kobject * kobj, char *new_name) { + int error = 0; + kobj = kobject_get(kobj); if (!kobj) - return; - sysfs_rename_dir(kobj, new_name); + return -EINVAL; + error = sysfs_rename_dir(kobj, new_name); kobject_put(kobj); + + return error; } /** diff --git a/net/core/dev.c b/net/core/dev.c index 02a254cd369c..0540421da57b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -792,6 +792,8 @@ int dev_alloc_name(struct net_device *dev, const char *name) */ int dev_change_name(struct net_device *dev, char *newname) { + int err = 0; + ASSERT_RTNL(); if (dev->flags & IFF_UP) @@ -801,7 +803,7 @@ int dev_change_name(struct net_device *dev, char *newname) return -EINVAL; if (strchr(newname, '%')) { - int err = dev_alloc_name(dev, newname); + err = dev_alloc_name(dev, newname); if (err < 0) return err; strcpy(newname, dev->name); @@ -811,12 +813,14 @@ int dev_change_name(struct net_device *dev, char *newname) else strlcpy(dev->name, newname, IFNAMSIZ); - hlist_del(&dev->name_hlist); - hlist_add_head(&dev->name_hlist, dev_name_hash(dev->name)); + err = class_device_rename(&dev->class_dev, dev->name); + if (!err) { + hlist_del(&dev->name_hlist); + hlist_add_head(&dev->name_hlist, dev_name_hash(dev->name)); + notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev); + } - class_device_rename(&dev->class_dev, dev->name); - notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev); - return 0; + return err; } /** |
