Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usbfs: Add a new disconnect-and-claim ioctl (v2)

Apps which deal with devices which also have a kernel driver, need to do
the following:
1) Check which driver is attached, so as to not detach the wrong driver
(ie detaching usbfs while another instance of the app is using the device)
2) Detach the kernel driver
3) Claim the interface

Where moving from one step to the next for both 1-2 and 2-3 consists of
a (small) race window. So currently such apps are racy and people just live
with it.

This patch adds a new ioctl which makes it possible for apps to do this
in a race free manner. For flexibility apps can choose to:
1) Specify the driver to disconnect
2) Specify to disconnect any driver except for the one named by the app
3) Disconnect any driver

Note that if there is no driver attached, the ioctl will just act like the
regular claim-interface ioctl, this is by design, as returning an error for
this condition would open a new bag of race-conditions.

Changes in v2:
-Fix indentation of if blocks where the condition spans multiple lines

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Hans de Goede and committed by
Greg Kroah-Hartman
0837e7e5 ba2f9dff

+49
+35
drivers/usb/core/devio.c
··· 1928 1928 return 0; 1929 1929 } 1930 1930 1931 + static int proc_disconnect_claim(struct dev_state *ps, void __user *arg) 1932 + { 1933 + struct usbdevfs_disconnect_claim dc; 1934 + struct usb_interface *intf; 1935 + 1936 + if (copy_from_user(&dc, arg, sizeof(dc))) 1937 + return -EFAULT; 1938 + 1939 + intf = usb_ifnum_to_if(ps->dev, dc.interface); 1940 + if (!intf) 1941 + return -EINVAL; 1942 + 1943 + if (intf->dev.driver) { 1944 + struct usb_driver *driver = to_usb_driver(intf->dev.driver); 1945 + 1946 + if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_IF_DRIVER) && 1947 + strncmp(dc.driver, intf->dev.driver->name, 1948 + sizeof(dc.driver)) != 0) 1949 + return -EBUSY; 1950 + 1951 + if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER) && 1952 + strncmp(dc.driver, intf->dev.driver->name, 1953 + sizeof(dc.driver)) == 0) 1954 + return -EBUSY; 1955 + 1956 + dev_dbg(&intf->dev, "disconnect by usbfs\n"); 1957 + usb_driver_release_interface(driver, intf); 1958 + } 1959 + 1960 + return claimintf(ps, dc.interface); 1961 + } 1962 + 1931 1963 /* 1932 1964 * NOTE: All requests here that have interface numbers as parameters 1933 1965 * are assuming that somehow the configuration has been prevented from ··· 2132 2100 break; 2133 2101 case USBDEVFS_GET_CAPABILITIES: 2134 2102 ret = proc_get_capabilities(ps, p); 2103 + break; 2104 + case USBDEVFS_DISCONNECT_CLAIM: 2105 + ret = proc_disconnect_claim(ps, p); 2135 2106 break; 2136 2107 } 2137 2108 usb_unlock_device(dev);
+14
include/linux/usbdevice_fs.h
··· 131 131 #define USBDEVFS_CAP_NO_PACKET_SIZE_LIM 0x04 132 132 #define USBDEVFS_CAP_BULK_SCATTER_GATHER 0x08 133 133 134 + /* USBDEVFS_DISCONNECT_CLAIM flags & struct */ 135 + 136 + /* disconnect-and-claim if the driver matches the driver field */ 137 + #define USBDEVFS_DISCONNECT_CLAIM_IF_DRIVER 0x01 138 + /* disconnect-and-claim except when the driver matches the driver field */ 139 + #define USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER 0x02 140 + 141 + struct usbdevfs_disconnect_claim { 142 + unsigned int interface; 143 + unsigned int flags; 144 + char driver[USBDEVFS_MAXDRIVERNAME + 1]; 145 + }; 146 + 134 147 #ifdef __KERNEL__ 135 148 #ifdef CONFIG_COMPAT 136 149 #include <linux/compat.h> ··· 224 211 #define USBDEVFS_CLAIM_PORT _IOR('U', 24, unsigned int) 225 212 #define USBDEVFS_RELEASE_PORT _IOR('U', 25, unsigned int) 226 213 #define USBDEVFS_GET_CAPABILITIES _IOR('U', 26, __u32) 214 + #define USBDEVFS_DISCONNECT_CLAIM _IOR('U', 27, struct usbdevfs_disconnect_claim) 227 215 228 216 #endif /* _LINUX_USBDEVICE_FS_H */