Add USB EHCI controller support.
[seabios.git] / src / 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 "util.h" // dprintf
8 #include "config.h" // CONFIG_USB_HUB
9 #include "usb-hub.h" // struct usb_hub_descriptor
10 #include "usb.h" // struct usb_s
11
12 static int
13 get_hub_desc(struct usb_pipe *pipe, struct usb_hub_descriptor *desc)
14 {
15     struct usb_ctrlrequest req;
16     req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE;
17     req.bRequest = USB_REQ_GET_DESCRIPTOR;
18     req.wValue = USB_DT_HUB<<8;
19     req.wIndex = 0;
20     req.wLength = sizeof(*desc);
21     return send_default_control(pipe, &req, desc);
22 }
23
24 static int
25 set_port_feature(struct usbhub_s *hub, int port, int feature)
26 {
27     struct usb_ctrlrequest req;
28     req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
29     req.bRequest = USB_REQ_SET_FEATURE;
30     req.wValue = feature;
31     req.wIndex = port;
32     req.wLength = 0;
33     mutex_lock(&hub->lock);
34     int ret = send_default_control(hub->pipe, &req, NULL);
35     mutex_unlock(&hub->lock);
36     return ret;
37 }
38
39 static int
40 clear_port_feature(struct usbhub_s *hub, int port, int feature)
41 {
42     struct usb_ctrlrequest req;
43     req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER;
44     req.bRequest = USB_REQ_CLEAR_FEATURE;
45     req.wValue = feature;
46     req.wIndex = port;
47     req.wLength = 0;
48     mutex_lock(&hub->lock);
49     int ret = send_default_control(hub->pipe, &req, NULL);
50     mutex_unlock(&hub->lock);
51     return ret;
52 }
53
54 static int
55 get_port_status(struct usbhub_s *hub, int port, struct usb_port_status *sts)
56 {
57     struct usb_ctrlrequest req;
58     req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_OTHER;
59     req.bRequest = USB_REQ_GET_STATUS;
60     req.wValue = 0;
61     req.wIndex = port;
62     req.wLength = sizeof(*sts);
63     mutex_lock(&hub->lock);
64     int ret = send_default_control(hub->pipe, &req, sts);
65     mutex_unlock(&hub->lock);
66     return ret;
67 }
68
69 static void
70 init_hub_port(void *data)
71 {
72     struct usbhub_s *hub = data;
73     u32 port = hub->port; // XXX - find better way to pass port
74
75     // Turn on power to port.
76     int ret = set_port_feature(hub, port, USB_PORT_FEAT_POWER);
77     if (ret)
78         goto fail;
79
80     // Wait for port power to stabilize.
81     msleep(hub->powerwait);
82
83     // Check periodically for a device connect.
84     struct usb_port_status sts;
85     u64 end = calc_future_tsc(USB_TIME_SIGATT);
86     for (;;) {
87         ret = get_port_status(hub, port, &sts);
88         if (ret)
89             goto fail;
90         if (sts.wPortStatus & USB_PORT_STAT_CONNECTION)
91             // Device connected.
92             break;
93         if (check_time(end))
94             // No device found.
95             goto done;
96         msleep(5);
97     }
98
99     // XXX - wait USB_TIME_ATTDB time?
100
101     // Reset port.
102     mutex_lock(&hub->cntl->resetlock);
103     ret = set_port_feature(hub, port, USB_PORT_FEAT_RESET);
104     if (ret)
105         goto resetfail;
106
107     // Wait for reset to complete.
108     end = calc_future_tsc(USB_TIME_DRST * 2);
109     for (;;) {
110         ret = get_port_status(hub, port, &sts);
111         if (ret)
112             goto resetfail;
113         if (!(sts.wPortStatus & USB_PORT_STAT_RESET))
114             break;
115         if (check_time(end)) {
116             warn_timeout();
117             goto resetfail;
118         }
119         msleep(5);
120     }
121
122     // Reset complete.
123     if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION))
124         // Device no longer present
125         goto resetfail;
126
127     // Set address of port
128     struct usb_pipe *pipe = usb_set_address(
129         hub, port, ((sts.wPortStatus & USB_PORT_STAT_SPEED_MASK)
130                     >> USB_PORT_STAT_SPEED_SHIFT));
131     if (!pipe)
132         goto resetfail;
133     mutex_unlock(&hub->cntl->resetlock);
134
135     // Configure the device
136     int count = configure_usb_device(pipe);
137     free_pipe(pipe);
138     if (!count) {
139         ret = clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE);
140         if (ret)
141             goto fail;
142     }
143     hub->devcount += count;
144 done:
145     hub->threads--;
146     return;
147
148 resetfail:
149     clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE);
150     mutex_unlock(&hub->cntl->resetlock);
151 fail:
152     dprintf(1, "Failure on hub port %d setup\n", port);
153     goto done;
154 }
155
156 // Configure a usb hub and then find devices connected to it.
157 int
158 usb_hub_init(struct usb_pipe *pipe)
159 {
160     ASSERT32FLAT();
161     if (!CONFIG_USB_HUB)
162         return -1;
163
164     struct usb_hub_descriptor desc;
165     int ret = get_hub_desc(pipe, &desc);
166     if (ret)
167         return ret;
168
169     struct usbhub_s hub;
170     memset(&hub, 0, sizeof(hub));
171     hub.pipe = pipe;
172     hub.cntl = pipe->cntl;
173     hub.powerwait = desc.bPwrOn2PwrGood * 2;
174
175     // Launch a thread for every port.
176     int i;
177     for (i=1; i<=desc.bNbrPorts; i++) {
178         hub.port = i;
179         hub.threads++;
180         run_thread(init_hub_port, &hub);
181     }
182
183     // Wait for threads to complete.
184     while (hub.threads)
185         yield();
186
187     dprintf(1, "Initialized USB HUB (%d ports used)\n", hub.devcount);
188     if (hub.devcount)
189         return 0;
190     return -1;
191 }