[pci] Add support for PCI MSI-X interrupts
[ipxe.git] / src / drivers / usb / usbhub.c
1 /*
2 * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <assert.h>
31 #include <byteswap.h>
32 #include <ipxe/usb.h>
33 #include "usbhub.h"
34
35 /** @file
36 *
37 * USB hub driver
38 *
39 */
40
41 /**
42 * Refill interrupt ring
43 *
44 * @v hubdev Hub device
45 */
46 static void hub_refill ( struct usb_hub_device *hubdev ) {
47 int rc;
48
49 /* Refill interrupt endpoint */
50 if ( ( rc = usb_refill ( &hubdev->intr ) ) != 0 ) {
51 DBGC ( hubdev, "HUB %s could not refill interrupt: %s\n",
52 hubdev->name, strerror ( rc ) );
53 /* Continue attempting to refill */
54 return;
55 }
56
57 /* Stop refill process */
58 process_del ( &hubdev->refill );
59 }
60
61 /** Refill process descriptor */
62 static struct process_descriptor hub_refill_desc =
63 PROC_DESC ( struct usb_hub_device, refill, hub_refill );
64
65 /**
66 * Complete interrupt transfer
67 *
68 * @v ep USB endpoint
69 * @v iobuf I/O buffer
70 * @v rc Completion status code
71 */
72 static void hub_complete ( struct usb_endpoint *ep,
73 struct io_buffer *iobuf, int rc ) {
74 struct usb_hub_device *hubdev =
75 container_of ( ep, struct usb_hub_device, intr );
76 struct usb_hub *hub = hubdev->hub;
77 uint8_t *data = iobuf->data;
78 unsigned int bits = ( 8 * iob_len ( iobuf ) );
79 unsigned int i;
80
81 /* Ignore packets cancelled when the endpoint closes */
82 if ( ! ep->open )
83 goto done;
84
85 /* Ignore packets with errors */
86 if ( rc != 0 ) {
87 DBGC ( hubdev, "HUB %s interrupt failed: %s\n",
88 hubdev->name, strerror ( rc ) );
89 DBGC_HDA ( hubdev, 0, iobuf->data, iob_len ( iobuf ) );
90 goto done;
91 }
92
93 /* Report any port status changes */
94 for ( i = 1 ; i <= hub->ports ; i++ ) {
95
96 /* Sanity check */
97 if ( i > bits ) {
98 DBGC ( hubdev, "HUB %s underlength interrupt:\n",
99 hubdev->name );
100 DBGC_HDA ( hubdev, 0, iobuf->data, iob_len ( iobuf ) );
101 goto done;
102 }
103
104 /* Report port status change if applicable */
105 if ( data[ i / 8 ] & ( 1 << ( i % 8 ) ) ) {
106 DBGC2 ( hubdev, "HUB %s port %d status changed\n",
107 hubdev->name, i );
108 usb_port_changed ( usb_port ( hub, i ) );
109 }
110 }
111
112 done:
113 /* Start refill process */
114 process_add ( &hubdev->refill );
115 }
116
117 /** Interrupt endpoint operations */
118 static struct usb_endpoint_driver_operations usb_hub_intr_operations = {
119 .complete = hub_complete,
120 };
121
122 /**
123 * Open hub
124 *
125 * @v hub USB hub
126 * @ret rc Return status code
127 */
128 static int hub_open ( struct usb_hub *hub ) {
129 struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
130 struct usb_device *usb = hubdev->usb;
131 unsigned int i;
132 int rc;
133
134 /* Ensure ports are powered */
135 for ( i = 1 ; i <= hub->ports ; i++ ) {
136 if ( ( rc = usb_hub_set_port_feature ( usb, i,
137 USB_HUB_PORT_POWER,
138 0 ) ) != 0 ) {
139 DBGC ( hubdev, "HUB %s port %d could not apply power: "
140 "%s\n", hubdev->name, i, strerror ( rc ) );
141 goto err_power;
142 }
143 }
144
145 /* Open interrupt endpoint */
146 if ( ( rc = usb_endpoint_open ( &hubdev->intr ) ) != 0 ) {
147 DBGC ( hubdev, "HUB %s could not register interrupt: %s\n",
148 hubdev->name, strerror ( rc ) );
149 goto err_open;
150 }
151
152 /* Start refill process */
153 process_add ( &hubdev->refill );
154
155 /* Refill interrupt ring */
156 hub_refill ( hubdev );
157
158 /* Delay to allow ports to stabilise on out-of-spec hubs */
159 if ( hubdev->flags & USB_HUB_SLOW_START )
160 mdelay ( USB_HUB_SLOW_START_DELAY_MS );
161
162 return 0;
163
164 usb_endpoint_close ( &hubdev->intr );
165 err_open:
166 err_power:
167 return rc;
168 }
169
170 /**
171 * Close hub
172 *
173 * @v hub USB hub
174 */
175 static void hub_close ( struct usb_hub *hub ) {
176 struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
177
178 /* Close interrupt endpoint */
179 usb_endpoint_close ( &hubdev->intr );
180
181 /* Stop refill process */
182 process_del ( &hubdev->refill );
183 }
184
185 /**
186 * Enable port
187 *
188 * @v hub USB hub
189 * @v port USB port
190 * @ret rc Return status code
191 */
192 static int hub_enable ( struct usb_hub *hub, struct usb_port *port ) {
193 struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
194 struct usb_device *usb = hubdev->usb;
195 struct usb_hub_port_status status;
196 unsigned int current;
197 unsigned int i;
198 int rc;
199
200 /* Initiate reset if applicable */
201 if ( ( hub->protocol < USB_PROTO_3_0 ) &&
202 ( ( rc = usb_hub_set_port_feature ( usb, port->address,
203 USB_HUB_PORT_RESET, 0 ) )!=0)){
204 DBGC ( hubdev, "HUB %s port %d could not initiate reset: %s\n",
205 hubdev->name, port->address, strerror ( rc ) );
206 return rc;
207 }
208
209 /* Wait for port to become enabled */
210 for ( i = 0 ; i < USB_HUB_ENABLE_MAX_WAIT_MS ; i++ ) {
211
212 /* Check for port being enabled */
213 if ( ( rc = usb_hub_get_port_status ( usb, port->address,
214 &status ) ) != 0 ) {
215 DBGC ( hubdev, "HUB %s port %d could not get status: "
216 "%s\n", hubdev->name, port->address,
217 strerror ( rc ) );
218 return rc;
219 }
220 current = le16_to_cpu ( status.current );
221 if ( current & ( 1 << USB_HUB_PORT_ENABLE ) )
222 return 0;
223
224 /* Delay */
225 mdelay ( 1 );
226 }
227
228 DBGC ( hubdev, "HUB %s port %d timed out waiting for enable\n",
229 hubdev->name, port->address );
230 return -ETIMEDOUT;
231 }
232
233 /**
234 * Disable port
235 *
236 * @v hub USB hub
237 * @v port USB port
238 * @ret rc Return status code
239 */
240 static int hub_disable ( struct usb_hub *hub, struct usb_port *port ) {
241 struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
242 struct usb_device *usb = hubdev->usb;
243 int rc;
244
245 /* Disable port */
246 if ( ( rc = usb_hub_clear_port_feature ( usb, port->address,
247 USB_HUB_PORT_ENABLE, 0 ) )!=0){
248 DBGC ( hubdev, "HUB %s port %d could not disable: %s\n",
249 hubdev->name, port->address, strerror ( rc ) );
250 return rc;
251 }
252
253 return 0;
254 }
255
256 /**
257 * Clear port status change bits
258 *
259 * @v hubdev USB hub device
260 * @v port Port number
261 * @v changed Port status change bits
262 * @ret rc Return status code
263 */
264 static int hub_clear_changes ( struct usb_hub_device *hubdev,
265 unsigned int port, uint16_t changed ) {
266 struct usb_device *usb = hubdev->usb;
267 unsigned int bit;
268 unsigned int feature;
269 int rc;
270
271 /* Clear each set bit */
272 for ( bit = 0 ; bit < 16 ; bit++ ) {
273
274 /* Skip unset bits */
275 if ( ! ( changed & ( 1 << bit ) ) )
276 continue;
277
278 /* Skip unused features */
279 feature = USB_HUB_C_FEATURE ( bit );
280 if ( ! ( hubdev->features & ( 1 << feature ) ) )
281 continue;
282
283 /* Clear bit */
284 if ( ( rc = usb_hub_clear_port_feature ( usb, port,
285 feature, 0 ) ) != 0 ) {
286 DBGC ( hubdev, "HUB %s port %d could not clear feature "
287 "%d: %s\n", hubdev->name, port, feature,
288 strerror ( rc ) );
289 return rc;
290 }
291 }
292
293 return 0;
294 }
295
296 /**
297 * Update port speed
298 *
299 * @v hub USB hub
300 * @v port USB port
301 * @ret rc Return status code
302 */
303 static int hub_speed ( struct usb_hub *hub, struct usb_port *port ) {
304 struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
305 struct usb_device *usb = hubdev->usb;
306 struct usb_hub_port_status status;
307 unsigned int current;
308 unsigned int changed;
309 int rc;
310
311 /* Get port status */
312 if ( ( rc = usb_hub_get_port_status ( usb, port->address,
313 &status ) ) != 0 ) {
314 DBGC ( hubdev, "HUB %s port %d could not get status: %s\n",
315 hubdev->name, port->address, strerror ( rc ) );
316 return rc;
317 }
318 current = le16_to_cpu ( status.current );
319 changed = le16_to_cpu ( status.changed );
320 DBGC2 ( hubdev, "HUB %s port %d status is %04x:%04x\n",
321 hubdev->name, port->address, changed, current );
322
323 /* Update port speed */
324 if ( current & ( 1 << USB_HUB_PORT_CONNECTION ) ) {
325 if ( hub->protocol >= USB_PROTO_3_0 ) {
326 port->speed = USB_SPEED_SUPER;
327 } else if ( current & ( 1 << USB_HUB_PORT_LOW_SPEED ) ) {
328 port->speed = USB_SPEED_LOW;
329 } else if ( current & ( 1 << USB_HUB_PORT_HIGH_SPEED ) ) {
330 port->speed = USB_SPEED_HIGH;
331 } else {
332 port->speed = USB_SPEED_FULL;
333 }
334 } else {
335 port->speed = USB_SPEED_NONE;
336 }
337
338 /* Record disconnections */
339 port->disconnected |= ( changed & ( 1 << USB_HUB_PORT_CONNECTION ) );
340
341 /* Clear port status change bits */
342 if ( ( rc = hub_clear_changes ( hubdev, port->address, changed ) ) != 0)
343 return rc;
344
345 return 0;
346 }
347
348 /**
349 * Clear transaction translator buffer
350 *
351 * @v hub USB hub
352 * @v port USB port
353 * @v ep USB endpoint
354 * @ret rc Return status code
355 */
356 static int hub_clear_tt ( struct usb_hub *hub, struct usb_port *port,
357 struct usb_endpoint *ep ) {
358 struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
359 struct usb_device *usb = hubdev->usb;
360 int rc;
361
362 /* Clear transaction translator buffer. All hubs must support
363 * single-TT operation; we simplify our code by supporting
364 * only this configuration.
365 */
366 if ( ( rc = usb_hub_clear_tt_buffer ( usb, ep->usb->address,
367 ep->address, ep->attributes,
368 USB_HUB_TT_SINGLE ) ) != 0 ) {
369 DBGC ( hubdev, "HUB %s port %d could not clear TT buffer: %s\n",
370 hubdev->name, port->address, strerror ( rc ) );
371 return rc;
372 }
373
374 return 0;
375 }
376
377 /** USB hub operations */
378 static struct usb_hub_driver_operations hub_operations = {
379 .open = hub_open,
380 .close = hub_close,
381 .enable = hub_enable,
382 .disable = hub_disable,
383 .speed = hub_speed,
384 .clear_tt = hub_clear_tt,
385 };
386
387 /**
388 * Probe USB hub
389 *
390 * @v func USB function
391 * @v config Configuration descriptor
392 * @ret rc Return status code
393 */
394 static int hub_probe ( struct usb_function *func,
395 struct usb_configuration_descriptor *config ) {
396 struct usb_device *usb = func->usb;
397 struct usb_bus *bus = usb->port->hub->bus;
398 struct usb_hub_device *hubdev;
399 struct usb_interface_descriptor *interface;
400 union usb_hub_descriptor desc;
401 unsigned int depth;
402 unsigned int ports;
403 int enhanced;
404 int rc;
405
406 /* Allocate and initialise structure */
407 hubdev = zalloc ( sizeof ( *hubdev ) );
408 if ( ! hubdev ) {
409 rc = -ENOMEM;
410 goto err_alloc;
411 }
412 enhanced = ( usb->port->protocol >= USB_PROTO_3_0 );
413 hubdev->name = func->name;
414 hubdev->usb = usb;
415 hubdev->features =
416 ( enhanced ? USB_HUB_FEATURES_ENHANCED : USB_HUB_FEATURES );
417 hubdev->flags = func->id->driver_data;
418 usb_endpoint_init ( &hubdev->intr, usb, &usb_hub_intr_operations );
419 usb_refill_init ( &hubdev->intr, 0, 0, USB_HUB_INTR_FILL );
420 process_init_stopped ( &hubdev->refill, &hub_refill_desc, NULL );
421
422 /* Locate hub interface descriptor */
423 interface = usb_interface_descriptor ( config, func->interface[0], 0 );
424 if ( ! interface ) {
425 DBGC ( hubdev, "HUB %s has no interface descriptor\n",
426 hubdev->name );
427 rc = -EINVAL;
428 goto err_interface;
429 }
430
431 /* Locate interrupt endpoint descriptor */
432 if ( ( rc = usb_endpoint_described ( &hubdev->intr, config, interface,
433 USB_INTERRUPT_IN, 0 ) ) != 0 ) {
434 DBGC ( hubdev, "HUB %s could not describe interrupt endpoint: "
435 "%s\n", hubdev->name, strerror ( rc ) );
436 goto err_endpoint;
437 }
438
439 /* Set hub depth */
440 depth = usb_depth ( usb );
441 if ( enhanced ) {
442 if ( ( rc = usb_hub_set_hub_depth ( usb, depth ) ) != 0 ) {
443 DBGC ( hubdev, "HUB %s could not set hub depth to %d: "
444 "%s\n", hubdev->name, depth, strerror ( rc ) );
445 goto err_set_hub_depth;
446 }
447 }
448
449 /* Get hub descriptor */
450 if ( ( rc = usb_hub_get_descriptor ( usb, enhanced, &desc ) ) != 0 ) {
451 DBGC ( hubdev, "HUB %s could not get hub descriptor: %s\n",
452 hubdev->name, strerror ( rc ) );
453 goto err_hub_descriptor;
454 }
455 ports = desc.basic.ports;
456 DBGC ( hubdev, "HUB %s has %d ports at depth %d%s\n", hubdev->name,
457 ports, depth, ( enhanced ? " (enhanced)" : "" ) );
458
459 /* Allocate hub */
460 hubdev->hub = alloc_usb_hub ( bus, usb, ports, &hub_operations );
461 if ( ! hubdev->hub ) {
462 rc = -ENOMEM;
463 goto err_alloc_hub;
464 }
465 usb_hub_set_drvdata ( hubdev->hub, hubdev );
466
467 /* Register hub */
468 if ( ( rc = register_usb_hub ( hubdev->hub ) ) != 0 ) {
469 DBGC ( hubdev, "HUB %s could not register: %s\n",
470 hubdev->name, strerror ( rc ) );
471 goto err_register_hub;
472 }
473
474 usb_func_set_drvdata ( func, hubdev );
475 return 0;
476
477 unregister_usb_hub ( hubdev->hub );
478 err_register_hub:
479 free_usb_hub ( hubdev->hub );
480 err_alloc_hub:
481 err_hub_descriptor:
482 err_set_hub_depth:
483 err_endpoint:
484 err_interface:
485 free ( hubdev );
486 err_alloc:
487 return rc;
488 }
489
490 /**
491 * Remove USB hub
492 *
493 * @v func USB function
494 * @ret rc Return status code
495 */
496 static void hub_remove ( struct usb_function *func ) {
497 struct usb_hub_device *hubdev = usb_func_get_drvdata ( func );
498 struct usb_hub *hub = hubdev->hub;
499 struct usb_device *usb = hubdev->usb;
500 struct usb_port *port;
501 unsigned int i;
502
503 /* If hub has been unplugged, mark all ports as unplugged */
504 if ( usb->port->disconnected ) {
505 for ( i = 1 ; i <= hub->ports ; i++ ) {
506 port = usb_port ( hub, i );
507 port->disconnected = 1;
508 port->speed = USB_SPEED_NONE;
509 }
510 }
511
512 /* Unregister hub */
513 unregister_usb_hub ( hubdev->hub );
514 assert ( ! process_running ( &hubdev->refill ) );
515
516 /* Free hub */
517 free_usb_hub ( hubdev->hub );
518
519 /* Free hub device */
520 free ( hubdev );
521 }
522
523 /** USB hub device IDs */
524 static struct usb_device_id hub_ids[] = {
525 {
526 .name = "avocent-hub",
527 .vendor = 0x0624,
528 .product = 0x0248,
529 .driver_data = USB_HUB_SLOW_START,
530 },
531 {
532 .name = "hub",
533 .vendor = USB_ANY_ID,
534 .product = USB_ANY_ID,
535 },
536 };
537
538 /** USB hub driver */
539 struct usb_driver usb_hub_driver __usb_driver = {
540 .ids = hub_ids,
541 .id_count = ( sizeof ( hub_ids ) / sizeof ( hub_ids[0] ) ),
542 .class = USB_CLASS_ID ( USB_CLASS_HUB, 0, USB_ANY_ID ),
543 .score = USB_SCORE_NORMAL,
544 .probe = hub_probe,
545 .remove = hub_remove,
546 };