]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
Merge https://source.denx.de/u-boot/custodians/u-boot-usb
authorTom Rini <trini@konsulko.com>
Sun, 14 Apr 2024 21:58:31 +0000 (15:58 -0600)
committerTom Rini <trini@konsulko.com>
Sun, 14 Apr 2024 21:58:31 +0000 (15:58 -0600)
common/usb.c
common/usb_kbd.c
doc/usage/environment.rst
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci.c
include/env_default.h
include/usb.h

index 836506dcd9e9f7ba462dea115f0e4fb4d3f74d80..99e6b857c74ce281431995f2fd41a4328ed9e6b4 100644 (file)
@@ -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
index 4cbc9acb73800091a9d3ecc654d4e0d10526a3d1..820f591fc5bb5d408be2fdba8868e51cb8d85c8b 100644 (file)
 
 #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
@@ -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 */
 };
 
index ebf75fa948ad1a544ba28a14673bb592b620fd80..7d4b448cb301dfec71d0f9d9f18e32ee04b7ee4f 100644 (file)
@@ -366,6 +366,19 @@ tftpwindowsize
     This means the count of blocks we can receive before
     sending ack to server.
 
+usb_ignorelist
+    Ignore USB devices to prevent binding them to an USB device driver. This can
+    be used to ignore devices are for some reason undesirable or causes crashes
+    u-boot's USB stack.
+    An example for undesired behavior is the keyboard emulation of security keys
+    like Yubikeys. U-boot currently supports only a single USB keyboard device
+    so try to probe an useful keyboard device. The default environment blocks
+    Yubico devices as common devices emulating keyboards.
+    Devices are matched by idVendor and idProduct. The variable contains a comma
+    separated list of idVendor:idProduct pairs as hexadecimal numbers joined
+    by a colon. '*' functions as a wildcard for idProduct to block all devices
+    with the specified idVendor.
+
 vlan
     When set to a value < 4095 the traffic over
     Ethernet is encapsulated/received over 802.1q
index b60661fe05e7317978dfb757cbd292658f781c0a..910c5f3352b805e1895f974aa6f23d8c7c7093e1 100644 (file)
@@ -685,6 +685,9 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
                reset_ep(udev, ep_index);
 
        ring = virt_dev->eps[ep_index].ring;
+       if (!ring)
+               return -EINVAL;
+
        /*
         * How much data is (potentially) left before the 64KB boundary?
         * XHCI Spec puts restriction( TABLE 49 and 6.4.1 section of XHCI Spec)
@@ -871,6 +874,8 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe,
        ep_index = usb_pipe_ep_index(pipe);
 
        ep_ring = virt_dev->eps[ep_index].ring;
+       if (!ep_ring)
+               return -EINVAL;
 
        /*
         * Check to see if the max packet size for the default control
index d13cbff9b3726f6f8f6ef26662157f991cac1a1a..741e186ee05bc30e7255646ef03f009c234bbf48 100644 (file)
@@ -475,67 +475,34 @@ static int xhci_configure_endpoints(struct usb_device *udev, bool ctx_change)
 }
 
 /**
- * Configure the endpoint, programming the device contexts.
+ * Fill endpoint contexts for interface descriptor ifdesc.
  *
- * @param udev pointer to the USB device structure
- * Return: returns the status of the xhci_configure_endpoints
+ * @param udev         pointer to the USB device structure
+ * @param ctrl         pointer to the xhci pravte device structure
+ * @param virt_dev     pointer to the xhci virtual device structure
+ * @param ifdesc       pointer to the USB interface config descriptor
+ * Return: returns the status of xhci_init_ep_contexts_if
  */
