blob: 42541ff3db9af4730ecaf3c8480c797d82569cb0 [file] [log] [blame]
Kevin O'Connor114592f2009-09-28 21:32:08 -04001// Main code for handling USB controllers and devices.
2//
3// Copyright (C) 2009 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
Kevin O'Connor9931bcc2011-06-20 22:23:02 -04008#include "pci.h" // foreachpci
Kevin O'Connor114592f2009-09-28 21:32:08 -04009#include "config.h" // CONFIG_*
10#include "pci_regs.h" // PCI_CLASS_REVISION
11#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_UHCI
Kevin O'Connord83c87b2013-01-21 01:14:12 -050012#include "usb-uhci.h" // uhci_setup
13#include "usb-ohci.h" // ohci_setup
14#include "usb-ehci.h" // ehci_setup
Kevin O'Connor114592f2009-09-28 21:32:08 -040015#include "usb-hid.h" // usb_keyboard_setup
Kevin O'Connord83c87b2013-01-21 01:14:12 -050016#include "usb-hub.h" // usb_hub_setup
17#include "usb-msc.h" // usb_msc_setup
18#include "usb-uas.h" // usb_uas_setup
Kevin O'Connor114592f2009-09-28 21:32:08 -040019#include "usb.h" // struct usb_s
Kevin O'Connor1c46a542009-10-17 23:53:32 -040020#include "biosvar.h" // GET_GLOBAL
Kevin O'Connor114592f2009-09-28 21:32:08 -040021
Kevin O'Connor3c160dd2010-02-17 23:06:52 -050022
23/****************************************************************
24 * Controller function wrappers
25 ****************************************************************/
26
Kevin O'Connor54bf2072012-03-09 07:52:33 -050027// Allocate an async pipe (control or bulk).
28struct usb_pipe *
Kevin O'Connorc3d96c22012-03-10 09:03:25 -050029usb_alloc_pipe(struct usbdevice_s *usbdev
30 , struct usb_endpoint_descriptor *epdesc)
Kevin O'Connor54bf2072012-03-09 07:52:33 -050031{
32 switch (usbdev->hub->cntl->type) {
Kevin O'Connor59f02832009-10-12 10:09:15 -040033 default:
34 case USB_TYPE_UHCI:
Kevin O'Connorc3d96c22012-03-10 09:03:25 -050035 return uhci_alloc_pipe(usbdev, epdesc);
Kevin O'Connor59f02832009-10-12 10:09:15 -040036 case USB_TYPE_OHCI:
Kevin O'Connorc3d96c22012-03-10 09:03:25 -050037 return ohci_alloc_pipe(usbdev, epdesc);
Kevin O'Connor190cc622010-03-09 19:43:52 -050038 case USB_TYPE_EHCI:
Kevin O'Connorc3d96c22012-03-10 09:03:25 -050039 return ehci_alloc_pipe(usbdev, epdesc);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050040 }
41}
42
43// Send a message on a control pipe using the default control descriptor.
44static int
45send_control(struct usb_pipe *pipe, int dir, const void *cmd, int cmdsize
46 , void *data, int datasize)
47{
48 ASSERT32FLAT();
Kevin O'Connor4547eb92010-02-28 02:17:28 -050049 switch (pipe->type) {
Kevin O'Connor357bdfa2010-02-26 08:57:13 -050050 default:
51 case USB_TYPE_UHCI:
52 return uhci_control(pipe, dir, cmd, cmdsize, data, datasize);
53 case USB_TYPE_OHCI:
54 return ohci_control(pipe, dir, cmd, cmdsize, data, datasize);
Kevin O'Connor190cc622010-03-09 19:43:52 -050055 case USB_TYPE_EHCI:
56 return ehci_control(pipe, dir, cmd, cmdsize, data, datasize);
Kevin O'Connor59f02832009-10-12 10:09:15 -040057 }
Kevin O'Connor114592f2009-09-28 21:32:08 -040058}
59
Kevin O'Connor7149fc82010-02-17 23:24:42 -050060int
Kevin O'Connor4547eb92010-02-28 02:17:28 -050061usb_send_bulk(struct usb_pipe *pipe_fl, int dir, void *data, int datasize)
Kevin O'Connor7149fc82010-02-17 23:24:42 -050062{
Kevin O'Connor1e3bd4f2012-05-24 23:56:19 -040063 switch (GET_LOWFLAT(pipe_fl->type)) {
Kevin O'Connor7149fc82010-02-17 23:24:42 -050064 default:
65 case USB_TYPE_UHCI:
Kevin O'Connor4547eb92010-02-28 02:17:28 -050066 return uhci_send_bulk(pipe_fl, dir, data, datasize);
Kevin O'Connor7149fc82010-02-17 23:24:42 -050067 case USB_TYPE_OHCI:
Kevin O'Connor2f968002010-09-19 22:10:08 -040068 return ohci_send_bulk(pipe_fl, dir, data, datasize);
Kevin O'Connor190cc622010-03-09 19:43:52 -050069 case USB_TYPE_EHCI:
70 return ehci_send_bulk(pipe_fl, dir, data, datasize);
Kevin O'Connor7149fc82010-02-17 23:24:42 -050071 }
72}
73
Kevin O'Connorac7eb5e2012-05-28 14:42:16 -040074int
Kevin O'Connor4547eb92010-02-28 02:17:28 -050075usb_poll_intr(struct usb_pipe *pipe_fl, void *data)
Kevin O'Connor114592f2009-09-28 21:32:08 -040076{
Kevin O'Connor1e3bd4f2012-05-24 23:56:19 -040077 switch (GET_LOWFLAT(pipe_fl->type)) {
Kevin O'Connor59f02832009-10-12 10:09:15 -040078 default:
79 case USB_TYPE_UHCI:
Kevin O'Connor4547eb92010-02-28 02:17:28 -050080 return uhci_poll_intr(pipe_fl, data);
Kevin O'Connor59f02832009-10-12 10:09:15 -040081 case USB_TYPE_OHCI:
Kevin O'Connor4547eb92010-02-28 02:17:28 -050082 return ohci_poll_intr(pipe_fl, data);
Kevin O'Connor190cc622010-03-09 19:43:52 -050083 case USB_TYPE_EHCI:
84 return ehci_poll_intr(pipe_fl, data);
Kevin O'Connor59f02832009-10-12 10:09:15 -040085 }
Kevin O'Connor114592f2009-09-28 21:32:08 -040086}
87
Kevin O'Connor3c160dd2010-02-17 23:06:52 -050088
89/****************************************************************
90 * Helper functions
91 ****************************************************************/
92
Kevin O'Connorfbf66f12012-03-10 09:13:58 -050093// Send a message to the default control pipe of a device.
94int
95send_default_control(struct usb_pipe *pipe, const struct usb_ctrlrequest *req
96 , void *data)
97{
98 return send_control(pipe, req->bRequestType & USB_DIR_IN
99 , req, sizeof(*req), data, req->wLength);
100}
101
102// Free an allocated control or bulk pipe.
103void
104free_pipe(struct usb_pipe *pipe)
105{
106 ASSERT32FLAT();
107 if (!pipe)
108 return;
109 // Add to controller's free list.
110 struct usb_s *cntl = pipe->cntl;
111 pipe->freenext = cntl->freelist;
112 cntl->freelist = pipe;
113}
114
115// Check for an available pipe on the freelist.
116struct usb_pipe *
117usb_getFreePipe(struct usb_s *cntl, u8 eptype)
118{
119 struct usb_pipe **pfree = &cntl->freelist;
120 for (;;) {
121 struct usb_pipe *pipe = *pfree;
122 if (!pipe)
123 return NULL;
124 if (pipe->eptype == eptype) {
125 *pfree = pipe->freenext;
126 return pipe;
127 }
128 pfree = &pipe->freenext;
129 }
130}
131
132// Fill "pipe" endpoint info from an endpoint descriptor.
133void
134usb_desc2pipe(struct usb_pipe *pipe, struct usbdevice_s *usbdev
135 , struct usb_endpoint_descriptor *epdesc)
136{
137 pipe->cntl = usbdev->hub->cntl;
138 pipe->type = usbdev->hub->cntl->type;
139 pipe->ep = epdesc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
140 pipe->devaddr = usbdev->devaddr;
141 pipe->speed = usbdev->speed;
142 pipe->maxpacket = epdesc->wMaxPacketSize;
143 pipe->eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
Kevin O'Connorfbf66f12012-03-10 09:13:58 -0500144}
145
146// Find the exponential period of the requested interrupt end point.
147int
148usb_getFrameExp(struct usbdevice_s *usbdev
149 , struct usb_endpoint_descriptor *epdesc)
150{
151 int period = epdesc->bInterval;
152 if (usbdev->speed != USB_HIGHSPEED)
153 return (period <= 0) ? 0 : __fls(period);
154 return (period <= 4) ? 0 : period - 4;
155}
156
Kevin O'Connor3c160dd2010-02-17 23:06:52 -0500157// Find the first endpoing of a given type in an interface description.
158struct usb_endpoint_descriptor *
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500159findEndPointDesc(struct usbdevice_s *usbdev, int type, int dir)
Kevin O'Connor3c160dd2010-02-17 23:06:52 -0500160{
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500161 struct usb_endpoint_descriptor *epdesc = (void*)&usbdev->iface[1];
Kevin O'Connor3c160dd2010-02-17 23:06:52 -0500162 for (;;) {
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500163 if ((void*)epdesc >= (void*)usbdev->iface + usbdev->imax
Kevin O'Connor3c160dd2010-02-17 23:06:52 -0500164 || epdesc->bDescriptorType == USB_DT_INTERFACE) {
165 return NULL;
166 }
167 if (epdesc->bDescriptorType == USB_DT_ENDPOINT
168 && (epdesc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == dir
169 && (epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == type)
170 return epdesc;
171 epdesc = (void*)epdesc + epdesc->bLength;
172 }
173}
174
Kevin O'Connor114592f2009-09-28 21:32:08 -0400175// Get the first 8 bytes of the device descriptor.
176static int
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500177get_device_info8(struct usb_pipe *pipe, struct usb_device_descriptor *dinfo)
Kevin O'Connor114592f2009-09-28 21:32:08 -0400178{
179 struct usb_ctrlrequest req;
180 req.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
181 req.bRequest = USB_REQ_GET_DESCRIPTOR;
182 req.wValue = USB_DT_DEVICE<<8;
183 req.wIndex = 0;
184 req.wLength = 8;
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500185 return send_default_control(pipe, &req, dinfo);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400186}
187
188static struct usb_config_descriptor *
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500189get_device_config(struct usb_pipe *pipe)
Kevin O'Connor114592f2009-09-28 21:32:08 -0400190{
191 struct usb_config_descriptor cfg;
192
193 struct usb_ctrlrequest req;
194 req.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
195 req.bRequest = USB_REQ_GET_DESCRIPTOR;
196 req.wValue = USB_DT_CONFIG<<8;
197 req.wIndex = 0;
198 req.wLength = sizeof(cfg);
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500199 int ret = send_default_control(pipe, &req, &cfg);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400200 if (ret)
201 return NULL;
202
203 void *config = malloc_tmphigh(cfg.wTotalLength);
204 if (!config)
205 return NULL;
206 req.wLength = cfg.wTotalLength;
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500207 ret = send_default_control(pipe, &req, config);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400208 if (ret)
209 return NULL;
210 //hexdump(config, cfg.wTotalLength);
211 return config;
212}
213
Kevin O'Connor114592f2009-09-28 21:32:08 -0400214static int
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500215set_configuration(struct usb_pipe *pipe, u16 val)
Kevin O'Connor114592f2009-09-28 21:32:08 -0400216{
217 struct usb_ctrlrequest req;
218 req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
219 req.bRequest = USB_REQ_SET_CONFIGURATION;
220 req.wValue = val;
221 req.wIndex = 0;
222 req.wLength = 0;
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500223 return send_default_control(pipe, &req, NULL);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400224}
225
Kevin O'Connor3c160dd2010-02-17 23:06:52 -0500226
227/****************************************************************
228 * Initialization and enumeration
229 ****************************************************************/
230
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500231// Assign an address to a device in the default state on the given
232// controller.
Kevin O'Connor4f74dad2012-03-06 08:20:40 -0500233static int
234usb_set_address(struct usbdevice_s *usbdev)
Kevin O'Connor114592f2009-09-28 21:32:08 -0400235{
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500236 ASSERT32FLAT();
Kevin O'Connor77ab4e12012-03-08 08:44:32 -0500237 struct usb_s *cntl = usbdev->hub->cntl;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500238 dprintf(3, "set_address %p\n", cntl);
239 if (cntl->maxaddr >= USB_MAXADDR)
Kevin O'Connor4f74dad2012-03-06 08:20:40 -0500240 return -1;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400241
Kevin O'Connor92650f22012-03-05 22:03:03 -0500242 // Create a pipe for the default address.
Kevin O'Connor77ab4e12012-03-08 08:44:32 -0500243 struct usb_endpoint_descriptor epdesc = {
244 .wMaxPacketSize = 8,
245 .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
246 };
Kevin O'Connorc3d96c22012-03-10 09:03:25 -0500247 usbdev->defpipe = usb_alloc_pipe(usbdev, &epdesc);
Kevin O'Connor77ab4e12012-03-08 08:44:32 -0500248 if (!usbdev->defpipe)
Kevin O'Connor4f74dad2012-03-06 08:20:40 -0500249 return -1;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500250
251 msleep(USB_TIME_RSTRCY);
252
Kevin O'Connor77ab4e12012-03-08 08:44:32 -0500253 // Send set_address command.
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500254 struct usb_ctrlrequest req;
255 req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
256 req.bRequest = USB_REQ_SET_ADDRESS;
257 req.wValue = cntl->maxaddr + 1;
258 req.wIndex = 0;
259 req.wLength = 0;
Kevin O'Connor77ab4e12012-03-08 08:44:32 -0500260 int ret = send_default_control(usbdev->defpipe, &req, NULL);
261 free_pipe(usbdev->defpipe);
262 if (ret)
Kevin O'Connor4f74dad2012-03-06 08:20:40 -0500263 return -1;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500264
265 msleep(USB_TIME_SETADDR_RECOVERY);
266
267 cntl->maxaddr++;
Kevin O'Connor77ab4e12012-03-08 08:44:32 -0500268 usbdev->devaddr = cntl->maxaddr;
Kevin O'Connorc3d96c22012-03-10 09:03:25 -0500269 usbdev->defpipe = usb_alloc_pipe(usbdev, &epdesc);
Kevin O'Connor77ab4e12012-03-08 08:44:32 -0500270 if (!usbdev->defpipe)
271 return -1;
Kevin O'Connor4f74dad2012-03-06 08:20:40 -0500272 return 0;
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500273}
274
275// Called for every found device - see if a driver is available for
276// this device and do setup if so.
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400277static int
Kevin O'Connor4f74dad2012-03-06 08:20:40 -0500278configure_usb_device(struct usbdevice_s *usbdev)
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500279{
280 ASSERT32FLAT();
Kevin O'Connor77ab4e12012-03-08 08:44:32 -0500281 dprintf(3, "config_usb: %p\n", usbdev->defpipe);
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500282
283 // Set the max packet size for endpoint 0 of this device.
Kevin O'Connor114592f2009-09-28 21:32:08 -0400284 struct usb_device_descriptor dinfo;
Kevin O'Connor77ab4e12012-03-08 08:44:32 -0500285 int ret = get_device_info8(usbdev->defpipe, &dinfo);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400286 if (ret)
287 return 0;
288 dprintf(3, "device rev=%04x cls=%02x sub=%02x proto=%02x size=%02x\n"
289 , dinfo.bcdUSB, dinfo.bDeviceClass, dinfo.bDeviceSubClass
290 , dinfo.bDeviceProtocol, dinfo.bMaxPacketSize0);
291 if (dinfo.bMaxPacketSize0 < 8 || dinfo.bMaxPacketSize0 > 64)
292 return 0;
Kevin O'Connor77ab4e12012-03-08 08:44:32 -0500293 free_pipe(usbdev->defpipe);
294 struct usb_endpoint_descriptor epdesc = {
295 .wMaxPacketSize = dinfo.bMaxPacketSize0,
296 .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
297 };
Kevin O'Connorc3d96c22012-03-10 09:03:25 -0500298 usbdev->defpipe = usb_alloc_pipe(usbdev, &epdesc);
Kevin O'Connor77ab4e12012-03-08 08:44:32 -0500299 if (!usbdev->defpipe)
300 return -1;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400301
302 // Get configuration
Kevin O'Connor77ab4e12012-03-08 08:44:32 -0500303 struct usb_config_descriptor *config = get_device_config(usbdev->defpipe);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400304 if (!config)
305 return 0;
306
307 // Determine if a driver exists for this device - only look at the
308 // first interface of the first configuration.
309 struct usb_interface_descriptor *iface = (void*)(&config[1]);
Kevin O'Connor0e885762010-05-01 22:14:40 -0400310 if (iface->bInterfaceClass != USB_CLASS_HID
311 && iface->bInterfaceClass != USB_CLASS_MASS_STORAGE
312 && iface->bInterfaceClass != USB_CLASS_HUB)
Kevin O'Connor54671c12010-02-15 18:58:12 -0500313 // Not a supported device.
Kevin O'Connor114592f2009-09-28 21:32:08 -0400314 goto fail;
315
Kevin O'Connor8ebcac02010-03-09 19:58:23 -0500316 // Set the configuration.
Kevin O'Connor77ab4e12012-03-08 08:44:32 -0500317 ret = set_configuration(usbdev->defpipe, config->bConfigurationValue);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400318 if (ret)
319 goto fail;
320
321 // Configure driver.
Kevin O'Connor6a8e8952012-03-08 07:20:30 -0500322 usbdev->config = config;
323 usbdev->iface = iface;
324 usbdev->imax = (void*)config + config->wTotalLength - (void*)iface;
Kevin O'Connor357bdfa2010-02-26 08:57:13 -0500325 if (iface->bInterfaceClass == USB_CLASS_HUB)
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500326 ret = usb_hub_setup(usbdev);
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +0200327 else if (iface->bInterfaceClass == USB_CLASS_MASS_STORAGE) {
328 if (iface->bInterfaceProtocol == US_PR_BULK)
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500329 ret = usb_msc_setup(usbdev);
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +0200330 if (iface->bInterfaceProtocol == US_PR_UAS)
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500331 ret = usb_uas_setup(usbdev);
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +0200332 } else
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500333 ret = usb_hid_setup(usbdev);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400334 if (ret)
335 goto fail;
336
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400337 free(config);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400338 return 1;
339fail:
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400340 free(config);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400341 return 0;
342}
343
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400344static void
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500345usb_hub_port_setup(void *data)
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400346{
Kevin O'Connor4f74dad2012-03-06 08:20:40 -0500347 struct usbdevice_s *usbdev = data;
348 struct usbhub_s *hub = usbdev->hub;
349 u32 port = usbdev->port;
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400350
351 // Detect if device present (and possibly start reset)
352 int ret = hub->op->detect(hub, port);
353 if (ret)
354 // No device present
355 goto done;
356
357 // Reset port and determine device speed
358 mutex_lock(&hub->cntl->resetlock);
359 ret = hub->op->reset(hub, port);
360 if (ret < 0)
361 // Reset failed
362 goto resetfail;
Kevin O'Connor4f74dad2012-03-06 08:20:40 -0500363 usbdev->speed = ret;
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400364
365 // Set address of port
Kevin O'Connor4f74dad2012-03-06 08:20:40 -0500366 ret = usb_set_address(usbdev);
367 if (ret) {
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400368 hub->op->disconnect(hub, port);
369 goto resetfail;
370 }
371 mutex_unlock(&hub->cntl->resetlock);
372
373 // Configure the device
Kevin O'Connor4f74dad2012-03-06 08:20:40 -0500374 int count = configure_usb_device(usbdev);
375 free_pipe(usbdev->defpipe);
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400376 if (!count)
377 hub->op->disconnect(hub, port);
378 hub->devcount += count;
379done:
380 hub->threads--;
Kevin O'Connor4f74dad2012-03-06 08:20:40 -0500381 free(usbdev);
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400382 return;
383
384resetfail:
385 mutex_unlock(&hub->cntl->resetlock);
386 goto done;
387}
388
389void
390usb_enumerate(struct usbhub_s *hub)
391{
392 u32 portcount = hub->portcount;
393 hub->threads = portcount;
394
395 // Launch a thread for every port.
396 int i;
397 for (i=0; i<portcount; i++) {
Kevin O'Connor4f74dad2012-03-06 08:20:40 -0500398 struct usbdevice_s *usbdev = malloc_tmphigh(sizeof(*usbdev));
399 if (!usbdev) {
400 warn_noalloc();
401 continue;
402 }
403 memset(usbdev, 0, sizeof(*usbdev));
404 usbdev->hub = hub;
405 usbdev->port = i;
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500406 run_thread(usb_hub_port_setup, usbdev);
Kevin O'Connord28b0fe2010-03-28 15:11:19 -0400407 }
408
409 // Wait for threads to complete.
410 while (hub->threads)
411 yield();
412}
413
Kevin O'Connor114592f2009-09-28 21:32:08 -0400414void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500415usb_setup(void)
Kevin O'Connor114592f2009-09-28 21:32:08 -0400416{
Kevin O'Connor59c75742010-02-13 18:49:24 -0500417 ASSERT32FLAT();
Kevin O'Connor114592f2009-09-28 21:32:08 -0400418 if (! CONFIG_USB)
419 return;
420
421 dprintf(3, "init usb\n");
422
Kevin O'Connor114592f2009-09-28 21:32:08 -0400423 // Look for USB controllers
424 int count = 0;
Kevin O'Connorb98a4b12013-06-08 21:53:36 -0400425 struct pci_device *pci, *ehcipci = NULL;
Kevin O'Connor9931bcc2011-06-20 22:23:02 -0400426 foreachpci(pci) {
427 if (pci->class != PCI_CLASS_SERIAL_USB)
Kevin O'Connor114592f2009-09-28 21:32:08 -0400428 continue;
429
Kevin O'Connorb98a4b12013-06-08 21:53:36 -0400430 if (!ehcipci || pci->bdf >= ehcipci->bdf) {
Kevin O'Connor190cc622010-03-09 19:43:52 -0500431 // Check to see if this device has an ehci controller
Kevin O'Connor190cc622010-03-09 19:43:52 -0500432 int found = 0;
Kevin O'Connor9931bcc2011-06-20 22:23:02 -0400433 ehcipci = pci;
Kevin O'Connor190cc622010-03-09 19:43:52 -0500434 for (;;) {
Kevin O'Connor9931bcc2011-06-20 22:23:02 -0400435 if (pci_classprog(ehcipci) == PCI_CLASS_SERIAL_USB_EHCI) {
Kevin O'Connor190cc622010-03-09 19:43:52 -0500436 // Found an ehci controller.
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500437 int ret = ehci_setup(ehcipci, count++, pci);
Kevin O'Connor190cc622010-03-09 19:43:52 -0500438 if (ret)
439 // Error
440 break;
441 count += found;
Kevin O'Connor9931bcc2011-06-20 22:23:02 -0400442 pci = ehcipci;
Kevin O'Connor190cc622010-03-09 19:43:52 -0500443 break;
444 }
Kevin O'Connor9931bcc2011-06-20 22:23:02 -0400445 if (ehcipci->class == PCI_CLASS_SERIAL_USB)
Kevin O'Connor190cc622010-03-09 19:43:52 -0500446 found++;
Kevin O'Connor2a9aeab2013-07-14 13:55:52 -0400447 ehcipci = container_of_or_null(
Kevin O'Connorb98a4b12013-06-08 21:53:36 -0400448 ehcipci->node.next, struct pci_device, node);
Kevin O'Connor9931bcc2011-06-20 22:23:02 -0400449 if (!ehcipci || (pci_bdf_to_busdev(ehcipci->bdf)
450 != pci_bdf_to_busdev(pci->bdf)))
Kevin O'Connor190cc622010-03-09 19:43:52 -0500451 // No ehci controller found.
452 break;
Kevin O'Connor190cc622010-03-09 19:43:52 -0500453 }
454 }
Kevin O'Connor114592f2009-09-28 21:32:08 -0400455
Kevin O'Connor9931bcc2011-06-20 22:23:02 -0400456 if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_UHCI)
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500457 uhci_setup(pci, count++);
Kevin O'Connor9931bcc2011-06-20 22:23:02 -0400458 else if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_OHCI)
Kevin O'Connord83c87b2013-01-21 01:14:12 -0500459 ohci_setup(pci, count++);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400460 }
461}