iPXE
Functions | Variables
usbhub.c File Reference

USB hub driver. More...

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <byteswap.h>
#include <ipxe/usb.h>
#include "usbhub.h"

Go to the source code of this file.

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static void hub_refill (struct usb_hub_device *hubdev)
 Refill interrupt ring.
static void hub_complete (struct usb_endpoint *ep, struct io_buffer *iobuf, int rc)
 Complete interrupt transfer.
static int hub_open (struct usb_hub *hub)
 Open hub.
static void hub_close (struct usb_hub *hub)
 Close hub.
static int hub_enable (struct usb_hub *hub, struct usb_port *port)
 Enable port.
static int hub_disable (struct usb_hub *hub, struct usb_port *port)
 Disable port.
static int hub_clear_changes (struct usb_hub_device *hubdev, unsigned int port, uint16_t changed)
 Clear port status change bits.
static int hub_speed (struct usb_hub *hub, struct usb_port *port)
 Update port speed.
static int hub_clear_tt (struct usb_hub *hub, struct usb_port *port, struct usb_endpoint *ep)
 Clear transaction translator buffer.
static int hub_probe (struct usb_function *func, struct usb_configuration_descriptor *config)
 Probe USB hub.
static void hub_remove (struct usb_function *func)
 Remove USB hub.

Variables

static struct process_descriptor hub_refill_desc
 Refill process descriptor.
static struct
usb_endpoint_driver_operations 
usb_hub_intr_operations
 Interrupt endpoint operations.
static struct
usb_hub_driver_operations 
hub_operations
 USB hub operations.
static struct usb_device_id hub_ids []
 USB hub device IDs.
struct usb_driver usb_hub_driver __usb_driver
 USB hub driver.

Detailed Description

USB hub driver.

Definition in file usbhub.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static void hub_refill ( struct usb_hub_device hubdev) [static]

Refill interrupt ring.

Parameters:
hubdevHub device

Definition at line 46 of file usbhub.c.

References DBGC, usb_hub_device::intr, usb_hub_device::name, process_del(), rc, usb_hub_device::refill, strerror(), and usb_refill().

Referenced by hub_open().

                                                         {
        int rc;

        /* Refill interrupt endpoint */
        if ( ( rc = usb_refill ( &hubdev->intr ) ) != 0 ) {
                DBGC ( hubdev, "HUB %s could not refill interrupt: %s\n",
                       hubdev->name, strerror ( rc ) );
                /* Continue attempting to refill */
                return;
        }

        /* Stop refill process */
        process_del ( &hubdev->refill );
}
static void hub_complete ( struct usb_endpoint ep,
struct io_buffer iobuf,
int  rc 
) [static]

Complete interrupt transfer.

Parameters:
epUSB endpoint
iobufI/O buffer
rcCompletion status code

Definition at line 72 of file usbhub.c.

References container_of, io_buffer::data, data, DBGC, DBGC2, DBGC_HDA, done, usb_hub_device::hub, intr, iob_len(), usb_hub_device::name, usb_endpoint::open, usb_hub::ports, process_add(), usb_hub_device::refill, strerror(), and usb_port_changed().

                                                             {
        struct usb_hub_device *hubdev =
                container_of ( ep, struct usb_hub_device, intr );
        struct usb_hub *hub = hubdev->hub;
        uint8_t *data = iobuf->data;
        unsigned int bits = ( 8 * iob_len ( iobuf ) );
        unsigned int i;

        /* Ignore packets cancelled when the endpoint closes */
        if ( ! ep->open )
                goto done;

        /* Ignore packets with errors */
        if ( rc != 0 ) {
                DBGC ( hubdev, "HUB %s interrupt failed: %s\n",
                       hubdev->name, strerror ( rc ) );
                DBGC_HDA ( hubdev, 0, iobuf->data, iob_len ( iobuf ) );
                goto done;
        }

        /* Report any port status changes */
        for ( i = 1 ; i <= hub->ports ; i++ ) {

                /* Sanity check */
                if ( i > bits ) {
                        DBGC ( hubdev, "HUB %s underlength interrupt:\n",
                               hubdev->name );
                        DBGC_HDA ( hubdev, 0, iobuf->data, iob_len ( iobuf ) );
                        goto done;
                }

                /* Report port status change if applicable */
                if ( data[ i / 8 ] & ( 1 << ( i % 8 ) ) ) {
                        DBGC2 ( hubdev, "HUB %s port %d status changed\n",
                                hubdev->name, i );
                        usb_port_changed ( usb_port ( hub, i ) );
                }
        }

 done:
        /* Start refill process */
        process_add ( &hubdev->refill );
}
static int hub_open ( struct usb_hub hub) [static]

