Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / drivers / usb.c
diff --git a/qemu/roms/openbios/drivers/usb.c b/qemu/roms/openbios/drivers/usb.c
new file mode 100644 (file)
index 0000000..c6e3717
--- /dev/null
@@ -0,0 +1,587 @@
+/*
+ * Driver for USB ported from CoreBoot
+ *
+ * Copyright (C) 2014 BALATON Zoltan
+ *
+ * This file was part of the libpayload project.
+ *
+ * Copyright (C) 2008-2010 coresystems GmbH
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "drivers/usb.h"
+#include "usb.h"
+#include "timer.h"
+#include "libc/byteorder.h"
+
+hci_t *usb_hcs = 0;
+
+static void usb_nop_init (usbdev_t *dev);
+
+static void
+usb_nop_destroy (usbdev_t *dev)
+{
+       if (dev->descriptor != 0)
+               free (dev->descriptor);
+       usb_nop_init (dev);
+       dev->address = -1;
+       dev->hub = -1;
+       dev->port = -1;
+}
+
+static void
+usb_nop_poll (usbdev_t *dev)
+{
+       return;
+}
+
+static void
+usb_nop_init (usbdev_t *dev)
+{
+       dev->descriptor = 0;
+       dev->destroy = usb_nop_destroy;
+       dev->poll = usb_nop_poll;
+}
+
+hci_t *
+new_controller (void)
+{
+       hci_t *controller = malloc (sizeof (hci_t));
+
+       if (controller) {
+               /* atomic */
+               controller->next = usb_hcs;
+               usb_hcs = controller;
+               /* atomic end */
+       }
+
+       return controller;
+}
+
+void
+detach_controller (hci_t *controller)
+{
+       if (controller == NULL)
+               return;
+       if (usb_hcs == controller) {
+               usb_hcs = controller->next;
+       } else {
+               hci_t *it = usb_hcs;
+               while (it != NULL) {
+                       if (it->next == controller) {
+                               it->next = controller->next;
+                               return;
+                       }
+                       it = it->next;
+               }
+       }
+}
+
+/**
+ * Shut down all controllers
+ */
+int
+usb_exit (void)
+{
+       while (usb_hcs != NULL) {
+               usb_hcs->shutdown(usb_hcs);
+       }
+       return 0;
+}
+
+/**
+ * Polls all hubs on all USB controllers, to find out about device changes
+ */
+void
+usb_poll (void)
+{
+       if (usb_hcs == 0)
+               return;
+       hci_t *controller = usb_hcs;
+       while (controller != NULL) {
+               int i;
+               for (i = 0; i < 128; i++) {
+                       if (controller->devices[i] != 0) {
+                               controller->devices[i]->poll (controller->devices[i]);
+                       }
+               }
+               controller = controller->next;
+       }
+}
+
+void
+init_device_entry (hci_t *controller, int i)
+{
+       if (controller->devices[i] != 0)
+               usb_debug("warning: device %d reassigned?\n", i);
+       controller->devices[i] = malloc(sizeof(usbdev_t));
+       controller->devices[i]->controller = controller;
+       controller->devices[i]->address = -1;
+       controller->devices[i]->hub = -1;
+       controller->devices[i]->port = -1;
+       controller->devices[i]->init = usb_nop_init;
+       controller->devices[i]->init (controller->devices[i]);
+}
+
+void
+set_feature (usbdev_t *dev, int endp, int feature, int rtype)
+{
+       dev_req_t dr;
+
+       dr.bmRequestType = rtype;
+       dr.data_dir = host_to_device;
+       dr.bRequest = SET_FEATURE;
+       dr.wValue = __cpu_to_le16(feature);
+       dr.wIndex = __cpu_to_le16(endp);
+       dr.wLength = 0;
+       dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
+}
+
+void
+get_status (usbdev_t *dev, int intf, int rtype, int len, void *data)
+{
+       dev_req_t dr;
+
+       dr.bmRequestType = rtype;
+       dr.data_dir = device_to_host;
+       dr.bRequest = GET_STATUS;
+       dr.wValue = 0;
+       dr.wIndex = __cpu_to_le16(intf);
+       dr.wLength = __cpu_to_le16(len);
+       dev->controller->control (dev, IN, sizeof (dr), &dr, len, data);
+}
+
+u8 *
+get_descriptor (usbdev_t *dev, unsigned char bmRequestType, int descType,
+               int descIdx, int langID)
+{
+       u8 buf[8];
+       u8 *result;
+       dev_req_t dr;
+       int size;
+
+       dr.bmRequestType = bmRequestType;
+       dr.data_dir = device_to_host;   // always like this for descriptors
+       dr.bRequest = GET_DESCRIPTOR;
+       dr.wValue = __cpu_to_le16((descType << 8) | descIdx);
+       dr.wIndex = __cpu_to_le16(langID);
+       dr.wLength = __cpu_to_le16(8);
+       if (dev->controller->control (dev, IN, sizeof (dr), &dr, 8, buf)) {
+               usb_debug ("getting descriptor size (type %x) failed\n",
+                       descType);
+       }
+
+       if (descType == 1) {
+               device_descriptor_t *dd = (device_descriptor_t *) buf;
+               usb_debug ("maxPacketSize0: %x\n", dd->bMaxPacketSize0);
+               if (dd->bMaxPacketSize0 != 0)
+                       dev->endpoints[0].maxpacketsize = dd->bMaxPacketSize0;
+       }
+
+       /* special case for configuration descriptors: they carry all their
+          subsequent descriptors with them, and keep the entire size at a
+          different location */
+       size = buf[0];
+       if (buf[1] == 2) {
+               int realsize = __le16_to_cpu(((unsigned short *) (buf + 2))[0]);
+               size = realsize;
+       }
+       result = malloc (size);
+       memset (result, 0, size);
+       dr.wLength = __cpu_to_le16(size);
+       if (dev->controller->
+           control (dev, IN, sizeof (dr), &dr, size, result)) {
+               usb_debug ("getting descriptor (type %x, size %x) failed\n",
+                       descType, size);
+       }
+
+       return result;
+}
+
+void
+set_configuration (usbdev_t *dev)
+{
+       dev_req_t dr;
+
+       dr.bmRequestType = 0;
+       dr.bRequest = SET_CONFIGURATION;
+       dr.wValue = __cpu_to_le16(dev->configuration[5]);
+       dr.wIndex = 0;
+       dr.wLength = 0;
+       dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
+}
+
+int
+clear_feature (usbdev_t *dev, int endp, int feature, int rtype)
+{
+       dev_req_t dr;
+
+       dr.bmRequestType = rtype;
+       dr.data_dir = host_to_device;
+       dr.bRequest = CLEAR_FEATURE;
+       dr.wValue = __cpu_to_le16(feature);
+       dr.wIndex = __cpu_to_le16(endp);
+       dr.wLength = 0;
+       return dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
+}
+
+int
+clear_stall (endpoint_t *ep)
+{
+       usbdev_t *dev = ep->dev;
+       int endp = ep->endpoint;
+       int rtype = gen_bmRequestType (host_to_device, standard_type,
+                                       endp ? endp_recp : dev_recp);
+
+       int ret = clear_feature (dev, endp, ENDPOINT_HALT, rtype);
+       ep->toggle = 0;
+       return ret;
+}
+
+/* returns free address or -1 */
+static int
+get_free_address (hci_t *controller)
+{
+       int i;
+       for (i = 1; i < 128; i++) {
+               if (controller->devices[i] == 0)
+                       return i;
+       }
+       usb_debug ("no free address found\n");
+       return -1;              // no free address
+}
+
+int
+generic_set_address (hci_t *controller, int speed, int hubport, int hubaddr)
+{
+       int adr = get_free_address (controller);        // address to set
+       dev_req_t dr;
+
+       memset (&dr, 0, sizeof (dr));
+       dr.data_dir = host_to_device;
+       dr.req_type = standard_type;
+       dr.req_recp = dev_recp;
+       dr.bRequest = SET_ADDRESS;
+       dr.wValue = __cpu_to_le16(adr);
+       dr.wIndex = 0;
+       dr.wLength = 0;
+
+       init_device_entry(controller, adr);
+       usbdev_t *dev = controller->devices[adr];
+       // dummy values for registering the address
+       dev->address = 0;
+       dev->hub = hubaddr;
+       dev->port = hubport;
+       dev->speed = speed;
+       dev->endpoints[0].dev = dev;
+       dev->endpoints[0].endpoint = 0;
+       dev->endpoints[0].maxpacketsize = 8;
+       dev->endpoints[0].toggle = 0;
+       dev->endpoints[0].direction = SETUP;
+       mdelay (50);
+       if (dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0)) {
+               return -1;
+       }
+       mdelay (50);
+
+       return adr;
+}
+
+/* Normalize bInterval to log2 of microframes */
+static int
+usb_decode_interval(const int speed, const endpoint_type type, const unsigned char bInterval)
+{
+#define LOG2(a) ((sizeof(unsigned) << 3) - __builtin_clz(a) - 1)
+       switch (speed) {
+       case LOW_SPEED:
+               switch (type) {
+               case ISOCHRONOUS: case INTERRUPT:
+                       return LOG2(bInterval) + 3;
+               default:
+                       return 0;
+               }
+       case FULL_SPEED:
+               switch (type) {
+               case ISOCHRONOUS:
+                       return (bInterval - 1) + 3;
+               case INTERRUPT:
+                       return LOG2(bInterval) + 3;
+               default:
+                       return 0;
+               }
+       case HIGH_SPEED:
+               switch (type) {
+               case ISOCHRONOUS: case INTERRUPT:
+                       return bInterval - 1;
+               default:
+                       return LOG2(bInterval);
+               }
+       case SUPER_SPEED:
+               switch (type) {
+               case ISOCHRONOUS: case INTERRUPT:
+                       return bInterval - 1;
+               default:
+                       return 0;
+               }
+       default:
+               return 0;
+       }
+#undef LOG2
+}
+
+static int
+set_address (hci_t *controller, int speed, int hubport, int hubaddr)
+{
+       int adr = controller->set_address(controller, speed, hubport, hubaddr);
+       if (adr < 0 || !controller->devices[adr]) {
+               usb_debug ("set_address failed\n");
+               return -1;
+       }
+       configuration_descriptor_t *cd;
+       device_descriptor_t *dd;
+
+       usbdev_t *dev = controller->devices[adr];
+       dev->address = adr;
+       dev->hub = hubaddr;
+       dev->port = hubport;
+       dev->speed = speed;
+       dev->descriptor = get_descriptor (dev, gen_bmRequestType
+               (device_to_host, standard_type, dev_recp), 1, 0, 0);
+       dd = (device_descriptor_t *) dev->descriptor;
+
+       usb_debug ("* found device (0x%04x:0x%04x, USB %x.%x)",
+                __le16_to_cpu(dd->idVendor), __le16_to_cpu(dd->idProduct),
+                __le16_to_cpu(dd->bcdUSB) >> 8, __le16_to_cpu(dd->bcdUSB) & 0xff);
+       dev->quirks = USB_QUIRK_NONE;
+
+       usb_debug ("\ndevice has %x configurations\n", dd->bNumConfigurations);
+       if (dd->bNumConfigurations == 0) {
+               /* device isn't usable */
+               usb_debug ("... no usable configuration!\n");
+               dev->address = 0;
+               return -1;
+       }
+
+       dev->configuration = get_descriptor (dev, gen_bmRequestType
+               (device_to_host, standard_type, dev_recp), 2, 0, 0);
+       cd = (configuration_descriptor_t *) dev->configuration;
+       interface_descriptor_t *interface =
+               (interface_descriptor_t *) (((char *) cd) + cd->bLength);
+       {
+               int i;
+               int num = cd->bNumInterfaces;
+               interface_descriptor_t *current = interface;
+               usb_debug ("device has %x interfaces\n", num);
+               if (num > 1) {
+                       usb_debug ("\nNOTICE: This driver defaults to using the first interface.\n"
+                                  "This might be the wrong choice and lead to limited functionality\n"
+                                  "of the device.\n");
+                        /* we limit to the first interface, as there was no need to
+                        * implement something else for the time being. If you need
+                        * it, see the SetInterface and GetInterface functions in
+                        * the USB specification, and adapt appropriately.
+                        */
+                       num = (num > 1) ? 1 : num;
+               }
+               for (i = 0; i < num; i++) {
+                       int j;
+                       usb_debug (" #%x has %x endpoints, interface %x:%x, protocol %x\n",
+                                       current->bInterfaceNumber, current->bNumEndpoints, current->bInterfaceClass, current->bInterfaceSubClass, current->bInterfaceProtocol);
+                       endpoint_descriptor_t *endp =
+                               (endpoint_descriptor_t *) (((char *) current)
+                                                          + current->bLength);
+                       /* Skip any non-endpoint descriptor */
+                       if (endp->bDescriptorType != 0x05)
+                               endp = (endpoint_descriptor_t *)(((char *)endp) + ((char *)endp)[0]);
+
+                       memset (dev->endpoints, 0, sizeof (dev->endpoints));
+                       dev->num_endp = 1;      // 0 always exists
+                       dev->endpoints[0].dev = dev;
+                       dev->endpoints[0].maxpacketsize = dd->bMaxPacketSize0;
+                       dev->endpoints[0].direction = SETUP;
+                       dev->endpoints[0].type = CONTROL;
+                       dev->endpoints[0].interval = usb_decode_interval(dev->speed, CONTROL, endp->bInterval);
+                       for (j = 1; j <= current->bNumEndpoints; j++) {
+#ifdef CONFIG_DEBUG_USB
+                               static const char *transfertypes[4] = {
+                                       "control", "isochronous", "bulk", "interrupt"
+                               };
+                               usb_debug ("   #%x: Endpoint %x (%s), max packet size %x, type %s\n", j, endp->bEndpointAddress & 0x7f, ((endp->bEndpointAddress & 0x80) != 0) ? "in" : "out", __le16_to_cpu(endp->wMaxPacketSize), transfertypes[endp->bmAttributes]);
+#endif
+                               endpoint_t *ep =
+                                       &dev->endpoints[dev->num_endp++];
+                               ep->dev = dev;
+                               ep->endpoint = endp->bEndpointAddress;
+                               ep->toggle = 0;
+                               ep->maxpacketsize = __le16_to_cpu(endp->wMaxPacketSize);
+                               ep->direction =
+                                       ((endp->bEndpointAddress & 0x80) ==
+                                        0) ? OUT : IN;
+                               ep->type = endp->bmAttributes;
+                               ep->interval = usb_decode_interval(dev->speed, ep->type, endp->bInterval);
+                               endp = (endpoint_descriptor_t
+                                       *) (((char *) endp) + endp->bLength);
+                       }
+                       current = (interface_descriptor_t *) endp;
+               }
+       }
+
+       if (controller->finish_device_config &&
+                       controller->finish_device_config(dev))
+               return adr; /* Device isn't configured correctly,
+                              only control transfers may work. */
+
+       set_configuration(dev);
+
+       int class = dd->bDeviceClass;
+       if (class == 0)
+               class = interface->bInterfaceClass;
+
+       usb_debug(", class: ");
+       switch (class) {
+       case audio_device:
+               usb_debug("audio\n");
+               break;
+       case comm_device:
+               usb_debug("communication\n");
+               break;
+       case hid_device:
+               usb_debug ("HID\n");
+#ifdef CONFIG_USB_HID
+               controller->devices[adr]->init = usb_hid_init;
+               return adr;
+#else
+               usb_debug ("NOTICE: USB HID support not compiled in\n");
+#endif
+               break;
+       case physical_device:
+               usb_debug("physical\n");
+               break;
+       case imaging_device:
+               usb_debug("camera\n");
+               break;
+       case printer_device:
+               usb_debug("printer\n");
+               break;
+       case msc_device:
+               usb_debug ("MSC\n");
+#ifdef CONFIG_USB_MSC
+               controller->devices[adr]->init = usb_msc_init;
+               return adr;
+#else
+               usb_debug ("NOTICE: USB MSC support not compiled in\n");
+#endif
+               break;
+       case hub_device:
+               usb_debug ("hub\n");
+#ifdef CONFIG_USB_HUB
+               controller->devices[adr]->init = usb_hub_init;
+               return adr;
+#else
+               usb_debug ("NOTICE: USB hub support not compiled in.\n");
+#endif
+               break;
+       case cdc_device:
+               usb_debug("CDC\n");
+               break;
+       case ccid_device:
+               usb_debug("smartcard / CCID\n");
+               break;
+       case security_device:
+               usb_debug("content security\n");
+               break;
+       case video_device:
+               usb_debug("video\n");
+               break;
+       case healthcare_device:
+               usb_debug("healthcare\n");
+               break;
+       case diagnostic_device:
+               usb_debug("diagnostic\n");
+               break;
+       case wireless_device:
+               usb_debug("wireless\n");
+               break;
+       default:
+               usb_debug("unsupported class %x\n", class);
+               break;
+       }
+       controller->devices[adr]->init = usb_generic_init;
+       return adr;
+}
+
+/*
+ * Should be called by the hub drivers whenever a physical detach occurs
+ * and can be called by usb class drivers if they are unsatisfied with a
+ * malfunctioning device.
+ */
+void
+usb_detach_device(hci_t *controller, int devno)
+{
+       /* check if device exists, as we may have
+          been called yet by the usb class driver */
+       if (controller->devices[devno]) {
+               controller->devices[devno]->destroy (controller->devices[devno]);
+               free(controller->devices[devno]);
+               controller->devices[devno] = NULL;
+               if (controller->destroy_device)
+                       controller->destroy_device(controller, devno);
+       }
+}
+
+int
+usb_attach_device(hci_t *controller, int hubaddress, int port, int speed)
+{
+#ifdef CONFIG_USB_DEBUG
+       static const char* speeds[] = { "full", "low", "high" };
+       usb_debug ("%sspeed device\n", (speed <= 2) ? speeds[speed] : "invalid value - no");
+#endif
+       int newdev = set_address (controller, speed, port, hubaddress);
+       if (newdev == -1)
+               return -1;
+       usbdev_t *newdev_t = controller->devices[newdev];
+       // determine responsible driver - current done in set_address
+       newdev_t->init (newdev_t);
+       /* init() may have called usb_detach_device() yet, so check */
+       return controller->devices[newdev] ? newdev : -1;
+}
+
+static void
+usb_generic_destroy (usbdev_t *dev)
+{
+       if (usb_generic_remove)
+               usb_generic_remove(dev);
+}
+
+void
+usb_generic_init (usbdev_t *dev)
+{
+       dev->data = NULL;
+       dev->destroy = usb_generic_destroy;
+
+       if (usb_generic_create)
+               usb_generic_create(dev);
+}