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);