Open hub.

Parameters:
hubUSB hub
Return values:
rcReturn status code

Definition at line 128 of file usbhub.c.

References DBGC, usb_hub_device::flags, hub_refill(), usb_hub_device::intr, mdelay(), usb_hub_device::name, usb_hub::ports, process_add(), rc, usb_hub_device::refill, strerror(), usb_hub_device::usb, usb_endpoint_close(), usb_endpoint_open(), usb_hub_get_drvdata(), USB_HUB_PORT_POWER, usb_hub_set_port_feature(), USB_HUB_SLOW_START, and USB_HUB_SLOW_START_DELAY_MS.

                                            {
        struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
        struct usb_device *usb = hubdev->usb;
        unsigned int i;
        int rc;

        /* Ensure ports are powered */
        for ( i = 1 ; i <= hub->ports ; i++ ) {
                if ( ( rc = usb_hub_set_port_feature ( usb, i,
                                                       USB_HUB_PORT_POWER,
                                                       0 ) ) != 0 ) {
                        DBGC ( hubdev, "HUB %s port %d could not apply power: "
                               "%s\n", hubdev->name, i, strerror ( rc ) );
                        goto err_power;
                }
        }

        /* Open interrupt endpoint */
        if ( ( rc = usb_endpoint_open ( &hubdev->intr ) ) != 0 ) {
                DBGC ( hubdev, "HUB %s could not register interrupt: %s\n",
                       hubdev->name, strerror ( rc ) );
                goto err_open;
        }

        /* Start refill process */
        process_add ( &hubdev->refill );

        /* Refill interrupt ring */
        hub_refill ( hubdev );

        /* Delay to allow ports to stabilise on out-of-spec hubs */
        if ( hubdev->flags & USB_HUB_SLOW_START )
                mdelay ( USB_HUB_SLOW_START_DELAY_MS );

        return 0;

        usb_endpoint_close ( &hubdev->intr );
 err_open:
 err_power:
        return rc;
}
static void hub_close ( struct usb_hub hub) [static]

Close hub.

Parameters:
hubUSB hub

Definition at line 175 of file usbhub.c.

References usb_hub_device::intr, process_del(), usb_hub_device::refill, usb_endpoint_close(), and usb_hub_get_drvdata().

                                              {
        struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );

        /* Close interrupt endpoint */
        usb_endpoint_close ( &hubdev->intr );

        /* Stop refill process */
        process_del ( &hubdev->refill );
}
static int hub_enable ( struct usb_hub hub,
struct usb_port port 
) [static]

Enable port.

Parameters:
hubUSB hub
portUSB port
Return values:
rcReturn status code

Definition at line 192 of file usbhub.c.

