ChangeSet 1.1066, 2003/05/07 00:19:52-07:00, greg@kroah.com [PATCH] USB: add usb class support for usb drivers that use the USB major This also consolodates the devfs calls for the USB drivers. drivers/usb/core/file.c | 123 ++++++++++++++++++++++++++++++++---------------- include/linux/usb.h | 28 ++++++++++ 2 files changed, 110 insertions(+), 41 deletions(-) diff -Nru a/drivers/usb/core/file.c b/drivers/usb/core/file.c --- a/drivers/usb/core/file.c Wed May 7 11:12:38 2003 +++ b/drivers/usb/core/file.c Wed May 7 11:12:38 2003 @@ -66,6 +66,10 @@ .open = usb_open, }; +static struct class usb_class = { + .name = "usb", +}; + int usb_major_init(void) { if (register_chrdev(USB_MAJOR, "usb", &usb_fops)) { @@ -74,41 +78,53 @@ } devfs_mk_dir("usb"); + class_register(&usb_class); return 0; } void usb_major_cleanup(void) { + class_unregister(&usb_class); devfs_remove("usb"); unregister_chrdev(USB_MAJOR, "usb"); } +static ssize_t show_dev(struct class_device *class_dev, char *buf) +{ + struct usb_interface *intf = class_dev_to_usb_interface(class_dev); + dev_t dev = MKDEV(USB_MAJOR, intf->minor); + return sprintf(buf, "%04x\n", dev); +} +static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL); + /** * usb_register_dev - register a USB device, and ask for a minor number - * @fops: the file operations for this USB device - * @minor: the requested starting minor for this device. - * @num_minors: number of minor numbers requested for this device - * @start_minor: place to put the new starting minor number + * @intf: pointer to the usb_interface that is being registered + * @class_driver: pointer to the usb_class_driver for this device * * This should be called by all USB drivers that use the USB major number. * If CONFIG_USB_DYNAMIC_MINORS is enabled, the minor number will be * dynamically allocated out of the list of available ones. If it is not * enabled, the minor number will be based on the next available free minor, - * starting at the requested @minor. + * starting at the class_driver->minor_base. + * + * This function also creates the devfs file for the usb device, if devfs + * is enabled, and creates a usb class device in the sysfs tree. * * usb_deregister_dev() must be called when the driver is done with * the minor numbers given out by this function. * * Returns -EINVAL if something bad happens with trying to register a - * device, and 0 on success, alone with a value that the driver should - * use in start_minor. + * device, and 0 on success. */ -int usb_register_dev (struct file_operations *fops, int minor, int num_minors, int *start_minor) +int usb_register_dev(struct usb_interface *intf, + struct usb_class_driver *class_driver) { - int i; - int j; - int good_spot; int retval = -EINVAL; + int minor_base = class_driver->minor_base; + int minor = 0; + char name[DEVICE_ID_SIZE]; + char *temp; #ifdef CONFIG_USB_DYNAMIC_MINORS /* @@ -116,65 +132,94 @@ * at zero to pack the devices into the smallest available space with * no holes in the minor range. */ - minor = 0; + minor_base = 0; #endif + intf->minor = -1; - dbg ("asking for %d minors, starting at %d", num_minors, minor); + dbg ("looking for a minor, starting at %d", minor_base); - if (fops == NULL) + if (class_driver->fops == NULL) goto exit; - *start_minor = 0; spin_lock (&minor_lock); - for (i = minor; i < MAX_USB_MINORS; ++i) { - if (usb_minors[i]) + for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) { + if (usb_minors[minor]) continue; - good_spot = 1; - for (j = 1; j <= num_minors-1; ++j) - if (usb_minors[i+j]) { - good_spot = 0; - break; - } - if (good_spot == 0) - continue; - - *start_minor = i; - dbg("found a minor chunk free, starting at %d", i); - for (i = *start_minor; i < (*start_minor + num_minors); ++i) - usb_minors[i] = fops; + usb_minors[minor] = class_driver->fops; retval = 0; - goto exit; + break; } -exit: spin_unlock (&minor_lock); + + if (retval) + goto exit; + + intf->minor = minor; + + /* handle the devfs registration */ + snprintf(name, DEVICE_ID_SIZE, class_driver->name, minor - minor_base); + devfs_register(NULL, name, 0, USB_MAJOR, minor, class_driver->mode, + class_driver->fops, NULL); + + /* create a usb class device for this usb interface */ + memset(&intf->class_dev, 0x00, sizeof(struct class_device)); + intf->class_dev.class = &usb_class; + intf->class_dev.dev = &intf->dev; + + temp = strrchr(name, '/'); + if (temp && (temp[1] != 0x00)) + ++temp; + else + temp = name; + snprintf(intf->class_dev.class_id, BUS_ID_SIZE, "%s", temp); + class_device_register(&intf->class_dev); + class_device_create_file (&intf->class_dev, &class_device_attr_dev); +exit: return retval; } EXPORT_SYMBOL(usb_register_dev); /** * usb_deregister_dev - deregister a USB device's dynamic minor. - * @num_minors: number of minor numbers to put back. - * @start_minor: the starting minor number + * @intf: pointer to the usb_interface that is being deregistered + * @class_driver: pointer to the usb_class_driver for this device * * Used in conjunction with usb_register_dev(). This function is called * when the USB driver is finished with the minor numbers gotten from a * call to usb_register_dev() (usually when the device is disconnected * from the system.) + * + * This function also cleans up the devfs file for the usb device, if devfs + * is enabled, and removes the usb class device from the sysfs tree. * * This should be called by all drivers that use the USB major number. */ -void usb_deregister_dev (int num_minors, int start_minor) +void usb_deregister_dev(struct usb_interface *intf, + struct usb_class_driver *class_driver) { - int i; + int minor_base = class_driver->minor_base; + char name[DEVICE_ID_SIZE]; + +#ifdef CONFIG_USB_DYNAMIC_MINORS + minor_base = 0; +#endif + + if (intf->minor == -1) + return; - dbg ("removing %d minors starting at %d", num_minors, start_minor); + dbg ("removing %d minor", intf->minor); spin_lock (&minor_lock); - for (i = start_minor; i < (start_minor + num_minors); ++i) - usb_minors[i] = NULL; + usb_minors[intf->minor] = NULL; spin_unlock (&minor_lock); + + snprintf(name, DEVICE_ID_SIZE, class_driver->name, intf->minor - minor_base); + devfs_remove (name); + + class_device_unregister(&intf->class_dev); + intf->minor = -1; } EXPORT_SYMBOL(usb_deregister_dev); diff -Nru a/include/linux/usb.h b/include/linux/usb.h --- a/include/linux/usb.h Wed May 7 11:12:38 2003 +++ b/include/linux/usb.h Wed May 7 11:12:38 2003 @@ -88,6 +88,7 @@ * function of the driver, after it has been assigned a minor * number from the USB core by calling usb_register_dev(). * @dev: driver model's view of this device + * @class_dev: driver model's class view of this device. * * USB device drivers attach to interfaces on a physical device. Each * interface encapsulates a single high level function, such as feeding @@ -121,8 +122,10 @@ struct usb_driver *driver; /* driver */ int minor; /* minor number this interface is bound to */ struct device dev; /* interface specific device info */ + struct class_device class_dev; }; #define to_usb_interface(d) container_of(d, struct usb_interface, dev) +#define class_dev_to_usb_interface(d) container_of(d, struct usb_interface, class_dev) #define interface_to_usbdev(intf) \ container_of(intf->dev.parent, struct usb_device, dev) @@ -441,6 +444,25 @@ extern struct bus_type usb_bus_type; +/** + * struct usb_class_driver - identifies a USB driver that wants to use the USB major number + * @name: devfs name for this driver. Will also be used by the driver + * class code to create a usb class device. + * @fops: pointer to the struct file_operations of this driver. + * @mode: the mode for the devfs file to be created for this driver. + * @minor_base: the start of the minor range for this driver. + * + * This structure is used for the usb_register_dev() and + * usb_unregister_dev() functions, to consolodate a number of the + * paramaters used for them. + */ +struct usb_class_driver { + char *name; + struct file_operations *fops; + mode_t mode; + int minor_base; +}; + /* * use these in module_init()/module_exit() * and don't forget MODULE_DEVICE_TABLE(usb, ...) @@ -448,8 +470,10 @@ extern int usb_register(struct usb_driver *); extern void usb_deregister(struct usb_driver *); -extern int usb_register_dev(struct file_operations *fops, int minor, int num_minors, int *start_minor); -extern void usb_deregister_dev(int num_minors, int start_minor); +extern int usb_register_dev(struct usb_interface *intf, + struct usb_class_driver *class_driver); +extern void usb_deregister_dev(struct usb_interface *intf, + struct usb_class_driver *class_driver); extern int usb_device_probe(struct device *dev); extern int usb_device_remove(struct device *dev);