Tom Rini 2024-04-14 15:58:31 -06:00
commit b03b49046a
7 changed files with 241 additions and 49 deletions

View 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

View File

@ -23,6 +23,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
@ -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 */
};

View 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

View 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

View 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);
}

View 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

View 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;