References usb_port::address, usb_hub_port_status::current, DBGC, ETIMEDOUT, le16_to_cpu, mdelay(), usb_hub_device::name, usb_hub::protocol, rc, strerror(), usb_hub_device::usb, USB_HUB_ENABLE_MAX_WAIT_MS, usb_hub_get_drvdata(), usb_hub_get_port_status(), USB_HUB_PORT_ENABLE, USB_HUB_PORT_RESET, usb_hub_set_port_feature(), and USB_PROTO_3_0.

                                                                     {
        struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
        struct usb_device *usb = hubdev->usb;
        struct usb_hub_port_status status;
        unsigned int current;
        unsigned int i;
        int rc;

        /* Initiate reset if applicable */
        if ( ( hub->protocol < USB_PROTO_3_0 ) &&
             ( ( rc = usb_hub_set_port_feature ( usb, port->address,
                                                 USB_HUB_PORT_RESET, 0 ) )!=0)){
                DBGC ( hubdev, "HUB %s port %d could not initiate reset: %s\n",
                       hubdev->name, port->address, strerror ( rc ) );
                return rc;
        }

        /* Wait for port to become enabled */
        for ( i = 0 ; i < USB_HUB_ENABLE_MAX_WAIT_MS ; i++ ) {

                /* Check for port being enabled */
                if ( ( rc = usb_hub_get_port_status ( usb, port->address,
                                                      &status ) ) != 0 ) {
                        DBGC ( hubdev, "HUB %s port %d could not get status: "
                               "%s\n", hubdev->name, port->address,
                               strerror ( rc ) );
                        return rc;
                }
                current = le16_to_cpu ( status.current );
                if ( current & ( 1 << USB_HUB_PORT_ENABLE ) )
                        return 0;

                /* Delay */
                mdelay ( 1 );
        }

        DBGC ( hubdev, "HUB %s port %d timed out waiting for enable\n",
               hubdev->name, port->address );
        return -ETIMEDOUT;
}
static int hub_disable ( struct usb_hub hub,
struct usb_port port 
) [static]

Disable port.

Parameters:
hubUSB hub
portUSB port
Return values:
rcReturn status code

Definition at line 240 of file usbhub.c.

References usb_port::address, DBGC, usb_hub_device::name, rc, strerror(), usb_hub_device::usb, usb_hub_clear_port_feature(), usb_hub_get_drvdata(), and USB_HUB_PORT_ENABLE.

                                                                      {
        struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
        struct usb_device *usb = hubdev->usb;
        int rc;

        /* Disable port */
        if ( ( rc = usb_hub_clear_port_feature ( usb, port->address,
                                                 USB_HUB_PORT_ENABLE, 0 ) )!=0){
                DBGC ( hubdev, "HUB %s port %d could not disable: %s\n",
                       hubdev->name, port->address, strerror ( rc ) );
                return rc;
        }

        return 0;
}
static int hub_clear_changes ( struct usb_hub_device hubdev,
unsigned int  port,
uint16_t  changed 
) [static]

Clear port status change bits.

Parameters:
hubdevUSB hub device
portPort number
changedPort status change bits
Return values:
rcReturn status code

Definition at line 264 of file usbhub.c.

References DBGC, feature, usb_hub_device::features, usb_hub_device::name, rc, strerror(), usb_hub_device::usb, USB_HUB_C_FEATURE, and usb_hub_clear_port_feature().

Referenced by hub_speed().

                                                                     {
        struct usb_device *usb = hubdev->usb;
        unsigned int bit;
        unsigned int feature;
        int rc;

        /* Clear each set bit */
        for ( bit = 0 ; bit < 16 ; bit++ ) {

                /* Skip unset bits */
                if ( ! ( changed & ( 1 << bit ) ) )
                        continue;

                /* Skip unused features */
                feature = USB_HUB_C_FEATURE ( bit );
                if ( ! ( hubdev->features & ( 1 << feature ) ) )
                        continue;

                /* Clear bit */
                if ( ( rc = usb_hub_clear_port_feature ( usb, port,
                                                         feature, 0 ) ) != 0 ) {
                        DBGC ( hubdev, "HUB %s port %d could not clear feature "
                               "%d: %s\n", hubdev->name, port, feature,
                               strerror ( rc ) );
                        return rc;
                }
        }

        return 0;
}
static int hub_speed ( struct usb_hub hub,
struct usb_port port 
) [static]

Update port speed.

