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

usb: core: allow a reference device for new_id

Often, usb drivers need some driver_info to get a device to work. To
have access to driver_info when using new_id, allow to pass a reference
vendor:product tuple from which new_id will inherit driver_info.

Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Wolfram Sang and committed by
Greg Kroah-Hartman
2fc82c2d c63fe8f6

+27 -6
+8 -2
Documentation/ABI/testing/sysfs-bus-usb
··· 50 50 This may allow the driver to support more hardware than 51 51 was included in the driver's static device ID support 52 52 table at compile time. The format for the device ID is: 53 - idVendor idProduct bInterfaceClass. 53 + idVendor idProduct bInterfaceClass RefIdVendor RefIdProduct 54 54 The vendor ID and device ID fields are required, the 55 - interface class is optional. 55 + rest is optional. The Ref* tuple can be used to tell the 56 + driver to use the same driver_data for the new device as 57 + it is used for the reference device. 56 58 Upon successfully adding an ID, the driver will probe 57 59 for the device and attempt to bind to it. For example: 58 60 # echo "8086 10f5" > /sys/bus/usb/drivers/foo/new_id 61 + 62 + Here add a new device (0458:7045) using driver_data from 63 + an already supported device (0458:704c): 64 + # echo "0458 7045 0 0458 704c" > /sys/bus/usb/drivers/foo/new_id 59 65 60 66 Reading from this file will list all dynamically added 61 67 device IDs in the same format, with one entry per
+15 -3
drivers/usb/core/driver.c
··· 37 37 * and cause the driver to probe for all devices again. 38 38 */ 39 39 ssize_t usb_store_new_id(struct usb_dynids *dynids, 40 + const struct usb_device_id *id_table, 40 41 struct device_driver *driver, 41 42 const char *buf, size_t count) 42 43 { ··· 45 44 u32 idVendor = 0; 46 45 u32 idProduct = 0; 47 46 unsigned int bInterfaceClass = 0; 47 + u32 refVendor, refProduct; 48 48 int fields = 0; 49 49 int retval = 0; 50 50 51 - fields = sscanf(buf, "%x %x %x", &idVendor, &idProduct, 52 - &bInterfaceClass); 51 + fields = sscanf(buf, "%x %x %x %x %x", &idVendor, &idProduct, 52 + &bInterfaceClass, &refVendor, &refProduct); 53 53 if (fields < 2) 54 54 return -EINVAL; 55 55 ··· 68 66 69 67 dynid->id.bInterfaceClass = (u8)bInterfaceClass; 70 68 dynid->id.match_flags |= USB_DEVICE_ID_MATCH_INT_CLASS; 69 + } 70 + 71 + if (fields > 4) { 72 + const struct usb_device_id *id = id_table; 73 + 74 + for (; id->match_flags; id++) 75 + if (id->idVendor == refVendor && id->idProduct == refProduct) { 76 + dynid->id.driver_info = id->driver_info; 77 + break; 78 + } 71 79 } 72 80 73 81 spin_lock(&dynids->lock); ··· 121 109 { 122 110 struct usb_driver *usb_drv = to_usb_driver(driver); 123 111 124 - return usb_store_new_id(&usb_drv->dynids, driver, buf, count); 112 + return usb_store_new_id(&usb_drv->dynids, usb_drv->id_table, driver, buf, count); 125 113 } 126 114 static DRIVER_ATTR_RW(new_id); 127 115
+3 -1
drivers/usb/serial/bus.c
··· 125 125 const char *buf, size_t count) 126 126 { 127 127 struct usb_serial_driver *usb_drv = to_usb_serial_driver(driver); 128 - ssize_t retval = usb_store_new_id(&usb_drv->dynids, driver, buf, count); 128 + ssize_t retval = usb_store_new_id(&usb_drv->dynids, usb_drv->id_table, 129 + driver, buf, count); 129 130 130 131 if (retval >= 0 && usb_drv->usb_driver != NULL) 131 132 retval = usb_store_new_id(&usb_drv->usb_driver->dynids, 133 + usb_drv->usb_driver->id_table, 132 134 &usb_drv->usb_driver->drvwrap.driver, 133 135 buf, count); 134 136 return retval;
+1
include/linux/usb.h
··· 965 965 }; 966 966 967 967 extern ssize_t usb_store_new_id(struct usb_dynids *dynids, 968 + const struct usb_device_id *id_table, 968 969 struct device_driver *driver, 969 970 const char *buf, size_t count); 970 971