From d2d64066658aa83fe0a8c992cdace1dd785b555b Mon Sep 17 00:00:00 2001 From: "Michael E. Brown" Date: Tue, 27 Apr 2004 21:50:29 -0700 Subject: [PATCH] add SMBIOS tables to sysfs -- UPDATED --- drivers/firmware/Kconfig | 8 ++ drivers/firmware/Makefile | 1 + drivers/firmware/smbios.c | 266 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/firmware/smbios.h | 53 +++++++++ 4 files changed, 328 insertions(+) create mode 100644 drivers/firmware/smbios.c create mode 100644 drivers/firmware/smbios.h diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index f5a883e5b223..e8a7c9a53d9e 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..c111e87a8303 --- /dev/null +++ b/drivers/firmware/smbios.c @@ -0,0 +1,266 @@ +/* + * linux/drivers/firmware/smbios.c + * Copyright (C) 2004 Dell Inc. + * by Michael Brown + * 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/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 +#include +#include +#include +#include +#include "smbios.h" + +MODULE_AUTHOR("Michael Brown "); +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; + struct kobject kobj; +}; + +/* 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 = to_smbios_device(kobj); + 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 = to_smbios_device(kobj); + 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 inline void +smbios_device_unregister(struct smbios_device *sdev) +{ + sysfs_remove_bin_file(&sdev->kobj, &tep_attr ); + sysfs_remove_bin_file(&sdev->kobj, &table_attr ); + kobject_unregister(&sdev->kobj); +} + +static int +smbios_device_register(struct smbios_device *sdev) +{ + int error; + + if (!sdev) + return 1; + + kobject_set_name(&sdev->kobj, "smbios"); + kobj_set_kset_s(sdev,smbios_subsys); + error = kobject_register(&sdev->kobj); + if (!error){ + sysfs_create_bin_file(&sdev->kobj, &tep_attr ); + sysfs_create_bin_file(&sdev->kobj, &table_attr ); + } + return error; +} + +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; + + rc = smbios_device_register(&the_smbios_device); + + if (rc) + firmware_unregister(&smbios_subsys); + + return rc; +} + +static void __exit +smbios_exit(void) +{ + smbios_device_unregister(&the_smbios_device); + 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 + * 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 + +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 */ -- cgit v1.2.3 From 5faee7a33fdf49041c451e3099615c52f0f3ace1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 27 Apr 2004 21:51:03 -0700 Subject: My cleanups to the smbios driver. --- drivers/firmware/smbios.c | 44 +++++++++++++------------------------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/drivers/firmware/smbios.c b/drivers/firmware/smbios.c index c111e87a8303..583ab0939c08 100644 --- a/drivers/firmware/smbios.c +++ b/drivers/firmware/smbios.c @@ -10,9 +10,9 @@ * * This code takes information provided by SMBIOS tables * and presents it in sysfs as: - * /sys/firmware/smbios/smbios - * |--> /table_entry_point - * |--> /table + * /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 @@ -42,7 +42,6 @@ MODULE_LICENSE("GPL"); struct smbios_device { struct smbios_table_entry_point table_eps; unsigned int smbios_table_real_length; - struct kobject kobj; }; /* there shall be only one */ @@ -143,7 +142,7 @@ static ssize_t smbios_read_table_entry_point(struct kobject *kobj, char *buffer, loff_t pos, size_t size) { - struct smbios_device *sdev = to_smbios_device(kobj); + struct smbios_device *sdev = &the_smbios_device; const char *p = (const char *)&(sdev->table_eps); unsigned int count = size > sizeof(sdev->table_eps) ? @@ -156,7 +155,7 @@ static ssize_t smbios_read_table(struct kobject *kobj, char *buffer, loff_t pos, size_t size) { - struct smbios_device *sdev = to_smbios_device(kobj); + struct smbios_device *sdev = &the_smbios_device; u8 *buf; unsigned int count = sdev->smbios_table_real_length - pos; int i = 0; @@ -204,30 +203,16 @@ static struct kobj_type ktype_smbios = { static decl_subsys(smbios,&ktype_smbios,NULL); -static inline void -smbios_device_unregister(struct smbios_device *sdev) +static void smbios_device_unregister(void) { - sysfs_remove_bin_file(&sdev->kobj, &tep_attr ); - sysfs_remove_bin_file(&sdev->kobj, &table_attr ); - kobject_unregister(&sdev->kobj); + sysfs_remove_bin_file(&smbios_subsys.kset.kobj, &tep_attr ); + sysfs_remove_bin_file(&smbios_subsys.kset.kobj, &table_attr ); } -static int -smbios_device_register(struct smbios_device *sdev) +static void __init smbios_device_register(void) { - int error; - - if (!sdev) - return 1; - - kobject_set_name(&sdev->kobj, "smbios"); - kobj_set_kset_s(sdev,smbios_subsys); - error = kobject_register(&sdev->kobj); - if (!error){ - sysfs_create_bin_file(&sdev->kobj, &tep_attr ); - sysfs_create_bin_file(&sdev->kobj, &table_attr ); - } - return error; + sysfs_create_bin_file(&smbios_subsys.kset.kobj, &tep_attr ); + sysfs_create_bin_file(&smbios_subsys.kset.kobj, &table_attr ); } static int __init @@ -247,10 +232,7 @@ smbios_init(void) if (rc) return rc; - rc = smbios_device_register(&the_smbios_device); - - if (rc) - firmware_unregister(&smbios_subsys); + smbios_device_register(); return rc; } @@ -258,7 +240,7 @@ smbios_init(void) static void __exit smbios_exit(void) { - smbios_device_unregister(&the_smbios_device); + smbios_device_unregister(); firmware_unregister(&smbios_subsys); } -- cgit v1.2.3 From 0b5efc22326143061ef8df753eecd5904a032d98 Mon Sep 17 00:00:00 2001 From: Marcel Sebek Date: Sun, 2 May 2004 06:28:38 -0700 Subject: [PATCH] Class support for ppdev.c --- drivers/char/ppdev.c | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) 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 #include #include +#include #include #include #include @@ -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); } -- cgit v1.2.3 From 106c7ec864a92235baaaf2db2844455dc0da05db Mon Sep 17 00:00:00 2001 From: "Hanna V. Linder" Date: Sun, 2 May 2004 06:28:58 -0700 Subject: [PATCH] Add class support to drivers/char/ip2main.c --- drivers/char/ip2main.c | 46 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) 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 #include #include +#include #include #include @@ -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); -- cgit v1.2.3 From 2e615ce145671178357e6e6fdf0e4e8f79a1a60e Mon Sep 17 00:00:00 2001 From: "Hanna V. Linder" Date: Sun, 2 May 2004 06:29:22 -0700 Subject: [PATCH] add class support to drivers/block/paride/pg.c This patch adds class support to pg.c, the parallel port generic ATAPI device driver. I have verified it compiles but do not have the hardware. If someone does and could test that would be helpful. --- drivers/block/paride/pg.c | 48 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 10 deletions(-) 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 #include #include +#include #include @@ -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); -- cgit v1.2.3 From f8d8c844cea6e5ccca597e6a347433ee25a735e5 Mon Sep 17 00:00:00 2001 From: "Hanna V. Linder" Date: Sun, 2 May 2004 06:29:41 -0700 Subject: [PATCH] add class support to drivers/block/paride/pt.c This patch adds class support to pt.c which "the high-level driver for parallel port ATAPI tape drives based on chips supported by the paride module." Which I dont have in order to test. I have verified it compiles but can not test it. If someone who has the hardware could I would appreciate it. --- drivers/block/paride/pt.c | 54 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 9 deletions(-) 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 #include #include +#include #include @@ -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++) -- cgit v1.2.3 From 571c7bece37d02c92f22fdc2b6a6b09be2931cdd Mon Sep 17 00:00:00 2001 From: "Hanna V. Linder" Date: Sun, 2 May 2004 06:29:59 -0700 Subject: [PATCH] add class support to drivers/char/tipar.c This patch adds class support to the Texas Instruments graphing calculators with a parallel link cable. I have verified it compiles. If someone has the hardware please verify it works. --- drivers/char/tipar.c | 49 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) 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 #include /* DevFs support */ #include /* Our code depend on parport */ +#include /* * 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"); -- cgit v1.2.3 From 38cf5e9249fc650ab8f3cf3d4ee01f41237c8906 Mon Sep 17 00:00:00 2001 From: Kenn Humborg Date: Sun, 2 May 2004 06:30:19 -0700 Subject: [PATCH] Re: Platform device matching On Mon, Apr 26, 2004 at 12:27:33AM +0100, Russell King wrote: > So, this comment needs updating: > > * So, extract the from the device, and compare it against > * the name of the driver. Return whether they match or not. Want a patch? --- drivers/base/platform.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 is the * enumerated instance of the device, like '0' or '42'. * Driver IDs are simply "". - * So, extract the from the device, and compare it against - * the name of the driver. Return whether they match or not. + * So, extract the 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) -- cgit v1.2.3 From 361f1a1aaa9072e57088d5fff92f3d5b793f9442 Mon Sep 17 00:00:00 2001 From: Olaf Hering Date: Tue, 4 May 2004 00:16:58 -0700 Subject: [PATCH] add simple class for adb This adds /sys/class/adb/, removes unused devfs lines and updates a comment to match reality. --- drivers/macintosh/adb.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) 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 #include #include -#include #include #include #include @@ -36,6 +35,7 @@ #include #include #include +#include #include #include #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"); } -- cgit v1.2.3 From 7a54ce5445641f9dcb1ba9ea9d1f347417c58daf Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 5 May 2004 00:40:43 -0700 Subject: Driver core: handle error if we run out of memory in kmap code --- drivers/base/map.c | 7 +++++++ 1 file changed, 7 insertions(+) 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; -- cgit v1.2.3 From 009196b400ed5494adee8f093b2b3c360c579a09 Mon Sep 17 00:00:00 2001 From: Daniele Bellucci Date: Wed, 5 May 2004 01:10:00 -0700 Subject: [PATCH] missing audit in bus_register() |How about using a goto on the error path to clean up properly |instead of the different return sections. .. here goes Take 2: --- drivers/base/bus.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/base/bus.c b/drivers/base/bus.c index ebb0ec31fa41..c17668d9f536 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -555,21 +555,36 @@ struct bus_type * find_bus(char * name) */ int bus_register(struct bus_type * bus) { + int retval; + kobject_set_name(&bus->subsys.kset.kobj,bus->name); 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; } -- cgit v1.2.3 From b863a25d3dca56d4c84c3cdcca089a42f1c088be Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 7 May 2004 00:22:49 -0700 Subject: Add modules to sysfs This patch adds basic kobject support to struct module, and it creates a /sys/module directory which contains all of the individual modules. Each module currently exports the refcount (if they are unloadable) and any module paramaters that are marked exportable in sysfs. Was written by me and Rusty over and over many times during the past 6 months. --- include/linux/module.h | 25 +++++++ include/linux/moduleparam.h | 4 +- kernel/module.c | 160 ++++++++++++++++++++++++++++++++++++++++++++ kernel/params.c | 2 +- 4 files changed, 188 insertions(+), 3 deletions(-) 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 #include #include +#include +#include #include #include @@ -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/kernel/module.c b/kernel/module.c index 5a9846bb1992..4c2af4d67df2 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -504,6 +504,22 @@ static int try_stop_module(struct module *mod, int flags, int *forced) return stop_machine_run(__try_stop_module, &sref, NR_CPUS); } +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; +} + unsigned int module_refcount(struct module *mod) { unsigned int i, total = 0; @@ -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; \ -- cgit v1.2.3 From 31c4141f53d272c501d5a71bf34a52301d60ab29 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 10 May 2004 23:32:24 -0700 Subject: Module attributes: fix build error if CONFIG_MODULE_UNLOAD=n Thanks to Andrew Morton for pointing this out to me. --- kernel/module.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/kernel/module.c b/kernel/module.c index 4c2af4d67df2..bbea6f7d710c 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) @@ -504,22 +520,6 @@ static int try_stop_module(struct module *mod, int flags, int *forced) return stop_machine_run(__try_stop_module, &sref, NR_CPUS); } -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; -} - unsigned int module_refcount(struct module *mod) { unsigned int i, total = 0; -- cgit v1.2.3 From e15f75b5b12dcb5665475dd12f1745ecd8c7bc43 Mon Sep 17 00:00:00 2001 From: "Hanna V. Linder" Date: Tue, 11 May 2004 00:31:21 -0700 Subject: [PATCH] Add class support to drivers/net/wan/cosa.c This patch adds sysfs class support to the Cosa driver. I have verified it compiles but do not have the hardware to test it. If someone could that would be helpful. --- drivers/net/wan/cosa.c | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) 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 #include #include +#include #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 \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 Date: Tue, 11 May 2004 02:31:46 -0700 Subject: [PATCH] kobject_set_name - error handling 1) kobject_set_name-cleanup-01.patch This patch corrects the following by checking the reutrn code from kobject_set_name(). bus_add_driver() bus_register() sys_dev_register() o The following patch cleansup the kobject_set_name() users. Basically checking return code from kobject_set_name(). There can be error returns like -ENOMEM or -EFAULT from kobject_set_name() if the name length exceeds KOBJ_NAME_LEN. --- drivers/base/bus.c | 11 +++++++++-- drivers/base/sys.c | 5 ++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/base/bus.c b/drivers/base/bus.c index c17668d9f536..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); @@ -557,7 +561,10 @@ int bus_register(struct bus_type * bus) { int retval; - kobject_set_name(&bus->subsys.kset.kobj,bus->name); + retval = kobject_set_name(&bus->subsys.kset.kobj,bus->name); + if (retval) + goto out; + subsys_set_kset(bus,bus_subsys); retval = subsystem_register(&bus->subsys); if (retval) 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 */ -- cgit v1.2.3 From 8bcb3f98404202e085120d21fcc6e66eb90677a7 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Tue, 11 May 2004 02:35:42 -0700 Subject: [PATCH] fix dev_printk to work even in the absence of an attached driver --- include/linux/device.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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...) \ -- cgit v1.2.3 From 427ac533882bb6f0a138cce26cfabd8467c081e3 Mon Sep 17 00:00:00 2001 From: Maneesh Soni Date: Tue, 11 May 2004 02:43:50 -0700 Subject: [PATCH] kobject/sysfs race fix The following patch fixes the race involved between unregistering a kobject and simultaneously opeing a corresponding attribute file in sysfs. Ideally sysfs should take a ref. to the kobject as long as it has dentries referring to the kobjects, but because of current limitations in module/kobject ref counting, sysfs's pinning of kobject leads to hang/delays in rmmod of certain modules. The patch checks for unhashed dentries in check_perm() while opening a sysfs file. If the dentry is still hashed then it goes ahead and takes the ref to kobject. This done under the per dentry lock. It does this in the inline routine sysfs_get_kobject(dentry). --- fs/sysfs/bin.c | 2 +- fs/sysfs/file.c | 2 +- fs/sysfs/sysfs.h | 13 +++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) 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/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; +} -- cgit v1.2.3 From f00beb55bde63c01a8ee200e33c7f3d72a4e92b7 Mon Sep 17 00:00:00 2001 From: Max Asbock Date: Thu, 13 May 2004 22:12:11 -0700 Subject: [PATCH] add ibmasm driver warning message [note, I changed this a bit to be nicer on the system log, greg k-h] --- drivers/misc/Kconfig | 8 +++++++- drivers/misc/ibmasm/module.c | 7 +++++++ 2 files changed, 14 insertions(+), 1 deletion(-) 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: -- cgit v1.2.3 From 51c0c34c8f0ac464c97aaef4754e7dc163dd586e Mon Sep 17 00:00:00 2001 From: Maneesh Soni Date: Thu, 13 May 2004 22:13:00 -0700 Subject: [PATCH] sysfs_rename_dir-cleanup o The following patch cleans up sysfs_rename_dir(). It now checks the return code of kobject_set_name() and propagates the error code to its callers. Because of this there are changes in the following two APIs. Both return int instead of void. int sysfs_rename_dir(struct kobject * kobj, const char *new_name) int kobject_rename(struct kobject * kobj, char *new_name) --- drivers/base/class.c | 6 ++++-- fs/sysfs/dir.c | 19 ++++++++++++++----- include/linux/kobject.h | 2 +- include/linux/sysfs.h | 2 +- lib/kobject.c | 10 +++++++--- net/core/dev.c | 16 ++++++++++------ 6 files changed, 37 insertions(+), 18 deletions(-) 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/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/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/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/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 43c273e74a7d..b14b8a310e4a 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; } /** -- cgit v1.2.3