Parameters:
hubUSB hub
portUSB port
Return values:
rcReturn status code

Definition at line 303 of file usbhub.c.

References usb_port::address, usb_hub_port_status::changed, usb_hub_port_status::current, DBGC, DBGC2, usb_port::disconnected, hub_clear_changes(), le16_to_cpu, usb_hub_device::name, usb_hub::protocol, rc, usb_port::speed, strerror(), usb_hub_device::usb, usb_hub_get_drvdata(), usb_hub_get_port_status(), USB_HUB_PORT_CONNECTION, USB_HUB_PORT_HIGH_SPEED, USB_HUB_PORT_LOW_SPEED, USB_PROTO_3_0, USB_SPEED_FULL, USB_SPEED_HIGH, USB_SPEED_LOW, USB_SPEED_NONE, and USB_SPEED_SUPER.

                                                                    {
        struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
        struct usb_device *usb = hubdev->usb;
        struct usb_hub_port_status status;
        unsigned int current;
        unsigned int changed;
        int rc;

        /* Get port status */
        if ( ( rc = usb_hub_get_port_status ( usb, port->address,
                                              &status ) ) != 0 ) {
                DBGC ( hubdev, "HUB %s port %d could not get status: %s\n",
                       hubdev->name, port->address, strerror ( rc ) );
                return rc;
        }
        current = le16_to_cpu ( status.current );
        changed = le16_to_cpu ( status.changed );
        DBGC2 ( hubdev, "HUB %s port %d status is %04x:%04x\n",
                hubdev->name, port->address, changed, current );

        /* Update port speed */
        if ( current & ( 1 << USB_HUB_PORT_CONNECTION ) ) {
                if ( hub->protocol >= USB_PROTO_3_0 ) {
                        port->speed = USB_SPEED_SUPER;
                } else if ( current & ( 1 << USB_HUB_PORT_LOW_SPEED ) ) {
                        port->speed = USB_SPEED_LOW;
                } else if ( current & ( 1 << USB_HUB_PORT_HIGH_SPEED ) ) {
                        port->speed = USB_SPEED_HIGH;
                } else {
                        port->speed = USB_SPEED_FULL;
                }
        } else {
                port->speed = USB_SPEED_NONE;
        }

        /* Record disconnections */
        port->disconnected |= ( changed & ( 1 << USB_HUB_PORT_CONNECTION ) );

        /* Clear port status change bits */
        if ( ( rc = hub_clear_changes ( hubdev, port->address, changed ) ) != 0)
                return rc;

        return 0;
}
static int hub_clear_tt ( struct usb_hub hub,
struct usb_port port,
struct usb_endpoint ep 
) [static]

Clear transaction translator buffer.

Parameters:
hubUSB hub
portUSB port
epUSB endpoint
Return values:
rcReturn status code

Definition at line 356 of file usbhub.c.

References usb_endpoint::address, usb_device::address, usb_port::address, usb_endpoint::attributes, DBGC, usb_hub_device::name, rc, strerror(), usb_hub_device::usb, usb_endpoint::usb, usb_hub_clear_tt_buffer(), usb_hub_get_drvdata(), and USB_HUB_TT_SINGLE.

                                                    {
        struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
        struct usb_device *usb = hubdev->usb;
        int rc;

        /* Clear transaction translator buffer.  All hubs must support
         * single-TT operation; we simplify our code by supporting
         * only this configuration.
         */
        if ( ( rc = usb_hub_clear_tt_buffer ( usb, ep->usb->address,
                                              ep->address, ep->attributes,
                                              USB_HUB_TT_SINGLE ) ) != 0 ) {
                DBGC ( hubdev, "HUB %s port %d could not clear TT buffer: %s\n",
                       hubdev->name, port->address, strerror ( rc ) );
                return rc;
        }

        return 0;
}
static int hub_probe ( struct usb_function func,
struct usb_configuration_descriptor config 
) [static]

Probe USB hub.

Parameters:
funcUSB function
configConfiguration descriptor
Return values:
rcReturn status code

