]> git.ipfire.org Git - thirdparty/u-boot.git/blobdiff - common/usb.c
Merge tag 'video-20240421' of https://source.denx.de/u-boot/custodians/u-boot-video
[thirdparty/u-boot.git] / common / usb.c
index 8d9efe516b7571247b979f067b02962f0d6ad9d7..99e6b857c74ce281431995f2fd41a4328ed9e6b4 100644 (file)
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Most of this source has been derived from the Linux USB
  * project:
@@ -13,8 +14,6 @@
  *
  * Adapted for U-Boot:
  * (C) Copyright 2001 Denis Peter, MPL AG Switzerland
- *
- * SPDX-License-Identifier:    GPL-2.0+
  */
 
 /*
@@ -29,6 +28,9 @@
 #include <common.h>
 #include <command.h>
 #include <dm.h>
+#include <dm/device_compat.h>
+#include <log.h>
+#include <malloc.h>
 #include <memalign.h>
 #include <asm/processor.h>
 #include <linux/compiler.h>
 #include <asm/unaligned.h>
 #include <errno.h>
 #include <usb.h>
-#ifdef CONFIG_4xx
-#include <asm/4xx_pci.h>
-#endif
+#include <linux/delay.h>
 
 #define USB_BUFSIZ     512
 
 static int asynch_allowed;
-char usb_started; /* flag for the started/stopped USB status */
+bool usb_started; /* flag for the started/stopped USB status */
 
-#ifndef CONFIG_DM_USB
+#if !CONFIG_IS_ENABLED(DM_USB)
 static struct usb_device usb_dev[USB_MAX_DEVICE];
 static int dev_index;
 
-#ifndef CONFIG_USB_MAX_CONTROLLER_COUNT
-#define CONFIG_USB_MAX_CONTROLLER_COUNT 1
-#endif
-
 /***************************************************************************
  * Init USB Device
  */
@@ -175,6 +171,12 @@ int usb_detect_change(void)
        return change;
 }
 
+/* Lock or unlock async schedule on the controller */
+__weak int usb_lock_async(struct usb_device *dev, int lock)
+{
+       return 0;
+}
+
 /*
  * disables the asynch behaviour of the control message. This is used for data
  * transfers that uses the exclusiv access to the control and bulk messages.
@@ -187,7 +189,7 @@ int usb_disable_asynch(int disable)
        asynch_allowed = !disable;
        return old_value;
 }
-#endif /* !CONFIG_DM_USB */
+#endif /* !CONFIG_IS_ENABLED(DM_USB) */
 
 
 /*-------------------------------------------------------------------
@@ -196,12 +198,15 @@ int usb_disable_asynch(int disable)
  */
 
 /*
- * submits an Interrupt Message
+ * submits an Interrupt Message. Some drivers may implement non-blocking
+ * polling: when non-block is true and the device is not responding return
+ * -EAGAIN instead of waiting for device to respond.
  */
-int usb_submit_int_msg(struct usb_device *dev, unsigned long pipe,
-                       void *buffer, int transfer_len, int interval)
+int usb_int_msg(struct usb_device *dev, unsigned long pipe,
+               void *buffer, int transfer_len, int interval, bool nonblock)
 {
-       return submit_int_msg(dev, pipe, buffer, transfer_len, interval);
+       return submit_int_msg(dev, pipe, buffer, transfer_len, interval,
+                             nonblock);
 }
 
 /*
@@ -440,12 +445,13 @@ static int usb_parse_config(struct usb_device *dev,
                        }
                        break;
                case USB_DT_ENDPOINT:
-                       if (head->bLength != USB_DT_ENDPOINT_SIZE) {
+                       if (head->bLength != USB_DT_ENDPOINT_SIZE &&
+                           head->bLength != USB_DT_ENDPOINT_AUDIO_SIZE) {
                                printf("ERROR: Invalid USB EP length (%d)\n",
                                        head->bLength);
                                break;
                        }
-                       if (index + USB_DT_ENDPOINT_SIZE >
+                       if (index + head->bLength >
                            dev->config.desc.wTotalLength) {
                                puts("USB EP descriptor overflowed buffer!\n");
                                break;
@@ -557,12 +563,10 @@ int usb_clear_halt(struct usb_device *dev, int pipe)
 static int usb_get_descriptor(struct usb_device *dev, unsigned char type,
                        unsigned char index, void *buf, int size)
 {
-       int res;
-       res = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
-                       USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
-                       (type << 8) + index, 0,
-                       buf, size, USB_CNTL_TIMEOUT);
-       return res;
+       return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                              USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+                              (type << 8) + index, 0, buf, size,
+                              USB_CNTL_TIMEOUT);
 }
 
 /**********************************************************************
@@ -612,14 +616,10 @@ int usb_get_configuration_no(struct usb_device *dev, int cfgno,
  */
 static int usb_set_address(struct usb_device *dev)
 {
-       int res;
-
        debug("set address %d\n", dev->devnum);
-       res = usb_control_msg(dev, usb_snddefctrl(dev),
-                               USB_REQ_SET_ADDRESS, 0,
-                               (dev->devnum), 0,
-                               NULL, 0, USB_CNTL_TIMEOUT);
-       return res;
+
+       return usb_control_msg(dev, usb_snddefctrl(dev), USB_REQ_SET_ADDRESS,
+                              0, (dev->devnum), 0, NULL, 0, USB_CNTL_TIMEOUT);
 }
 
 /********************************************************************
@@ -858,7 +858,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
  * the USB device are static allocated [USB_MAX_DEVICE].
  */
 
