Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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: default avatarKay Sievers <kay.sievers@vrfy.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 87840289
Loading
Loading
Loading
Loading
+24 −1
Original line number Diff line number Diff line
@@ -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)"
+51 −43
Original line number Diff line number Diff line
@@ -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;
		}
	if (dev->devt == MKDEV(USB_DEVICE_MAJOR, minor))
		return 1;
	return 0;
}
	up(&usb_device_class->sem);

	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);
}
+13 −45
Original line number Diff line number Diff line
@@ -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,33 +633,19 @@ 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,
			   buffer, buffer_size, &length,
			   "INTERFACE=%d/%d/%d",
			   alt->desc.bInterfaceClass,
			   alt->desc.bInterfaceSubClass,
			   alt->desc.bInterfaceProtocol))
			   "BUSNUM=%03d",
			   usb_dev->bus->busnum))
		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))
			   "DEVNUM=%03d",
			   usb_dev->devnum))
		return -ENOMEM;
	}

	envp[i] = NULL;

	return 0;
}

@@ -687,7 +656,6 @@ static int usb_uevent(struct device *dev, char **envp,
{
	return -ENODEV;
}

#endif	/* CONFIG_HOTPLUG */

/**
+7 −3
Original line number Diff line number Diff line
@@ -1367,9 +1367,13 @@ 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);
	if (err) {
+1 −1
Original line number Diff line number Diff line
@@ -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");
Loading