From 18f288b8d48331d6d7391486d7fa355d3bfd2a9c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 4 Apr 2024 08:25:52 +0200 Subject: usb: Add environment based device ignorelist Add the environment variable "usb_ignorelist" to prevent USB devices listed in it from being bound to drivers. This allows to ignore devices which are undesirable or trigger bugs in u-boot's USB stack. Devices emulating keyboards are one example of undesirable devices as u-boot currently supports only a single USB keyboard device. Most commonly, people run into this with Yubikeys, so let's ignore those in the default environment. Based on previous USB keyboard specific patches for the same purpose. Link: https://lore.kernel.org/u-boot/7ab604fb-0fec-4f5e-8708-7a3a7e2cb568@denx.de/ Reviewed-by: Neal Gompa Reviewed-by: Marek Vasut Signed-off-by: Janne Grunau --- common/usb.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) (limited to 'common') diff --git a/common/usb.c b/common/usb.c index 836506dcd9e..99e6b857c74 100644 --- a/common/usb.c +++ b/common/usb.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -1084,6 +1085,54 @@ static int usb_prepare_device(struct usb_device *dev, int addr, bool do_read, return 0; } +static int usb_device_is_ignored(u16 id_vendor, u16 id_product) +{ + ulong vid, pid; + char *end; + const char *cur = NULL; + + /* ignore list depends on env support */ + if (!CONFIG_IS_ENABLED(ENV_SUPPORT)) + return 0; + + cur = env_get("usb_ignorelist"); + + /* parse "usb_ignorelist" strictly */ + while (cur && cur[0] != '\0') { + vid = simple_strtoul(cur, &end, 0); + /* + * If strtoul did not parse a single digit or the next char is + * not ':' the ignore list is malformed. + */ + if (cur == end || end[0] != ':') + return -EINVAL; + + cur = end + 1; + pid = simple_strtoul(cur, &end, 0); + /* Consider '*' as wildcard for the product ID */ + if (cur == end && end[0] == '*') { + pid = U16_MAX + 1; + end++; + } + /* + * The ignore list is malformed if no product ID / wildcard was + * parsed or entries are not separated by ',' or terminated with + * '\0'. + */ + if (cur == end || (end[0] != ',' && end[0] != '\0')) + return -EINVAL; + + if (id_vendor == vid && (pid > U16_MAX || id_product == pid)) + return -ENODEV; + + if (end[0] == '\0') + break; + cur = end + 1; + } + + return 0; +} + int usb_select_config(struct usb_device *dev) { unsigned char *tmpbuf = NULL; @@ -1099,6 +1148,27 @@ int usb_select_config(struct usb_device *dev) le16_to_cpus(&dev->descriptor.idProduct); le16_to_cpus(&dev->descriptor.bcdDevice); + /* ignore devices from usb_ignorelist */ + err = usb_device_is_ignored(dev->descriptor.idVendor, + dev->descriptor.idProduct); + if (err == -ENODEV) { + debug("Ignoring USB device 0x%x:0x%x\n", + dev->descriptor.idVendor, dev->descriptor.idProduct); + return err; + } else if (err == -EINVAL) { + /* + * Continue on "usb_ignorelist" parsing errors. The list is + * parsed for each device returning the error would result in + * ignoring all USB devices. + * Since the parsing error is independent of the probed device + * report errors with printf instead of dev_err. + */ + printf("usb_ignorelist parse error in \"%s\"\n", + env_get("usb_ignorelist")); + } else if (err < 0) { + return err; + } + /* * Kingston DT Ultimate 32GB USB 3.0 seems to be extremely sensitive * about this first Get Descriptor request. If there are any other -- cgit v1.3.1 From 575c68279c41b95804bfe531ea1f7407205fcc1e Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 4 Apr 2024 08:25:53 +0200 Subject: usb: kbd: support Apple Magic Keyboards (2021) Apple USB keyboards (Magic Keyboard from 2021 (product id 0x029c)) carry the HID keyboard boot protocol on the second interface descriptor. Probe via vendor and product IDs since the class/subclass/protocol match uses the first interface descriptor. Probe the two first interface descriptors for the HID keyboard boot protocol. USB configuration descriptor for reference: | Bus 003 Device 002: ID 05ac:029c Apple, Inc. Magic Keyboard | Device Descriptor: | bLength 18 | bDescriptorType 1 | bcdUSB 2.00 | bDeviceClass 0 [unknown] | bDeviceSubClass 0 [unknown] | bDeviceProtocol 0 | bMaxPacketSize0 64 | idVendor 0x05ac Apple, Inc. | idProduct 0x029c Magic Keyboard | bcdDevice 3.90 | iManufacturer 1 Apple Inc. | iProduct 2 Magic Keyboard | iSerial 3 ... | bNumConfigurations 1 | Configuration Descriptor: | bLength 9 | bDescriptorType 2 | wTotalLength 0x003b | bNumInterfaces 2 | bConfigurationValue 1 | iConfiguration 4 Keyboard | bmAttributes 0xa0 | (Bus Powered) | Remote Wakeup | MaxPower 500mA | Interface Descriptor: | bLength 9 | bDescriptorType 4 | bInterfaceNumber 0 | bAlternateSetting 0 | bNumEndpoints 1 | bInterfaceClass 3 Human Interface Device | bInterfaceSubClass 0 [unknown] | bInterfaceProtocol 0 | iInterface 5 Device Management | HID Device Descriptor: | bLength 9 | bDescriptorType 33 | bcdHID 1.10 | bCountryCode 0 Not supported | bNumDescriptors 1 | bDescriptorType 34 Report | wDescriptorLength 83 | Report Descriptors: | ** UNAVAILABLE ** | Endpoint Descriptor: | bLength 7 | bDescriptorType 5 | bEndpointAddress 0x81 EP 1 IN | bmAttributes 3 | Transfer Type Interrupt | Synch Type None | Usage Type Data | wMaxPacketSize 0x0010 1x 16 bytes | bInterval 8 | Interface Descriptor: | bLength 9 | bDescriptorType 4 | bInterfaceNumber 1 | bAlternateSetting 0 | bNumEndpoints 1 | bInterfaceClass 3 Human Interface Device | bInterfaceSubClass 1 Boot Interface Subclass | bInterfaceProtocol 1 Keyboard | iInterface 6 Keyboard / Boot | HID Device Descriptor: | bLength 9 | bDescriptorType 33 | bcdHID 1.10 | bCountryCode 13 International (ISO) | bNumDescriptors 1 | bDescriptorType 34 Report | wDescriptorLength 207 | Report Descriptors: | ** UNAVAILABLE ** | Endpoint Descriptor: | bLength 7 | bDescriptorType 5 | bEndpointAddress 0x82 EP 2 IN | bmAttributes 3 | Transfer Type Interrupt | Synch Type None | Usage Type Data | wMaxPacketSize 0x0010 1x 16 bytes | bInterval 8 Reviewed-by: Marek Vasut Reviewed-by: Neal Gompa Signed-off-by: Janne Grunau --- common/usb_kbd.c | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) (limited to 'common') diff --git a/common/usb_kbd.c b/common/usb_kbd.c index 4cbc9acb738..b2361bbf18e 100644 --- a/common/usb_kbd.c +++ b/common/usb_kbd.c @@ -23,6 +23,14 @@ #include +/* + * USB vendor and product IDs used for quirks. + */ +#define USB_VENDOR_ID_APPLE 0x05ac +#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021 0x029c +#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021 0x029a +#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021 0x029f + /* * If overwrite_console returns 1, the stdin, stderr and stdout * are switched to the serial port, else the settings in the @@ -106,6 +114,8 @@ struct usb_kbd_pdata { unsigned long last_report; struct int_queue *intq; + uint32_t ifnum; + uint32_t repeat_delay; uint32_t usb_in_pointer; @@ -150,8 +160,8 @@ static void usb_kbd_put_queue(struct usb_kbd_pdata *data, u8 c) */ static void usb_kbd_setled(struct usb_device *dev) { - struct usb_interface *iface = &dev->config.if_desc[0]; struct usb_kbd_pdata *data = dev->privptr; + struct usb_interface *iface = &dev->config.if_desc[data->ifnum]; ALLOC_ALIGN_BUFFER(uint32_t, leds, 1, USB_DMA_MINALIGN); *leds = data->flags & USB_KBD_LEDMASK; @@ -365,7 +375,7 @@ static inline void usb_kbd_poll_for_event(struct usb_device *dev) #if defined(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP) struct usb_interface *iface; struct usb_kbd_pdata *data = dev->privptr; - iface = &dev->config.if_desc[0]; + iface = &dev->config.if_desc[data->ifnum]; usb_get_report(dev, iface->desc.bInterfaceNumber, 1, 0, data->new, USB_KBD_BOOT_REPORT_SIZE); if (memcmp(data->old, data->new, USB_KBD_BOOT_REPORT_SIZE)) { @@ -509,6 +519,8 @@ static int usb_kbd_probe_dev(struct usb_device *dev, unsigned int ifnum) data->new = memalign(USB_DMA_MINALIGN, roundup(USB_KBD_BOOT_REPORT_SIZE, USB_DMA_MINALIGN)); + data->ifnum = ifnum; + /* Insert private data into USB device structure */ dev->privptr = data; @@ -561,10 +573,17 @@ static int probe_usb_keyboard(struct usb_device *dev) { char *stdinname; struct stdio_dev usb_kbd_dev; + unsigned int ifnum; + unsigned int max_ifnum = min((unsigned int)USB_MAX_ACTIVE_INTERFACES, + (unsigned int)dev->config.no_of_if); int error; /* Try probing the keyboard */ - if (usb_kbd_probe_dev(dev, 0) != 1) + for (ifnum = 0; ifnum < max_ifnum; ifnum++) { + if (usb_kbd_probe_dev(dev, ifnum) == 1) + break; + } + if (ifnum >= max_ifnum) return -ENOENT; /* Register the keyboard */ @@ -731,6 +750,18 @@ static const struct usb_device_id kbd_id_table[] = { .bInterfaceSubClass = USB_SUB_HID_BOOT, .bInterfaceProtocol = USB_PROT_HID_KEYBOARD, }, + { + USB_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021), + }, + { + USB_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021), + }, + { + USB_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021), + }, { } /* Terminating entry */ }; -- cgit v1.3.1 From 63f6a449bffe46beca89580d3efa48e5d041025c Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Thu, 4 Apr 2024 08:25:54 +0200 Subject: usb: kbd: Add probe quirk for Apple and Keychron keyboards Those keyboards do not return the current device state. Polling will timeout unless there are key presses. This is not a problem during operation but the initial device state query during probing will fail. Skip this step in usb_kbd_probe_dev() to make these devices useable. Not all Apple keyboards behave like this. A keyboard with USB vendor/product ID 05ac:0221 is reported to work with the current code. Unfortunately some Keychron keyboards "re-use" Apple's vendor ID and show the same behavior (Keychron C2, 05ac:024f for example). Reviewed-by: Marek Vasut Reviewed-by: Neal Gompa Signed-off-by: Janne Grunau --- common/usb_kbd.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'common') diff --git a/common/usb_kbd.c b/common/usb_kbd.c index b2361bbf18e..820f591fc5b 100644 --- a/common/usb_kbd.c +++ b/common/usb_kbd.c @@ -31,6 +31,10 @@ #define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021 0x029a #define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021 0x029f +#define USB_VENDOR_ID_KEYCHRON 0x3434 + +#define USB_HID_QUIRK_POLL_NO_REPORT_IDLE BIT(0) + /* * If overwrite_console returns 1, the stdin, stderr and stdout * are switched to the serial port, else the settings in the @@ -474,6 +478,7 @@ static int usb_kbd_probe_dev(struct usb_device *dev, unsigned int ifnum) struct usb_interface *iface; struct usb_endpoint_descriptor *ep; struct usb_kbd_pdata *data; + unsigned int quirks = 0; int epNum; if (dev->descriptor.bNumConfigurations != 1) @@ -506,6 +511,15 @@ static int usb_kbd_probe_dev(struct usb_device *dev, unsigned int ifnum) debug("USB KBD: found interrupt EP: 0x%x\n", ep->bEndpointAddress); + switch (dev->descriptor.idVendor) { + case USB_VENDOR_ID_APPLE: + case USB_VENDOR_ID_KEYCHRON: + quirks |= USB_HID_QUIRK_POLL_NO_REPORT_IDLE; + break; + default: + break; + } + data = malloc(sizeof(struct usb_kbd_pdata)); if (!data) { printf("USB KBD: Error allocating private data\n"); @@ -546,6 +560,14 @@ static int usb_kbd_probe_dev(struct usb_device *dev, unsigned int ifnum) usb_set_idle(dev, iface->desc.bInterfaceNumber, 0, 0); #endif + /* + * Apple and Keychron keyboards do not report the device state. Reports + * are only returned during key presses. + */ + if (quirks & USB_HID_QUIRK_POLL_NO_REPORT_IDLE) { + debug("USB KBD: quirk: skip testing device state\n"); + return 1; + } debug("USB KBD: enable interrupt pipe...\n"); #ifdef CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE data->intq = create_int_queue(dev, data->intpipe, 1, -- cgit v1.3.1