-static int xhci_set_configuration(struct usb_device *udev)
+static int xhci_init_ep_contexts_if(struct usb_device *udev,
+                                   struct xhci_ctrl *ctrl,
+                                   struct xhci_virt_device *virt_dev,
+                                   struct usb_interface *ifdesc
+       )
 {
-       struct xhci_container_ctx *in_ctx;
-       struct xhci_container_ctx *out_ctx;
-       struct xhci_input_control_ctx *ctrl_ctx;
-       struct xhci_slot_ctx *slot_ctx;
        struct xhci_ep_ctx *ep_ctx[MAX_EP_CTX_NUM];
        int cur_ep;
-       int max_ep_flag = 0;
        int ep_index;
        unsigned int dir;
        unsigned int ep_type;
-       struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
-       int num_of_ep;
-       int ep_flag = 0;
        u64 trb_64 = 0;
-       int slot_id = udev->slot_id;
-       struct xhci_virt_device *virt_dev = ctrl->devs[slot_id];
-       struct usb_interface *ifdesc;
        u32 max_esit_payload;
        unsigned int interval;
        unsigned int mult;
        unsigned int max_burst;
        unsigned int avg_trb_len;
        unsigned int err_count = 0;
+       int num_of_ep = ifdesc->no_of_ep;
 
-       out_ctx = virt_dev->out_ctx;
-       in_ctx = virt_dev->in_ctx;
-
-       num_of_ep = udev->config.if_desc[0].no_of_ep;
-       ifdesc = &udev->config.if_desc[0];
-
-       ctrl_ctx = xhci_get_input_control_ctx(in_ctx);
-       /* Initialize the input context control */
-       ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG);
-       ctrl_ctx->drop_flags = 0;
-
-       /* EP_FLAG gives values 1 & 4 for EP1OUT and EP2IN */
-       for (cur_ep = 0; cur_ep < num_of_ep; cur_ep++) {
-               ep_flag = xhci_get_ep_index(&ifdesc->ep_desc[cur_ep]);
-               ctrl_ctx->add_flags |= cpu_to_le32(1 << (ep_flag + 1));
-               if (max_ep_flag < ep_flag)
-                       max_ep_flag = ep_flag;
-       }
-
-       xhci_inval_cache((uintptr_t)out_ctx->bytes, out_ctx->size);
-
-       /* slot context */
-       xhci_slot_copy(ctrl, in_ctx, out_ctx);
-       slot_ctx = xhci_get_slot_ctx(ctrl, in_ctx);
-       slot_ctx->dev_info &= ~(cpu_to_le32(LAST_CTX_MASK));
-       slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(max_ep_flag + 1) | 0);
-
-       xhci_endpoint_copy(ctrl, in_ctx, out_ctx, 0);
-
-       /* filling up ep contexts */
        for (cur_ep = 0; cur_ep < num_of_ep; cur_ep++) {
                struct usb_endpoint_descriptor *endpt_desc = NULL;
                struct usb_ss_ep_comp_descriptor *ss_ep_comp_desc = NULL;
@@ -561,7 +528,8 @@ static int xhci_set_configuration(struct usb_device *udev)
                avg_trb_len = max_esit_payload;
 
                ep_index = xhci_get_ep_index(endpt_desc);
-               ep_ctx[ep_index] = xhci_get_ep_ctx(ctrl, in_ctx, ep_index);
+               ep_ctx[ep_index] = xhci_get_ep_ctx(ctrl, virt_dev->in_ctx,
+                                                  ep_index);
 
                /* Allocate the ep rings */
                virt_dev->eps[ep_index].ring = xhci_ring_alloc(ctrl, 1, true);
@@ -614,6 +582,72 @@ static int xhci_set_configuration(struct usb_device *udev)
                }
        }
 
