diff options
| author | Kay Sievers <kay.sievers@vrfy.org> | 2007-03-13 10:59:31 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-04-27 16:28:37 -0400 |
| commit | 9f8b17e643fe6aa505629658445849397bda4e4f (patch) | |
| tree | 30c45914f7be9f355db30964323673c7d37080e8 | |
| parent | 87840289637e9ea95118ebd76e2e335fdcddd725 (diff) | |
USB: make usbdevices export their device nodes instead of using a separate class
o The "real" usb-devices export now a device node which can
populate /dev/bus/usb.
o The usb_device class is optional now and can be disabled in the
kernel config. Major/minor of the "real" devices and class devices
are the same.
o The environment of the usb-device event contains DEVNUM and BUSNUM to
help udev and get rid of the ugly udev rule we need for the class
devices.
o The usb-devices and usb-interfaces share the same bus, so I used
the new "struct device_type" to let these devices identify
themselves. This also removes the current logic of using a magic
platform-pointer.
The name of the device_type is also added to the environment
which makes it easier to distinguish the different kinds of devices
on the same subsystem.
It looks like this:
add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:1d.1/usb2/2-1
SUBSYSTEM=usb
SEQNUM=1533
MAJOR=189
MINOR=131
DEVTYPE=usb_device
PRODUCT=46d/c03e/2000
TYPE=0/0/0
BUSNUM=002
DEVNUM=004
This udev rule works as a replacement for usb_device class devices:
SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", \
NAME="bus/usb/$env{BUSNUM}/$env{DEVNUM}", MODE="0644"
Updated patch, which needs the device_type patches in Greg's tree.
I also got a bugzilla assigned for this. :)
https://bugzilla.novell.com/show_bug.cgi?id=250659
Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
| -rw-r--r-- | drivers/usb/core/Kconfig | 25 | ||||
| -rw-r--r-- | drivers/usb/core/devio.c | 94 | ||||
| -rw-r--r-- | drivers/usb/core/driver.c | 58 | ||||
| -rw-r--r-- | drivers/usb/core/hub.c | 10 | ||||
| -rw-r--r-- | drivers/usb/core/inode.c | 2 | ||||
| -rw-r--r-- | drivers/usb/core/message.c | 65 | ||||
| -rw-r--r-- | drivers/usb/core/usb.c | 20 | ||||
| -rw-r--r-- | drivers/usb/core/usb.h | 14 | ||||
| -rw-r--r-- | include/linux/usb.h | 10 |
9 files changed, 183 insertions, 115 deletions
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 2fc0f88a3d86..f493fb1eaa27 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig | |||
| @@ -31,7 +31,30 @@ config USB_DEVICEFS | |||
| 31 | For the format of the various /proc/bus/usb/ files, please read | 31 | For the format of the various /proc/bus/usb/ files, please read |
| 32 | <file:Documentation/usb/proc_usb_info.txt>. | 32 | <file:Documentation/usb/proc_usb_info.txt>. |
| 33 | 33 | ||
| 34 | Most users want to say Y here. | 34 | Usbfs files can't handle Access Control Lists (ACL), which are the |
| 35 | default way to grant access to USB devices for untrusted users of a | ||
| 36 | desktop system. The usbfs functionality is replaced by real | ||
| 37 | device-nodes managed by udev. These nodes live in /dev/bus/usb and | ||
| 38 | are used by libusb. | ||
| 39 | |||
| 40 | config USB_DEVICE_CLASS | ||
| 41 | bool "USB device class-devices (DEPRECATED)" | ||
| 42 | depends on USB | ||
| 43 | default n | ||
| 44 | ---help--- | ||
| 45 | Userspace access to USB devices is granted by device-nodes exported | ||
| 46 | directly from the usbdev in sysfs. Old versions of the driver | ||
| 47 | core and udev needed additional class devices to export device nodes. | ||
| 48 | |||
| 49 | These additional devices are difficult to handle in userspace, if | ||
| 50 | information about USB interfaces must be available. One device contains | ||
| 51 | the device node, the other device contains the interface data. Both | ||
| 52 | devices are at the same level in sysfs (siblings) and one can't access | ||
| 53 | the other. The device node created directly by the usbdev is the parent | ||
| 54 | device of the interface and therefore easily accessible from the interface | ||
| 55 | event. | ||
| 56 | |||
| 57 | This option provides backward compatibility if needed. | ||
| 35 | 58 | ||
| 36 | config USB_DYNAMIC_MINORS | 59 | config USB_DYNAMIC_MINORS |
| 37 | bool "Dynamic USB minor allocation (EXPERIMENTAL)" | 60 | bool "Dynamic USB minor allocation (EXPERIMENTAL)" |
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index fc3545ddb06e..e023f3d56248 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c | |||
| @@ -57,7 +57,6 @@ | |||
| 57 | 57 | ||
| 58 | #define USB_MAXBUS 64 | 58 | #define USB_MAXBUS 64 |
| 59 | #define USB_DEVICE_MAX USB_MAXBUS * 128 | 59 | #define USB_DEVICE_MAX USB_MAXBUS * 128 |
| 60 | static struct class *usb_device_class; | ||
| 61 | 60 | ||
| 62 | /* Mutual exclusion for removal, open, and release */ | 61 | /* Mutual exclusion for removal, open, and release */ |
| 63 | DEFINE_MUTEX(usbfs_mutex); | 62 | DEFINE_MUTEX(usbfs_mutex); |
| @@ -514,22 +513,25 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsig | |||
| 514 | return ret; | 513 | return ret; |
| 515 | } | 514 | } |
| 516 | 515 | ||
| 517 | static struct usb_device *usbdev_lookup_minor(int minor) | 516 | static int __match_minor(struct device *dev, void *data) |
| 518 | { | 517 | { |
| 519 | struct device *device; | 518 | int minor = *((int *)data); |
| 520 | struct usb_device *udev = NULL; | ||
| 521 | 519 | ||
| 522 | down(&usb_device_class->sem); | 520 | if (dev->devt == MKDEV(USB_DEVICE_MAJOR, minor)) |
| 523 | list_for_each_entry(device, &usb_device_class->devices, node) { | 521 | return 1; |
| 524 | if (device->devt == MKDEV(USB_DEVICE_MAJOR, minor)) { | 522 | return 0; |
| 525 | udev = device->platform_data; | 523 | } |
| 526 | break; | ||
| 527 | } | ||
| 528 | } | ||
| 529 | up(&usb_device_class->sem); | ||
| 530 | 524 | ||
| 531 | return udev; | 525 | static struct usb_device *usbdev_lookup_by_minor(int minor) |
| 532 | }; | 526 | { |
| 527 | struct device *dev; | ||
| 528 | |||
| 529 | dev = bus_find_device(&usb_bus_type, NULL, &minor, __match_minor); | ||
| 530 | if (!dev) | ||
| 531 | return NULL; | ||
| 532 | put_device(dev); | ||
| 533 | return container_of(dev, struct usb_device, dev); | ||
| 534 | } | ||
| 533 | 535 | ||
| 534 | /* | 536 | /* |
| 535 | * file operations | 537 | * file operations |
| @@ -548,11 +550,14 @@ static int usbdev_open(struct inode *inode, struct file *file) | |||
| 548 | goto out; | 550 | goto out; |
| 549 | 551 | ||
| 550 | ret = -ENOENT; | 552 | ret = -ENOENT; |
| 551 | /* check if we are called from a real node or usbfs */ | 553 | /* usbdev device-node */ |
| 552 | if (imajor(inode) == USB_DEVICE_MAJOR) | 554 | if (imajor(inode) == USB_DEVICE_MAJOR) |
| 553 | dev = usbdev_lookup_minor(iminor(inode)); | 555 | dev = usbdev_lookup_by_minor(iminor(inode)); |
| 556 | #ifdef CONFIG_USB_DEVICEFS | ||
| 557 | /* procfs file */ | ||
| 554 | if (!dev) | 558 | if (!dev) |
| 555 | dev = inode->i_private; | 559 | dev = inode->i_private; |
| 560 | #endif | ||
| 556 | if (!dev) | 561 | if (!dev) |
| 557 | goto out; | 562 | goto out; |
| 558 | ret = usb_autoresume_device(dev); | 563 | ret = usb_autoresume_device(dev); |
| @@ -1570,7 +1575,7 @@ static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wai | |||
| 1570 | return mask; | 1575 | return mask; |
| 1571 | } | 1576 | } |
| 1572 | 1577 | ||
| 1573 | const struct file_operations usbfs_device_file_operations = { | 1578 | const struct file_operations usbdev_file_operations = { |
| 1574 | .llseek = usbdev_lseek, | 1579 | .llseek = usbdev_lseek, |
| 1575 | .read = usbdev_read, | 1580 | .read = usbdev_read, |
| 1576 | .poll = usbdev_poll, | 1581 | .poll = usbdev_poll, |
| @@ -1579,50 +1584,53 @@ const struct file_operations usbfs_device_file_operations = { | |||
| 1579 | .release = usbdev_release, | 1584 | .release = usbdev_release, |
| 1580 | }; | 1585 | }; |
| 1581 | 1586 | ||
| 1582 | static int usbdev_add(struct usb_device *dev) | 1587 | #ifdef CONFIG_USB_DEVICE_CLASS |
| 1588 | static struct class *usb_classdev_class; | ||
| 1589 | |||
| 1590 | static int usb_classdev_add(struct usb_device *dev) | ||
| 1583 | { | 1591 | { |
| 1584 | int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1); | 1592 | int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1); |
| 1585 | 1593 | ||
| 1586 | dev->usbfs_dev = device_create(usb_device_class, &dev->dev, | 1594 | dev->usb_classdev = device_create(usb_classdev_class, &dev->dev, |
| 1587 | MKDEV(USB_DEVICE_MAJOR, minor), | 1595 | MKDEV(USB_DEVICE_MAJOR, minor), |
| 1588 | "usbdev%d.%d", dev->bus->busnum, dev->devnum); | 1596 | "usbdev%d.%d", dev->bus->busnum, dev->devnum); |
| 1589 | if (IS_ERR(dev->usbfs_dev)) | 1597 | if (IS_ERR(dev->usb_classdev)) |
| 1590 | return PTR_ERR(dev->usbfs_dev); | 1598 | return PTR_ERR(dev->usb_classdev); |
| 1591 | 1599 | ||
| 1592 | dev->usbfs_dev->platform_data = dev; | ||
| 1593 | return 0; | 1600 | return 0; |
| 1594 | } | 1601 | } |
| 1595 | 1602 | ||
| 1596 | static void usbdev_remove(struct usb_device *dev) | 1603 | static void usb_classdev_remove(struct usb_device *dev) |
| 1597 | { | 1604 | { |
| 1598 | device_unregister(dev->usbfs_dev); | 1605 | device_unregister(dev->usb_classdev); |
| 1599 | } | 1606 | } |
| 1600 | 1607 | ||
| 1601 | static int usbdev_notify(struct notifier_block *self, unsigned long action, | 1608 | static int usb_classdev_notify(struct notifier_block *self, |
| 1602 | void *dev) | 1609 | unsigned long action, void *dev) |
| 1603 | { | 1610 | { |
| 1604 | switch (action) { | 1611 | switch (action) { |
| 1605 | case USB_DEVICE_ADD: | 1612 | case USB_DEVICE_ADD: |
| 1606 | if (usbdev_add(dev)) | 1613 | if (usb_classdev_add(dev)) |
| 1607 | return NOTIFY_BAD; | 1614 | return NOTIFY_BAD; |
| 1608 | break; | 1615 | break; |
| 1609 | case USB_DEVICE_REMOVE: | 1616 | case USB_DEVICE_REMOVE: |
| 1610 | usbdev_remove(dev); | 1617 | usb_classdev_remove(dev); |
| 1611 | break; | 1618 | break; |
| 1612 | } | 1619 | } |
