Initial support for booting from USB drives.
[seabios.git] / src / usb.c
index ecb86839576c40951024d5bc08a4389efc324164..cc3b2012c0fb62cb6c66598c04409f3c73464f1a 100644 (file)
--- a/src/usb.c
+++ b/src/usb.c
 #include "usb-uhci.h" // uhci_init
 #include "usb-ohci.h" // ohci_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
+ ****************************************************************/
+
+// 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
              , void *data, int datasize)
@@ -32,19 +40,50 @@ send_control(u32 endp, int dir, const void *cmd, int cmdsize
 }
 
 struct usb_pipe *
-alloc_intr_pipe(u32 endp, int period)
+alloc_bulk_pipe(u32 endp)
 {
     struct usb_s *cntl = endp2cntl(endp);
     switch (cntl->type) {
     default:
     case USB_TYPE_UHCI:
-        return uhci_alloc_intr_pipe(endp, period);
+        return uhci_alloc_bulk_pipe(endp);
     case USB_TYPE_OHCI:
-        return ohci_alloc_intr_pipe(endp, period);
+        return NULL;
     }
 }
 
 int
+usb_send_bulk(struct usb_pipe *pipe, int dir, void *data, int datasize)
+{
+    u32 endp = GET_FLATPTR(pipe->endp);
+    struct usb_s *cntl = endp2cntl(endp);
+    switch (cntl->type) {
+    default:
+    case USB_TYPE_UHCI:
+        return uhci_send_bulk(pipe, dir, data, datasize);
+    case USB_TYPE_OHCI:
+        return -1;
+    }
+}
+
+struct usb_pipe *
+alloc_intr_pipe(u32 endp, int period)
+{
+    struct usb_s *cntl = endp2cntl(endp);
+    // Find the exponential period of the requested time.
+    if (period <= 0)
+        period = 1;
+    int frameexp = __fls(period);
+    switch (cntl->type) {
+    default:
+    case USB_TYPE_UHCI:
+        return uhci_alloc_intr_pipe(endp, frameexp);
+    case USB_TYPE_OHCI:
+        return ohci_alloc_intr_pipe(endp, frameexp);
+    }
+}
+
+int noinline
 usb_poll_intr(struct usb_pipe *pipe, void *data)
 {
     u32 endp = GET_FLATPTR(pipe->endp);
@@ -58,6 +97,40 @@ usb_poll_intr(struct usb_pipe *pipe, void *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;
+    }
+}
+
+// Build an encoded "endp" from an endpoint descriptor.
+u32
+mkendpFromDesc(u32 endp, struct usb_endpoint_descriptor *epdesc)
+{
+    return mkendp(endp2cntl(endp), endp2devaddr(endp)
+                  , epdesc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK
+                  , endp2speed(endp), epdesc->wMaxPacketSize);
+}
+
+// Send a message to the default control pipe of a device.
 int
 send_default_control(u32 endp, const struct usb_ctrlrequest *req, void *data)
 {
@@ -121,7 +194,7 @@ set_address(u32 endp)
     int ret = send_default_control(endp, &req, NULL);
     if (ret)
         return 0;
-    mdelay(2);
+    msleep(USB_TIME_SETADDR_RECOVERY);
 
     cntl->maxaddr++;
     return mkendp(cntl, cntl->maxaddr, 0, endp2speed(endp), endp2maxsize(endp));
@@ -139,12 +212,17 @@ set_configuration(u32 endp, u16 val)
     return send_default_control(endp, &req, NULL);
 }
 
+
+/****************************************************************
+ * Initialization and enumeration
+ ****************************************************************/
+
 // 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)
 {
-    dprintf(1, "config_usb: %p %d\n", cntl, lowspeed);
+    dprintf(3, "config_usb: %p %d\n", cntl, lowspeed);
 
     // Get device info
     u32 endp = mkendp(cntl, 0, 0, lowspeed, 8);
@@ -167,10 +245,12 @@ configure_usb_device(struct usb_s *cntl, int lowspeed)
     // Determine if a driver exists for this device - only look at the
     // 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
+    if ((iface->bInterfaceClass != USB_CLASS_HID
+         || iface->bInterfaceSubClass != USB_INTERFACE_SUBCLASS_BOOT
+         || iface->bInterfaceProtocol != USB_INTERFACE_PROTOCOL_KEYBOARD)
+        && (iface->bInterfaceClass != USB_CLASS_MASS_STORAGE)
+        && (iface->bInterfaceClass != USB_CLASS_HUB))
+        // Not a supported device.
         goto fail;
 
     // Set the address and configure device.
@@ -182,8 +262,15 @@ configure_usb_device(struct usb_s *cntl, int lowspeed)
         goto fail;
 
     // Configure driver.
-    ret = usb_keyboard_init(endp, iface, ((void*)config + config->wTotalLength
-                                          - (void*)iface));
+    if (iface->bInterfaceClass == USB_CLASS_HUB) {
+        free(config);
+        return usb_hub_init(endp);
+    }
+    int imax = (void*)config + config->wTotalLength - (void*)iface;
+    if (iface->bInterfaceClass == USB_CLASS_MASS_STORAGE)
+        ret = usb_msc_init(endp, iface, imax);
+    else
+        ret = usb_keyboard_init(endp, iface, imax);
     if (ret)
         goto fail;
 
@@ -195,8 +282,9 @@ fail:
 }
 
 void
-usb_setup()
+usb_setup(void)
 {
+    ASSERT32FLAT();
     if (! CONFIG_USB)
         return;
 
@@ -216,17 +304,15 @@ usb_setup()
         struct usb_s *cntl = &USBControllers[count];
         cntl->bdf = bdf;
 
-        int devcount = 0;
         if (code == PCI_CLASS_SERIAL_USB_UHCI)
-            devcount = uhci_init(cntl);
+            run_thread(uhci_init, cntl);
         else if (code == PCI_CLASS_SERIAL_USB_OHCI)
-            devcount = ohci_init(cntl);
+            run_thread(ohci_init, cntl);
+        else
+            continue;
 
-        if (devcount > 0) {
-            // Success
-            count++;
-            if (count >= ARRAY_SIZE(USBControllers))
-                break;
-        }
+        count++;
+        if (count >= ARRAY_SIZE(USBControllers))
+            break;
     }
 }