+       return 0;
+}
+
+/**
+ * Configure the endpoint, programming the device contexts.
+ *
+ * @param udev pointer to the USB device structure
+ * Return: returns the status of the xhci_configure_endpoints
+ */
+static int xhci_set_configuration(struct usb_device *udev)
+{
+       struct xhci_container_ctx *out_ctx;
+       struct xhci_container_ctx *in_ctx;
+       struct xhci_input_control_ctx *ctrl_ctx;
+       struct xhci_slot_ctx *slot_ctx;
+       int err;
+       int cur_ep;
+       int max_ep_flag = 0;
+       struct xhci_ctrl *ctrl = xhci_get_ctrl(udev);
+       int num_of_ep;
+       int ep_flag = 0;
+       int slot_id = udev->slot_id;
+       struct xhci_virt_device *virt_dev = ctrl->devs[slot_id];
+       struct usb_interface *ifdesc;
+       unsigned int ifnum;
+       unsigned int max_ifnum = min((unsigned int)USB_MAX_ACTIVE_INTERFACES,
+                                    (unsigned int)udev->config.no_of_if);
+
+       out_ctx = virt_dev->out_ctx;
+       in_ctx = virt_dev->in_ctx;
+
+       ctrl_ctx = xhci_get_input_control_ctx(in_ctx);
+       /* Initialize the input context control */
+       ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG);
+       ctrl_ctx->drop_flags = 0;
+
+       for (ifnum = 0; ifnum < max_ifnum; ifnum++) {
+               ifdesc = &udev->config.if_desc[ifnum];
+               num_of_ep = ifdesc->no_of_ep;
+               /* EP_FLAG gives values 1 & 4 for EP1OUT and EP2IN */
+               for (cur_ep = 0; cur_ep < num_of_ep; cur_ep++) {
+                       ep_flag = xhci_get_ep_index(&ifdesc->ep_desc[cur_ep]);
+                       ctrl_ctx->add_flags |= cpu_to_le32(1 << (ep_flag + 1));
+                       if (max_ep_flag < ep_flag)
+                               max_ep_flag = ep_flag;
+               }
+       }
+
+       xhci_inval_cache((uintptr_t)out_ctx->bytes, out_ctx->size);
+
+       /* slot context */
+       xhci_slot_copy(ctrl, in_ctx, out_ctx);
+       slot_ctx = xhci_get_slot_ctx(ctrl, in_ctx);
+       slot_ctx->dev_info &= ~(cpu_to_le32(LAST_CTX_MASK));
+       slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(max_ep_flag + 1) | 0);
+
+       xhci_endpoint_copy(ctrl, in_ctx, out_ctx, 0);
+
+       /* filling up ep contexts */
+       for (ifnum = 0; ifnum < max_ifnum; ifnum++) {
+               ifdesc = &udev->config.if_desc[ifnum];
+               err = xhci_init_ep_contexts_if(udev, ctrl, virt_dev, ifdesc);
+               if (err < 0)
+                       return err;
+       }
+
        return xhci_configure_endpoints(udev, false);
 }
 
index 2ca4a087d3b63d4ab1905943967c24de3095cf45..8ee500d1709c15526dfb277c8be0d86d7a74e317 100644 (file)
@@ -99,6 +99,17 @@ const char default_environment[] = {
 #ifdef CONFIG_SYS_SOC
        "soc="          CONFIG_SYS_SOC                  "\0"
 #endif
+#ifdef CONFIG_USB_HOST
+       "usb_ignorelist="
+#ifdef CONFIG_USB_KEYBOARD
+       /* Ignore Yubico devices. Currently only a single USB keyboard device is
+        * supported and the emulated HID keyboard Yubikeys present is useless
+        * as keyboard.
+        */
+       "0x1050:*,"
+#endif
+       "\0"
+#endif
 #ifdef CONFIG_ENV_IMPORT_FDT
        "env_fdt_path=" CONFIG_ENV_FDT_PATH             "\0"
 #endif
index 09e3f0cb309c64e443d5bcd787421c4a7308c260..3aafdc8bfd12e0783768d5e7f045299ed9e5c2dd 100644 (file)
@@ -49,6 +49,12 @@ extern bool usb_started; /* flag for the started/stopped USB status */
  */
 #define USB_TIMEOUT_MS(pipe) (usb_pipebulk(pipe) ? 5000 : 1000)
 
+/*
+ * The xhcd hcd driver prepares only a limited number interfaces / endpoints.
+ * Define this limit so that drivers do not exceed it.
+ */
+#define USB_MAX_ACTIVE_INTERFACES      2
+
 /* device request (setup) */
 struct devrequest {
        __u8    requesttype;