Definition at line 394 of file usbhub.c.

References alloc_usb_hub(), usb_hub_descriptor::basic, bus, usb_hub::bus, DBGC, usb_device_id::driver_data, EINVAL, usb_hub_descriptor::enhanced, ENOMEM, usb_hub_device::features, usb_hub_device::flags, free, free_usb_hub(), usb_hub_device::hub, usb_port::hub, usb_function::id, usb_interface_descriptor::interface, usb_hub_device::intr, usb_hub_device::name, usb_function::name, NULL, usb_device::port, usb_hub_descriptor_basic::ports, process_init_stopped(), usb_port::protocol, rc, usb_hub_device::refill, register_usb_hub(), strerror(), unregister_usb_hub(), usb_hub_device::usb, usb_function::usb, usb_depth(), usb_endpoint_described(), usb_endpoint_init(), usb_func_set_drvdata(), USB_HUB_FEATURES, USB_HUB_FEATURES_ENHANCED, usb_hub_get_descriptor(), USB_HUB_INTR_FILL, usb_hub_set_drvdata(), usb_hub_set_hub_depth(), USB_INTERRUPT_IN, USB_PROTO_3_0, usb_refill_init(), and zalloc().

                                                                     {
        struct usb_device *usb = func->usb;
        struct usb_bus *bus = usb->port->hub->bus;
        struct usb_hub_device *hubdev;
        struct usb_interface_descriptor *interface;
        union usb_hub_descriptor desc;
        unsigned int depth;
        unsigned int ports;
        int enhanced;
        int rc;

        /* Allocate and initialise structure */
        hubdev = zalloc ( sizeof ( *hubdev ) );
        if ( ! hubdev ) {
                rc = -ENOMEM;
                goto err_alloc;
        }
        enhanced = ( usb->port->protocol >= USB_PROTO_3_0 );
        hubdev->name = func->name;
        hubdev->usb = usb;
        hubdev->features =
                ( enhanced ? USB_HUB_FEATURES_ENHANCED : USB_HUB_FEATURES );
        hubdev->flags = func->id->driver_data;
        usb_endpoint_init ( &hubdev->intr, usb, &usb_hub_intr_operations );
        usb_refill_init ( &hubdev->intr, 0, 0, USB_HUB_INTR_FILL );
        process_init_stopped ( &hubdev->refill, &hub_refill_desc, NULL );

        /* Locate hub interface descriptor */
        interface = usb_interface_descriptor ( config, func->interface[0], 0 );
        if ( ! interface ) {
                DBGC ( hubdev, "HUB %s has no interface descriptor\n",
                       hubdev->name );
                rc = -EINVAL;
                goto err_interface;
        }

        /* Locate interrupt endpoint descriptor */
        if ( ( rc = usb_endpoint_described ( &hubdev->intr, config, interface,
                                             USB_INTERRUPT_IN, 0 ) ) != 0 ) {
                DBGC ( hubdev, "HUB %s could not describe interrupt endpoint: "
                       "%s\n", hubdev->name, strerror ( rc ) );
                goto err_endpoint;
        }

        /* Set hub depth */
        depth = usb_depth ( usb );
        if ( enhanced ) {
                if ( ( rc = usb_hub_set_hub_depth ( usb, depth ) ) != 0 ) {
                        DBGC ( hubdev, "HUB %s could not set hub depth to %d: "
                               "%s\n", hubdev->name, depth, strerror ( rc ) );
                        goto err_set_hub_depth;
                }
        }

        /* Get hub descriptor */
        if ( ( rc = usb_hub_get_descriptor ( usb, enhanced, &desc ) ) != 0 ) {
                DBGC ( hubdev, "HUB %s could not get hub descriptor: %s\n",
                       hubdev->name, strerror ( rc ) );
                goto err_hub_descriptor;
        }
        ports = desc.basic.ports;
        DBGC ( hubdev, "HUB %s has %d ports at depth %d%s\n", hubdev->name,
               ports, depth, ( enhanced ? " (enhanced)" : "" ) );

        /* Allocate hub */
        hubdev->hub = alloc_usb_hub ( bus, usb, ports, &hub_operations );
        if ( ! hubdev->hub ) {
                rc = -ENOMEM;
                goto err_alloc_hub;
        }
        usb_hub_set_drvdata ( hubdev->hub, hubdev );

        /* Register hub */
        if ( ( rc = register_usb_hub ( hubdev->hub ) ) != 0 ) {
                DBGC ( hubdev, "HUB %s could not register: %s\n",
                       hubdev->name, strerror ( rc ) );
                goto err_register_hub;
        }

        usb_func_set_drvdata ( func, hubdev );
        return 0;

        unregister_usb_hub ( hubdev->hub );
 err_register_hub:
        free_usb_hub ( hubdev->hub );
 err_alloc_hub:
 err_hub_descriptor:
 err_set_hub_depth:
 err_endpoint:
 err_interface:
        free ( hubdev );
 err_alloc:
        return rc;
}
static void hub_remove ( struct usb_function func) [static]

