2 * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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
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.
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL
);
42 * Refill interrupt ring
44 * @v hubdev Hub device
46 static void hub_refill ( struct usb_hub_device
*hubdev
) {
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 */
57 /* Stop refill process */
58 process_del ( &hubdev
->refill
);
61 /** Refill process descriptor */
62 static struct process_descriptor hub_refill_desc
=
63 PROC_DESC ( struct usb_hub_device
, refill
, hub_refill
);
66 * Complete interrupt transfer
70 * @v rc Completion status code
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
) );
81 /* Ignore packets cancelled when the endpoint closes */
85 /* Ignore packets with errors */
87 DBGC ( hubdev
, "HUB %s interrupt failed: %s\n",
88 hubdev
->name
, strerror ( rc
) );
89 DBGC_HDA ( hubdev
, 0, iobuf
->data
, iob_len ( iobuf
) );
93 /* Report any port status changes */
94 for ( i
= 1 ; i
<= hub
->ports
; i
++ ) {
98 DBGC ( hubdev
, "HUB %s underlength interrupt:\n",
100 DBGC_HDA ( hubdev
, 0, iobuf
->data
, iob_len ( iobuf
) );
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",
108 usb_port_changed ( usb_port ( hub
, i
) );
114 /* Recycle I/O buffer */
115 usb_recycle ( &hubdev
->intr
, iobuf
);
117 /* Start refill process */
118 process_add ( &hubdev
->refill
);
121 /** Interrupt endpoint operations */
122 static struct usb_endpoint_driver_operations usb_hub_intr_operations
= {
123 .complete
= hub_complete
,
130 * @ret rc Return status code
132 static int hub_open ( struct usb_hub
*hub
) {
133 struct usb_hub_device
*hubdev
= usb_hub_get_drvdata ( hub
);
134 struct usb_device
*usb
= hubdev
->usb
;
138 /* Ensure ports are powered */
139 for ( i
= 1 ; i
<= hub
->ports
; i
++ ) {
140 if ( ( rc
= usb_hub_set_port_feature ( usb
, i
,
143 DBGC ( hubdev
, "HUB %s port %d could not apply power: "
144 "%s\n", hubdev
->name
, i
, strerror ( rc
) );
149 /* Open interrupt endpoint */
150 if ( ( rc
= usb_endpoint_open ( &hubdev
->intr
) ) != 0 ) {
151 DBGC ( hubdev
, "HUB %s could not register interrupt: %s\n",
152 hubdev
->name
, strerror ( rc
) );
156 /* Start refill process */
157 process_add ( &hubdev
->refill
);
159 /* Refill interrupt ring */
160 hub_refill ( hubdev
);
162 /* Delay to allow ports to stabilise on out-of-spec hubs */
163 if ( hubdev
->flags
& USB_HUB_SLOW_START
)
164 mdelay ( USB_HUB_SLOW_START_DELAY_MS
);
168 usb_endpoint_close ( &hubdev
->intr
);
179 static void hub_close ( struct usb_hub
*hub
) {
180 struct usb_hub_device
*hubdev
= usb_hub_get_drvdata ( hub
);
182 /* Close interrupt endpoint */
183 usb_endpoint_close ( &hubdev
->intr
);
185 /* Stop refill process */
186 process_del ( &hubdev
->refill
);
194 * @ret rc Return status code
196 static int hub_enable ( struct usb_hub
*hub
, struct usb_port
*port
) {
197 struct usb_hub_device
*hubdev
= usb_hub_get_drvdata ( hub
);
198 struct usb_device
*usb
= hubdev
->usb
;
199 struct usb_hub_port_status status
;
200 unsigned int current
;
204 /* Initiate reset if applicable */
205 if ( ( hub
->protocol
< USB_PROTO_3_0
) &&
206 ( ( rc
= usb_hub_set_port_feature ( usb
, port
->address
,
207 USB_HUB_PORT_RESET
, 0 ) )!=0)){
208 DBGC ( hubdev
, "HUB %s port %d could not initiate reset: %s\n",
209 hubdev
->name
, port
->address
, strerror ( rc
) );
213 /* Wait for port to become enabled */
214 for ( i
= 0 ; i
< USB_HUB_ENABLE_MAX_WAIT_MS
; i
++ ) {
216 /* Check for port being enabled */
217 if ( ( rc
= usb_hub_get_port_status ( usb
, port
->address
,
219 DBGC ( hubdev
, "HUB %s port %d could not get status: "
220 "%s\n", hubdev
->name
, port
->address
,
224 current
= le16_to_cpu ( status
.current
);
225 if ( current
& ( 1 << USB_HUB_PORT_ENABLE
) )
232 DBGC ( hubdev
, "HUB %s port %d timed out waiting for enable\n",
233 hubdev
->name
, port
->address
);
242 * @ret rc Return status code
244 static int hub_disable ( struct usb_hub
*hub
, struct usb_port
*port
) {
245 struct usb_hub_device
*hubdev
= usb_hub_get_drvdata ( hub
);
246 struct usb_device
*usb
= hubdev
->usb
;
250 if ( ( hub
->protocol
< USB_PROTO_3_0
) &&
251 ( ( rc
= usb_hub_clear_port_feature ( usb
, port
->address
,
254 DBGC ( hubdev
, "HUB %s port %d could not disable: %s\n",
255 hubdev
->name
, port
->address
, strerror ( rc
) );
263 * Clear port status change bits
265 * @v hubdev USB hub device
266 * @v port Port number
267 * @v changed Port status change bits
268 * @ret rc Return status code
270 static int hub_clear_changes ( struct usb_hub_device
*hubdev
,
271 unsigned int port
, uint16_t changed
) {
272 struct usb_device
*usb
= hubdev
->usb
;
274 unsigned int feature
;
277 /* Clear each set bit */
278 for ( bit
= 0 ; bit
< 16 ; bit
++ ) {
280 /* Skip unset bits */
281 if ( ! ( changed
& ( 1 << bit
) ) )
284 /* Skip unused features */
285 feature
= USB_HUB_C_FEATURE ( bit
);
286 if ( ! ( hubdev
->features
& ( 1 << feature
) ) )
290 if ( ( rc
= usb_hub_clear_port_feature ( usb
, port
,
291 feature
, 0 ) ) != 0 ) {
292 DBGC ( hubdev
, "HUB %s port %d could not clear feature "
293 "%d: %s\n", hubdev
->name
, port
, feature
,
307 * @ret rc Return status code
309 static int hub_speed ( struct usb_hub
*hub
, struct usb_port
*port
) {
310 struct usb_hub_device
*hubdev
= usb_hub_get_drvdata ( hub
);
311 struct usb_device
*usb
= hubdev
->usb
;
312 struct usb_hub_port_status status
;
313 unsigned int current
;
314 unsigned int changed
;
317 /* Get port status */
318 if ( ( rc
= usb_hub_get_port_status ( usb
, port
->address
,
320 DBGC ( hubdev
, "HUB %s port %d could not get status: %s\n",
321 hubdev
->name
, port
->address
, strerror ( rc
) );
324 current
= le16_to_cpu ( status
.current
);
325 changed
= le16_to_cpu ( status
.changed
);
326 DBGC2 ( hubdev
, "HUB %s port %d status is %04x:%04x\n",
327 hubdev
->name
, port
->address
, changed
, current
);
329 /* Update port speed */
330 if ( current
& ( 1 << USB_HUB_PORT_CONNECTION
) ) {
331 if ( hub
->protocol
>= USB_PROTO_3_0
) {
332 port
->speed
= USB_SPEED_SUPER
;
333 } else if ( current
& ( 1 << USB_HUB_PORT_LOW_SPEED
) ) {
334 port
->speed
= USB_SPEED_LOW
;
335 } else if ( current
& ( 1 << USB_HUB_PORT_HIGH_SPEED
) ) {
336 port
->speed
= USB_SPEED_HIGH
;
338 port
->speed
= USB_SPEED_FULL
;
341 port
->speed
= USB_SPEED_NONE
;
344 /* Record disconnections */
345 port
->disconnected
|= ( changed
& ( 1 << USB_HUB_PORT_CONNECTION
) );
347 /* Clear port status change bits */
348 if ( ( rc
= hub_clear_changes ( hubdev
, port
->address
, changed
) ) != 0)
355 * Clear transaction translator buffer
360 * @ret rc Return status code
362 static int hub_clear_tt ( struct usb_hub
*hub
, struct usb_port
*port
,
363 struct usb_endpoint
*ep
) {
364 struct usb_hub_device
*hubdev
= usb_hub_get_drvdata ( hub
);
365 struct usb_device
*usb
= hubdev
->usb
;
368 /* Clear transaction translator buffer. All hubs must support
369 * single-TT operation; we simplify our code by supporting
370 * only this configuration.
372 if ( ( rc
= usb_hub_clear_tt_buffer ( usb
, ep
->usb
->address
,
373 ep
->address
, ep
->attributes
,
374 USB_HUB_TT_SINGLE
) ) != 0 ) {
375 DBGC ( hubdev
, "HUB %s port %d could not clear TT buffer: %s\n",
376 hubdev
->name
, port
->address
, strerror ( rc
) );
383 /** USB hub operations */
384 static struct usb_hub_driver_operations hub_operations
= {
387 .enable
= hub_enable
,
388 .disable
= hub_disable
,
390 .clear_tt
= hub_clear_tt
,
396 * @v func USB function
397 * @v config Configuration descriptor
398 * @ret rc Return status code
400 static int hub_probe ( struct usb_function
*func
,
401 struct usb_configuration_descriptor
*config
) {
402 struct usb_device
*usb
= func
->usb
;
403 struct usb_bus
*bus
= usb
->port
->hub
->bus
;
404 struct usb_hub_device
*hubdev
;
405 struct usb_interface_descriptor
*interface
;
406 union usb_hub_descriptor desc
;
412 /* Allocate and initialise structure */
413 hubdev
= zalloc ( sizeof ( *hubdev
) );
418 enhanced
= ( usb
->port
->protocol
>= USB_PROTO_3_0
);
419 hubdev
->name
= func
->name
;
422 ( enhanced ? USB_HUB_FEATURES_ENHANCED
: USB_HUB_FEATURES
);
423 hubdev
->flags
= func
->id
->driver_data
;
424 usb_endpoint_init ( &hubdev
->intr
, usb
, &usb_hub_intr_operations
);
425 usb_refill_init ( &hubdev
->intr
, 0, 0, USB_HUB_INTR_FILL
);
426 process_init_stopped ( &hubdev
->refill
, &hub_refill_desc
, NULL
);
428 /* Locate hub interface descriptor */
429 interface
= usb_interface_descriptor ( config
, func
->interface
[0], 0 );
431 DBGC ( hubdev
, "HUB %s has no interface descriptor\n",
437 /* Locate interrupt endpoint descriptor */
438 if ( ( rc
= usb_endpoint_described ( &hubdev
->intr
, config
, interface
,
439 USB_INTERRUPT_IN
, 0 ) ) != 0 ) {
440 DBGC ( hubdev
, "HUB %s could not describe interrupt endpoint: "
441 "%s\n", hubdev
->name
, strerror ( rc
) );
446 depth
= usb_depth ( usb
);
448 if ( ( rc
= usb_hub_set_hub_depth ( usb
, depth
) ) != 0 ) {
449 DBGC ( hubdev
, "HUB %s could not set hub depth to %d: "
450 "%s\n", hubdev
->name
, depth
, strerror ( rc
) );
451 goto err_set_hub_depth
;
455 /* Get hub descriptor */
456 if ( ( rc
= usb_hub_get_descriptor ( usb
, enhanced
, &desc
) ) != 0 ) {
457 DBGC ( hubdev
, "HUB %s could not get hub descriptor: %s\n",
458 hubdev
->name
, strerror ( rc
) );
459 goto err_hub_descriptor
;
461 ports
= desc
.basic
.ports
;
462 DBGC ( hubdev
, "HUB %s has %d ports at depth %d%s\n", hubdev
->name
,
463 ports
, depth
, ( enhanced ?
" (enhanced)" : "" ) );
466 hubdev
->hub
= alloc_usb_hub ( bus
, usb
, ports
, &hub_operations
);
467 if ( ! hubdev
->hub
) {
471 usb_hub_set_drvdata ( hubdev
->hub
, hubdev
);
474 if ( ( rc
= register_usb_hub ( hubdev
->hub
) ) != 0 ) {
475 DBGC ( hubdev
, "HUB %s could not register: %s\n",
476 hubdev
->name
, strerror ( rc
) );
477 goto err_register_hub
;
480 usb_func_set_drvdata ( func
, hubdev
);
483 unregister_usb_hub ( hubdev
->hub
);
485 free_usb_hub ( hubdev
->hub
);
499 * @v func USB function
500 * @ret rc Return status code
502 static void hub_remove ( struct usb_function
*func
) {
503 struct usb_hub_device
*hubdev
= usb_func_get_drvdata ( func
);
504 struct usb_hub
*hub
= hubdev
->hub
;
505 struct usb_device
*usb
= hubdev
->usb
;
506 struct usb_port
*port
;
509 /* If hub has been unplugged, mark all ports as unplugged */
510 if ( usb
->port
->disconnected
) {
511 for ( i
= 1 ; i
<= hub
->ports
; i
++ ) {
512 port
= usb_port ( hub
, i
);
513 port
->disconnected
= 1;
514 port
->speed
= USB_SPEED_NONE
;
519 unregister_usb_hub ( hubdev
->hub
);
520 assert ( ! process_running ( &hubdev
->refill
) );
523 free_usb_hub ( hubdev
->hub
);
525 /* Free hub device */
529 /** USB hub device IDs */
530 static struct usb_device_id hub_ids
[] = {
532 .name
= "avocent-hub",
535 .driver_data
= USB_HUB_SLOW_START
,
539 .vendor
= USB_ANY_ID
,
540 .product
= USB_ANY_ID
,
544 /** USB hub driver */
545 struct usb_driver usb_hub_driver __usb_driver
= {
547 .id_count
= ( sizeof ( hub_ids
) / sizeof ( hub_ids
[0] ) ),
548 .class = USB_CLASS_ID ( USB_CLASS_HUB
, 0, USB_ANY_ID
),
549 .score
= USB_SCORE_NORMAL
,
551 .remove
= hub_remove
,