-#ifndef CONFIG_DM_USB
+#if !CONFIG_IS_ENABLED(DM_USB)
 
 /* returns a pointer to the device with the index [index].
  * if the device is not assigned (dev->devnum==-1) returns NULL
@@ -915,7 +915,7 @@ __weak int usb_alloc_device(struct usb_device *udev)
 {
        return 0;
 }
-#endif /* !CONFIG_DM_USB */
+#endif /* !CONFIG_IS_ENABLED(DM_USB) */
 
 static int usb_hub_port_reset(struct usb_device *dev, struct usb_device *hub)
 {
@@ -978,27 +978,39 @@ static int usb_setup_descriptor(struct usb_device *dev, bool do_read)
        dev->epmaxpacketin[0] = dev->descriptor.bMaxPacketSize0;
        dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0;
 
-       if (do_read) {
+       if (do_read && dev->speed == USB_SPEED_FULL) {
                int err;
 
                /*
-                * Validate we've received only at least 8 bytes, not that we've
-                * received the entire descriptor. The reasoning is:
-                * - The code only uses fields in the first 8 bytes, so that's all we
-                *   need to have fetched at this stage.
-                * - The smallest maxpacket size is 8 bytes. Before we know the actual
-                *   maxpacket the device uses, the USB controller may only accept a
-                *   single packet. Consequently we are only guaranteed to receive 1
-                *   packet (at least 8 bytes) even in a non-error case.
+                * Validate we've received only at least 8 bytes, not that
+                * we've received the entire descriptor. The reasoning is:
+                * - The code only uses fields in the first 8 bytes, so
+                *   that's all we need to have fetched at this stage.
+                * - The smallest maxpacket size is 8 bytes. Before we know
+                *   the actual maxpacket the device uses, the USB controller
+                *   may only accept a single packet. Consequently we are only
+                *   guaranteed to receive 1 packet (at least 8 bytes) even in
+                *   a non-error case.
                 *
-                * At least the DWC2 controller needs to be programmed with the number
-                * of packets in addition to the number of bytes. A request for 64
-                * bytes of data with the maxpacket guessed as 64 (above) yields a
-                * request for 1 packet.
+                * At least the DWC2 controller needs to be programmed with
+                * the number of packets in addition to the number of bytes.
+                * A request for 64 bytes of data with the maxpacket guessed
+                * as 64 (above) yields a request for 1 packet.
                 */
                err = get_descriptor_len(dev, 64, 8);
                if (err)
                        return err;
+
+               /*
+                * Logitech Unifying Receiver 046d:c52b bcdDevice 12.10 seems
+                * sensitive about the first Get Descriptor request. If there
+                * are any other requests in the same microframe, the device
+                * reports bogus data, first of the descriptor parts is not
+                * sent to the host. Wait over one microframe duration here
+                * (1mS for USB 1.x , 125uS for USB 2.0) to avoid triggering
+                * the issue.
+                */
+               mdelay(1);
        }
 
        dev->epmaxpacketin[0] = dev->descriptor.bMaxPacketSize0;
@@ -1017,7 +1029,7 @@ static int usb_setup_descriptor(struct usb_device *dev, bool do_read)
                dev->maxpacketsize = PACKET_SIZE_64;
                break;
        default:
-               printf("usb_new_device: invalid max packet size\n");
+               printf("%s: invalid max packet size\n", __func__);
                return -EIO;
        }
 
@@ -1059,6 +1071,65 @@ static int usb_prepare_device(struct usb_device *dev, int addr, bool do_read,
 
        mdelay(10);     /* Let the SET_ADDRESS settle */
 
+       /*
+        * If we haven't read device descriptor before, read it here
+        * after device is assigned an address. This is only applicable
+        * to xHCI so far.
+        */
+       if (!do_read) {
+               err = usb_setup_descriptor(dev, true);
+               if (err)
+                       return err;
+       }
+
+       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;
 }
 
@@ -1077,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
@@ -1163,7 +1255,7 @@ int usb_setup_device(struct usb_device *dev, bool do_read,
        return ret;
 }
 
-#ifndef CONFIG_DM_USB
+#if !CONFIG_IS_ENABLED(DM_USB)
 /*
  * By the time we get here, the device has gotten a new device ID
  * and is in the default state. We need to identify the thing and
@@ -1182,7 +1274,7 @@ int usb_new_device(struct usb_device *dev)
         * with the device. So a get_descriptor will fail before any
         * of that is done for XHCI unlike EHCI.
         */
-#ifdef CONFIG_USB_XHCI
+#ifdef CONFIG_USB_XHCI_HCD
        do_read = false;
 #endif
        err = usb_setup_device(dev, do_read, dev->parent);
@@ -1212,14 +1304,14 @@ int board_usb_cleanup(int index, enum usb_init_type init)
 
 bool usb_device_has_child_on_port(struct usb_device *parent, int port)
 {
-#ifdef CONFIG_DM_USB
+#if CONFIG_IS_ENABLED(DM_USB)
        return false;
 #else
        return parent->children[port] != NULL;
 #endif
 }
 
-#ifdef CONFIG_DM_USB
+#if CONFIG_IS_ENABLED(DM_USB)
 void usb_find_usb2_hub_address_port(struct usb_device *udev,
                               uint8_t *hub_address, uint8_t *hub_port)
 {