Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / seabios / src / hw / usb-hub.c
1 // Code for handling standard USB hubs.
2 //
3 // Copyright (C) 2010  Kevin O'Connor <kevin@koconnor.net>
4 //
5 // This file may be distributed under the terms of the GNU LGPLv3 license.
6
7 #include "config.h" // CONFIG_USB_HUB
8 #include "output.h" // dprintf
9 #include "string.h" // memset
10 #include "usb.h" // struct usb_s
11 #include "usb-hub.h" // struct usb_hub_descriptor
12 #include "util.h" // timer_calc
13
14 static int
15 get_hub_desc(struct usb_pipe *pipe, struct usb_hub_descriptor *desc)
16 {
17     struct usb_ctrlrequest req;
18     req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE;
19     req.bRequest = USB_REQ_GET_DESCRIPTOR;
20     if (pipe->speed == USB_SUPERSPEED)
21         req.wValue = USB_DT_HUB3<<8;
22     else
23         req.wValue = USB_DT_HUB<<8;
24     req.wIndex = 0;
25     req.wLength = sizeof(*desc);
26     return usb_send_default_control(pipe, &req, desc);
27 }
28
29 static int
30 set_hub_depth(struct usb_pipe *pipe, u16 depth)
31 {
32     struct usb_ctrlrequest req;
33     req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE;
34     req.bRequest = HUB_REQ_SET_HUB_DEPTH;
35     req.wValue = depth;
36     req.wIndex = 0;
37     req.wLength = 0;
38     return usb_send_default_control(pipe, &req, NULL);
39 }
40
41 static int
42 set_port_feature(struct usbhub_s *hub, int port, int feature)
43 {
44     struct usb_ctrlrequest req;
45     req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
46     req.bRequest = USB_REQ_SET_FEATURE;
47     req.wValue = feature;
48     req.wIndex = port + 1;
49     req.wLength = 0;
50     mutex_lock(&hub->lock);
51     int ret = usb_send_default_control(hub->usbdev->defpipe, &req, NULL);
52     mutex_unlock(&hub->lock);
53     return ret;
54 }
55
56 static int
57 clear_port_feature(struct usbhub_s *hub, int port, int feature)
58 {
59     struct usb_ctrlrequest req;
60     req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
61     req.bRequest = USB_REQ_CLEAR_FEATURE;
62     req.wValue = feature;
63     req.wIndex = port + 1;
64     req.wLength = 0;
65     mutex_lock(&hub->lock);
66     int ret = usb_send_default_control(hub->usbdev->defpipe, &req, NULL);
67     mutex_unlock(&hub->lock);
68     return ret;
69 }
70
71 static int
72 get_port_status(struct usbhub_s *hub, int port, struct usb_port_status *sts)
73 {
74     struct usb_ctrlrequest req;
75     req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_OTHER;
76     req.bRequest = USB_REQ_GET_STATUS;
77     req.wValue = 0;
78     req.wIndex = port + 1;
79     req.wLength = sizeof(*sts);
80     mutex_lock(&hub->lock);
81     int ret = usb_send_default_control(hub->usbdev->defpipe, &req, sts);
82     mutex_unlock(&hub->lock);
83     return ret;
84 }
85
86 // Check if device attached to port
87 static int
88 usb_hub_detect(struct usbhub_s *hub, u32 port)
89 {
90     struct usb_port_status sts;
91     int ret = get_port_status(hub, port, &sts);
92     if (ret) {
93         dprintf(1, "Failure on hub port %d detect\n", port);
94         return -1;
95     }
96     return (sts.wPortStatus & USB_PORT_STAT_CONNECTION) ? 1 : 0;
97 }
98
99 // Disable port
100 static void
101 usb_hub_disconnect(struct usbhub_s *hub, u32 port)
102 {
103     int ret = clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE);
104     if (ret)
105         dprintf(1, "Failure on hub port %d disconnect\n", port);
106 }
107
108 // Reset device on port
109 static int
110 usb_hub_reset(struct usbhub_s *hub, u32 port)
111 {
112     int ret = set_port_feature(hub, port, USB_PORT_FEAT_RESET);
113     if (ret)
114         goto fail;
115
116     // Wait for reset to complete.
117     struct usb_port_status sts;
118     u32 end = timer_calc(USB_TIME_DRST * 2);
119     for (;;) {
120         ret = get_port_status(hub, port, &sts);
121         if (ret)
122             goto fail;
123         if (!(sts.wPortStatus & USB_PORT_STAT_RESET)
124             && (hub->usbdev->speed != USB_SUPERSPEED
125                 || !(sts.wPortStatus & USB_PORT_STAT_LINK_MASK)))
126             break;
127         if (timer_check(end)) {
128             warn_timeout();
129             goto fail;
130         }
131         msleep(5);
132     }
133
134     // Reset complete.
135     if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION))
136         // Device no longer present
137         return -1;
138
139     if (hub->usbdev->speed == USB_SUPERSPEED)
140         return USB_SUPERSPEED;
141     return ((sts.wPortStatus & USB_PORT_STAT_SPEED_MASK)
142             >> USB_PORT_STAT_SPEED_SHIFT);
143
144 fail:
145     dprintf(1, "Failure on hub port %d reset\n", port);
146     usb_hub_disconnect(hub, port);
147     return -1;
148 }
149
150 static struct usbhub_op_s HubOp = {
151     .detect = usb_hub_detect,
152     .reset = usb_hub_reset,
153     .disconnect = usb_hub_disconnect,
154 };
155
156 // Configure a usb hub and then find devices connected to it.
157 int
158 usb_hub_setup(struct usbdevice_s *usbdev)
159 {
160     ASSERT32FLAT();
161     if (!CONFIG_USB_HUB)
162         return -1;
163
164     struct usb_hub_descriptor desc;
165     int ret = get_hub_desc(usbdev->defpipe, &desc);
166     if (ret)
167         return ret;
168
169     struct usbhub_s hub;
170     memset(&hub, 0, sizeof(hub));
171     hub.usbdev = usbdev;
172     hub.cntl = usbdev->defpipe->cntl;
173     hub.portcount = desc.bNbrPorts;
174     hub.op = &HubOp;
175
176     if (usbdev->speed == USB_SUPERSPEED) {
177         int depth = 0;
178         struct usbdevice_s *parent = usbdev->hub->usbdev;
179         while (parent) {
180             depth++;
181             parent = parent->hub->usbdev;
182         }
183
184         ret = set_hub_depth(usbdev->defpipe, depth);
185         if (ret)
186             return ret;
187     }
188
189     // Turn on power to ports.
190     int port;
191     for (port=0; port<desc.bNbrPorts; port++) {
192         ret = set_port_feature(&hub, port, USB_PORT_FEAT_POWER);
193         if (ret)
194             return ret;
195     }
196     // Wait for port power to stabilize.
197     msleep(desc.bPwrOn2PwrGood * 2);
198
199     usb_enumerate(&hub);
200
201     dprintf(1, "Initialized USB HUB (%d ports used)\n", hub.devcount);
202     if (hub.devcount)
203         return 0;
204     return -1;
205 }