Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / SLOF / lib / libusb / usb-core.c
diff --git a/qemu/roms/SLOF/lib/libusb/usb-core.c b/qemu/roms/SLOF/lib/libusb/usb-core.c
new file mode 100644 (file)
index 0000000..6719c57
--- /dev/null
@@ -0,0 +1,590 @@
+/*****************************************************************************
+ * Copyright (c) 2013 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ *     IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <string.h>
+#include "usb-core.h"
+
+#undef DEBUG
+//#define DEBUG
+#ifdef DEBUG
+#define dprintf(_x ...) do { printf(_x); } while(0)
+#else
+#define dprintf(_x ...)
+#endif
+
+#define __unused __attribute__((unused))
+
+struct usb_hcd_ops *head;
+struct usb_dev *devpool;
+#define USB_DEVPOOL_SIZE 4096
+
+static struct usb_dev *usb_alloc_devpool(void)
+{
+       struct usb_dev *head, *curr, *prev;
+       unsigned int dev_count = 0, i;
+
+       head = SLOF_alloc_mem(USB_DEVPOOL_SIZE);
+       if (!head)
+               return NULL;
+
+       dev_count = USB_DEVPOOL_SIZE/sizeof(struct usb_dev);
+       dprintf("%s: %d number of devices\n", __func__, dev_count);
+       /* Although an array, link them*/
+       for (i = 0, curr = head, prev = NULL; i < dev_count; i++, curr++) {
+               if (prev)
+                       prev->next = curr;
+               curr->next = NULL;
+               prev = curr;
+       }
+
+#ifdef DEBUG
+       for (i = 0, curr = head; curr; curr = curr->next)
+               printf("%s: %d dev %p\n", __func__, i++, curr);
+#endif
+
+       return head;
+}
+
+struct usb_dev *usb_devpool_get(void)
+{
+       struct usb_dev *new;
+
+       if (!devpool) {
+               devpool = usb_alloc_devpool();
+               if (!devpool)
+                       return NULL;
+       }
+
+       new = devpool;
+       devpool = devpool->next;
+       memset(new, 0, sizeof(*new));
+       new->next = NULL;
+       return new;
+}
+
+void usb_devpool_put(struct usb_dev *dev)
+{
+       struct usb_dev *curr;
+       if (!dev && !devpool)
+               return;
+
+       curr = devpool;
+       while (curr->next)
+               curr = curr->next;
+       curr->next = dev;
+       dev->next = NULL;
+}
+
+#ifndef DEBUG
+#define validate_hcd_ops(dev) (dev && dev->hcidev && dev->hcidev->ops)
+#else
+int validate_hcd_ops(struct usb_dev *dev)
+{
+       int ret = true;
+
+       if (!dev) {
+               printf("dev is NULL\n");
+               ret = false;
+       } else if (!dev->hcidev) {
+               printf("hcidev is NULL\n");
+               ret = false;
+       } else if (!dev->hcidev->ops)  {
+               printf("ops is NULL\n");
+               ret = false;
+       }
+       return ret;
+}
+#endif
+
+struct usb_pipe *usb_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep,
+                       char *buf, size_t len)
+{
+       if (validate_hcd_ops(dev) && dev->hcidev->ops->get_pipe)
+               return dev->hcidev->ops->get_pipe(dev, ep, buf, len);
+       else {
+               printf("%s: Failed\n", __func__);
+               return NULL;
+       }
+}
+
+void usb_put_pipe(struct usb_pipe *pipe)
+{
+       struct usb_dev *dev = NULL;
+       if (pipe && pipe->dev) {
+               dev = pipe->dev;
+               if (validate_hcd_ops(dev) && dev->hcidev->ops->put_pipe)
+                       dev->hcidev->ops->put_pipe(pipe);
+       }
+}
+
+int usb_poll_intr(struct usb_pipe *pipe, uint8_t *buf)
+{
+       struct usb_dev *dev = NULL;
+       if (pipe && pipe->dev) {
+               dev = pipe->dev;
+               if (validate_hcd_ops(dev) && dev->hcidev->ops->poll_intr)
+                       return dev->hcidev->ops->poll_intr(pipe, buf);
+       }
+       return 0;
+}
+
+void usb_hcd_register(struct usb_hcd_ops *ops)
+{
+       struct usb_hcd_ops *list;
+
+       if (!ops)
+               printf("Error");
+       dprintf("Registering %s %d\n", ops->name, ops->usb_type);
+
+       if (head) {
+               list = head;
+               while (list->next)
+                       list = list->next;
+               list->next = ops;
+       } else
+               head = ops;
+}
+
+void usb_hcd_init(void *hcidev)
+{
+       struct usb_hcd_dev *dev = hcidev;
+       struct usb_hcd_ops *list = head;
+
+       if (!dev) {
+               printf("Device Error");
+               return;
+       }
+
+       while (list) {
+               if (list->usb_type == dev->type) {
+                       dprintf("usb_ops(%p) for the controller found\n", list);
+                       dev->ops = list;
+                       dev->ops->init(dev);
+                       return;
+               }
+               list = list->next;
+       }
+
+       dprintf("usb_ops for the controller not found\n");
+}
+
+void usb_hcd_exit(void *_hcidev)
+{
+       struct usb_hcd_dev *hcidev = _hcidev;
+
+       dprintf("%s: enter \n", __func__);
+       if (!hcidev) {
+               printf("Device Error");
+               return;
+       }
+
+       if (hcidev->ops->exit)
+               hcidev->ops->exit(hcidev);
+}
+
+int usb_send_ctrl(struct usb_pipe *pipe, struct usb_dev_req *req, void *data)
+{
+       struct usb_dev *dev = NULL;
+       if (!pipe)
+               return false;
+       dev = pipe->dev;
+       if (validate_hcd_ops(dev) && dev->hcidev->ops->send_ctrl)
+               return dev->hcidev->ops->send_ctrl(pipe, req, data);
+       else {
+               printf("%s: Failed\n", __func__);
+               return false;
+       }
+}
+
+int usb_transfer_ctrl(void *dev, void *req, void *data)
+{
+       struct usb_pipe *pipe = NULL;
+       struct usb_dev *usbdev;
+
+       if (!dev)
+               return false;
+       usbdev = (struct usb_dev *)dev;
+       pipe = usbdev->control;
+       return usb_send_ctrl(pipe, req, data);
+}
+
+int usb_transfer_bulk(void *dev, int dir, void *td, void *td_phys, void *data, int size)
+{
+       struct usb_pipe *pipe = NULL;
+       struct usb_dev *usbdev;
+
+       if (!dev)
+               return false;
+       usbdev = (struct usb_dev *)dev;
+       pipe = (dir == USB_PIPE_OUT) ? usbdev->bulk_out : usbdev->bulk_in;
+       if (!pipe)
+               return false;
+       if (validate_hcd_ops(usbdev) && usbdev->hcidev->ops->transfer_bulk)
+               return usbdev->hcidev->ops->transfer_bulk(pipe, td, td_phys, data, size);
+       else {
+               printf("%s: Failed\n", __func__);
+               return false;
+       }
+}
+
+/*
+ * USB Specification 1.1
+ *     9.3 USB Device Requests
+ *     9.4 Standard Device Requests
+ */
+static int usb_set_address(struct usb_dev *dev, uint32_t port)
+{
+       struct usb_dev_req req;
+       struct usb_hcd_dev *hcidev;
+
+       if (!dev)
+               return false;
+
+       hcidev = dev->hcidev;
+       req.bmRequestType = 0;
+       req.bRequest = REQ_SET_ADDRESS;
+       req.wIndex = 0;
+       req.wLength = 0;
+       req.wValue = cpu_to_le16((uint16_t)(hcidev->nextaddr));
+       if (usb_send_ctrl(dev->control, &req, NULL)) {
+               dev->addr = hcidev->nextaddr++;
+               return true;
+       } else
+               return false;
+}
+
+static int usb_get_device_descr(struct usb_dev *dev, void *data, size_t size)
+{
+       struct usb_dev_req req;
+
+       if (!dev)
+               return false;
+
+       req.bmRequestType = 0x80;
+       req.bRequest = REQ_GET_DESCRIPTOR;
+       req.wIndex = 0;
+       req.wLength = cpu_to_le16((uint16_t) size);
+       req.wValue = cpu_to_le16(DESCR_TYPE_DEVICE << 8);
+       return usb_send_ctrl(dev->control, &req, data);
+}
+
+static int usb_get_config_descr(struct usb_dev *dev, void *data, size_t size)
+{
+       struct usb_dev_req req;
+
+       if (!dev)
+               return false;
+
+       req.bmRequestType = 0x80;
+       req.bRequest = REQ_GET_DESCRIPTOR;
+       req.wIndex = 0;
+       req.wLength = cpu_to_le16((uint16_t) size);
+       req.wValue = cpu_to_le16(DESCR_TYPE_CONFIGURATION << 8);
+       return usb_send_ctrl(dev->control, &req, data);
+
+}
+
+static int usb_set_config(struct usb_dev *dev, uint8_t cfg_value)
+{
+       struct usb_dev_req req;
+
+       if (!dev)
+               return false;
+
+       req.bmRequestType = 0x00;
+       req.bRequest = REQ_SET_CONFIGURATION;
+       req.wIndex = 0;
+       req.wLength = 0;
+       req.wValue = cpu_to_le16(0x00FF & cfg_value);
+       return usb_send_ctrl(dev->control, &req, NULL);
+}
+
+static int usb_clear_halt(struct usb_pipe *pipe)
+{
+       struct usb_dev_req req;
+       struct usb_dev *dev;
+
+       if (pipe && pipe->dev) {
+               dev = pipe->dev;
+               dprintf("Clearing port %d dir %d type %d\n",
+                       pipe->epno, pipe->dir, pipe->type);
+               req.bmRequestType = REQT_DIR_OUT | REQT_REC_EP;
+               req.bRequest = REQ_CLEAR_FEATURE;
+               req.wValue = FEATURE_ENDPOINT_HALT;
+               req.wIndex = cpu_to_le16(pipe->epno | pipe->dir);
+               req.wLength = 0;
+               return usb_send_ctrl(dev->control, &req, NULL);
+       }
+       return false;
+}
+
+int usb_dev_populate_pipe(struct usb_dev *dev, struct usb_ep_descr *ep,
+                       void *buf, size_t len)
+{
+       uint8_t dir, type;
+
+       dir = (ep->bEndpointAddress & 0x80) >> 7;
+       type = ep->bmAttributes & USB_EP_TYPE_MASK;
+
+       dprintf("EP: %s: %d size %d type %d\n", dir ? "IN " : "OUT",
+               ep->bEndpointAddress & 0xF, le16_to_cpu(ep->wMaxPacketSize),
+               type);
+       if (type == USB_EP_TYPE_BULK) {
+               if (dir)
+                       dev->bulk_in = usb_get_pipe(dev, ep, buf, len);
+               else
+                       dev->bulk_out = usb_get_pipe(dev, ep, buf, len);
+       } else if (type == USB_EP_TYPE_INTR)
+               dev->intr = usb_get_pipe(dev, ep, buf, len);
+
+       return true;
+}
+
+static void usb_dev_copy_epdesc(struct usb_dev *dev, struct usb_ep_descr *ep)
+{
+       uint32_t ep_cnt;
+
+       ep_cnt = dev->ep_cnt;
+       if (ep_cnt < USB_DEV_EP_MAX)
+               memcpy((void *)&dev->ep[ep_cnt], ep, sizeof(*ep));
+       else
+               dprintf("usb-core: only %d EPs supported\n", USB_DEV_EP_MAX);
+       dev->ep_cnt++;
+}
+
+int usb_hid_init(void *vdev)
+{
+       struct usb_dev *dev;
+       dev = (struct usb_dev *) vdev;
+       if (!dev)
+               return false;
+       if (dev->class == DEV_HID_KEYB)
+               usb_hid_kbd_init(dev);
+       return true;
+}
+
+int usb_hid_exit(void *vdev)
+{
+       struct usb_dev *dev;
+       dev = (struct usb_dev *) vdev;
+       if (!dev)
+               return false;
+       if (dev->class == DEV_HID_KEYB)
+               usb_hid_kbd_exit(dev);
+       return true;
+}
+
+#define usb_get_intf_class(x) ((x & 0x00FF0000) >> 16)
+
+int usb_msc_init(void *vdev)
+{
+       struct usb_dev *dev;
+       int i;
+
+       dev = (struct usb_dev *) vdev;
+       dprintf("%s: enter %x\n", __func__, dev->class);
+       if (!dev)
+               return false;
+       if (usb_get_intf_class(dev->class) == 8) {
+               for (i = 0; i < dev->ep_cnt; i++) {
+                       if ((dev->ep[i].bmAttributes & USB_EP_TYPE_MASK)
+                               == USB_EP_TYPE_BULK)
+                               usb_dev_populate_pipe(dev, &dev->ep[i], NULL, 0);
+               }
+       }
+       return true;
+}
+
+int usb_msc_exit(void *vdev)
+{
+       struct  usb_dev *dev;
+       dev = (struct usb_dev *) vdev;
+       dprintf("%s: enter %x\n", __func__, dev->class);
+       if (!dev)
+               return false;
+       if (usb_get_intf_class(dev->class) == 8) {
+               if (dev->bulk_in)
+                       usb_put_pipe(dev->bulk_in);
+               if (dev->bulk_out)
+                       usb_put_pipe(dev->bulk_out);
+       }
+       return true;
+}
+
+static int usb_msc_reset(struct usb_dev *dev)
+{
+       struct usb_dev_req req;
+
+       if (!dev)
+               return false;
+
+       req.bmRequestType = REQT_TYPE_CLASS | REQT_REC_INTERFACE | REQT_DIR_OUT;
+       req.bRequest = 0xFF;
+       req.wLength = 0;
+       req.wValue = 0;
+       req.wIndex = cpu_to_le16(dev->intf_num);
+       return usb_send_ctrl(dev->control, &req, NULL);
+}
+
+void usb_msc_resetrecovery(struct usb_dev *dev)
+{
+       // usb_msc_reset(dev);
+       usb_clear_halt(dev->bulk_in);
+       usb_clear_halt(dev->bulk_out);
+       SLOF_msleep(2);
+}
+
+static int usb_handle_device(struct usb_dev *dev, struct usb_dev_config_descr *cfg,
+               uint8_t *ptr, uint16_t len)
+{
+       struct usb_dev_intf_descr *intf = NULL;
+       struct usb_ep_descr *ep = NULL;
+       struct usb_dev_hid_descr *hid __unused = NULL;
+       uint8_t desc_len, desc_type;
+
+       len -= sizeof(struct usb_dev_config_descr);
+       ptr = (uint8_t *)(ptr + sizeof(struct usb_dev_config_descr));
+
+       while (len > 0) {
+               desc_len = *ptr;
+               desc_type = *(ptr + 1);
+               switch (desc_type) {
+               case DESCR_TYPE_INTERFACE:
+                       intf = (struct usb_dev_intf_descr *)ptr;
+                       dev->class = intf->bInterfaceClass << 16 |
+                               intf->bInterfaceSubClass << 8 |
+                               intf->bInterfaceProtocol;
+                       break;
+               case DESCR_TYPE_ENDPOINT:
+                       ep = (struct usb_ep_descr *)ptr;
+                       dev->intf_num = intf->bInterfaceNumber;
+                       usb_dev_copy_epdesc(dev, ep);
+                       break;
+               case DESCR_TYPE_HID:
+                       hid = (struct usb_dev_hid_descr *)ptr;
+                       dprintf("hid-report %d size %d\n",
+                               hid->bReportType, le16_to_cpu(hid->wReportLength));
+                       break;
+               case DESCR_TYPE_HUB:
+                       break;
+               default:
+                       printf("ptr %p desc_type %d\n", ptr, desc_type);
+               }
+               ptr += desc_len;
+               len -= desc_len;
+       }
+       return true;
+}
+
+int setup_new_device(struct usb_dev *dev, unsigned int port)
+{
+       struct usb_dev_descr descr;
+       struct usb_dev_config_descr cfg;
+       struct usb_ep_descr ep;
+       uint16_t len;
+       void *data = NULL;
+
+       dprintf("usb: %s - port %d\n", __func__, port);
+
+       dev->addr = 0;
+       dev->port = port;
+       ep.bEndpointAddress = 0;
+       ep.bmAttributes = USB_EP_TYPE_CONTROL;
+       ep.wMaxPacketSize = cpu_to_le16(8);
+       dev->control = usb_get_pipe(dev, &ep, NULL, 0);
+
+       if (!usb_get_device_descr(dev, &descr, 8))
+               goto fail;
+       dev->control->mps = descr.bMaxPacketSize0;
+
+       /*
+        * For USB3.0 ADDRESS-SLOT command takes care of setting
+        * address, skip this during generic device setup for USB3.0
+        * devices
+        */
+       if (dev->speed != USB_SUPER_SPEED) {
+               /*
+                * Qemu starts the port number from 1 which was
+                * revealed in bootindex and resulted in mismatch for
+                * storage devices names. Adjusting this here for
+                * compatibility.
+                */
+               dev->port = port + 1;
+               if(!usb_set_address(dev, dev->port))
+                       goto fail;
+       }
+       mb();
+       SLOF_msleep(100);
+
+       if (!usb_get_device_descr(dev, &descr, sizeof(struct usb_dev_descr)))
+               goto fail;
+
+       if (!usb_get_config_descr(dev, &cfg, sizeof(struct usb_dev_config_descr)))
+               goto fail;
+
+       len = le16_to_cpu(cfg.wTotalLength);
+       /* No device config descriptor present */
+       if (len == sizeof(struct usb_dev_config_descr))
+               goto fail;
+
+       data = SLOF_dma_alloc(len);
+       if (!data) {
+               printf("%s: alloc failed %d\n", __func__, port);
+               goto fail;
+       }
+
+       if (!usb_get_config_descr(dev, data, len))
+               goto fail_mem_free;
+       if (!usb_set_config(dev, cfg.bConfigurationValue))
+               goto fail_mem_free;
+       mb();
+       SLOF_msleep(100);
+
+       if (!usb_handle_device(dev, &cfg, data, len))
+               goto fail_mem_free;
+
+       switch (usb_get_intf_class(dev->class)) {
+       case 3:
+               dprintf("HID found %06X\n", dev->class);
+               slof_usb_handle(dev);
+               break;
+       case 8:
+               dprintf("MASS STORAGE found %d %06X\n", dev->intf_num,
+                       dev->class);
+               if ((dev->class & 0x50) != 0x50) { /* Bulk-only supported */
+                       printf("Device not supported %06X\n", dev->class);
+                       goto fail_mem_free;
+               }
+
+               if (!usb_msc_reset(dev)) {
+                       printf("%s: bulk reset failed\n", __func__);
+                       goto fail_mem_free;
+               }
+               SLOF_msleep(100);
+               slof_usb_handle(dev);
+               break;
+       case 9:
+               dprintf("HUB found\n");
+               slof_usb_handle(dev);
+               break;
+       default:
+               printf("USB Interface class -%x- Not supported\n", dev->class);
+               break;
+       }
+
+       SLOF_dma_free(data, len);
+       return true;
+fail_mem_free:
+       SLOF_dma_free(data, len);
+fail:
+       return false;
+}