The kernel supports already a device authorization because of wireless USB. These is usable for wired usb devices, too. These new interface authorization allows to enable or disable individual interfaces per bitmask instead a whole device. The authorization is done stepwise for each respective interface group (defined by respective mask) to avoid side effects. The interface group mask would updated at authorization. After that the usb interfaces are enabled. Finally, the device would probed. Signed-off-by: Stefan Koch <skoch@xxxxxxx> --- drivers/usb/core/hub.c | 117 +++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/core/message.c | 4 +- drivers/usb/core/usb.h | 1 + 3 files changed, 121 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 3b71516..75b1555 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2577,6 +2577,123 @@ out_authorized: return result; } +/* + * authorize or deauthorize an usb interface per bitmask + * + * @dev: device structure + * @mask: authorization mask + * 1 is to authorize, 0 is not to authorize + * + * example: 0b00000101 authorizes interface 1 and 3 + * and deauthorizes all other interfaces + * + * Returns: 0 at success, value<0 at failure + */ +int usb_device_set_mask(struct device *dev, u32 mask) +{ + struct usb_device *usb_dev = NULL; + struct usb_host_config *actconfig = NULL; + u32 oldmask = 0; + unsigned i, n, confNr; + + /* get device lock */ + device_lock(dev); + + usb_dev = to_usb_device(dev); + + if (usb_dev) + actconfig = usb_dev->actconfig; + + if (!usb_dev || !actconfig) { + device_unlock(dev); + return -ENODEV; + } + + usb_dev->mask_changed = 1; + + /* get current configuration number */ + confNr = actconfig->desc.bConfigurationValue; + + /* number of device's interfaces */ + n = actconfig->desc.bNumInterfaces; + + oldmask = usb_dev->mask; + + /* set the flags first and unbind interface if applicable */ + for (i = 0; i < n; i++) { + bool oldauth = oldmask & (1 << i) ? true : false; + bool auth = mask & (1 << i) ? true : false; + struct usb_interface *intf = actconfig->interface[i]; + struct device *intf_dev = NULL; + struct usb_device *intf_usb_dev = NULL; + + if (!intf) { + device_unlock(dev); + return -ENODEV; + } + + intf_dev = &intf->dev; + + if (intf_dev) + intf_usb_dev = to_usb_device(intf_dev); + + if (!intf_dev || !intf_usb_dev) { + device_unlock(dev); + return -ENODEV; + } + + if (!auth && oldauth) { /* deauthorize */ + usb_dev->mask &= ~(1 << i); /* update mask */ + intf->authorized = 0; /* update flag */ + intf->unregistering = 1; + usb_forced_unbind_intf(intf); + } else if (auth && !oldauth) { /* authorize */ + usb_dev->mask |= (1 << i); /* update mask */ + intf->authorized = 1; /* update flag */ + } + } + + /* disable or enable interfaces and delete device if applicable */ + for (i = 0; i < n; i++) { + bool oldauth = oldmask & (1 << i) ? true : false; + bool auth = mask & (1 << i) ? true : false; + struct usb_interface *intf = actconfig->interface[i]; + + if (!intf) { + device_unlock(dev); + return -ENODEV; + } + + if (!auth && oldauth) { /* deauthorize */ + /* disable interface's endpoints for usage */ + usb_disable_interface(usb_dev, intf, true); + } else if (auth && !oldauth) { /* authorize */ + /* enable interface's endpoints for usage */ + usb_enable_interface(usb_dev, intf, true); + } + } + + /* probe an interface at authorization */ + for (i = 0; i < n; i++) { + bool oldauth = oldmask & (1 << i) ? true : false; + bool auth = mask & (1 << i) ? true : false; + struct usb_interface *intf = actconfig->interface[i]; + + if (!intf) { + device_unlock(dev); + return -ENODEV; + } + + if (auth && !oldauth) { /* deauthorize */ + bus_probe_device(&intf->dev); + } + } + + /* release device lock */ + device_unlock(dev); + + return 0; +} /* Returns 1 if @hub is a WUSB root hub, 0 otherwise */ static unsigned hub_is_wusb(struct usb_hub *hub) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index a353a42..448afba 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1830,7 +1830,8 @@ free_interfaces: intf->intf_assoc = find_iad(dev, cp, alt->desc.bInterfaceNumber); intf->cur_altsetting = alt; - usb_enable_interface(dev, intf, true); + if (intf->authorized) + usb_enable_interface(dev, intf, true); intf->dev.parent = &dev->dev; intf->dev.driver = NULL; intf->dev.bus = &usb_bus_type; @@ -1910,6 +1911,7 @@ free_interfaces: } usb_autosuspend_device(dev); + dev->mask_changed = 0; /* reset mask change status */ return 0; } EXPORT_SYMBOL_GPL(usb_set_configuration); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 7eb1e26..994b0c3 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -27,6 +27,7 @@ extern void usb_release_interface_cache(struct kref *ref); extern void usb_disable_device(struct usb_device *dev, int skip_ep0); extern int usb_deauthorize_device(struct usb_device *); extern int usb_authorize_device(struct usb_device *); +extern int usb_device_set_mask(struct device *dev, u32 mask); extern void usb_detect_quirks(struct usb_device *udev); extern void usb_detect_interface_quirks(struct usb_device *udev); extern int usb_remove_device(struct usb_device *udev); -- 2.1.4 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html