X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=src%2Fusb.c;h=1f69d16aa2b738d732125891d8aff0b50602f45a;hb=refs%2Fheads%2Fcoreboot;hp=7d4520ea8b32a752850a434c7b4fdcece59ff062;hpb=8bbc79c435c896ad0107725fa77c7aeb860af625;p=seabios.git diff --git a/src/usb.c b/src/usb.c index 7d4520e..1f69d16 100644 --- a/src/usb.c +++ b/src/usb.c @@ -11,67 +11,182 @@ #include "pci_ids.h" // PCI_CLASS_SERIAL_USB_UHCI #include "usb-uhci.h" // uhci_init #include "usb-ohci.h" // ohci_init +#include "usb-ehci.h" // ehci_init #include "usb-hid.h" // usb_keyboard_setup +#include "usb-hub.h" // usb_hub_init +#include "usb-msc.h" // usb_msc_init #include "usb.h" // struct usb_s #include "biosvar.h" // GET_GLOBAL -struct usb_s USBControllers[16] VAR16VISIBLE; +/**************************************************************** + * Controller function wrappers + ****************************************************************/ + +// Free an allocated control or bulk pipe. +void +free_pipe(struct usb_pipe *pipe) +{ + ASSERT32FLAT(); + if (!pipe) + return; + switch (pipe->type) { + default: + case USB_TYPE_UHCI: + return uhci_free_pipe(pipe); + case USB_TYPE_OHCI: + return ohci_free_pipe(pipe); + case USB_TYPE_EHCI: + return ehci_free_pipe(pipe); + } +} + +// Allocate a control pipe to a default endpoint (which can only be +// used by 32bit code) +static struct usb_pipe * +alloc_default_control_pipe(struct usb_pipe *dummy) +{ + switch (dummy->type) { + default: + case USB_TYPE_UHCI: + return uhci_alloc_control_pipe(dummy); + case USB_TYPE_OHCI: + return ohci_alloc_control_pipe(dummy); + case USB_TYPE_EHCI: + return ehci_alloc_control_pipe(dummy); + } +} + +// Send a message on a control pipe using the default control descriptor. static int -send_control(u32 endp, int dir, const void *cmd, int cmdsize +send_control(struct usb_pipe *pipe, int dir, const void *cmd, int cmdsize , void *data, int datasize) { - struct usb_s *cntl = endp2cntl(endp); - switch (cntl->type) { + ASSERT32FLAT(); + switch (pipe->type) { default: case USB_TYPE_UHCI: - return uhci_control(endp, dir, cmd, cmdsize, data, datasize); + return uhci_control(pipe, dir, cmd, cmdsize, data, datasize); case USB_TYPE_OHCI: - return ohci_control(endp, dir, cmd, cmdsize, data, datasize); + return ohci_control(pipe, dir, cmd, cmdsize, data, datasize); + case USB_TYPE_EHCI: + return ehci_control(pipe, dir, cmd, cmdsize, data, datasize); } } +// Fill "pipe" endpoint info from an endpoint descriptor. +static void +desc2pipe(struct usb_pipe *newpipe, struct usb_pipe *origpipe + , struct usb_endpoint_descriptor *epdesc) +{ + memcpy(newpipe, origpipe, sizeof(*newpipe)); + newpipe->ep = epdesc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + newpipe->maxpacket = epdesc->wMaxPacketSize; +} + struct usb_pipe * -alloc_intr_pipe(u32 endp, int period) +alloc_bulk_pipe(struct usb_pipe *pipe, struct usb_endpoint_descriptor *epdesc) { - struct usb_s *cntl = endp2cntl(endp); + struct usb_pipe dummy; + desc2pipe(&dummy, pipe, epdesc); + switch (pipe->type) { + default: + case USB_TYPE_UHCI: + return uhci_alloc_bulk_pipe(&dummy); + case USB_TYPE_OHCI: + return ohci_alloc_bulk_pipe(&dummy); + case USB_TYPE_EHCI: + return ehci_alloc_bulk_pipe(&dummy); + } +} + +int +usb_send_bulk(struct usb_pipe *pipe_fl, int dir, void *data, int datasize) +{ + switch (GET_FLATPTR(pipe_fl->type)) { + default: + case USB_TYPE_UHCI: + return uhci_send_bulk(pipe_fl, dir, data, datasize); + case USB_TYPE_OHCI: + return ohci_send_bulk(pipe_fl, dir, data, datasize); + case USB_TYPE_EHCI: + return ehci_send_bulk(pipe_fl, dir, data, datasize); + } +} + +struct usb_pipe * +alloc_intr_pipe(struct usb_pipe *pipe, struct usb_endpoint_descriptor *epdesc) +{ + struct usb_pipe dummy; + desc2pipe(&dummy, pipe, epdesc); // Find the exponential period of the requested time. - if (period <= 0) - period = 1; - int frameexp = __fls(period); - switch (cntl->type) { + int period = epdesc->bInterval; + int frameexp; + if (pipe->speed != USB_HIGHSPEED) + frameexp = (period <= 0) ? 0 : __fls(period); + else + frameexp = (period <= 4) ? 0 : period - 4; + switch (pipe->type) { default: case USB_TYPE_UHCI: - return uhci_alloc_intr_pipe(endp, frameexp); + return uhci_alloc_intr_pipe(&dummy, frameexp); case USB_TYPE_OHCI: - return ohci_alloc_intr_pipe(endp, frameexp); + return ohci_alloc_intr_pipe(&dummy, frameexp); + case USB_TYPE_EHCI: + return ehci_alloc_intr_pipe(&dummy, frameexp); } } int noinline -usb_poll_intr(struct usb_pipe *pipe, void *data) +usb_poll_intr(struct usb_pipe *pipe_fl, void *data) { - u32 endp = GET_FLATPTR(pipe->endp); - struct usb_s *cntl = endp2cntl(endp); - switch (GET_GLOBAL(cntl->type)) { + switch (GET_FLATPTR(pipe_fl->type)) { default: case USB_TYPE_UHCI: - return uhci_poll_intr(pipe, data); + return uhci_poll_intr(pipe_fl, data); case USB_TYPE_OHCI: - return ohci_poll_intr(pipe, data); + return ohci_poll_intr(pipe_fl, data); + case USB_TYPE_EHCI: + return ehci_poll_intr(pipe_fl, data); } } + +/**************************************************************** + * Helper functions + ****************************************************************/ + +// Find the first endpoing of a given type in an interface description. +struct usb_endpoint_descriptor * +findEndPointDesc(struct usb_interface_descriptor *iface, int imax + , int type, int dir) +{ + struct usb_endpoint_descriptor *epdesc = (void*)&iface[1]; + for (;;) { + if ((void*)epdesc >= (void*)iface + imax + || epdesc->bDescriptorType == USB_DT_INTERFACE) { + return NULL; + } + if (epdesc->bDescriptorType == USB_DT_ENDPOINT + && (epdesc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == dir + && (epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == type) + return epdesc; + epdesc = (void*)epdesc + epdesc->bLength; + } +} + +// Send a message to the default control pipe of a device. int -send_default_control(u32 endp, const struct usb_ctrlrequest *req, void *data) +send_default_control(struct usb_pipe *pipe, const struct usb_ctrlrequest *req + , void *data) { - return send_control(endp, req->bRequestType & USB_DIR_IN + return send_control(pipe, req->bRequestType & USB_DIR_IN , req, sizeof(*req), data, req->wLength); } // Get the first 8 bytes of the device descriptor. static int -get_device_info8(struct usb_device_descriptor *dinfo, u32 endp) +get_device_info8(struct usb_pipe *pipe, struct usb_device_descriptor *dinfo) { struct usb_ctrlrequest req; req.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE; @@ -79,11 +194,11 @@ get_device_info8(struct usb_device_descriptor *dinfo, u32 endp) req.wValue = USB_DT_DEVICE<<8; req.wIndex = 0; req.wLength = 8; - return send_default_control(endp, &req, dinfo); + return send_default_control(pipe, &req, dinfo); } static struct usb_config_descriptor * -get_device_config(u32 endp) +get_device_config(struct usb_pipe *pipe) { struct usb_config_descriptor cfg; @@ -93,7 +208,7 @@ get_device_config(u32 endp) req.wValue = USB_DT_CONFIG<<8; req.wIndex = 0; req.wLength = sizeof(cfg); - int ret = send_default_control(endp, &req, &cfg); + int ret = send_default_control(pipe, &req, &cfg); if (ret) return NULL; @@ -101,20 +216,68 @@ get_device_config(u32 endp) if (!config) return NULL; req.wLength = cfg.wTotalLength; - ret = send_default_control(endp, &req, config); + ret = send_default_control(pipe, &req, config); if (ret) return NULL; //hexdump(config, cfg.wTotalLength); return config; } -static u32 -set_address(u32 endp) +static int +set_configuration(struct usb_pipe *pipe, u16 val) { - dprintf(3, "set_address %x\n", endp); - struct usb_s *cntl = endp2cntl(endp); + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE; + req.bRequest = USB_REQ_SET_CONFIGURATION; + req.wValue = val; + req.wIndex = 0; + req.wLength = 0; + return send_default_control(pipe, &req, NULL); +} + + +/**************************************************************** + * Initialization and enumeration + ****************************************************************/ + +// Assign an address to a device in the default state on the given +// controller. +static struct usb_pipe * +usb_set_address(struct usbhub_s *hub, int port, int speed) +{ + ASSERT32FLAT(); + struct usb_s *cntl = hub->cntl; + dprintf(3, "set_address %p\n", cntl); if (cntl->maxaddr >= USB_MAXADDR) - return 0; + return NULL; + + struct usb_pipe *defpipe = cntl->defaultpipe; + if (!defpipe) { + // Create a pipe for the default address. + struct usb_pipe dummy; + memset(&dummy, 0, sizeof(dummy)); + dummy.cntl = cntl; + dummy.type = cntl->type; + dummy.maxpacket = 8; + dummy.path = (u64)-1; + cntl->defaultpipe = defpipe = alloc_default_control_pipe(&dummy); + if (!defpipe) + return NULL; + } + defpipe->speed = speed; + if (hub->pipe) { + if (hub->pipe->speed == USB_HIGHSPEED) { + defpipe->tt_devaddr = hub->pipe->devaddr; + defpipe->tt_port = port; + } else { + defpipe->tt_devaddr = hub->pipe->tt_devaddr; + defpipe->tt_port = hub->pipe->tt_port; + } + } else { + defpipe->tt_devaddr = defpipe->tt_port = 0; + } + + msleep(USB_TIME_RSTRCY); struct usb_ctrlrequest req; req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE; @@ -122,38 +285,33 @@ set_address(u32 endp) req.wValue = cntl->maxaddr + 1; req.wIndex = 0; req.wLength = 0; - int ret = send_default_control(endp, &req, NULL); + int ret = send_default_control(defpipe, &req, NULL); if (ret) - return 0; + return NULL; + msleep(USB_TIME_SETADDR_RECOVERY); cntl->maxaddr++; - return mkendp(cntl, cntl->maxaddr, 0, endp2speed(endp), endp2maxsize(endp)); -} - -static int -set_configuration(u32 endp, u16 val) -{ - struct usb_ctrlrequest req; - req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE; - req.bRequest = USB_REQ_SET_CONFIGURATION; - req.wValue = val; - req.wIndex = 0; - req.wLength = 0; - return send_default_control(endp, &req, NULL); + defpipe->devaddr = cntl->maxaddr; + struct usb_pipe *pipe = alloc_default_control_pipe(defpipe); + defpipe->devaddr = 0; + if (hub->pipe) + pipe->path = hub->pipe->path; + pipe->path = (pipe->path << 8) | port; + return pipe; } // Called for every found device - see if a driver is available for // this device and do setup if so. -int -configure_usb_device(struct usb_s *cntl, int lowspeed) +static int +configure_usb_device(struct usb_pipe *pipe) { - dprintf(1, "config_usb: %p %d\n", cntl, lowspeed); + ASSERT32FLAT(); + dprintf(3, "config_usb: %p\n", pipe); - // Get device info - u32 endp = mkendp(cntl, 0, 0, lowspeed, 8); + // Set the max packet size for endpoint 0 of this device. struct usb_device_descriptor dinfo; - int ret = get_device_info8(&dinfo, endp); + int ret = get_device_info8(pipe, &dinfo); if (ret) return 0; dprintf(3, "device rev=%04x cls=%02x sub=%02x proto=%02x size=%02x\n" @@ -161,10 +319,10 @@ configure_usb_device(struct usb_s *cntl, int lowspeed) , dinfo.bDeviceProtocol, dinfo.bMaxPacketSize0); if (dinfo.bMaxPacketSize0 < 8 || dinfo.bMaxPacketSize0 > 64) return 0; - endp = mkendp(cntl, 0, 0, lowspeed, dinfo.bMaxPacketSize0); + pipe->maxpacket = dinfo.bMaxPacketSize0; // Get configuration - struct usb_config_descriptor *config = get_device_config(endp); + struct usb_config_descriptor *config = get_device_config(pipe); if (!config) return 0; @@ -172,22 +330,24 @@ configure_usb_device(struct usb_s *cntl, int lowspeed) // first interface of the first configuration. struct usb_interface_descriptor *iface = (void*)(&config[1]); if (iface->bInterfaceClass != USB_CLASS_HID - || iface->bInterfaceSubClass != USB_INTERFACE_SUBCLASS_BOOT - || iface->bInterfaceProtocol != USB_INTERFACE_PROTOCOL_KEYBOARD) - // Not a "boot" keyboard + && iface->bInterfaceClass != USB_CLASS_MASS_STORAGE + && iface->bInterfaceClass != USB_CLASS_HUB) + // Not a supported device. goto fail; - // Set the address and configure device. - endp = set_address(endp); - if (!endp) - goto fail; - ret = set_configuration(endp, config->bConfigurationValue); + // Set the configuration. + ret = set_configuration(pipe, config->bConfigurationValue); if (ret) goto fail; // Configure driver. - ret = usb_keyboard_init(endp, iface, ((void*)config + config->wTotalLength - - (void*)iface)); + int imax = (void*)config + config->wTotalLength - (void*)iface; + if (iface->bInterfaceClass == USB_CLASS_HUB) + ret = usb_hub_init(pipe); + else if (iface->bInterfaceClass == USB_CLASS_MASS_STORAGE) + ret = usb_msc_init(pipe, iface, imax); + else + ret = usb_hid_init(pipe, iface, imax); if (ret) goto fail; @@ -198,6 +358,66 @@ fail: return 0; } +static void +usb_init_hub_port(void *data) +{ + struct usbhub_s *hub = data; + u32 port = hub->port; // XXX - find better way to pass port + + // Detect if device present (and possibly start reset) + int ret = hub->op->detect(hub, port); + if (ret) + // No device present + goto done; + + // Reset port and determine device speed + mutex_lock(&hub->cntl->resetlock); + ret = hub->op->reset(hub, port); + if (ret < 0) + // Reset failed + goto resetfail; + + // Set address of port + struct usb_pipe *pipe = usb_set_address(hub, port, ret); + if (!pipe) { + hub->op->disconnect(hub, port); + goto resetfail; + } + mutex_unlock(&hub->cntl->resetlock); + + // Configure the device + int count = configure_usb_device(pipe); + free_pipe(pipe); + if (!count) + hub->op->disconnect(hub, port); + hub->devcount += count; +done: + hub->threads--; + return; + +resetfail: + mutex_unlock(&hub->cntl->resetlock); + goto done; +} + +void +usb_enumerate(struct usbhub_s *hub) +{ + u32 portcount = hub->portcount; + hub->threads = portcount; + + // Launch a thread for every port. + int i; + for (i=0; iport = i; + run_thread(usb_init_hub_port, hub); + } + + // Wait for threads to complete. + while (hub->threads) + yield(); +} + void usb_setup(void) { @@ -207,29 +427,42 @@ usb_setup(void) dprintf(3, "init usb\n"); - usb_keyboard_setup(); - // Look for USB controllers int count = 0; - int bdf, max; - foreachpci(bdf, max) { - u32 code = pci_config_readl(bdf, PCI_CLASS_REVISION) >> 8; - - if (code >> 8 != PCI_CLASS_SERIAL_USB) + struct pci_device *ehcipci = PCIDevices; + struct pci_device *pci; + foreachpci(pci) { + if (pci->class != PCI_CLASS_SERIAL_USB) continue; - struct usb_s *cntl = &USBControllers[count]; - cntl->bdf = bdf; - - if (code == PCI_CLASS_SERIAL_USB_UHCI) - run_thread(uhci_init, cntl); - else if (code == PCI_CLASS_SERIAL_USB_OHCI) - run_thread(ohci_init, cntl); - else - continue; + if (pci->bdf >= ehcipci->bdf) { + // Check to see if this device has an ehci controller + int found = 0; + ehcipci = pci; + for (;;) { + if (pci_classprog(ehcipci) == PCI_CLASS_SERIAL_USB_EHCI) { + // Found an ehci controller. + int ret = ehci_init(ehcipci, count++, pci); + if (ret) + // Error + break; + count += found; + pci = ehcipci; + break; + } + if (ehcipci->class == PCI_CLASS_SERIAL_USB) + found++; + ehcipci = ehcipci->next; + if (!ehcipci || (pci_bdf_to_busdev(ehcipci->bdf) + != pci_bdf_to_busdev(pci->bdf))) + // No ehci controller found. + break; + } + } - count++; - if (count >= ARRAY_SIZE(USBControllers)) - break; + if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_UHCI) + uhci_init(pci, count++); + else if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_OHCI) + ohci_init(pci, count++); } }