1 /* usb.c - USB Hub Support. */
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/>.
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;
35 struct grub_usb_hub
*next
;
36 grub_usb_controller_t controller
;
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
;
55 dev
= grub_zalloc (sizeof (struct grub_usb_device
));
59 dev
->controller
= *controller
;
62 dev
->hubaddr
= hubaddr
;
64 err
= grub_usb_device_initialize (dev
);
71 /* Assign a new address to the device. */
72 for (i
= 1; i
< GRUB_USBHUB_MAX_DEVICES
; i
++)
74 if (! grub_usb_devs
[i
])
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
);
86 err
= grub_usb_control_msg (dev
,
88 | GRUB_USB_REQTYPE_STANDARD
89 | GRUB_USB_REQTYPE_TARGET_DEV
),
90 GRUB_USB_REQ_SET_ADDRESS
,
94 for (i
= 0; i
< 8; i
++)
95 grub_free (dev
->config
[i
].descconf
);
101 dev
->initialized
= 1;
102 grub_usb_devs
[i
] = dev
;
104 grub_dprintf ("usb", "Added new usb device: %p, addr=%d\n",
106 grub_dprintf ("usb", "speed=%d, port=%d, hubaddr=%d\n",
107 speed
, port
, hubaddr
);
109 /* Wait "recovery interval", spec. says 2ms */
112 grub_usb_device_attach (dev
);
118 static grub_usb_err_t
119 grub_usb_add_hub (grub_usb_device_t dev
)
121 struct grub_usb_usb_hubdesc hubdesc
;
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
);
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
,
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]));
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
,
160 /* Rest will be done on next usb poll. */
161 for (i
= 0; i
< dev
->config
[0].interf
[0].descif
->endpointcnt
;
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
)
171 dev
->hub_endpoint
= endp
;
172 len
= endp
->maxpacket
;
173 if (len
> sizeof (dev
->statuschange
))
174 len
= sizeof (dev
->statuschange
);
176 = grub_usb_bulk_read_background (dev
, endp
->endp_addr
, len
,
177 (char *) &dev
->statuschange
);
184 return GRUB_ERR_NONE
;
188 attach_root_port (struct grub_usb_hub
*hub
, int portno
,
189 grub_usb_speed_t speed
)
191 grub_usb_device_t dev
;
194 grub_usb_speed_t current_speed
= GRUB_USB_SPEED_NONE
;
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);
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
++)
214 current_speed
= hub
->controller
->dev
->detect_dev
215 (hub
->controller
, portno
, &changed
);
216 if (current_speed
== GRUB_USB_SPEED_NONE
)
219 grub_dprintf ("usb", "total=%d\n", total
);
223 /* Enable the port. */
224 err
= hub
->controller
->dev
->portstatus (hub
->controller
, portno
, 1);
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;
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
);
245 grub_usb_root_hub (grub_usb_controller_t controller
)
248 struct grub_usb_hub
*hub
;
251 hub
= grub_malloc (sizeof (*hub
));
253 return GRUB_USB_ERR_INTERNAL
;
257 hub
->controller
= grub_malloc (sizeof (*controller
));
258 if (!hub
->controller
)
261 return GRUB_USB_ERR_INTERNAL
;
264 grub_memcpy (hub
->controller
, controller
, sizeof (*controller
));
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
);
272 grub_free (hub
->controller
);
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
,
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
);
296 detach_device (grub_usb_device_t dev
)
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;
323 poll_nonroot_hub (grub_usb_device_t dev
)
327 grub_uint8_t changed
;
328 grub_size_t actual
, len
;
331 if (!dev
->hub_transfer
)
334 err
= grub_usb_check_transfer (dev
->hub_transfer
, &actual
);
336 if (err
== GRUB_USB_ERR_WAIT
)
339 changed
= dev
->statuschange
;
341 len
= dev
->hub_endpoint
->maxpacket
;
342 if (len
> sizeof (dev
->statuschange
))
343 len
= sizeof (dev
->statuschange
);
345 = grub_usb_bulk_read_background (dev
, dev
->hub_endpoint
->endp_addr
, len
,
346 (char *) &dev
->statuschange
);
348 if (err
|| actual
== 0 || changed
== 0)
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
)))
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",
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
++)
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
,
425 sizeof (current_status
),
426 (char *) ¤t_status
);
432 if (!(current_status
& GRUB_USB_HUB_STATUS_PORT_CONNECTED
))
435 grub_dprintf ("usb", "(non-root) total=%d\n", total
);
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
,
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;
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
;
477 if (status
& GRUB_USB_HUB_STATUS_PORT_HIGHSPEED
)
478 speed
= GRUB_USB_SPEED_HIGH
;
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;
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
);
503 grub_usb_poll_devices (void)
505 struct grub_usb_hub
*hub
;
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
;
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
,
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
);
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
);
553 grub_millisleep (50);
559 grub_usb_iterate (int (*hook
) (grub_usb_device_t dev
))
563 for (i
= 0; i
< GRUB_USBHUB_MAX_DEVICES
; i
++)
565 if (grub_usb_devs
[i
])
567 if (hook (grub_usb_devs
[i
]))