// 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 "types.h" #include "defs.h" #include "param.h" #include "memlayout.h" #include "mmu.h" #include "spinlock.h" #include "dma.h" #include "usb.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/usb.h. A fixed pool, like everything else here: sixteen is more // interfaces than a desk of keyboards, mice and receivers adds up to. #define NIFDEV 16 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; } // --------------------------------------------------------------------------- void usb_attach(struct usb_dev *dev) { struct usb_config_desc *cd; void *buf = get_scratch(); int total; if(buf == 0) return; // 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; } cprintf("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){ cprintf("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) { 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; }