PCI devices live at /sys/devices/pci0000:00/<addr> and USB devices under the xHCI controller that carries their bus, below that bus's root hub usbN; /sys/bus/pci/devices and /sys/bus/usb/devices are symlinks into it, and every device has its subsystem and driver links, which is how udev learns SUBSYSTEM and DRIVER. Writing to a device's uevent now announces it with MODALIAS, PCI_ID, PRODUCT and the rest, so udevadm trigger covers them, and the USB core sends add when a device is attached and remove as it goes. The root hub is announced when the controller comes up, before anything is plugged in. Checked under QEMU: lspci and udevadm info through the links, udevadm monitor showing the kernel and udev add event for device_add usb-kbd/usb-mouse and the remove event for device_del, and a triggered add for usb1 and 1-5. Known limits: the bus-to-controller mapping is a convention (bus N is the Nth xHCI in PCI order); PCI bridges are not parents; USB interfaces and /dev/bus/usb nodes are absent; busybox's lsusb reads /dev/bus/usb and prints nothing. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao
452 lines
13 KiB
C
452 lines
13 KiB
C
// The USB core: everything between "the controller can talk to a device" and
|
|
// "a driver knows what the device is".
|
|
//
|
|
// The split matters. xhci.c deals in TRBs, slot IDs and doorbells; nothing
|
|
// here knows any of those words. A class driver deals in reports and
|
|
// commands; nothing here knows those either. What lives in the middle is the
|
|
// part of USB that is the same for every device ever made: read the
|
|
// configuration, choose one, look at the interface, find someone who wants it.
|
|
|
|
#include "core/types.h"
|
|
#include "core/defs.h"
|
|
#include "core/param.h"
|
|
#include "arch/memlayout.h"
|
|
#include "arch/mmu.h"
|
|
#include "core/spinlock.h"
|
|
#include "core/dma.h"
|
|
#include "dev/usb.h"
|
|
#include "core/hwinfo.h"
|
|
|
|
// A staging buffer for control transfers.
|
|
//
|
|
// It exists because a descriptor has to be read into memory the controller
|
|
// can reach and that does not straddle a 64 KB boundary -- a rule that holds
|
|
// for xHCI data stages and that a static buffer inside some other structure
|
|
// satisfies only by luck. One page, allocated once, copied out of afterwards.
|
|
static void *scratch;
|
|
|
|
static void *
|
|
get_scratch(void)
|
|
{
|
|
if(scratch == 0)
|
|
scratch = dma_alloc(4096, 4096);
|
|
return scratch;
|
|
}
|
|
|
|
static int
|
|
get_descriptor(struct usb_dev *dev, int type, int index, void *buf, int len)
|
|
{
|
|
struct usb_setup s;
|
|
|
|
// Clear first. The scratch buffer is shared by every device on the bus, and
|
|
// a control transfer reports success without saying how many bytes it
|
|
// actually moved -- so a device that answers short leaves the previous
|
|
// device's descriptor visible past the end of its own, and the parser
|
|
// happily walks into it. Zeroed bytes stop the walk instead, because a
|
|
// descriptor whose length is 0 is rejected.
|
|
memset(buf, 0, len);
|
|
|
|
s.bmRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
|
|
s.bRequest = USB_REQ_GET_DESCRIPTOR;
|
|
s.wValue = (type << 8) | index;
|
|
s.wIndex = 0;
|
|
s.wLength = len;
|
|
return dev->ops->control(dev, &s, buf, len);
|
|
}
|
|
|
|
static int
|
|
set_configuration(struct usb_dev *dev, int value)
|
|
{
|
|
struct usb_setup s;
|
|
|
|
s.bmRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
|
|
s.bRequest = USB_REQ_SET_CONFIG;
|
|
s.wValue = value;
|
|
s.wIndex = 0;
|
|
s.wLength = 0;
|
|
return dev->ops->control(dev, &s, 0, 0);
|
|
}
|
|
|
|
// Walk the configuration descriptor.
|
|
//
|
|
// What comes back from GET_DESCRIPTOR(CONFIG) is not one descriptor: it is
|
|
// the configuration descriptor followed by every interface and endpoint
|
|
// descriptor belonging to it, packed end to end, each one starting with its
|
|
// own length. That length is the only thing that can be relied on to find the
|
|
// next -- descriptor types this kernel has never heard of appear in the
|
|
// middle (HID's own, class-specific ones) and skipping them by size is the
|
|
// only way through. A parser that assumed a fixed order would work on one
|
|
// device and fail on the next.
|
|
//
|
|
// `nth` picks the interface: the nth one at alternate setting 0, counted from
|
|
// zero. Returns 0 if there is one, -1 when there are no more.
|
|
static int
|
|
parse_config(struct usb_dev *dev, int nth)
|
|
{
|
|
int off = 0;
|
|
int seen_iface = 0;
|
|
int count = 0;
|
|
|
|
dev->has_intr_in = dev->has_bulk_in = dev->has_bulk_out = 0;
|
|
|
|
while(off + 2 <= dev->conflen){
|
|
uint8 len = dev->conf[off];
|
|
uint8 type = dev->conf[off + 1];
|
|
|
|
if(len < 2 || off + len > dev->conflen)
|
|
break; // malformed: stop rather than guess
|
|
|
|
if(type == USB_DT_INTERFACE && len >= (int)sizeof(dev->iface)){
|
|
struct usb_interface_desc *id = (struct usb_interface_desc *)&dev->conf[off];
|
|
if(seen_iface)
|
|
break; // the next interface: this one is done
|
|
// Alternate settings are the same interface again with other
|
|
// endpoints. Setting 0 is the one in force after SET_CONFIGURATION.
|
|
if(id->bAlternateSetting == 0 && count++ == nth){
|
|
memmove(&dev->iface, id, sizeof(dev->iface));
|
|
seen_iface = 1;
|
|
}
|
|
} else if(type == USB_DT_ENDPOINT && seen_iface &&
|
|
len >= (int)sizeof(struct usb_endpoint_desc)){
|
|
struct usb_endpoint_desc ep;
|
|
int in;
|
|
|
|
memmove(&ep, &dev->conf[off], sizeof(ep));
|
|
in = USB_EP_IS_IN(ep.bEndpointAddress);
|
|
|
|
switch(USB_EP_XFERTYPE(ep.bmAttributes)){
|
|
case USB_EP_INTERRUPT:
|
|
if(in && !dev->has_intr_in){ dev->intr_in = ep; dev->has_intr_in = 1; }
|
|
break;
|
|
case USB_EP_BULK:
|
|
if(in && !dev->has_bulk_in){ dev->bulk_in = ep; dev->has_bulk_in = 1; }
|
|
if(!in && !dev->has_bulk_out){ dev->bulk_out = ep; dev->has_bulk_out = 1; }
|
|
break;
|
|
}
|
|
}
|
|
off += len;
|
|
}
|
|
return seen_iface ? 0 : -1;
|
|
}
|
|
|
|
// One usb_dev for every interface after the first. See `base` in
|
|
// include/dev/usb.h. A fixed pool, like everything else here. It was sixteen,
|
|
// which a dock -- hub, Ethernet, audio, card reader, each two or three
|
|
// interfaces -- and a couple of receivers already reach. 672 bytes each.
|
|
#define NIFDEV 64
|
|
static struct usb_dev ifdevs[NIFDEV];
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Class drivers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
extern const struct usb_driver usb_hid_driver;
|
|
void usb_bind_all(struct usb_dev *dev);
|
|
|
|
static int
|
|
matches(const struct usb_driver *drv, struct usb_dev *dev)
|
|
{
|
|
if(drv->class != 0xFF && drv->class != dev->iface.bInterfaceClass)
|
|
return 0;
|
|
if(drv->subclass != 0xFF && drv->subclass != dev->iface.bInterfaceSubClass)
|
|
return 0;
|
|
if(drv->protocol != 0xFF && drv->protocol != dev->iface.bInterfaceProtocol)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
// Offer a device to one driver. Returns 1 if it was claimed.
|
|
int
|
|
usb_offer(const struct usb_driver *drv, struct usb_dev *dev)
|
|
{
|
|
if(dev->driver || !matches(drv, dev))
|
|
return 0;
|
|
if(drv->probe(dev) != 0)
|
|
return 0;
|
|
dev->driver = drv->name;
|
|
dev->drv = drv;
|
|
return 1;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// What is plugged in, for /sys/bus/usb/devices.
|
|
//
|
|
// The controller owns each usb_dev; this only remembers which ones are
|
|
// attached, from usb_attach until usb_detach, and copies their identity out
|
|
// under the lock so a reader never follows a pointer the controller may be
|
|
// about to reuse. A bus number is handed to each controller the first time one
|
|
// of its devices appears, in that order, as Linux numbers usb1, usb2.
|
|
|
|
#define NPRESENT 64
|
|
#define NBUSES 8
|
|
|
|
static struct spinlock present_lock;
|
|
static int present_init;
|
|
static struct usb_dev *present[NPRESENT];
|
|
static void *buses[NBUSES];
|
|
|
|
// Give a controller its bus number if it has none. Caller holds present_lock.
|
|
// Returns the number (1-based), or 0 if there is no room.
|
|
static int
|
|
bus_number(void *hc)
|
|
{
|
|
for(int b = 0; b < NBUSES; b++){
|
|
if(buses[b] == hc) return b + 1;
|
|
if(buses[b] == 0){ buses[b] = hc; return b + 1; }
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
present_init_once(void)
|
|
{
|
|
if(!present_init){
|
|
initlock(&present_lock, "usbpresent");
|
|
present_init = 1;
|
|
}
|
|
}
|
|
|
|
// A controller is up: number its bus now and announce its root hub, so usbN
|
|
// is in /sys before anything is plugged into it. The loader of the driver that
|
|
// calls this runs once per controller, in PCI order -- which is what ties bus
|
|
// N to the Nth controller in sysfs.c.
|
|
void
|
|
usb_bus_add(void *hc)
|
|
{
|
|
int n;
|
|
|
|
present_init_once();
|
|
acquire(&present_lock);
|
|
n = bus_number(hc);
|
|
release(&present_lock);
|
|
if(n)
|
|
sysfs_usb_root_uevent(n, "add");
|
|
}
|
|
|
|
static void
|
|
present_add(struct usb_dev *dev)
|
|
{
|
|
present_init_once();
|
|
acquire(&present_lock);
|
|
for(int i = 0; i < NPRESENT; i++)
|
|
if(present[i] == 0){ present[i] = dev; break; }
|
|
bus_number(dev->hc);
|
|
release(&present_lock);
|
|
}
|
|
|
|
static void
|
|
present_del(struct usb_dev *dev)
|
|
{
|
|
if(!present_init)
|
|
return;
|
|
acquire(&present_lock);
|
|
for(int i = 0; i < NPRESENT; i++)
|
|
if(present[i] == dev) present[i] = 0;
|
|
release(&present_lock);
|
|
}
|
|
|
|
static void
|
|
put_num(char *out, int *n, int cap, int v)
|
|
{
|
|
char t[12];
|
|
int k = 0;
|
|
do { t[k++] = '0' + v % 10; v /= 10; } while(v);
|
|
while(k && *n < cap - 1) out[(*n)++] = t[--k];
|
|
out[*n] = 0;
|
|
}
|
|
|
|
// Copy `d`'s identity out. Caller holds present_lock, or is the one thread
|
|
// that owns `d` (attach and detach).
|
|
static void
|
|
fill_info(struct usb_dev *d, struct usb_info *o)
|
|
{
|
|
memset(o, 0, sizeof *o);
|
|
o->bus = 1;
|
|
for(int b = 0; b < NBUSES; b++)
|
|
if(buses[b] == d->hc){ o->bus = b + 1; break; }
|
|
o->devnum = d->slot;
|
|
o->speed = d->speed;
|
|
o->vid = d->desc.idVendor;
|
|
o->pid = d->desc.idProduct;
|
|
o->bcd = d->desc.bcdDevice;
|
|
o->cls = d->desc.bDeviceClass;
|
|
o->subcls = d->desc.bDeviceSubClass;
|
|
o->proto = d->desc.bDeviceProtocol;
|
|
o->nconf = d->desc.bNumConfigurations;
|
|
|
|
// "<bus>-<root port>[.<hub port>...]": the route string holds one
|
|
// downstream port per tier below the root, a nibble each.
|
|
int n = 0, cap = (int)sizeof o->path;
|
|
put_num(o->path, &n, cap, o->bus);
|
|
o->path[n++] = '-';
|
|
put_num(o->path, &n, cap, d->port);
|
|
for(int t = 0; t < d->tier && n < cap - 3; t++){
|
|
o->path[n++] = '.';
|
|
put_num(o->path, &n, cap, (int)((d->route >> (4 * t)) & 0xF));
|
|
}
|
|
|
|
const char *drv = d->driver;
|
|
for(int k = 0; !drv && k < NIFDEV; k++)
|
|
if(ifdevs[k].in_use && ifdevs[k].base == d)
|
|
drv = ifdevs[k].driver;
|
|
if(drv)
|
|
safestrcpy(o->driver, drv, sizeof o->driver);
|
|
}
|
|
|
|
int
|
|
usb_info_nth(int idx, struct usb_info *o)
|
|
{
|
|
if(!present_init)
|
|
return -1;
|
|
acquire(&present_lock);
|
|
int seen = 0;
|
|
for(int i = 0; i < NPRESENT; i++){
|
|
struct usb_dev *d = present[i];
|
|
if(d == 0 || seen++ != idx)
|
|
continue;
|
|
fill_info(d, o);
|
|
release(&present_lock);
|
|
return 0;
|
|
}
|
|
release(&present_lock);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
usb_nbuses(void)
|
|
{
|
|
int n = 0;
|
|
if(!present_init)
|
|
return 0;
|
|
acquire(&present_lock);
|
|
for(int b = 0; b < NBUSES; b++)
|
|
if(buses[b])
|
|
n++;
|
|
release(&present_lock);
|
|
return n;
|
|
}
|
|
|
|
// Announce `dev` to udev. Attach says "add" once the device is in the table,
|
|
// so a rule that opens its /sys directory finds it; detach says "remove"
|
|
// first, while the directory still exists.
|
|
static void
|
|
announce(struct usb_dev *dev, const char *action)
|
|
{
|
|
struct usb_info u;
|
|
if(!present_init)
|
|
return;
|
|
acquire(&present_lock);
|
|
fill_info(dev, &u);
|
|
release(&present_lock);
|
|
sysfs_usb_uevent(&u, action);
|
|
}
|
|
|
|
void
|
|
usb_attach(struct usb_dev *dev)
|
|
{
|
|
struct usb_config_desc *cd;
|
|
void *buf = get_scratch();
|
|
int total;
|
|
|
|
if(buf == 0)
|
|
return;
|
|
present_add(dev);
|
|
announce(dev, "add");
|
|
|
|
// Nine bytes first, because wTotalLength is in them and there is no other
|
|
// way to know how much to ask for.
|
|
if(get_descriptor(dev, USB_DT_CONFIG, 0, buf, 9) < 0){
|
|
cprintf("usb: slot %d would not give up its configuration\n", dev->slot);
|
|
return;
|
|
}
|
|
cd = buf;
|
|
total = cd->wTotalLength;
|
|
|
|
// A configuration that does not contain its own descriptor is not one we
|
|
// can do anything with, and reading it as a length would walk off the end.
|
|
if(cd->bDescriptorType != USB_DT_CONFIG || total < (int)sizeof(*cd)){
|
|
cprintf("usb: slot %d returned a malformed configuration descriptor\n",
|
|
dev->slot);
|
|
return;
|
|
}
|
|
if(total > USB_MAXCONF)
|
|
total = USB_MAXCONF;
|
|
|
|
if(get_descriptor(dev, USB_DT_CONFIG, 0, buf, total) < 0)
|
|
return;
|
|
memmove(dev->conf, buf, total);
|
|
dev->conflen = total;
|
|
|
|
// A device is not obliged to do anything at all until it has been
|
|
// configured -- before this, endpoints other than 0 do not exist.
|
|
if(set_configuration(dev, cd->bConfigurationValue) < 0){
|
|
cprintf("usb: slot %d refused SET_CONFIGURATION\n", dev->slot);
|
|
return;
|
|
}
|
|
|
|
// Every interface, each offered to the class drivers on its own. This used
|
|
// to stop after the first, which is how a wireless receiver's mouse --
|
|
// interface 1, behind the keyboard on 0 -- was never heard from.
|
|
for(int n = 0; ; n++){
|
|
struct usb_dev *d = dev;
|
|
|
|
if(n > 0){
|
|
d = 0;
|
|
for(int i = 0; i < NIFDEV; i++)
|
|
if(!ifdevs[i].in_use){ d = &ifdevs[i]; break; }
|
|
if(d == 0){
|
|
cprintf("usb: out of interface slots; the rest are not used\n");
|
|
break;
|
|
}
|
|
*d = *dev;
|
|
d->base = dev;
|
|
d->driver = 0;
|
|
d->drv = 0;
|
|
d->drvdata = 0;
|
|
d->complete = 0;
|
|
}
|
|
if(parse_config(d, n) < 0){
|
|
if(n > 0)
|
|
d->in_use = 0;
|
|
break;
|
|
}
|
|
|
|
kinfo("usb: interface %d: class %d.%d protocol %d, %d endpoint%s%s%s%s\n",
|
|
d->iface.bInterfaceNumber,
|
|
d->iface.bInterfaceClass, d->iface.bInterfaceSubClass,
|
|
d->iface.bInterfaceProtocol, d->iface.bNumEndpoints,
|
|
d->iface.bNumEndpoints == 1 ? "" : "s",
|
|
d->has_intr_in ? ", interrupt IN" : "",
|
|
d->has_bulk_in ? ", bulk IN" : "",
|
|
d->has_bulk_out ? ", bulk OUT" : "");
|
|
|
|
usb_bind_all(d);
|
|
|
|
if(d->driver == 0){
|
|
kinfo("usb: no driver for this interface\n");
|
|
if(n > 0)
|
|
d->in_use = 0; // nothing will ever use it; give it back
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tell every driver bound to any interface of `dev` that it is gone, and give
|
|
// the interface slots back. The controller frees the endpoints afterwards.
|
|
void
|
|
usb_detach(struct usb_dev *dev)
|
|
{
|
|
announce(dev, "remove");
|
|
present_del(dev);
|
|
for(int i = 0; i < NIFDEV; i++){
|
|
struct usb_dev *d = &ifdevs[i];
|
|
if(!d->in_use || d->base != dev)
|
|
continue;
|
|
if(d->drv && d->drv->disconnect)
|
|
d->drv->disconnect(d);
|
|
d->in_use = 0;
|
|
}
|
|
if(dev->drv && dev->drv->disconnect)
|
|
dev->drv->disconnect(dev);
|
|
dev->drv = 0;
|
|
dev->driver = 0;
|
|
}
|