diff options
Diffstat (limited to 'qemu/roms/SLOF/lib/libusb/usb-hub.c')
-rw-r--r-- | qemu/roms/SLOF/lib/libusb/usb-hub.c | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/qemu/roms/SLOF/lib/libusb/usb-hub.c b/qemu/roms/SLOF/lib/libusb/usb-hub.c new file mode 100644 index 000000000..7059cd019 --- /dev/null +++ b/qemu/roms/SLOF/lib/libusb/usb-hub.c @@ -0,0 +1,183 @@ +/***************************************************************************** + * 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 <stdio.h> +#include <string.h> +#include "usb-core.h" + +#undef HUB_DEBUG +//#define HUB_DEBUG +#ifdef HUB_DEBUG +#define dprintf(_x ...) do { printf(_x); } while(0) +#else +#define dprintf(_x ...) +#endif + +/* + * USB Spec 1.1 + * 11.16.2 Class-specific Requests + */ +struct usb_hub_ps { + uint16_t wPortStatus; + uint16_t wPortChange; +} __attribute__((packed)); + +#define HUB_PS_CONNECTION (1 << 0) +#define HUB_PS_ENABLE (1 << 1) +#define HUB_PS_SUSPEND (1 << 2) +#define HUB_PS_OVER_CURRENT (1 << 3) +#define HUB_PS_RESET (1 << 4) +#define HUB_PS_POWER (1 << 8) +#define HUB_PS_LOW_SPEED (1 << 9) + +#define HUB_PF_CONNECTION 0 +#define HUB_PF_ENABLE 1 +#define HUB_PF_SUSPEND 2 +#define HUB_PF_OVER_CURRENT 3 +#define HUB_PF_RESET 4 +#define HUB_PF_POWER 8 +#define HUB_PF_LOWSPEED 9 +#define HUB_PF_C_CONNECTION 16 +#define HUB_PF_C_ENABLE 17 +#define HUB_PF_C_SUSPEND 18 +#define HUB_PF_C_OVER_CURRENT 19 +#define HUB_PF_C_RESET 20 + +static int usb_get_hub_desc(struct usb_dev *dev, void *data, size_t size) +{ + struct usb_dev_req req; + if (!dev) + return false; + req.bmRequestType = REQT_DIR_IN | REQT_TYPE_CLASS | REQT_REC_DEVICE; + req.bRequest = REQ_GET_DESCRIPTOR; + req.wIndex = 0; + req.wLength = cpu_to_le16((uint16_t) size); + req.wValue = cpu_to_le16(DESCR_TYPE_HUB << 8); + return usb_send_ctrl(dev->control, &req, data); +} + +static int hub_get_port_status(struct usb_dev *dev, int port, void *data, size_t size) +{ + struct usb_dev_req req; + if (!dev) + return false; + req.bmRequestType = REQT_DIR_IN | REQT_TYPE_CLASS | REQT_REC_OTHER; + req.bRequest = REQ_GET_STATUS; + req.wValue = 0; + req.wIndex = cpu_to_le16((uint16_t)(port + 1)); + req.wLength = cpu_to_le16((uint16_t)size); + return usb_send_ctrl(dev->control, &req, data); +} + +static int hub_set_port_feature(struct usb_dev *dev, int port, int feature) +{ + struct usb_dev_req req; + if (!dev) + return false; + req.bmRequestType = REQT_DIR_OUT | REQT_TYPE_CLASS | REQT_REC_OTHER; + req.bRequest = REQ_SET_FEATURE; + req.wLength = 0; + req.wValue = cpu_to_le16((uint16_t)feature); + req.wIndex = cpu_to_le16((uint16_t)(port + 1)); + return usb_send_ctrl(dev->control, &req, NULL); +} + +#if 0 +static int hub_clear_port_feature(struct usb_dev *dev, int port, int feature) +{ + struct usb_dev_req req; + if (!dev) + return false; + req.bmRequestType = REQT_DIR_OUT | REQT_TYPE_CLASS | REQT_REC_OTHER; + req.bRequest = REQ_CLEAR_FEATURE; + req.wLength = 0; + req.wValue = cpu_to_le16((uint16_t)feature); + req.wIndex = cpu_to_le16((uint16_t)(port + 1)); + return usb_send_ctrl(dev->control, &req, NULL); +} +#endif + +static int hub_check_port(struct usb_dev *dev, int port) +{ + struct usb_hub_ps ps; + uint32_t time; + + if (!hub_get_port_status(dev, port, &ps, sizeof(ps))) + return false; + dprintf("Port Status %04X Port Change %04X\n", + le16_to_cpu(ps.wPortStatus), + le16_to_cpu(ps.wPortChange)); + + if (!(le16_to_cpu(ps.wPortStatus) & HUB_PS_POWER)) { + hub_set_port_feature(dev, port, HUB_PF_POWER); + SLOF_msleep(100); + time = SLOF_GetTimer() + USB_TIMEOUT; + while (time > SLOF_GetTimer()) { + cpu_relax(); + hub_get_port_status(dev, port, &ps, sizeof(ps)); + if (le16_to_cpu(ps.wPortStatus) & HUB_PS_CONNECTION) { + dprintf("power on Port Status %04X Port Change %04X\n", + le16_to_cpu(ps.wPortStatus), + le16_to_cpu(ps.wPortChange)); + break; + } + } + } + + if (le16_to_cpu(ps.wPortStatus) & HUB_PS_CONNECTION) { + hub_set_port_feature(dev, port, HUB_PF_RESET); + SLOF_msleep(100); + time = SLOF_GetTimer() + USB_TIMEOUT; + while (time > SLOF_GetTimer()) { + cpu_relax(); + hub_get_port_status(dev, port, &ps, sizeof(ps)); + if (!(le16_to_cpu(ps.wPortStatus) & HUB_PS_RESET)) { + dprintf("reset Port Status %04X Port Change %04X\n", + le16_to_cpu(ps.wPortStatus), + le16_to_cpu(ps.wPortChange)); + return true; + } + } + } + return false; +} + +unsigned int usb_hub_init(void *hubdev) +{ + struct usb_dev *dev = hubdev; + struct usb_dev_hub_descr hub; + struct usb_dev *newdev; + int i; + + dprintf("%s: enter %p\n", __func__, dev); + if (!dev) { + printf("usb-hub: NULL\n"); + return false; + } + memset(&hub, 0, sizeof(hub)); + usb_get_hub_desc(dev, &hub, sizeof(hub)); + dprintf("usb-hub: ports connected %d\n", hub.bNbrPorts); + for (i = 0; i < hub.bNbrPorts; i++) { + dprintf("usb-hub: ports scanning %d\n", i); + if (hub_check_port(dev, i)) { + dprintf("***********************************************\n"); + dprintf("\t\tusb-hub: device found %d\n", i); + dprintf("***********************************************\n"); + newdev = usb_devpool_get(); + dprintf("usb-hub: allocated device %p\n", newdev); + newdev->hcidev = dev->hcidev; + if (!setup_new_device(newdev, i)) + printf("usb-hub: unable to setup device on port %d\n", i); + } + } + return true; +} |