driver core: export MAJOR/MINOR to the hotplug env
Subsystems (3)
cover driver core: export MAJOR/MINOR to the hotplug env
This patch sequence moves the creation of the sysfs "dev" file of the class devices into the driver core. The struct class_device contains a dev_t value now. If set, the driver core will create the "dev" file containing the major/minor numbers automatically.
The MAJOR/MINOR values are also exported to the hotplug environment. This makes it easy for userspace, especially udev to know if it should wait for a "dev" file to create a device node or if it can just ignore the event. We currently carry a compiled in blacklist around for that reason.
It would also be possible to run some "tiny udev" while sysfs is not available - just by reading the hotplug call or the netlink-uevent.
Thanks,
Kay
Quoted 5 lines · Kay Sievers
This patch sequence moves the creation of the sysfs "dev" file of the class devices into the driver core. The struct
class_devicecontains adev_tvalue now. If set, the driver core will create the "dev" file containing the major/minor numbers automatically.
Quoted 7 lines
The MAJOR/MINOR values are also exported to the hotplug environment. This makes it easy for userspace, especially udev to know if it should wait for a "dev" file to create a device node or if it can just ignore the event. We currently carry a compiled in blacklist around for that reason.
It would also be possible to run some "tiny udev" while sysfs is not available - just by reading the hotplug call or the netlink-uevent.
This is great, thanks for doing this. I've applied all of these patches to my trees, and they'll show up in the next -mm release.
Hm, that class_simple interface is looking like the way we should move toward, as it's "simple" to use, instead of the more complex class code. I'll have to look at migrating more code to use it over time, or move that interface back into the class code itself...
thanks,
greg k-h
Quoted 11 lines · Greg KH
Quoted 10 lines · Kay Sievers
This patch sequence moves the creation of the sysfs "dev" file of the class devices into the driver core. The struct
class_devicecontains adev_tvalue now. If set, the driver core will create the "dev" file containing the major/minor numbers automatically.The MAJOR/MINOR values are also exported to the hotplug environment. This makes it easy for userspace, especially udev to know if it should wait for a "dev" file to create a device node or if it can just ignore the event. We currently carry a compiled in blacklist around for that reason.
Quoted 5 lines
Quoted 2 lines
It would also be possible to run some "tiny udev" while sysfs is not available - just by reading the hotplug call or the netlink-uevent.
This is great, thanks for doing this. I've applied all of these patches to my trees, and they'll show up in the next -mm release.
Fine, thanks.
Quoted 4 lines
Hm, that
class_simpleinterface is looking like the way we should move toward, as it's "simple" to use, instead of the more complex class code. I'll have to look at migrating more code to use it over time, or move that interface back into the class code itself...
Nice idea! What about keeping a list of devices belonging to a specific class in an own list in 'struct class' and maintaining that list with class_device_add(), class_device_del()?
A class driver may use that list to keep track of its own devices if wanted and class_simple would not be needed anymore as everything would be available in the core.
Thanks,
Kay
Quoted 9 lines · Kay Sievers
Quoted 4 lines · Greg KH
Hm, that
class_simpleinterface is looking like the way we should move toward, as it's "simple" to use, instead of the more complex class code. I'll have to look at migrating more code to use it over time, or move that interface back into the class code itself...Nice idea! What about keeping a list of devices belonging to a specific class in an own list in 'struct class' and maintaining that list with
class_device_add(),class_device_del()?
What would that help out with?
Quoted 3 lines
A class driver may use that list to keep track of its own devices if wanted and
class_simplewould not be needed anymore as everything would be available in the core.
I must be tired, but I don't see how class_simple could be dropped if that was done. Care to explain it with pseudo-code or something?
thanks,
greg k-h
Quoted 11 lines · Greg KH
Quoted 9 lines · Kay Sievers
Quoted 4 lines · Greg KH
Hm, that
class_simpleinterface is looking like the way we should move toward, as it's "simple" to use, instead of the more complex class code. I'll have to look at migrating more code to use it over time, or move that interface back into the class code itself...Nice idea! What about keeping a list of devices belonging to a specific class in an own list in 'struct class' and maintaining that list with
class_device_add(),class_device_del()?
Quoted 8 lines
What would that help out with?
Quoted 3 lines
A class driver may use that list to keep track of its own devices if wanted and
class_simplewould not be needed anymore as everything would be available in the core.I must be tired, but I don't see how
class_simplecould be dropped if that was done. Care to explain it with pseudo-code or something?
Sure, here is a patch for illustration. The needed class_simple logic is moved to the core. It uses the class's children list to keep track of the created devices. The tty and the input layer are changed to use the new interface.
With this change, a driver still has the convenient class support without the need to care about allocation and release functions. But it uses the same data structures like a full featured class implementation.
Thanks,
Kay
===== drivers/base/class.c 1.58 vs edited =====
drivers/base/class.c +102 -0
@@ -16,6 +16,7 @@#include <linux/init.h> #include <linux/string.h> #include <linux/kdev_t.h> +#include <linux/err.h> #include "base.h" #define to_class_attr (_attr) container_of(_attr, struct class_attribute, attr)
@@ -161,6 +162,52 @@ void class_unregister(struct class * clssubsystem_unregister (&cls->subsys); } +static void class_create_release (struct class *cls) +{ +kfree (cls); +} + +static void class_device_create_release (struct class_device *class_dev) +{ +kfree (class_dev); +} + +struct class *class_create (char *name) +{ +struct class *cls; +int retval; + + cls =kmalloc (sizeof (struct class ),GFP_KERNEL ); +if (!cls) { + retval = -ENOMEM ; + goto error; + } +memset (cls,0x00 ,sizeof (struct class )); + + cls->name = name; + cls->class_release = class_create_release; + cls->release = class_device_create_release; + + retval =class_register (cls); +if (retval) + goto error; + +return cls; + +error: +kfree (cls); +return ERR_PTR (retval); +} +EXPORT_SYMBOL_GPL (class_create); + +void class_destroy (struct class *cls) +{ +if ((cls ==NULL ) || (IS_ERR (cls))) +return ; + +class_unregister (cls); +} +EXPORT_SYMBOL_GPL (class_destroy);/* Class Device Stuff */
@@ -468,6 +515,42 @@ int class_device_register(struct class_dreturn class_device_add (class_dev); } +struct class_device *class_device_create (struct class *cls,dev_t devt, +struct device *device,char *fmt, ...) +{ +va_list args; +struct class_device *class_dev; +int retval; + +if (cls ==NULL ||IS_ERR (cls)) +return ERR_PTR (-ENODEV ); + + class_dev =kmalloc (sizeof (struct class_device ),GFP_KERNEL ); +if (!class_dev) { + retval = -ENOMEM ; + goto error; + } +memset (class_dev,0x00 ,sizeof (struct class_device )); + + class_dev->devt = devt; + class_dev->dev = device; + class_dev->class = cls; + +va_start (args, fmt); +vsnprintf (class_dev->class_id,BUS_ID_SIZE , fmt, args); +va_end (args); + retval =class_device_register (class_dev); +if (retval) + goto error; + +return 0 ; + +error: +kfree (class_dev); +return ERR_PTR (retval); +} +EXPORT_SYMBOL_GPL (class_device_create); +void class_device_del (struct class_device *class_dev) {struct class * parent = class_dev->class;
@@ -499,6 +582,25 @@ void class_device_unregister(struct clasclass_device_del (class_dev);class_device_put (class_dev); } + +void class_device_destroy (struct class *cls,dev_t devt) +{ +struct class_device *class_dev =NULL ; +struct class_device *class_dev_tmp; + +down_write (&cls->subsys.rwsem); +list_for_each_entry (class_dev_tmp, &cls->children, node) { +if (class_dev_tmp->devt == devt) { + class_dev = class_dev_tmp; +break ; + } + } +up_write (&cls->subsys.rwsem); + +if (class_dev) +class_device_unregister (class_dev); +} +EXPORT_SYMBOL_GPL (class_device_destroy);int class_device_rename (struct class_device *class_dev,char *new_name) {
===== drivers/char/tty_io.c 1.159 vs edited =====
drivers/char/tty_io.c +8 -8
@@ -2650,7 +2650,7 @@ static void tty_default_put_char(struct tty->driver->write (tty, &ch,1 ); } -static struct class_simple *tty_class; +static struct class *tty_class;/** * tty_register_device - register a tty device
@@ -2683,7 +2683,7 @@ void tty_register_device(struct tty_drivpty_line_name (driver, index, name); elsetty_line_name (driver ,index ,name ); -class_simple_device_add (tty_class, dev, device, name); +class_device_create (tty_class, dev, device, name); }/**
@@ -2697,7 +2697,7 @@ void tty_register_device(struct tty_drivvoid tty_unregister_device (struct tty_driver *driver,unsigned index) {devfs_remove ("%s%d" , driver->devfs_name, index + driver->name_base); -class_simple_device_remove (MKDEV (driver->major, driver->minor_start) + index); +class_device_destroy (tty_class,MKDEV (driver->major, driver->minor_start) + index); }EXPORT_SYMBOL (tty_register_device);
@@ -2914,7 +2914,7 @@ extern int vty_init(void);static int __init tty_class_init (void ) { - tty_class =class_simple_create (THIS_MODULE ,"tty" ); + tty_class =class_create ("tty" );if (IS_ERR (tty_class))return PTR_ERR (tty_class);return 0 ;
@@ -2943,14 +2943,14 @@ static int __init tty_init(void) register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)panic ("Couldn't register /dev/tty driver\n" );devfs_mk_cdev (MKDEV (TTYAUX_MAJOR ,0 ),S_IFCHR |S_IRUGO |S_IWUGO ,"tty" ); -class_simple_device_add (tty_class,MKDEV (TTYAUX_MAJOR ,0 ),NULL ,"tty" ); +class_device_create (tty_class,MKDEV (TTYAUX_MAJOR ,0 ),NULL ,"tty" );cdev_init (&console_cdev, &console_fops);if (cdev_add (&console_cdev,MKDEV (TTYAUX_MAJOR ,1 ),1 ) ||register_chrdev_region (MKDEV (TTYAUX_MAJOR ,1 ),1 ,"/dev/console" ) <0 )panic ("Couldn't register /dev/console driver\n" );devfs_mk_cdev (MKDEV (TTYAUX_MAJOR ,1 ),S_IFCHR |S_IRUSR |S_IWUSR ,"console" ); -class_simple_device_add (tty_class,MKDEV (TTYAUX_MAJOR ,1 ),NULL ,"console" ); +class_device_create (tty_class,MKDEV (TTYAUX_MAJOR ,1 ),NULL ,"console" );#ifdef CONFIG_UNIX98_PTYS cdev_init (&ptmx_cdev, &ptmx_fops);
@@ -2958,7 +2958,7 @@ static int __init tty_init(void) register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0)panic ("Couldn't register /dev/ptmx driver\n" );devfs_mk_cdev (MKDEV (TTYAUX_MAJOR ,2 ),S_IFCHR |S_IRUGO |S_IWUGO ,"ptmx" ); -class_simple_device_add (tty_class,MKDEV (TTYAUX_MAJOR ,2 ),NULL ,"ptmx" ); +class_device_create (tty_class,MKDEV (TTYAUX_MAJOR ,2 ),NULL ,"ptmx" );#endif #ifdef CONFIG_VT
@@ -2967,7 +2967,7 @@ static int __init tty_init(void) register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)panic ("Couldn't register /dev/tty0 driver\n" );devfs_mk_cdev (MKDEV (TTY_MAJOR ,0 ),S_IFCHR |S_IRUSR |S_IWUSR ,"vc/0" ); -class_simple_device_add (tty_class,MKDEV (TTY_MAJOR ,0 ),NULL ,"tty0" ); +class_device_create (tty_class,MKDEV (TTY_MAJOR ,0 ),NULL ,"tty0" );vty_init ();#endif
===== drivers/input/evdev.c 1.42 vs edited =====
drivers/input/evdev.c +5 -4
@@ -428,9 +428,9 @@ static struct input_handle *evdev_connecdevfs_mk_cdev (MKDEV (INPUT_MAJOR ,EVDEV_MINOR_BASE + minor),S_IFCHR |S_IRUGO |S_IWUSR ,"input/event%d" , minor); -class_simple_device_add (input_class, -MKDEV (INPUT_MAJOR ,EVDEV_MINOR_BASE + minor), - dev->dev,"event%d" , minor); +class_device_create (input_class, +MKDEV (INPUT_MAJOR ,EVDEV_MINOR_BASE + minor), + dev->dev,"event%d" , minor);return &evdev->handle; }
@@ -439,7 +439,8 @@ static void evdev_disconnect(struct inpu {struct evdev *evdev = handle->private; -class_simple_device_remove (MKDEV (INPUT_MAJOR ,EVDEV_MINOR_BASE + evdev->minor)); +class_device_destroy (input_class, +MKDEV (INPUT_MAJOR ,EVDEV_MINOR_BASE + evdev->minor));devfs_remove ("input/event%d" , evdev->minor); evdev->exist =0 ;
===== drivers/input/input.c 1.49 vs edited =====
drivers/input/input.c +5 -5
@@ -709,13 +709,13 @@ static int __init input_proc_init(void)static inline int input_proc_init (void ) {return 0 ; }#endif -struct class_simple *input_class; +struct class *input_class;static int __init input_init (void ) {int retval = -ENOMEM ; - input_class =class_simple_create (THIS_MODULE ,"input" ); + input_class =class_create ("input" );if (IS_ERR (input_class))return PTR_ERR (input_class);input_proc_init ();
@@ -725,7 +725,7 @@ static int __init input_init(void)remove_proc_entry ("devices" , proc_bus_input_dir);remove_proc_entry ("handlers" , proc_bus_input_dir);remove_proc_entry ("input" , proc_bus); -class_simple_destroy (input_class); +class_destroy (input_class);return retval; }
@@ -735,7 +735,7 @@ static int __init input_init(void)remove_proc_entry ("handlers" , proc_bus_input_dir);remove_proc_entry ("input" , proc_bus);unregister_chrdev (INPUT_MAJOR ,"input" ); -class_simple_destroy (input_class); +class_destroy (input_class); }return retval; }
@@ -748,7 +748,7 @@ static void __exit input_exit(void)devfs_remove ("input" );unregister_chrdev (INPUT_MAJOR ,"input" ); -class_simple_destroy (input_class); +class_destroy (input_class); }subsys_initcall (input_init);
===== drivers/input/joydev.c 1.28 vs edited =====
drivers/input/joydev.c +4 -4
@@ -453,9 +453,9 @@ static struct input_handle *joydev_connedevfs_mk_cdev (MKDEV (INPUT_MAJOR ,JOYDEV_MINOR_BASE + minor),S_IFCHR |S_IRUGO |S_IWUSR ,"input/js%d" , minor); -class_simple_device_add (input_class, -MKDEV (INPUT_MAJOR ,JOYDEV_MINOR_BASE + minor), - dev->dev,"js%d" , minor); +class_device_create (input_class, +MKDEV (INPUT_MAJOR ,JOYDEV_MINOR_BASE + minor), + dev->dev,"js%d" , minor);return &joydev->handle; }
@@ -464,7 +464,7 @@ static void joydev_disconnect(struct inp {struct joydev *joydev = handle->private; -class_simple_device_remove (MKDEV (INPUT_MAJOR ,JOYDEV_MINOR_BASE + joydev->minor)); +class_device_destroy (MKDEV (INPUT_MAJOR ,JOYDEV_MINOR_BASE + joydev->minor));devfs_remove ("input/js%d" , joydev->minor); joydev->exist =0 ;
===== drivers/input/mousedev.c 1.46 vs edited =====
drivers/input/mousedev.c +9 -7
@@ -633,9 +633,9 @@ static struct input_handle *mousedev_condevfs_mk_cdev (MKDEV (INPUT_MAJOR ,MOUSEDEV_MINOR_BASE + minor),S_IFCHR |S_IRUGO |S_IWUSR ,"input/mouse%d" , minor); -class_simple_device_add (input_class, -MKDEV (INPUT_MAJOR ,MOUSEDEV_MINOR_BASE + minor), - dev->dev,"mouse%d" , minor); +class_device_create (input_class, +MKDEV (INPUT_MAJOR ,MOUSEDEV_MINOR_BASE + minor), + dev->dev,"mouse%d" , minor);return &mousedev->handle; }
@@ -644,7 +644,8 @@ static void mousedev_disconnect(struct i {struct mousedev *mousedev = handle->private; -class_simple_device_remove (MKDEV (INPUT_MAJOR ,MOUSEDEV_MINOR_BASE + mousedev->minor)); +class_device_destroy (input_class, +MKDEV (INPUT_MAJOR ,MOUSEDEV_MINOR_BASE + mousedev->minor));devfs_remove ("input/mouse%d" , mousedev->minor); mousedev->exist =0 ;
@@ -717,8 +718,8 @@ static int __init mousedev_init(void)devfs_mk_cdev (MKDEV (INPUT_MAJOR ,MOUSEDEV_MINOR_BASE +MOUSEDEV_MIX ),S_IFCHR |S_IRUGO |S_IWUSR ,"input/mice" ); -class_simple_device_add (input_class,MKDEV (INPUT_MAJOR ,MOUSEDEV_MINOR_BASE +MOUSEDEV_MIX ), -NULL ,"mice" ); +class_device_create (input_class, +MKDEV (INPUT_MAJOR ,MOUSEDEV_MINOR_BASE +MOUSEDEV_MIX ),NULL ,"mice" );#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX if (!(psaux_registered = !misc_register (&psaux_mouse)))
@@ -737,7 +738,8 @@ static void __exit mousedev_exit(void)misc_deregister (&psaux_mouse);#endif devfs_remove ("input/mice" ); -class_simple_device_remove (MKDEV (INPUT_MAJOR ,MOUSEDEV_MINOR_BASE +MOUSEDEV_MIX )); +class_device_destroy (input_class, +MKDEV (INPUT_MAJOR ,MOUSEDEV_MINOR_BASE +MOUSEDEV_MIX ));input_unregister_handler (&mousedev_handler); }
===== drivers/input/tsdev.c 1.20 vs edited =====
drivers/input/tsdev.c +5 -4
@@ -416,9 +416,9 @@ static struct input_handle *tsdev_connec S_IFCHR|S_IRUGO|S_IWUSR, "input/ts%d", minor);devfs_mk_cdev (MKDEV (INPUT_MAJOR ,TSDEV_MINOR_BASE + minor +TSDEV_MINORS /2 ),S_IFCHR |S_IRUGO |S_IWUSR ,"input/tsraw%d" , minor); -class_simple_device_add (input_class, -MKDEV (INPUT_MAJOR ,TSDEV_MINOR_BASE + minor), - dev->dev,"ts%d" , minor); +class_device_create (input_class, +MKDEV (INPUT_MAJOR ,TSDEV_MINOR_BASE + minor), + dev->dev,"ts%d" , minor);return &tsdev->handle; }
@@ -427,7 +427,8 @@ static void tsdev_disconnect(struct inpu {struct tsdev *tsdev = handle->private; -class_simple_device_remove (MKDEV (INPUT_MAJOR ,TSDEV_MINOR_BASE + tsdev->minor)); +class_device_destroy (input_class, +MKDEV (INPUT_MAJOR ,TSDEV_MINOR_BASE + tsdev->minor));devfs_remove ("input/ts%d" , tsdev->minor);devfs_remove ("input/tsraw%d" , tsdev->minor); tsdev->exist =0 ;
===== include/linux/device.h 1.136 vs edited =====
include/linux/device.h +6 -0
@@ -245,6 +245,12 @@ struct class_interface {extern int class_interface_register (struct class_interface *);extern void class_interface_unregister (struct class_interface *); +extern struct class *class_create (char *name); +extern void class_destroy (struct class *cls); +extern struct class_device *class_device_create (struct class *cls,dev_t devt, +struct device *device,char *fmt, ...); +extern void class_device_destroy (struct class *cls,dev_t devt); +/* interface for class simple stuff */ extern struct class_simple *class_simple_create (struct module *owner,char *name);extern void class_simple_destroy (struct class_simple *cs);
===== include/linux/input.h 1.54 vs edited =====
include/linux/input.h +1 -1
@@ -1010,7 +1010,7 @@ static inline void input_set_abs_params( dev->absbit[LONG (axis)] |=BIT (axis); } -extern struct class_simple *input_class; +extern struct class *input_class;#endif #endif