summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorTom Rini <[email protected]>2024-04-14 15:58:31 -0600
committerTom Rini <[email protected]>2024-04-14 15:58:31 -0600
commitb03b49046af5dfca599d2ce8f0aafed89b97aa91 (patch)
tree36f06c2f124f8181549bc57a59777976ed1eaee1 /common
parent57cb92de7a9d01be1ce72af003651efc80ac67e2 (diff)
parent63f6a449bffe46beca89580d3efa48e5d041025c (diff)
Merge https://source.denx.de/u-boot/custodians/u-boot-usb
Diffstat (limited to 'common')
-rw-r--r--common/usb.c70
-rw-r--r--common/usb_kbd.c59
2 files changed, 126 insertions, 3 deletions
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 <common.h>
#include <command.h>
#include <dm.h>
+#include <dm/device_compat.h>
#include <log.h>
#include <malloc.h>
#include <memalign.h>
@@ -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
diff --git a/common/usb_kbd.c b/common/usb_kbd.c
index 4cbc9acb738..820f591fc5b 100644
--- a/common/usb_kbd.c
+++ b/common/usb_kbd.c
@@ -24,6 +24,18 @@
#include <usb.h>
/*
+ * 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
+
+#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
* environment are used
@@ -106,6 +118,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 +164,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 +379,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)) {
@@ -464,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)
@@ -496,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");
@@ -509,6 +533,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;
@@ -534,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,
@@ -561,10 +595,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 +772,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 */
};