Commit 3baa0c9d authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

[PATCH] USB: add usb class support for usb drivers that use the USB major

This also consolodates the devfs calls for the USB drivers.
parent 264e19ff
......@@ -66,6 +66,10 @@ static struct file_operations usb_fops = {
.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 @@ int usb_major_init(void)
}
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 @@ int usb_register_dev (struct file_operations *fops, int minor, int num_minors, i
* 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);
......
......@@ -88,6 +88,7 @@ struct usb_host_interface {
* 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_interface {
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 @@ struct usb_driver {
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 struct bus_type usb_bus_type;
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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment