Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / SLOF / lib / libusb / usb-hub.c
1 /*****************************************************************************
2  * Copyright (c) 2013 IBM Corporation
3  * All rights reserved.
4  * This program and the accompanying materials
5  * are made available under the terms of the BSD License
6  * which accompanies this distribution, and is available at
7  * http://www.opensource.org/licenses/bsd-license.php
8  *
9  * Contributors:
10  *     IBM Corporation - initial implementation
11  *****************************************************************************/
12
13 #include <stdio.h>
14 #include <string.h>
15 #include "usb-core.h"
16
17 #undef HUB_DEBUG
18 //#define HUB_DEBUG
19 #ifdef HUB_DEBUG
20 #define dprintf(_x ...) do { printf(_x); } while(0)
21 #else
22 #define dprintf(_x ...)
23 #endif
24
25 /*
26  * USB Spec 1.1
27  *            11.16.2 Class-specific Requests
28  */
29 struct usb_hub_ps {
30         uint16_t wPortStatus;
31         uint16_t wPortChange;
32 } __attribute__((packed));
33
34 #define HUB_PS_CONNECTION            (1 << 0)
35 #define HUB_PS_ENABLE                (1 << 1)
36 #define HUB_PS_SUSPEND               (1 << 2)
37 #define HUB_PS_OVER_CURRENT          (1 << 3)
38 #define HUB_PS_RESET                 (1 << 4)
39 #define HUB_PS_POWER                 (1 << 8)
40 #define HUB_PS_LOW_SPEED             (1 << 9)
41
42 #define HUB_PF_CONNECTION        0
43 #define HUB_PF_ENABLE            1
44 #define HUB_PF_SUSPEND           2
45 #define HUB_PF_OVER_CURRENT      3
46 #define HUB_PF_RESET             4
47 #define HUB_PF_POWER             8
48 #define HUB_PF_LOWSPEED          9
49 #define HUB_PF_C_CONNECTION      16
50 #define HUB_PF_C_ENABLE          17
51 #define HUB_PF_C_SUSPEND         18
52 #define HUB_PF_C_OVER_CURRENT    19
53 #define HUB_PF_C_RESET           20
54
55 static int usb_get_hub_desc(struct usb_dev *dev, void *data, size_t size)
56 {
57         struct usb_dev_req req;
58         if (!dev)
59                 return false;
60         req.bmRequestType = REQT_DIR_IN | REQT_TYPE_CLASS | REQT_REC_DEVICE;
61         req.bRequest = REQ_GET_DESCRIPTOR;
62         req.wIndex = 0;
63         req.wLength = cpu_to_le16((uint16_t) size);
64         req.wValue = cpu_to_le16(DESCR_TYPE_HUB << 8);
65         return usb_send_ctrl(dev->control, &req, data);
66 }
67
68 static int hub_get_port_status(struct usb_dev *dev, int port, void *data, size_t size)
69 {
70         struct usb_dev_req req;
71         if (!dev)
72                 return false;
73         req.bmRequestType = REQT_DIR_IN | REQT_TYPE_CLASS | REQT_REC_OTHER;
74         req.bRequest = REQ_GET_STATUS;
75         req.wValue = 0;
76         req.wIndex = cpu_to_le16((uint16_t)(port + 1));
77         req.wLength = cpu_to_le16((uint16_t)size);
78         return usb_send_ctrl(dev->control, &req, data);
79 }
80
81 static int hub_set_port_feature(struct usb_dev *dev, int port, int feature)
82 {
83         struct usb_dev_req req;
84         if (!dev)
85                 return false;
86         req.bmRequestType = REQT_DIR_OUT | REQT_TYPE_CLASS | REQT_REC_OTHER;
87         req.bRequest = REQ_SET_FEATURE;
88         req.wLength = 0;
89         req.wValue = cpu_to_le16((uint16_t)feature);
90         req.wIndex = cpu_to_le16((uint16_t)(port + 1));
91         return usb_send_ctrl(dev->control, &req, NULL);
92 }
93
94 #if 0
95 static int hub_clear_port_feature(struct usb_dev *dev, int port, int feature)
96 {
97         struct usb_dev_req req;
98         if (!dev)
99                 return false;
100         req.bmRequestType = REQT_DIR_OUT | REQT_TYPE_CLASS | REQT_REC_OTHER;
101         req.bRequest = REQ_CLEAR_FEATURE;
102         req.wLength = 0;
103         req.wValue = cpu_to_le16((uint16_t)feature);
104         req.wIndex = cpu_to_le16((uint16_t)(port + 1));
105         return usb_send_ctrl(dev->control, &req, NULL);
106 }
107 #endif
108
109 static int hub_check_port(struct usb_dev *dev, int port)
110 {
111         struct usb_hub_ps ps;
112         uint32_t time;
113
114         if (!hub_get_port_status(dev, port, &ps, sizeof(ps)))
115                 return false;
116         dprintf("Port Status %04X Port Change %04X\n",
117                 le16_to_cpu(ps.wPortStatus),
118                 le16_to_cpu(ps.wPortChange));
119
120         if (!(le16_to_cpu(ps.wPortStatus) & HUB_PS_POWER)) {
121                 hub_set_port_feature(dev, port, HUB_PF_POWER);
122                 SLOF_msleep(100);
123                 time = SLOF_GetTimer() + USB_TIMEOUT;
124                 while (time > SLOF_GetTimer()) {
125                         cpu_relax();
126                         hub_get_port_status(dev, port, &ps, sizeof(ps));
127                         if (le16_to_cpu(ps.wPortStatus) & HUB_PS_CONNECTION) {
128                                 dprintf("power on Port Status %04X Port Change %04X\n",
129                                         le16_to_cpu(ps.wPortStatus),
130                                         le16_to_cpu(ps.wPortChange));
131                                 break;
132                         }
133                 }
134         }
135
136         if (le16_to_cpu(ps.wPortStatus) & HUB_PS_CONNECTION) {
137                 hub_set_port_feature(dev, port, HUB_PF_RESET);
138                 SLOF_msleep(100);
139                 time = SLOF_GetTimer() + USB_TIMEOUT;
140                 while (time > SLOF_GetTimer()) {
141                         cpu_relax();
142                         hub_get_port_status(dev, port, &ps, sizeof(ps));
143                         if (!(le16_to_cpu(ps.wPortStatus) & HUB_PS_RESET)) {
144                                 dprintf("reset Port Status %04X Port Change %04X\n",
145                                         le16_to_cpu(ps.wPortStatus),
146                                         le16_to_cpu(ps.wPortChange));
147                                 return true;
148                         }
149                 }
150         }
151         return false;
152 }
153
154 unsigned int usb_hub_init(void *hubdev)
155 {
156         struct usb_dev *dev = hubdev;
157         struct usb_dev_hub_descr hub;
158         struct usb_dev *newdev;
159         int i;
160
161         dprintf("%s: enter %p\n", __func__, dev);
162         if (!dev) {
163                 printf("usb-hub: NULL\n");
164                 return false;
165         }
166         memset(&hub, 0, sizeof(hub));
167         usb_get_hub_desc(dev, &hub, sizeof(hub));
168         dprintf("usb-hub: ports connected %d\n", hub.bNbrPorts);
169         for (i = 0; i < hub.bNbrPorts; i++) {
170                 dprintf("usb-hub: ports scanning %d\n", i);
171                 if (hub_check_port(dev, i)) {
172                         dprintf("***********************************************\n");
173                         dprintf("\t\tusb-hub: device found %d\n", i);
174                         dprintf("***********************************************\n");
175                         newdev = usb_devpool_get();
176                         dprintf("usb-hub: allocated device %p\n", newdev);
177                         newdev->hcidev = dev->hcidev;
178                         if (!setup_new_device(newdev, i))
179                                 printf("usb-hub: unable to setup device on port %d\n", i);
180                 }
181         }
182         return true;
183 }