+// SPDX-License-Identifier: GPL-2.0+
/*
* Most of this source has been derived from the Linux USB
* project:
*
* Adapted for U-Boot:
* (C) Copyright 2001 Denis Peter, MPL AG Switzerland
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
/*
#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>
+#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
*/
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.
asynch_allowed = !disable;
return old_value;
}
-#endif /* !CONFIG_DM_USB */
+#endif /* !CONFIG_IS_ENABLED(DM_USB) */
/*-------------------------------------------------------------------
*/
/*
- * 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);
}
/*
* 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
{
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)
{
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;
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;
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
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
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)
{