Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / bus / usb / usbhub.c
blob5fc5eba392df70e76da71f8ed90fc06ae109d414
1 /* usb.c - USB Hub Support. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2008 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/dl.h>
21 #include <grub/mm.h>
22 #include <grub/usb.h>
23 #include <grub/misc.h>
24 #include <grub/time.h>
26 #define GRUB_USBHUB_MAX_DEVICES 128
28 /* USB Supports 127 devices, with device 0 as special case. */
29 static struct grub_usb_device *grub_usb_devs[GRUB_USBHUB_MAX_DEVICES];
31 static int rescan = 0;
33 struct grub_usb_hub
35 struct grub_usb_hub *next;
36 grub_usb_controller_t controller;
37 int nports;
38 struct grub_usb_device **devices;
39 grub_usb_device_t dev;
42 static struct grub_usb_hub *hubs;
44 /* Add a device that currently has device number 0 and resides on
45 CONTROLLER, the Hub reported that the device speed is SPEED. */
46 static grub_usb_device_t
47 grub_usb_hub_add_dev (grub_usb_controller_t controller,
48 grub_usb_speed_t speed,
49 int port, int hubaddr)
51 grub_usb_device_t dev;
52 int i;
53 grub_usb_err_t err;
55 dev = grub_zalloc (sizeof (struct grub_usb_device));
56 if (! dev)
57 return NULL;
59 dev->controller = *controller;
60 dev->speed = speed;
61 dev->port = port;
62 dev->hubaddr = hubaddr;
64 err = grub_usb_device_initialize (dev);
65 if (err)
67 grub_free (dev);
68 return NULL;
71 /* Assign a new address to the device. */
72 for (i = 1; i < GRUB_USBHUB_MAX_DEVICES; i++)
74 if (! grub_usb_devs[i])
75 break;
77 if (i == GRUB_USBHUB_MAX_DEVICES)
79 grub_error (GRUB_ERR_IO, "can't assign address to USB device");
80 for (i = 0; i < 8; i++)
81 grub_free (dev->config[i].descconf);
82 grub_free (dev);
83 return NULL;
86 err = grub_usb_control_msg (dev,
87 (GRUB_USB_REQTYPE_OUT
88 | GRUB_USB_REQTYPE_STANDARD
89 | GRUB_USB_REQTYPE_TARGET_DEV),
90 GRUB_USB_REQ_SET_ADDRESS,
91 i, 0, 0, NULL);
92 if (err)
94 for (i = 0; i < 8; i++)
95 grub_free (dev->config[i].descconf);
96 grub_free (dev);
97 return NULL;
100 dev->addr = i;
101 dev->initialized = 1;
102 grub_usb_devs[i] = dev;
104 grub_dprintf ("usb", "Added new usb device: %p, addr=%d\n",
105 dev, i);
106 grub_dprintf ("usb", "speed=%d, port=%d, hubaddr=%d\n",
107 speed, port, hubaddr);
109 /* Wait "recovery interval", spec. says 2ms */
110 grub_millisleep (2);
112 grub_usb_device_attach (dev);
114 return dev;
118 static grub_usb_err_t
119 grub_usb_add_hub (grub_usb_device_t dev)
121 struct grub_usb_usb_hubdesc hubdesc;
122 grub_usb_err_t err;
123 int i;
125 err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
126 | GRUB_USB_REQTYPE_CLASS
127 | GRUB_USB_REQTYPE_TARGET_DEV),
128 GRUB_USB_REQ_GET_DESCRIPTOR,
129 (GRUB_USB_DESCRIPTOR_HUB << 8) | 0,
130 0, sizeof (hubdesc), (char *) &hubdesc);
131 if (err)
132 return err;
133 grub_dprintf ("usb", "Hub descriptor:\n\t\t len:%d, typ:0x%02x, cnt:%d, char:0x%02x, pwg:%d, curr:%d\n",
134 hubdesc.length, hubdesc.type, hubdesc.portcnt,
135 hubdesc.characteristics, hubdesc.pwdgood,
136 hubdesc.current);
138 /* Activate the first configuration. Hubs should have only one conf. */
139 grub_dprintf ("usb", "Hub set configuration\n");
140 grub_usb_set_configuration (dev, 1);
142 dev->children = grub_zalloc (hubdesc.portcnt * sizeof (dev->children[0]));
143 if (!dev->children)
144 return GRUB_USB_ERR_INTERNAL;
145 dev->nports = hubdesc.portcnt;
147 /* Power on all Hub ports. */
148 for (i = 1; i <= hubdesc.portcnt; i++)
150 grub_dprintf ("usb", "Power on - port %d\n", i);
151 /* Power on the port and wait for possible device connect */
152 grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
153 | GRUB_USB_REQTYPE_CLASS
154 | GRUB_USB_REQTYPE_TARGET_OTHER),
155 GRUB_USB_REQ_SET_FEATURE,
156 GRUB_USB_HUB_FEATURE_PORT_POWER,
157 i, 0, NULL);
160 /* Rest will be done on next usb poll. */
161 for (i = 0; i < dev->config[0].interf[0].descif->endpointcnt;
162 i++)
164 struct grub_usb_desc_endp *endp = NULL;
165 endp = &dev->config[0].interf[0].descendp[i];
167 if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp)
168 == GRUB_USB_EP_INTERRUPT)
170 grub_size_t len;
171 dev->hub_endpoint = endp;
172 len = endp->maxpacket;
173 if (len > sizeof (dev->statuschange))
174 len = sizeof (dev->statuschange);
175 dev->hub_transfer
176 = grub_usb_bulk_read_background (dev, endp->endp_addr, len,
177 (char *) &dev->statuschange);
178 break;
182 rescan = 1;
184 return GRUB_ERR_NONE;
187 static void
188 attach_root_port (struct grub_usb_hub *hub, int portno,
189 grub_usb_speed_t speed)
191 grub_usb_device_t dev;
192 grub_err_t err;
193 int total, i;
194 grub_usb_speed_t current_speed = GRUB_USB_SPEED_NONE;
195 int changed=0;
197 #if 0
198 /* Specification does not say about disabling of port when device
199 * connected. If disabling is really necessary for some devices,
200 * delete this #if 0 and related #endif */
201 /* Disable the port. XXX: Why? */
202 err = hub->controller->dev->portstatus (hub->controller, portno, 0);
203 if (err)
204 return;
205 #endif
206 /* Wait for completion of insertion and stable power (USB spec.)
207 * Should be at least 100ms, some devices requires more...
208 * There is also another thing - some devices have worse contacts
209 * and connected signal is unstable for some time - we should handle
210 * it - but prevent deadlock in case when device is too faulty... */
211 for (total = i = 0; (i < 250) && (total < 2000); i++, total++)
213 grub_millisleep (1);
214 current_speed = hub->controller->dev->detect_dev
215 (hub->controller, portno, &changed);
216 if (current_speed == GRUB_USB_SPEED_NONE)
217 i = 0;
219 grub_dprintf ("usb", "total=%d\n", total);
220 if (total >= 2000)
221 return;
223 /* Enable the port. */
224 err = hub->controller->dev->portstatus (hub->controller, portno, 1);
225 if (err)
226 return;
227 hub->controller->dev->pending_reset = grub_get_time_ms () + 5000;
229 grub_millisleep (10);
231 /* Enable the port and create a device. */
232 dev = grub_usb_hub_add_dev (hub->controller, speed, portno, 0);
233 hub->controller->dev->pending_reset = 0;
234 if (! dev)
235 return;
237 hub->devices[portno] = dev;
239 /* If the device is a Hub, scan it for more devices. */
240 if (dev->descdev.class == 0x09)
241 grub_usb_add_hub (dev);
244 grub_usb_err_t
245 grub_usb_root_hub (grub_usb_controller_t controller)
247 int i;
248 struct grub_usb_hub *hub;
249 int changed=0;
251 hub = grub_malloc (sizeof (*hub));
252 if (!hub)
253 return GRUB_USB_ERR_INTERNAL;
255 hub->next = hubs;
256 hubs = hub;
257 hub->controller = grub_malloc (sizeof (*controller));
258 if (!hub->controller)
260 grub_free (hub);
261 return GRUB_USB_ERR_INTERNAL;
264 grub_memcpy (hub->controller, controller, sizeof (*controller));
265 hub->dev = 0;
267 /* Query the number of ports the root Hub has. */
268 hub->nports = controller->dev->hubports (controller);
269 hub->devices = grub_zalloc (sizeof (hub->devices[0]) * hub->nports);
270 if (!hub->devices)
272 grub_free (hub->controller);
273 grub_free (hub);
274 return GRUB_USB_ERR_INTERNAL;
277 for (i = 0; i < hub->nports; i++)
279 grub_usb_speed_t speed;
280 if (!controller->dev->pending_reset)
282 speed = controller->dev->detect_dev (hub->controller, i,
283 &changed);
285 if (speed != GRUB_USB_SPEED_NONE)
286 attach_root_port (hub, i, speed);
290 return GRUB_USB_ERR_NONE;
293 static void detach_device (grub_usb_device_t dev);
295 static void
296 detach_device (grub_usb_device_t dev)
298 unsigned i;
299 int k;
300 if (!dev)
301 return;
302 if (dev->descdev.class == GRUB_USB_CLASS_HUB)
304 if (dev->hub_transfer)
305 grub_usb_cancel_transfer (dev->hub_transfer);
307 for (i = 0; i < dev->nports; i++)
308 detach_device (dev->children[i]);
309 grub_free (dev->children);
311 for (i = 0; i < ARRAY_SIZE (dev->config); i++)
312 if (dev->config[i].descconf)
313 for (k = 0; k < dev->config[i].descconf->numif; k++)
315 struct grub_usb_interface *inter = &dev->config[i].interf[k];
316 if (inter && inter->detach_hook)
317 inter->detach_hook (dev, i, k);
319 grub_usb_devs[dev->addr] = 0;
322 static void
323 poll_nonroot_hub (grub_usb_device_t dev)
325 grub_usb_err_t err;
326 unsigned i;
327 grub_uint8_t changed;
328 grub_size_t actual, len;
329 int j, total;
331 if (!dev->hub_transfer)
332 return;
334 err = grub_usb_check_transfer (dev->hub_transfer, &actual);
336 if (err == GRUB_USB_ERR_WAIT)
337 return;
339 changed = dev->statuschange;
341 len = dev->hub_endpoint->maxpacket;
342 if (len > sizeof (dev->statuschange))
343 len = sizeof (dev->statuschange);
344 dev->hub_transfer
345 = grub_usb_bulk_read_background (dev, dev->hub_endpoint->endp_addr, len,
346 (char *) &dev->statuschange);
348 if (err || actual == 0 || changed == 0)
349 return;
351 /* Iterate over the Hub ports. */
352 for (i = 1; i <= dev->nports; i++)
354 grub_uint32_t status;
355 grub_uint32_t current_status = 0;
357 if (!(changed & (1 << i)))
358 continue;
360 /* Get the port status. */
361 err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
362 | GRUB_USB_REQTYPE_CLASS
363 | GRUB_USB_REQTYPE_TARGET_OTHER),
364 GRUB_USB_REQ_GET_STATUS,
365 0, i, sizeof (status), (char *) &status);
367 grub_dprintf ("usb", "dev = %p, i = %d, status = %08x\n",
368 dev, i, status);
370 if (err)
371 continue;
373 /* FIXME: properly handle these conditions. */
374 if (status & GRUB_USB_HUB_STATUS_C_PORT_ENABLED)
375 grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
376 | GRUB_USB_REQTYPE_CLASS
377 | GRUB_USB_REQTYPE_TARGET_OTHER),
378 GRUB_USB_REQ_CLEAR_FEATURE,
379 GRUB_USB_HUB_FEATURE_C_PORT_ENABLED, i, 0, 0);
381 if (status & GRUB_USB_HUB_STATUS_C_PORT_SUSPEND)
382 grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
383 | GRUB_USB_REQTYPE_CLASS
384 | GRUB_USB_REQTYPE_TARGET_OTHER),
385 GRUB_USB_REQ_CLEAR_FEATURE,
386 GRUB_USB_HUB_FEATURE_C_PORT_SUSPEND, i, 0, 0);
388 if (status & GRUB_USB_HUB_STATUS_C_PORT_OVERCURRENT)
389 grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
390 | GRUB_USB_REQTYPE_CLASS
391 | GRUB_USB_REQTYPE_TARGET_OTHER),
392 GRUB_USB_REQ_CLEAR_FEATURE,
393 GRUB_USB_HUB_FEATURE_C_PORT_OVERCURRENT, i, 0, 0);
395 if (!dev->controller.dev->pending_reset &&
396 (status & GRUB_USB_HUB_STATUS_C_PORT_CONNECTED))
398 grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
399 | GRUB_USB_REQTYPE_CLASS
400 | GRUB_USB_REQTYPE_TARGET_OTHER),
401 GRUB_USB_REQ_CLEAR_FEATURE,
402 GRUB_USB_HUB_FEATURE_C_PORT_CONNECTED, i, 0, 0);
404 detach_device (dev->children[i - 1]);
405 dev->children[i - 1] = NULL;
407 /* Connected and status of connection changed ? */
408 if (status & GRUB_USB_HUB_STATUS_PORT_CONNECTED)
410 /* A device is actually connected to this port. */
411 /* Wait for completion of insertion and stable power (USB spec.)
412 * Should be at least 100ms, some devices requires more...
413 * There is also another thing - some devices have worse contacts
414 * and connected signal is unstable for some time - we should handle
415 * it - but prevent deadlock in case when device is too faulty... */
416 for (total = j = 0; (j < 250) && (total < 2000); j++, total++)
418 grub_millisleep (1);
419 /* Get the port status. */
420 err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
421 | GRUB_USB_REQTYPE_CLASS
422 | GRUB_USB_REQTYPE_TARGET_OTHER),
423 GRUB_USB_REQ_GET_STATUS,
424 0, i,
425 sizeof (current_status),
426 (char *) &current_status);
427 if (err)
429 total = 2000;
430 break;
432 if (!(current_status & GRUB_USB_HUB_STATUS_PORT_CONNECTED))
433 j = 0;
435 grub_dprintf ("usb", "(non-root) total=%d\n", total);
436 if (total >= 2000)
437 continue;
439 /* Now do reset of port. */
440 grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
441 | GRUB_USB_REQTYPE_CLASS
442 | GRUB_USB_REQTYPE_TARGET_OTHER),
443 GRUB_USB_REQ_SET_FEATURE,
444 GRUB_USB_HUB_FEATURE_PORT_RESET,
445 i, 0, 0);
446 rescan = 1;
447 /* We cannot reset more than one device at the same time !
448 * Resetting more devices together results in very bad
449 * situation: more than one device has default address 0
450 * at the same time !!!
451 * Additionaly, we cannot perform another reset
452 * anywhere on the same OHCI controller until
453 * we will finish addressing of reseted device ! */
454 dev->controller.dev->pending_reset = grub_get_time_ms () + 5000;
455 return;
459 if (status & GRUB_USB_HUB_STATUS_C_PORT_RESET)
461 grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
462 | GRUB_USB_REQTYPE_CLASS
463 | GRUB_USB_REQTYPE_TARGET_OTHER),
464 GRUB_USB_REQ_CLEAR_FEATURE,
465 GRUB_USB_HUB_FEATURE_C_PORT_RESET, i, 0, 0);
467 if (status & GRUB_USB_HUB_STATUS_PORT_CONNECTED)
469 grub_usb_speed_t speed;
470 grub_usb_device_t next_dev;
472 /* Determine the device speed. */
473 if (status & GRUB_USB_HUB_STATUS_PORT_LOWSPEED)
474 speed = GRUB_USB_SPEED_LOW;
475 else
477 if (status & GRUB_USB_HUB_STATUS_PORT_HIGHSPEED)
478 speed = GRUB_USB_SPEED_HIGH;
479 else
480 speed = GRUB_USB_SPEED_FULL;
483 /* Wait a recovery time after reset, spec. says 10ms */
484 grub_millisleep (10);
486 /* Add the device and assign a device address to it. */
487 next_dev = grub_usb_hub_add_dev (&dev->controller, speed, i, dev->addr);
488 dev->controller.dev->pending_reset = 0;
489 if (! next_dev)
490 continue;
492 dev->children[i - 1] = next_dev;
494 /* If the device is a Hub, scan it for more devices. */
495 if (next_dev->descdev.class == 0x09)
496 grub_usb_add_hub (next_dev);
502 void
503 grub_usb_poll_devices (void)
505 struct grub_usb_hub *hub;
506 int i;
508 for (hub = hubs; hub; hub = hub->next)
510 /* Do we have to recheck number of ports? */
511 /* No, it should be never changed, it should be constant. */
512 for (i = 0; i < hub->nports; i++)
514 grub_usb_speed_t speed = GRUB_USB_SPEED_NONE;
515 int changed = 0;
517 if (!hub->controller->dev->pending_reset)
519 /* Check for possible timeout */
520 if (grub_get_time_ms () > hub->controller->dev->pending_reset)
522 /* Something went wrong, reset device was not
523 * addressed properly, timeout happened */
524 hub->controller->dev->pending_reset = 0;
525 speed = hub->controller->dev->detect_dev (hub->controller,
526 i, &changed);
529 if (changed)
531 detach_device (hub->devices[i]);
532 hub->devices[i] = NULL;
533 if (speed != GRUB_USB_SPEED_NONE)
534 attach_root_port (hub, i, speed);
539 while (1)
541 rescan = 0;
543 /* We should check changes of non-root hubs too. */
544 for (i = 0; i < GRUB_USBHUB_MAX_DEVICES; i++)
546 grub_usb_device_t dev = grub_usb_devs[i];
548 if (dev && dev->descdev.class == 0x09)
549 poll_nonroot_hub (dev);
551 if (!rescan)
552 break;
553 grub_millisleep (50);
559 grub_usb_iterate (int (*hook) (grub_usb_device_t dev))
561 int i;
563 for (i = 0; i < GRUB_USBHUB_MAX_DEVICES; i++)
565 if (grub_usb_devs[i])
567 if (hook (grub_usb_devs[i]))
568 return 1;
572 return 0;