iPXE
usbhub.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <stdlib.h>
00027 #include <string.h>
00028 #include <unistd.h>
00029 #include <errno.h>
00030 #include <assert.h>
00031 #include <byteswap.h>
00032 #include <ipxe/usb.h>
00033 #include "usbhub.h"
00034 
00035 /** @file
00036  *
00037  * USB hub driver
00038  *
00039  */
00040 
00041 /**
00042  * Refill interrupt ring
00043  *
00044  * @v hubdev            Hub device
00045  */
00046 static void hub_refill ( struct usb_hub_device *hubdev ) {
00047         int rc;
00048 
00049         /* Refill interrupt endpoint */
00050         if ( ( rc = usb_refill ( &hubdev->intr ) ) != 0 ) {
00051                 DBGC ( hubdev, "HUB %s could not refill interrupt: %s\n",
00052                        hubdev->name, strerror ( rc ) );
00053                 /* Continue attempting to refill */
00054                 return;
00055         }
00056 
00057         /* Stop refill process */
00058         process_del ( &hubdev->refill );
00059 }
00060 
00061 /** Refill process descriptor */
00062 static struct process_descriptor hub_refill_desc =
00063         PROC_DESC ( struct usb_hub_device, refill, hub_refill );
00064 
00065 /**
00066  * Complete interrupt transfer
00067  *
00068  * @v ep                USB endpoint
00069  * @v iobuf             I/O buffer
00070  * @v rc                Completion status code
00071  */
00072 static void hub_complete ( struct usb_endpoint *ep,
00073                            struct io_buffer *iobuf, int rc ) {
00074         struct usb_hub_device *hubdev =
00075                 container_of ( ep, struct usb_hub_device, intr );
00076         struct usb_hub *hub = hubdev->hub;
00077         uint8_t *data = iobuf->data;
00078         unsigned int bits = ( 8 * iob_len ( iobuf ) );
00079         unsigned int i;
00080 
00081         /* Ignore packets cancelled when the endpoint closes */
00082         if ( ! ep->open )
00083                 goto done;
00084 
00085         /* Ignore packets with errors */
00086         if ( rc != 0 ) {
00087                 DBGC ( hubdev, "HUB %s interrupt failed: %s\n",
00088                        hubdev->name, strerror ( rc ) );
00089                 DBGC_HDA ( hubdev, 0, iobuf->data, iob_len ( iobuf ) );
00090                 goto done;
00091         }
00092 
00093         /* Report any port status changes */
00094         for ( i = 1 ; i <= hub->ports ; i++ ) {
00095 
00096                 /* Sanity check */
00097                 if ( i > bits ) {
00098                         DBGC ( hubdev, "HUB %s underlength interrupt:\n",
00099                                hubdev->name );
00100                         DBGC_HDA ( hubdev, 0, iobuf->data, iob_len ( iobuf ) );
00101                         goto done;
00102                 }
00103 
00104                 /* Report port status change if applicable */
00105                 if ( data[ i / 8 ] & ( 1 << ( i % 8 ) ) ) {
00106                         DBGC2 ( hubdev, "HUB %s port %d status changed\n",
00107                                 hubdev->name, i );
00108                         usb_port_changed ( usb_port ( hub, i ) );
00109                 }
00110         }
00111 
00112  done:
00113         /* Start refill process */
00114         process_add ( &hubdev->refill );
00115 }
00116 
00117 /** Interrupt endpoint operations */
00118 static struct usb_endpoint_driver_operations usb_hub_intr_operations = {
00119         .complete = hub_complete,
00120 };
00121 
00122 /**
00123  * Open hub
00124  *
00125  * @v hub               USB hub
00126  * @ret rc              Return status code
00127  */
00128 static int hub_open ( struct usb_hub *hub ) {
00129         struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
00130         struct usb_device *usb = hubdev->usb;
00131         unsigned int i;
00132         int rc;
00133 
00134         /* Ensure ports are powered */
00135         for ( i = 1 ; i <= hub->ports ; i++ ) {
00136                 if ( ( rc = usb_hub_set_port_feature ( usb, i,
00137                                                        USB_HUB_PORT_POWER,
00138                                                        0 ) ) != 0 ) {
00139                         DBGC ( hubdev, "HUB %s port %d could not apply power: "
00140                                "%s\n", hubdev->name, i, strerror ( rc ) );
00141                         goto err_power;
00142                 }
00143         }
00144 
00145         /* Open interrupt endpoint */
00146         if ( ( rc = usb_endpoint_open ( &hubdev->intr ) ) != 0 ) {
00147                 DBGC ( hubdev, "HUB %s could not register interrupt: %s\n",
00148                        hubdev->name, strerror ( rc ) );
00149                 goto err_open;
00150         }
00151 
00152         /* Start refill process */
00153         process_add ( &hubdev->refill );
00154 
00155         /* Refill interrupt ring */
00156         hub_refill ( hubdev );
00157 
00158         /* Delay to allow ports to stabilise on out-of-spec hubs */
00159         if ( hubdev->flags & USB_HUB_SLOW_START )
00160                 mdelay ( USB_HUB_SLOW_START_DELAY_MS );
00161 
00162         return 0;
00163 
00164         usb_endpoint_close ( &hubdev->intr );
00165  err_open:
00166  err_power:
00167         return rc;
00168 }
00169 
00170 /**
00171  * Close hub
00172  *
00173  * @v hub               USB hub
00174  */
00175 static void hub_close ( struct usb_hub *hub ) {
00176         struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
00177 
00178         /* Close interrupt endpoint */
00179         usb_endpoint_close ( &hubdev->intr );
00180 
00181         /* Stop refill process */
00182         process_del ( &hubdev->refill );
00183 }
00184 
00185 /**
00186  * Enable port
00187  *
00188  * @v hub               USB hub
00189  * @v port              USB port
00190  * @ret rc              Return status code
00191  */
00192 static int hub_enable ( struct usb_hub *hub, struct usb_port *port ) {
00193         struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
00194         struct usb_device *usb = hubdev->usb;
00195         struct usb_hub_port_status status;
00196         unsigned int current;
00197         unsigned int i;
00198         int rc;
00199 
00200         /* Initiate reset if applicable */
00201         if ( ( hub->protocol < USB_PROTO_3_0 ) &&
00202              ( ( rc = usb_hub_set_port_feature ( usb, port->address,
00203                                                  USB_HUB_PORT_RESET, 0 ) )!=0)){
00204                 DBGC ( hubdev, "HUB %s port %d could not initiate reset: %s\n",
00205                        hubdev->name, port->address, strerror ( rc ) );
00206                 return rc;
00207         }
00208 
00209         /* Wait for port to become enabled */
00210         for ( i = 0 ; i < USB_HUB_ENABLE_MAX_WAIT_MS ; i++ ) {
00211 
00212                 /* Check for port being enabled */
00213                 if ( ( rc = usb_hub_get_port_status ( usb, port->address,
00214                                                       &status ) ) != 0 ) {
00215                         DBGC ( hubdev, "HUB %s port %d could not get status: "
00216                                "%s\n", hubdev->name, port->address,
00217                                strerror ( rc ) );
00218                         return rc;
00219                 }
00220                 current = le16_to_cpu ( status.current );
00221                 if ( current & ( 1 << USB_HUB_PORT_ENABLE ) )
00222                         return 0;
00223 
00224                 /* Delay */
00225                 mdelay ( 1 );
00226         }
00227 
00228         DBGC ( hubdev, "HUB %s port %d timed out waiting for enable\n",
00229                hubdev->name, port->address );
00230         return -ETIMEDOUT;
00231 }
00232 
00233 /**
00234  * Disable port
00235  *
00236  * @v hub               USB hub
00237  * @v port              USB port
00238  * @ret rc              Return status code
00239  */
00240 static int hub_disable ( struct usb_hub *hub, struct usb_port *port ) {
00241         struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
00242         struct usb_device *usb = hubdev->usb;
00243         int rc;
00244 
00245         /* Disable port */
00246         if ( ( rc = usb_hub_clear_port_feature ( usb, port->address,
00247                                                  USB_HUB_PORT_ENABLE, 0 ) )!=0){
00248                 DBGC ( hubdev, "HUB %s port %d could not disable: %s\n",
00249                        hubdev->name, port->address, strerror ( rc ) );
00250                 return rc;
00251         }
00252 
00253         return 0;
00254 }
00255 
00256 /**
00257  * Clear port status change bits
00258  *
00259  * @v hubdev            USB hub device
00260  * @v port              Port number
00261  * @v changed           Port status change bits
00262  * @ret rc              Return status code
00263  */
00264 static int hub_clear_changes ( struct usb_hub_device *hubdev,
00265                                unsigned int port, uint16_t changed ) {
00266         struct usb_device *usb = hubdev->usb;
00267         unsigned int bit;
00268         unsigned int feature;
00269         int rc;
00270 
00271         /* Clear each set bit */
00272         for ( bit = 0 ; bit < 16 ; bit++ ) {
00273 
00274                 /* Skip unset bits */
00275                 if ( ! ( changed & ( 1 << bit ) ) )
00276                         continue;
00277 
00278                 /* Skip unused features */
00279                 feature = USB_HUB_C_FEATURE ( bit );
00280                 if ( ! ( hubdev->features & ( 1 << feature ) ) )
00281                         continue;
00282 
00283                 /* Clear bit */
00284                 if ( ( rc = usb_hub_clear_port_feature ( usb, port,
00285                                                          feature, 0 ) ) != 0 ) {
00286                         DBGC ( hubdev, "HUB %s port %d could not clear feature "
00287                                "%d: %s\n", hubdev->name, port, feature,
00288                                strerror ( rc ) );
00289                         return rc;
00290                 }
00291         }
00292 
00293         return 0;
00294 }
00295 
00296 /**
00297  * Update port speed
00298  *
00299  * @v hub               USB hub
00300  * @v port              USB port
00301  * @ret rc              Return status code
00302  */
00303 static int hub_speed ( struct usb_hub *hub, struct usb_port *port ) {
00304         struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
00305         struct usb_device *usb = hubdev->usb;
00306         struct usb_hub_port_status status;
00307         unsigned int current;
00308         unsigned int changed;
00309         int rc;
00310 
00311         /* Get port status */
00312         if ( ( rc = usb_hub_get_port_status ( usb, port->address,
00313                                               &status ) ) != 0 ) {
00314                 DBGC ( hubdev, "HUB %s port %d could not get status: %s\n",
00315                        hubdev->name, port->address, strerror ( rc ) );
00316                 return rc;
00317         }
00318         current = le16_to_cpu ( status.current );
00319         changed = le16_to_cpu ( status.changed );
00320         DBGC2 ( hubdev, "HUB %s port %d status is %04x:%04x\n",
00321                 hubdev->name, port->address, changed, current );
00322 
00323         /* Update port speed */
00324         if ( current & ( 1 << USB_HUB_PORT_CONNECTION ) ) {
00325                 if ( hub->protocol >= USB_PROTO_3_0 ) {
00326                         port->speed = USB_SPEED_SUPER;
00327                 } else if ( current & ( 1 << USB_HUB_PORT_LOW_SPEED ) ) {
00328                         port->speed = USB_SPEED_LOW;
00329                 } else if ( current & ( 1 << USB_HUB_PORT_HIGH_SPEED ) ) {
00330                         port->speed = USB_SPEED_HIGH;
00331                 } else {
00332                         port->speed = USB_SPEED_FULL;
00333                 }
00334         } else {
00335                 port->speed = USB_SPEED_NONE;
00336         }
00337 
00338         /* Record disconnections */
00339         port->disconnected |= ( changed & ( 1 << USB_HUB_PORT_CONNECTION ) );
00340 
00341         /* Clear port status change bits */
00342         if ( ( rc = hub_clear_changes ( hubdev, port->address, changed ) ) != 0)
00343                 return rc;
00344 
00345         return 0;
00346 }
00347 
00348 /**
00349  * Clear transaction translator buffer
00350  *
00351  * @v hub               USB hub
00352  * @v port              USB port
00353  * @v ep                USB endpoint
00354  * @ret rc              Return status code
00355  */
00356 static int hub_clear_tt ( struct usb_hub *hub, struct usb_port *port,
00357                           struct usb_endpoint *ep ) {
00358         struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
00359         struct usb_device *usb = hubdev->usb;
00360         int rc;
00361 
00362         /* Clear transaction translator buffer.  All hubs must support
00363          * single-TT operation; we simplify our code by supporting
00364          * only this configuration.
00365          */
00366         if ( ( rc = usb_hub_clear_tt_buffer ( usb, ep->usb->address,
00367                                               ep->address, ep->attributes,
00368                                               USB_HUB_TT_SINGLE ) ) != 0 ) {
00369                 DBGC ( hubdev, "HUB %s port %d could not clear TT buffer: %s\n",
00370                        hubdev->name, port->address, strerror ( rc ) );
00371                 return rc;
00372         }
00373 
00374         return 0;
00375 }
00376 
00377 /** USB hub operations */
00378 static struct usb_hub_driver_operations hub_operations = {
00379         .open = hub_open,
00380         .close = hub_close,
00381         .enable = hub_enable,
00382         .disable = hub_disable,
00383         .speed = hub_speed,
00384         .clear_tt = hub_clear_tt,
00385 };
00386 
00387 /**
00388  * Probe USB hub
00389  *
00390  * @v func              USB function
00391  * @v config            Configuration descriptor
00392  * @ret rc              Return status code
00393  */
00394 static int hub_probe ( struct usb_function *func,
00395                        struct usb_configuration_descriptor *config ) {
00396         struct usb_device *usb = func->usb;
00397         struct usb_bus *bus = usb->port->hub->bus;
00398         struct usb_hub_device *hubdev;
00399         struct usb_interface_descriptor *interface;
00400         union usb_hub_descriptor desc;
00401         unsigned int depth;
00402         unsigned int ports;
00403         int enhanced;
00404         int rc;
00405 
00406         /* Allocate and initialise structure */
00407         hubdev = zalloc ( sizeof ( *hubdev ) );
00408         if ( ! hubdev ) {
00409                 rc = -ENOMEM;
00410                 goto err_alloc;
00411         }
00412         enhanced = ( usb->port->protocol >= USB_PROTO_3_0 );
00413         hubdev->name = func->name;
00414         hubdev->usb = usb;
00415         hubdev->features =
00416                 ( enhanced ? USB_HUB_FEATURES_ENHANCED : USB_HUB_FEATURES );
00417         hubdev->flags = func->id->driver_data;
00418         usb_endpoint_init ( &hubdev->intr, usb, &usb_hub_intr_operations );
00419         usb_refill_init ( &hubdev->intr, 0, 0, USB_HUB_INTR_FILL );
00420         process_init_stopped ( &hubdev->refill, &hub_refill_desc, NULL );
00421 
00422         /* Locate hub interface descriptor */
00423         interface = usb_interface_descriptor ( config, func->interface[0], 0 );
00424         if ( ! interface ) {
00425                 DBGC ( hubdev, "HUB %s has no interface descriptor\n",
00426                        hubdev->name );
00427                 rc = -EINVAL;
00428                 goto err_interface;
00429         }
00430 
00431         /* Locate interrupt endpoint descriptor */
00432         if ( ( rc = usb_endpoint_described ( &hubdev->intr, config, interface,
00433                                              USB_INTERRUPT_IN, 0 ) ) != 0 ) {
00434                 DBGC ( hubdev, "HUB %s could not describe interrupt endpoint: "
00435                        "%s\n", hubdev->name, strerror ( rc ) );
00436                 goto err_endpoint;
00437         }
00438 
00439         /* Set hub depth */
00440         depth = usb_depth ( usb );
00441         if ( enhanced ) {
00442                 if ( ( rc = usb_hub_set_hub_depth ( usb, depth ) ) != 0 ) {
00443                         DBGC ( hubdev, "HUB %s could not set hub depth to %d: "
00444                                "%s\n", hubdev->name, depth, strerror ( rc ) );
00445                         goto err_set_hub_depth;
00446                 }
00447         }
00448 
00449         /* Get hub descriptor */
00450         if ( ( rc = usb_hub_get_descriptor ( usb, enhanced, &desc ) ) != 0 ) {
00451                 DBGC ( hubdev, "HUB %s could not get hub descriptor: %s\n",
00452                        hubdev->name, strerror ( rc ) );
00453                 goto err_hub_descriptor;
00454         }
00455         ports = desc.basic.ports;
00456         DBGC ( hubdev, "HUB %s has %d ports at depth %d%s\n", hubdev->name,
00457                ports, depth, ( enhanced ? " (enhanced)" : "" ) );
00458 
00459         /* Allocate hub */
00460         hubdev->hub = alloc_usb_hub ( bus, usb, ports, &hub_operations );
00461         if ( ! hubdev->hub ) {
00462                 rc = -ENOMEM;
00463                 goto err_alloc_hub;
00464         }
00465         usb_hub_set_drvdata ( hubdev->hub, hubdev );
00466 
00467         /* Register hub */
00468         if ( ( rc = register_usb_hub ( hubdev->hub ) ) != 0 ) {
00469                 DBGC ( hubdev, "HUB %s could not register: %s\n",
00470                        hubdev->name, strerror ( rc ) );
00471                 goto err_register_hub;
00472         }
00473 
00474         usb_func_set_drvdata ( func, hubdev );
00475         return 0;
00476 
00477         unregister_usb_hub ( hubdev->hub );
00478  err_register_hub:
00479         free_usb_hub ( hubdev->hub );
00480  err_alloc_hub:
00481  err_hub_descriptor:
00482  err_set_hub_depth:
00483  err_endpoint:
00484  err_interface:
00485         free ( hubdev );
00486  err_alloc:
00487         return rc;
00488 }
00489 
00490 /**
00491  * Remove USB hub
00492  *
00493  * @v func              USB function
00494  * @ret rc              Return status code
00495  */
00496 static void hub_remove ( struct usb_function *func ) {
00497         struct usb_hub_device *hubdev = usb_func_get_drvdata ( func );
00498         struct usb_hub *hub = hubdev->hub;
00499         struct usb_device *usb = hubdev->usb;
00500         struct usb_port *port;
00501         unsigned int i;
00502 
00503         /* If hub has been unplugged, mark all ports as unplugged */
00504         if ( usb->port->disconnected ) {
00505                 for ( i = 1 ; i <= hub->ports ; i++ ) {
00506                         port = usb_port ( hub, i );
00507                         port->disconnected = 1;
00508                         port->speed = USB_SPEED_NONE;
00509                 }
00510         }
00511 
00512         /* Unregister hub */
00513         unregister_usb_hub ( hubdev->hub );
00514         assert ( ! process_running ( &hubdev->refill ) );
00515 
00516         /* Free hub */
00517         free_usb_hub ( hubdev->hub );
00518 
00519         /* Free hub device */
00520         free ( hubdev );
00521 }
00522 
00523 /** USB hub device IDs */
00524 static struct usb_device_id hub_ids[] = {
00525         {
00526                 .name = "avocent-hub",
00527                 .vendor = 0x0624,
00528                 .product = 0x0248,
00529                 .driver_data = USB_HUB_SLOW_START,
00530         },
00531         {
00532                 .name = "hub",
00533                 .vendor = USB_ANY_ID,
00534                 .product = USB_ANY_ID,
00535         },
00536 };
00537 
00538 /** USB hub driver */
00539 struct usb_driver usb_hub_driver __usb_driver = {
00540         .ids = hub_ids,
00541         .id_count = ( sizeof ( hub_ids ) / sizeof ( hub_ids[0] ) ),
00542         .class = USB_CLASS_ID ( USB_CLASS_HUB, 0, USB_ANY_ID ),
00543         .score = USB_SCORE_NORMAL,
00544         .probe = hub_probe,
00545         .remove = hub_remove,
00546 };