// 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. static void parse_config(struct usb_dev *dev) { int off = 0; int seen_iface = 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)){ if(seen_iface) break; // first interface only, for now memmove(&dev->iface, &dev->conf[off], 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; } } // --------------------------------------------------------------------------- // 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; 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; parse_config(dev); // 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; } cprintf("usb: interface class %d.%d protocol %d, %d endpoint%s%s%s%s\n", dev->iface.bInterfaceClass, dev->iface.bInterfaceSubClass, dev->iface.bInterfaceProtocol, dev->iface.bNumEndpoints, dev->iface.bNumEndpoints == 1 ? "" : "s", dev->has_intr_in ? ", interrupt IN" : "", dev->has_bulk_in ? ", bulk IN" : "", dev->has_bulk_out ? ", bulk OUT" : ""); usb_bind_all(dev); if(dev->driver == 0) cprintf("usb: no driver for this device\n"); }