Remove USB hub.

Parameters:
funcUSB function
Return values:
rcReturn status code

Definition at line 496 of file usbhub.c.

References assert, usb_port::disconnected, free, free_usb_hub(), usb_hub_device::hub, port, usb_device::port, usb_hub::ports, process_running(), usb_hub_device::refill, usb_port::speed, unregister_usb_hub(), usb_hub_device::usb, usb_func_get_drvdata(), usb_port(), and USB_SPEED_NONE.

                                                     {
        struct usb_hub_device *hubdev = usb_func_get_drvdata ( func );
        struct usb_hub *hub = hubdev->hub;
        struct usb_device *usb = hubdev->usb;
        struct usb_port *port;
        unsigned int i;

        /* If hub has been unplugged, mark all ports as unplugged */
        if ( usb->port->disconnected ) {
                for ( i = 1 ; i <= hub->ports ; i++ ) {
                        port = usb_port ( hub, i );
                        port->disconnected = 1;
                        port->speed = USB_SPEED_NONE;
                }
        }

        /* Unregister hub */
        unregister_usb_hub ( hubdev->hub );
        assert ( ! process_running ( &hubdev->refill ) );

        /* Free hub */
        free_usb_hub ( hubdev->hub );

        /* Free hub device */
        free ( hubdev );
}

Variable Documentation

Initial value:
        PROC_DESC ( struct usb_hub_device, refill, hub_refill )

Refill process descriptor.

Definition at line 62 of file usbhub.c.

Initial value:
 {
        .complete = hub_complete,
}

Interrupt endpoint operations.

Definition at line 118 of file usbhub.c.

Initial value:
 {
        .open = hub_open,
        .close = hub_close,
        .enable = hub_enable,
        .disable = hub_disable,
        .speed = hub_speed,
        .clear_tt = hub_clear_tt,
}

USB hub operations.

Definition at line 378 of file usbhub.c.

struct usb_device_id hub_ids[] [static]
Initial value:
 {
        {
                .name = "avocent-hub",
                .vendor = 0x0624,
                .product = 0x0248,
                .driver_data = USB_HUB_SLOW_START,
        },
        {
                .name = "hub",
                .vendor = USB_ANY_ID,
                .product = USB_ANY_ID,
        },
}

USB hub device IDs.

Definition at line 524 of file usbhub.c.

struct usb_driver usb_hub_driver __usb_driver
Initial value:
 {
        .ids = hub_ids,
        .id_count = ( sizeof ( hub_ids ) / sizeof ( hub_ids[0] ) ),
        .class = USB_CLASS_ID ( USB_CLASS_HUB, 0, USB_ANY_ID ),
        .score = USB_SCORE_NORMAL,
        .probe = hub_probe,
        .remove = hub_remove,
}

USB hub driver.

Definition at line 539 of file usbhub.c.