Commit 9f8b17e6 authored by Kay Sievers's avatar Kay Sievers Committed by Greg Kroah-Hartman

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=250659Signed-off-by: default avatarKay Sievers <kay.sievers@vrfy.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 87840289
......@@ -31,7 +31,30 @@ config USB_DEVICEFS
For the format of the various /proc/bus/usb/ files, please read
<file:Documentation/usb/proc_usb_info.txt>.
Most users want to say Y here.
Usbfs files can't handle Access Control Lists (ACL), which are the
default way to grant access to USB devices for untrusted users of a
desktop system. The usbfs functionality is replaced by real
device-nodes managed by udev. These nodes live in /dev/bus/usb and
are used by libusb.
config USB_DEVICE_CLASS
bool "USB device class-devices (DEPRECATED)"
depends on USB
default n
---help---
Userspace access to USB devices is granted by device-nodes exported
directly from the usbdev in sysfs. Old versions of the driver
core and udev needed additional class devices to export device nodes.
These additional devices are difficult to handle in userspace, if
information about USB interfaces must be available. One device contains
the device node, the other device contains the interface data. Both
devices are at the same level in sysfs (siblings) and one can't access
the other. The device node created directly by the usbdev is the parent
device of the interface and therefore easily accessible from the interface
event.
This option provides backward compatibility if needed.
config USB_DYNAMIC_MINORS
bool "Dynamic USB minor allocation (EXPERIMENTAL)"
......
......@@ -57,7 +57,6 @@
#define USB_MAXBUS 64
#define USB_DEVICE_MAX USB_MAXBUS * 128
static struct class *usb_device_class;
/* Mutual exclusion for removal, open, and release */
DEFINE_MUTEX(usbfs_mutex);
......@@ -514,22 +513,25 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsig
return ret;
}
static struct usb_device *usbdev_lookup_minor(int minor)
static int __match_minor(struct device *dev, void *data)
{
struct device *device;
struct usb_device *udev = NULL;
int minor = *((int *)data);
down(&usb_device_class->sem);
list_for_each_entry(device, &usb_device_class->devices, node) {
if (device->devt == MKDEV(USB_DEVICE_MAJOR, minor)) {
udev = device->platform_data;
break;
}
}
up(&usb_device_class->sem);
if (dev->devt == MKDEV(USB_DEVICE_MAJOR, minor))
return 1;
return 0;
}
return udev;
};
static struct usb_device *usbdev_lookup_by_minor(int minor)
{
struct device *dev;
dev = bus_find_device(&usb_bus_type, NULL, &minor, __match_minor);
if (!dev)
return NULL;
put_device(dev);
return container_of(dev, struct usb_device, dev);
}
/*
* file operations
......@@ -548,11 +550,14 @@ static int usbdev_open(struct inode *inode, struct file *file)
goto out;
ret = -ENOENT;
/* check if we are called from a real node or usbfs */
/* usbdev device-node */
if (imajor(inode) == USB_DEVICE_MAJOR)
dev = usbdev_lookup_minor(iminor(inode));
dev = usbdev_lookup_by_minor(iminor(inode));
#ifdef CONFIG_USB_DEVICEFS
/* procfs file */
if (!dev)
dev = inode->i_private;
#endif
if (!dev)
goto out;
ret = usb_autoresume_device(dev);
......@@ -1570,7 +1575,7 @@ static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wai
return mask;
}
const struct file_operations usbfs_device_file_operations = {
const struct file_operations usbdev_file_operations = {
.llseek = usbdev_lseek,
.read = usbdev_read,
.poll = usbdev_poll,
......@@ -1579,50 +1584,53 @@ const struct file_operations usbfs_device_file_operations = {
.release = usbdev_release,
};
static int usbdev_add(struct usb_device *dev)
#ifdef CONFIG_USB_DEVICE_CLASS
static struct class *usb_classdev_class;
static int usb_classdev_add(struct usb_device *dev)
{
int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1);
dev->usbfs_dev = device_create(usb_device_class, &dev->dev,
dev->usb_classdev = device_create(usb_classdev_class, &dev->dev,
MKDEV(USB_DEVICE_MAJOR, minor),
"usbdev%d.%d", dev->bus->busnum, dev->devnum);
if (IS_ERR(dev->usbfs_dev))
return PTR_ERR(dev->usbfs_dev);
if (IS_ERR(dev->usb_classdev))
return PTR_ERR(dev->usb_classdev);
dev->usbfs_dev->platform_data = dev;
return 0;
}
static void usbdev_remove(struct usb_device *dev)
static void usb_classdev_remove(struct usb_device *dev)
{
device_unregister(dev->usbfs_dev);
device_unregister(dev->usb_classdev);
}
static int usbdev_notify(struct notifier_block *self, unsigned long action,
void *dev)
static int usb_classdev_notify(struct notifier_block *self,
unsigned long action, void *dev)
{
switch (action) {
case USB_DEVICE_ADD:
if (usbdev_add(dev))
if (usb_classdev_add(dev))
return NOTIFY_BAD;
break;
case USB_DEVICE_REMOVE:
usbdev_remove(dev);
usb_classdev_remove(dev);
break;
}
return NOTIFY_OK;
}
static struct notifier_block usbdev_nb = {
.notifier_call = usbdev_notify,
.notifier_call = usb_classdev_notify,
};
#endif
static struct cdev usb_device_cdev = {
.kobj = {.name = "usb_device", },
.owner = THIS_MODULE,
};
int __init usbdev_init(void)
int __init usb_devio_init(void)
{
int retval;
......@@ -1632,38 +1640,38 @@ int __init usbdev_init(void)
err("unable to register minors for usb_device");
goto out;
}
cdev_init(&usb_device_cdev, &usbfs_device_file_operations);
cdev_init(&usb_device_cdev, &usbdev_file_operations);
retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);
if (retval) {
err("unable to get usb_device major %d", USB_DEVICE_MAJOR);
goto error_cdev;
}
usb_device_class = class_create(THIS_MODULE, "usb_device");
if (IS_ERR(usb_device_class)) {
#ifdef CONFIG_USB_DEVICE_CLASS
usb_classdev_class = class_create(THIS_MODULE, "usb_device");
if (IS_ERR(usb_classdev_class)) {
err("unable to register usb_device class");
retval = PTR_ERR(usb_device_class);
goto error_class;
retval = PTR_ERR(usb_classdev_class);
cdev_del(&usb_device_cdev);
usb_classdev_class = NULL;
goto out;
}
usb_register_notify(&usbdev_nb);
#endif
out:
return retval;
error_class:
usb_device_class = NULL;
cdev_del(&usb_device_cdev);
error_cdev:
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
goto out;
}
void usbdev_cleanup(void)
void usb_devio_cleanup(void)
{
#ifdef CONFIG_USB_DEVICE_CLASS
usb_unregister_notify(&usbdev_nb);
class_destroy(usb_device_class);
class_destroy(usb_classdev_class);
#endif
cdev_del(&usb_device_cdev);
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
}
......@@ -574,23 +574,10 @@ static int usb_device_match(struct device *dev, struct device_driver *drv)
}
#ifdef CONFIG_HOTPLUG
/*
* This sends an uevent to userspace, typically helping to load driver
* or other modules, configure the device, and more. Drivers can provide
* a MODULE_DEVICE_TABLE to help with module loading subtasks.
*
* We're called either from khubd (the typical case) or from root hub
* (init, kapmd, modprobe, rmmod, etc), but the agents need to handle
* delays in event delivery. Use sysfs (and DEVPATH) to make sure the
* device (and this configuration!) are still present.
*/
static int usb_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
{
struct usb_interface *intf;
struct usb_device *usb_dev;
struct usb_host_interface *alt;
int i = 0;
int length = 0;
......@@ -600,13 +587,11 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp,
/* driver is often null here; dev_dbg() would oops */
pr_debug ("usb %s: uevent\n", dev->bus_id);
if (is_usb_device(dev)) {
if (is_usb_device(dev))
usb_dev = to_usb_device(dev);
alt = NULL;
} else {
intf = to_usb_interface(dev);
else {
struct usb_interface *intf = to_usb_interface(dev);
usb_dev = interface_to_usbdev(intf);
alt = intf->cur_altsetting;
}
if (usb_dev->devnum < 0) {
......@@ -621,9 +606,7 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp,
#ifdef CONFIG_USB_DEVICEFS
/* If this is available, userspace programs can directly read
* all the device descriptors we don't tell them about. Or
* even act as usermode drivers.
*
* FIXME reduce hardwired intelligence here
* act as usermode drivers.
*/
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
......@@ -650,44 +633,29 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp,
usb_dev->descriptor.bDeviceProtocol))
return -ENOMEM;
if (!is_usb_device(dev)) {
if (add_uevent_var(envp, num_envp, &i,
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"INTERFACE=%d/%d/%d",
alt->desc.bInterfaceClass,
alt->desc.bInterfaceSubClass,
alt->desc.bInterfaceProtocol))
return -ENOMEM;
"BUSNUM=%03d",
usb_dev->bus->busnum))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i,
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
le16_to_cpu(usb_dev->descriptor.idVendor),
le16_to_cpu(usb_dev->descriptor.idProduct),
le16_to_cpu(usb_dev->descriptor.bcdDevice),
usb_dev->descriptor.bDeviceClass,
usb_dev->descriptor.bDeviceSubClass,
usb_dev->descriptor.bDeviceProtocol,
alt->desc.bInterfaceClass,
alt->desc.bInterfaceSubClass,
alt->desc.bInterfaceProtocol))
return -ENOMEM;
}
"DEVNUM=%03d",
usb_dev->devnum))
return -ENOMEM;
envp[i] = NULL;
return 0;
}
#else
static int usb_uevent(struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size)
int num_envp, char *buffer, int buffer_size)
{
return -ENODEV;
}
#endif /* CONFIG_HOTPLUG */
/**
......
......@@ -1367,11 +1367,15 @@ int usb_new_device(struct usb_device *udev)
}
#endif
/* export the usbdev device-node for libusb */
udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
/* Register the device. The device driver is responsible
* for adding the device files to usbfs and sysfs and for
* configuring the device.
* for adding the device files to sysfs and for configuring
* the device.
*/
err = device_add (&udev->dev);
err = device_add(&udev->dev);
if (err) {
dev_err(&udev->dev, "can't device_add, error %d\n", err);
goto fail;
......
......@@ -662,7 +662,7 @@ static void usbfs_add_device(struct usb_device *dev)
sprintf (name, "%03d", dev->devnum);
dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG,
dev->bus->usbfs_dentry, dev,
&usbfs_device_file_operations,
&usbdev_file_operations,
devuid, devgid);
if (dev->usbfs_dentry == NULL) {
err ("error creating usbfs device entry");
......
......@@ -1305,7 +1305,7 @@ int usb_reset_configuration(struct usb_device *dev)
return 0;
}
static void release_interface(struct device *dev)
void usb_release_interface(struct device *dev)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_interface_cache *intfc =
......@@ -1315,6 +1315,67 @@ static void release_interface(struct device *dev)
kfree(intf);
}
#ifdef CONFIG_HOTPLUG
static int usb_if_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
{
struct usb_device *usb_dev;
struct usb_interface *intf;
struct usb_host_interface *alt;
int i = 0;
int length = 0;
if (!dev)
return -ENODEV;
/* driver is often null here; dev_dbg() would oops */
pr_debug ("usb %s: uevent\n", dev->bus_id);
intf = to_usb_interface(dev);
usb_dev = interface_to_usbdev(intf);
alt = intf->cur_altsetting;
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"INTERFACE=%d/%d/%d",
alt->desc.bInterfaceClass,
alt->desc.bInterfaceSubClass,
alt->desc.bInterfaceProtocol))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
le16_to_cpu(usb_dev->descriptor.idVendor),
le16_to_cpu(usb_dev->descriptor.idProduct),
le16_to_cpu(usb_dev->descriptor.bcdDevice),
usb_dev->descriptor.bDeviceClass,
usb_dev->descriptor.bDeviceSubClass,
usb_dev->descriptor.bDeviceProtocol,
alt->desc.bInterfaceClass,
alt->desc.bInterfaceSubClass,
alt->desc.bInterfaceProtocol))
return -ENOMEM;
envp[i] = NULL;
return 0;
}
#else
static int usb_if_uevent(struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size)
{
return -ENODEV;
}
#endif /* CONFIG_HOTPLUG */
struct device_type usb_if_device_type = {
.name = "usb_interface",
.release = usb_release_interface,
.uevent = usb_if_uevent,
};
/*
* usb_set_configuration - Makes a particular device setting be current
* @dev: the device whose configuration is being updated
......@@ -1478,8 +1539,8 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
intf->dev.parent = &dev->dev;
intf->dev.driver = NULL;
intf->dev.bus = &usb_bus_type;
intf->dev.type = &usb_if_device_type;
intf->dev.dma_mask = dev->dev.dma_mask;
intf->dev.release = release_interface;
device_initialize (&intf->dev);
mark_quiesced(intf);
sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
......
......@@ -197,6 +197,11 @@ static void usb_release_dev(struct device *dev)
kfree(udev);
}
struct device_type usb_device_type = {
.name = "usb_device",
.release = usb_release_dev,
};
#ifdef CONFIG_PM
static int ksuspend_usb_init(void)
......@@ -247,13 +252,10 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
device_initialize(&dev->dev);
dev->dev.bus = &usb_bus_type;
dev->dev.type = &usb_device_type;
dev->dev.dma_mask = bus->controller->dma_mask;
dev->dev.release = usb_release_dev;
dev->state = USB_STATE_ATTACHED;
/* This magic assignment distinguishes devices from interfaces */
dev->dev.platform_data = &usb_generic_driver;
INIT_LIST_HEAD(&dev->ep0.urb_list);
dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
......@@ -882,9 +884,9 @@ static int __init usb_init(void)
retval = usb_register(&usbfs_driver);
if (retval)
goto driver_register_failed;
retval = usbdev_init();
retval = usb_devio_init();
if (retval)
goto usbdevice_init_failed;
goto usb_devio_init_failed;
retval = usbfs_init();
if (retval)
goto fs_init_failed;
......@@ -899,8 +901,8 @@ static int __init usb_init(void)
hub_init_failed:
usbfs_cleanup();
fs_init_failed:
usbdev_cleanup();
usbdevice_init_failed:
usb_devio_cleanup();
usb_devio_init_failed:
usb_deregister(&usbfs_driver);
driver_register_failed:
usb_major_cleanup();
......@@ -927,7 +929,7 @@ static void __exit usb_exit(void)
usb_major_cleanup();
usbfs_cleanup();
usb_deregister(&usbfs_driver);
usbdev_cleanup();
usb_devio_cleanup();
usb_hub_cleanup();
usb_host_cleanup();
bus_unregister(&usb_bus_type);
......
......@@ -78,15 +78,13 @@ static inline int usb_autoresume_device(struct usb_device *udev)
extern struct workqueue_struct *ksuspend_usb_wq;
extern struct bus_type usb_bus_type;
extern struct device_type usb_device_type;
extern struct device_type usb_if_device_type;
extern struct usb_device_driver usb_generic_driver;
/* Here's how we tell apart devices and interfaces. Luckily there's
* no such thing as a platform USB device, so we can steal the use
* of the platform_data field. */
static inline int is_usb_device(const struct device *dev)
{
return dev->platform_data == &usb_generic_driver;
return dev->type == &usb_device_type;
}
/* Do the same for device drivers and interface drivers. */
......@@ -122,11 +120,11 @@ extern const char *usbcore_name;
extern struct mutex usbfs_mutex;
extern struct usb_driver usbfs_driver;
extern const struct file_operations usbfs_devices_fops;
extern const struct file_operations usbfs_device_file_operations;
extern const struct file_operations usbdev_file_operations;
extern void usbfs_conn_disc_event(void);
extern int usbdev_init(void);
extern void usbdev_cleanup(void);
extern int usb_devio_init(void);
extern void usb_devio_cleanup(void);
struct dev_state {
struct list_head list; /* state list */
......
......@@ -299,8 +299,9 @@ struct usb_bus {
int bandwidth_int_reqs; /* number of Interrupt requests */
int bandwidth_isoc_reqs; /* number of Isoc. requests */
#ifdef CONFIG_USB_DEVICEFS
struct dentry *usbfs_dentry; /* usbfs dentry entry for the bus */
#endif
struct class_device *class_dev; /* class device for this bus */
#if defined(CONFIG_USB_MON)
......@@ -373,9 +374,12 @@ struct usb_device {
char *serial; /* iSerialNumber string, if present */
struct list_head filelist;
struct device *usbfs_dev;
#ifdef CONFIG_USB_DEVICE_CLASS
struct device *usb_classdev;
#endif
#ifdef CONFIG_USB_DEVICEFS
struct dentry *usbfs_dentry; /* usbfs dentry entry for the device */
#endif
/*
* Child devices - these can be either new devices
* (if this is a hub device), or different instances
......
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