diff options
author | RajithaY <rajithax.yerrumsetty@intel.com> | 2017-04-25 03:31:15 -0700 |
---|---|---|
committer | Rajitha Yerrumchetty <rajithax.yerrumsetty@intel.com> | 2017-05-22 06:48:08 +0000 |
commit | bb756eebdac6fd24e8919e2c43f7d2c8c4091f59 (patch) | |
tree | ca11e03542edf2d8f631efeca5e1626d211107e3 /qemu/roms/ipxe/src/drivers/bus/usb.c | |
parent | a14b48d18a9ed03ec191cf16b162206998a895ce (diff) |
Adding qemu as a submodule of KVMFORNFV
This Patch includes the changes to add qemu as a submodule to
kvmfornfv repo and make use of the updated latest qemu for the
execution of all testcase
Change-Id: I1280af507a857675c7f81d30c95255635667bdd7
Signed-off-by:RajithaY<rajithax.yerrumsetty@intel.com>
Diffstat (limited to 'qemu/roms/ipxe/src/drivers/bus/usb.c')
-rw-r--r-- | qemu/roms/ipxe/src/drivers/bus/usb.c | 2128 |
1 files changed, 0 insertions, 2128 deletions
diff --git a/qemu/roms/ipxe/src/drivers/bus/usb.c b/qemu/roms/ipxe/src/drivers/bus/usb.c deleted file mode 100644 index 2019e3341..000000000 --- a/qemu/roms/ipxe/src/drivers/bus/usb.c +++ /dev/null @@ -1,2128 +0,0 @@ -/* - * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * You can also choose to distribute this program under the terms of - * the Unmodified Binary Distribution Licence (as given in the file - * COPYING.UBDL), provided that you have satisfied its requirements. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <strings.h> -#include <unistd.h> -#include <errno.h> -#include <assert.h> -#include <byteswap.h> -#include <ipxe/usb.h> -#include <ipxe/cdc.h> - -/** @file - * - * Universal Serial Bus (USB) - * - */ - -/** List of USB buses */ -struct list_head usb_buses = LIST_HEAD_INIT ( usb_buses ); - -/** List of changed ports */ -static struct list_head usb_changed = LIST_HEAD_INIT ( usb_changed ); - -/** List of halted endpoints */ -static struct list_head usb_halted = LIST_HEAD_INIT ( usb_halted ); - -/****************************************************************************** - * - * Utility functions - * - ****************************************************************************** - */ - -/** - * Get USB speed name (for debugging) - * - * @v speed Speed - * @ret name Speed name - */ -static inline const char * usb_speed_name ( unsigned int speed ) { - static const char *exponents[4] = { "", "k", "M", "G" }; - static char buf[ 10 /* "xxxxxXbps" + NUL */ ]; - unsigned int mantissa; - unsigned int exponent; - - /* Extract mantissa and exponent */ - mantissa = USB_SPEED_MANTISSA ( speed ); - exponent = USB_SPEED_EXPONENT ( speed ); - - /* Name speed */ - switch ( speed ) { - case USB_SPEED_NONE: return "DETACHED"; - case USB_SPEED_LOW: return "low"; - case USB_SPEED_FULL: return "full"; - case USB_SPEED_HIGH: return "high"; - case USB_SPEED_SUPER: return "super"; - default: - snprintf ( buf, sizeof ( buf ), "%d%sbps", - mantissa, exponents[exponent] ); - return buf; - } -} - -/** - * Transcribe USB BCD-coded value (for debugging) - * - * @v bcd BCD-coded value - * @ret string Transcribed value - */ -static inline const char * usb_bcd ( uint16_t bcd ) { - static char buf[ 6 /* "xx.xx" + NUL */ ]; - uint8_t high = ( bcd >> 8 ); - uint8_t low = ( bcd >> 0 ); - - snprintf ( buf, sizeof ( buf ), "%x.%02x", high, low ); - return buf; -} - -/****************************************************************************** - * - * USB descriptors - * - ****************************************************************************** - */ - -/** - * Locate USB interface association descriptor - * - * @v config Configuraton descriptor - * @v first First interface number - * @ret desc Interface association descriptor, or NULL if not found - */ -static struct usb_interface_association_descriptor * -usb_interface_association_descriptor ( struct usb_configuration_descriptor - *config, - unsigned int first ) { - struct usb_interface_association_descriptor *desc; - - /* Find a matching interface association descriptor */ - for_each_config_descriptor ( desc, config ) { - if ( ( desc->header.type == - USB_INTERFACE_ASSOCIATION_DESCRIPTOR ) && - ( desc->first == first ) ) - return desc; - } - return NULL; -} - -/** - * Locate USB interface descriptor - * - * @v config Configuraton descriptor - * @v interface Interface number - * @v alternate Alternate setting - * @ret desc Interface descriptor, or NULL if not found - */ -struct usb_interface_descriptor * -usb_interface_descriptor ( struct usb_configuration_descriptor *config, - unsigned int interface, unsigned int alternate ) { - struct usb_interface_descriptor *desc; - - /* Find a matching interface descriptor */ - for_each_config_descriptor ( desc, config ) { - if ( ( desc->header.type == USB_INTERFACE_DESCRIPTOR ) && - ( desc->interface == interface ) && - ( desc->alternate == alternate ) ) - return desc; - } - return NULL; -} - -/** - * Locate USB endpoint descriptor - * - * @v config Configuration descriptor - * @v interface Interface descriptor - * @v type Endpoint (internal) type - * @v index Endpoint index - * @ret desc Descriptor, or NULL if not found - */ -struct usb_endpoint_descriptor * -usb_endpoint_descriptor ( struct usb_configuration_descriptor *config, - struct usb_interface_descriptor *interface, - unsigned int type, unsigned int index ) { - struct usb_endpoint_descriptor *desc; - unsigned int attributes = ( type & USB_ENDPOINT_ATTR_TYPE_MASK ); - unsigned int direction = ( type & USB_DIR_IN ); - - /* Find a matching endpoint descriptor */ - for_each_interface_descriptor ( desc, config, interface ) { - if ( ( desc->header.type == USB_ENDPOINT_DESCRIPTOR ) && - ( ( desc->attributes & - USB_ENDPOINT_ATTR_TYPE_MASK ) == attributes ) && - ( ( desc->endpoint & USB_DIR_IN ) == direction ) && - ( index-- == 0 ) ) - return desc; - } - return NULL; -} - -/** - * Locate USB endpoint companion descriptor - * - * @v config Configuration descriptor - * @v desc Endpoint descriptor - * @ret descx Companion descriptor, or NULL if not found - */ -struct usb_endpoint_companion_descriptor * -usb_endpoint_companion_descriptor ( struct usb_configuration_descriptor *config, - struct usb_endpoint_descriptor *desc ) { - struct usb_endpoint_companion_descriptor *descx; - - /* Get companion descriptor, if present */ - descx = container_of ( usb_next_descriptor ( &desc->header ), - struct usb_endpoint_companion_descriptor, - header ); - return ( ( usb_is_within_config ( config, &descx->header ) && - descx->header.type == USB_ENDPOINT_COMPANION_DESCRIPTOR ) - ? descx : NULL ); -} - -/****************************************************************************** - * - * USB endpoint - * - ****************************************************************************** - */ - -/** - * Get USB endpoint name (for debugging) - * - * @v ep USB endpoint - * @ret name Endpoint name - */ -const char * usb_endpoint_name ( struct usb_endpoint *ep ) { - static char buf[ 9 /* "EPxx OUT" + NUL */ ]; - unsigned int address = ep->address; - - snprintf ( buf, sizeof ( buf ), "EP%d%s", - ( address & USB_ENDPOINT_MAX ), - ( address ? - ( ( address & USB_ENDPOINT_IN ) ? " IN" : " OUT" ) : "" )); - return buf; -} - -/** - * Describe USB endpoint from device configuration - * - * @v ep USB endpoint - * @v config Configuration descriptor - * @v interface Interface descriptor - * @v type Endpoint (internal) type - * @v index Endpoint index - * @ret rc Return status code - */ -int usb_endpoint_described ( struct usb_endpoint *ep, - struct usb_configuration_descriptor *config, - struct usb_interface_descriptor *interface, - unsigned int type, unsigned int index ) { - struct usb_device *usb = ep->usb; - struct usb_port *port = usb->port; - struct usb_endpoint_descriptor *desc; - struct usb_endpoint_companion_descriptor *descx; - unsigned int sizes; - unsigned int burst; - unsigned int interval; - size_t mtu; - - /* Locate endpoint descriptor */ - desc = usb_endpoint_descriptor ( config, interface, type, index ); - if ( ! desc ) - return -ENOENT; - - /* Locate companion descriptor, if any */ - descx = usb_endpoint_companion_descriptor ( config, desc ); - - /* Calculate MTU and burst size */ - sizes = le16_to_cpu ( desc->sizes ); - mtu = USB_ENDPOINT_MTU ( sizes ); - burst = ( descx ? descx->burst : USB_ENDPOINT_BURST ( sizes ) ); - - /* Calculate interval */ - if ( ( type & USB_ENDPOINT_ATTR_TYPE_MASK ) == - USB_ENDPOINT_ATTR_INTERRUPT ) { - if ( port->speed >= USB_SPEED_HIGH ) { - /* 2^(desc->interval-1) is a microframe count */ - interval = ( 1 << ( desc->interval - 1 ) ); - } else { - /* desc->interval is a (whole) frame count */ - interval = ( desc->interval << 3 ); - } - } else { - /* desc->interval is a microframe count */ - interval = desc->interval; - } - - /* Describe endpoint */ - usb_endpoint_describe ( ep, desc->endpoint, desc->attributes, - mtu, burst, interval ); - return 0; -} - -/** - * Open USB endpoint - * - * @v ep USB endpoint - * @ret rc Return status code - */ -int usb_endpoint_open ( struct usb_endpoint *ep ) { - struct usb_device *usb = ep->usb; - unsigned int idx = USB_ENDPOINT_IDX ( ep->address ); - int rc; - - /* Populate host controller operations */ - ep->host = &usb->port->hub->bus->op->endpoint; - - /* Add to endpoint list */ - if ( usb->ep[idx] != NULL ) { - DBGC ( usb, "USB %s %s is already open\n", - usb->name, usb_endpoint_name ( ep ) ); - rc = -EALREADY; - goto err_already; - } - usb->ep[idx] = ep; - INIT_LIST_HEAD ( &ep->halted ); - - /* Open endpoint */ - if ( ( rc = ep->host->open ( ep ) ) != 0 ) { - DBGC ( usb, "USB %s %s could not open: %s\n", usb->name, - usb_endpoint_name ( ep ), strerror ( rc ) ); - goto err_open; - } - ep->open = 1; - - DBGC2 ( usb, "USB %s %s opened with MTU %zd, burst %d, interval %d\n", - usb->name, usb_endpoint_name ( ep ), ep->mtu, ep->burst, - ep->interval ); - return 0; - - ep->open = 0; - ep->host->close ( ep ); - err_open: - usb->ep[idx] = NULL; - err_already: - if ( ep->max ) - usb_flush ( ep ); - return rc; -} - -/** - * Clear transaction translator (if applicable) - * - * @v ep USB endpoint - * @ret rc Return status code - */ -static int usb_endpoint_clear_tt ( struct usb_endpoint *ep ) { - struct usb_device *usb = ep->usb; - struct usb_port *tt; - int rc; - - /* Do nothing if this is a periodic endpoint */ - if ( ep->attributes & USB_ENDPOINT_ATTR_PERIODIC ) - return 0; - - /* Do nothing if this endpoint is not behind a transaction translator */ - tt = usb_transaction_translator ( usb ); - if ( ! tt ) - return 0; - - /* Clear transaction translator buffer */ - if ( ( rc = tt->hub->driver->clear_tt ( tt->hub, tt, ep ) ) != 0 ) { - DBGC ( usb, "USB %s %s could not clear transaction translator: " - "%s\n", usb->name, usb_endpoint_name ( ep ), - strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Close USB endpoint - * - * @v ep USB endpoint - */ -void usb_endpoint_close ( struct usb_endpoint *ep ) { - struct usb_device *usb = ep->usb; - unsigned int idx = USB_ENDPOINT_IDX ( ep->address ); - - /* Sanity checks */ - assert ( usb->ep[idx] == ep ); - - /* Close endpoint */ - ep->open = 0; - ep->host->close ( ep ); - assert ( ep->fill == 0 ); - - /* Remove from endpoint list */ - usb->ep[idx] = NULL; - list_del ( &ep->halted ); - - /* Discard any recycled buffers, if applicable */ - if ( ep->max ) - usb_flush ( ep ); - - /* Clear transaction translator, if applicable */ - usb_endpoint_clear_tt ( ep ); -} - -/** - * Reset USB endpoint - * - * @v ep USB endpoint - * @ret rc Return status code - */ -static int usb_endpoint_reset ( struct usb_endpoint *ep ) { - struct usb_device *usb = ep->usb; - unsigned int type; - int rc; - - /* Sanity check */ - assert ( ! list_empty ( &ep->halted ) ); - - /* Reset endpoint */ - if ( ( rc = ep->host->reset ( ep ) ) != 0 ) { - DBGC ( usb, "USB %s %s could not reset: %s\n", - usb->name, usb_endpoint_name ( ep ), strerror ( rc ) ); - return rc; - } - - /* Clear transaction translator, if applicable */ - if ( ( rc = usb_endpoint_clear_tt ( ep ) ) != 0 ) - return rc; - - /* Clear endpoint halt, if applicable */ - type = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); - if ( ( type != USB_ENDPOINT_ATTR_CONTROL ) && - ( ( rc = usb_clear_feature ( usb, USB_RECIP_ENDPOINT, - USB_ENDPOINT_HALT, - ep->address ) ) != 0 ) ) { - DBGC ( usb, "USB %s %s could not clear endpoint halt: %s\n", - usb->name, usb_endpoint_name ( ep ), strerror ( rc ) ); - return rc; - } - - /* Remove from list of halted endpoints */ - list_del ( &ep->halted ); - INIT_LIST_HEAD ( &ep->halted ); - - DBGC ( usb, "USB %s %s reset\n", - usb->name, usb_endpoint_name ( ep ) ); - return 0; -} - -/** - * Update endpoint MTU - * - * @v ep USB endpoint - * @v mtu New MTU - * @ret rc Return status code - */ -static int usb_endpoint_mtu ( struct usb_endpoint *ep, size_t mtu ) { - struct usb_device *usb = ep->usb; - int rc; - - /* Update MTU */ - ep->mtu = mtu; - if ( ( rc = ep->host->mtu ( ep ) ) != 0 ) { - DBGC ( usb, "USB %s %s could not update MTU: %s\n", - usb->name, usb_endpoint_name ( ep ), strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Enqueue USB message transfer - * - * @v ep USB endpoint - * @v request Request - * @v value Value parameter - * @v index Index parameter - * @v iobuf I/O buffer - * @ret rc Return status code - * - * The I/O buffer must have sufficient headroom to contain a setup - * packet. - */ -int usb_message ( struct usb_endpoint *ep, unsigned int request, - unsigned int value, unsigned int index, - struct io_buffer *iobuf ) { - struct usb_device *usb = ep->usb; - struct usb_port *port = usb->port; - struct usb_setup_packet *packet; - size_t len = iob_len ( iobuf ); - int rc; - - /* Sanity check */ - assert ( iob_headroom ( iobuf ) >= sizeof ( *packet ) ); - - /* Fail immediately if device has been unplugged */ - if ( port->speed == USB_SPEED_NONE ) - return -ENODEV; - - /* Reset endpoint if required */ - if ( ( ! list_empty ( &ep->halted ) ) && - ( ( rc = usb_endpoint_reset ( ep ) ) != 0 ) ) - return rc; - - /* Zero input data buffer (if applicable) */ - if ( request & USB_DIR_IN ) - memset ( iobuf->data, 0, len ); - - /* Construct setup packet */ - packet = iob_push ( iobuf, sizeof ( *packet ) ); - packet->request = cpu_to_le16 ( request ); - packet->value = cpu_to_le16 ( value ); - packet->index = cpu_to_le16 ( index ); - packet->len = cpu_to_le16 ( len ); - - /* Enqueue message transfer */ - if ( ( rc = ep->host->message ( ep, iobuf ) ) != 0 ) { - DBGC ( usb, "USB %s %s could not enqueue message transfer: " - "%s\n", usb->name, usb_endpoint_name ( ep ), - strerror ( rc ) ); - return rc; - } - - /* Increment fill level */ - ep->fill++; - - return 0; -} - -/** - * Enqueue USB stream transfer - * - * @v ep USB endpoint - * @v iobuf I/O buffer - * @v terminate Terminate using a short packet - * @ret rc Return status code - */ -int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf, - int terminate ) { - struct usb_device *usb = ep->usb; - struct usb_port *port = usb->port; - int rc; - - /* Fail immediately if device has been unplugged */ - if ( port->speed == USB_SPEED_NONE ) - return -ENODEV; - - /* Reset endpoint if required */ - if ( ( ! list_empty ( &ep->halted ) ) && - ( ( rc = usb_endpoint_reset ( ep ) ) != 0 ) ) - return rc; - - /* Enqueue stream transfer */ - if ( ( rc = ep->host->stream ( ep, iobuf, terminate ) ) != 0 ) { - DBGC ( usb, "USB %s %s could not enqueue stream transfer: %s\n", - usb->name, usb_endpoint_name ( ep ), strerror ( rc ) ); - return rc; - } - - /* Increment fill level */ - ep->fill++; - - return 0; -} - -/** - * Complete transfer (possibly with error) - * - * @v ep USB endpoint - * @v iobuf I/O buffer - * @v rc Completion status code - */ -void usb_complete_err ( struct usb_endpoint *ep, struct io_buffer *iobuf, - int rc ) { - struct usb_device *usb = ep->usb; - - /* Decrement fill level */ - assert ( ep->fill > 0 ); - ep->fill--; - - /* Schedule reset, if applicable */ - if ( ( rc != 0 ) && ep->open ) { - DBGC ( usb, "USB %s %s completion failed: %s\n", - usb->name, usb_endpoint_name ( ep ), strerror ( rc ) ); - list_del ( &ep->halted ); - list_add_tail ( &ep->halted, &usb_halted ); - } - - /* Report completion */ - ep->driver->complete ( ep, iobuf, rc ); -} - -/****************************************************************************** - * - * Endpoint refilling - * - ****************************************************************************** - */ - -/** - * Prefill endpoint recycled buffer list - * - * @v ep USB endpoint - * @ret rc Return status code - */ -int usb_prefill ( struct usb_endpoint *ep ) { - struct io_buffer *iobuf; - size_t len = ( ep->len ? ep->len : ep->mtu ); - unsigned int fill; - int rc; - - /* Sanity checks */ - assert ( ep->fill == 0 ); - assert ( ep->max > 0 ); - assert ( list_empty ( &ep->recycled ) ); - - /* Fill recycled buffer list */ - for ( fill = 0 ; fill < ep->max ; fill++ ) { - - /* Allocate I/O buffer */ - iobuf = alloc_iob ( len ); - if ( ! iobuf ) { - rc = -ENOMEM; - goto err_alloc; - } - - /* Add to recycled buffer list */ - list_add_tail ( &iobuf->list, &ep->recycled ); - } - - return 0; - - err_alloc: - usb_flush ( ep ); - return rc; -} - -/** - * Refill endpoint - * - * @v ep USB endpoint - * @ret rc Return status code - */ -int usb_refill ( struct usb_endpoint *ep ) { - struct io_buffer *iobuf; - size_t len = ( ep->len ? ep->len : ep->mtu ); - int rc; - - /* Sanity checks */ - assert ( ep->open ); - assert ( ep->max > 0 ); - - /* Refill endpoint */ - while ( ep->fill < ep->max ) { - - /* Get or allocate buffer */ - if ( list_empty ( &ep->recycled ) ) { - /* Recycled buffer list is empty; allocate new buffer */ - iobuf = alloc_iob ( len ); - if ( ! iobuf ) - return -ENOMEM; - } else { - /* Get buffer from recycled buffer list */ - iobuf = list_first_entry ( &ep->recycled, - struct io_buffer, list ); - assert ( iobuf != NULL ); - list_del ( &iobuf->list ); - } - - /* Reset buffer to maximum size */ - assert ( iob_len ( iobuf ) <= len ); - iob_put ( iobuf, ( len - iob_len ( iobuf ) ) ); - - /* Enqueue buffer */ - if ( ( rc = usb_stream ( ep, iobuf, 0 ) ) != 0 ) { - list_add ( &iobuf->list, &ep->recycled ); - return rc; - } - } - - return 0; -} - -/** - * Discard endpoint recycled buffer list - * - * @v ep USB endpoint - */ -void usb_flush ( struct usb_endpoint *ep ) { - struct io_buffer *iobuf; - struct io_buffer *tmp; - - /* Sanity checks */ - assert ( ! ep->open ); - assert ( ep->max > 0 ); - - /* Free all I/O buffers */ - list_for_each_entry_safe ( iobuf, tmp, &ep->recycled, list ) { - list_del ( &iobuf->list ); - free_iob ( iobuf ); - } -} - -/****************************************************************************** - * - * Control endpoint - * - ****************************************************************************** - */ - -/** USB control transfer pseudo-header */ -struct usb_control_pseudo_header { - /** Completion status */ - int rc; -}; - -/** - * Complete USB control transfer - * - * @v ep USB endpoint - * @v iobuf I/O buffer - * @v rc Completion status code - */ -static void usb_control_complete ( struct usb_endpoint *ep, - struct io_buffer *iobuf, int rc ) { - struct usb_device *usb = ep->usb; - struct usb_control_pseudo_header *pshdr; - - /* Record completion status in buffer */ - pshdr = iob_push ( iobuf, sizeof ( *pshdr ) ); - pshdr->rc = rc; - - /* Add to list of completed I/O buffers */ - list_add_tail ( &iobuf->list, &usb->complete ); -} - -/** USB control endpoint driver operations */ -static struct usb_endpoint_driver_operations usb_control_operations = { - .complete = usb_control_complete, -}; - -/** - * Issue USB control transaction - * - * @v usb USB device - * @v request Request - * @v value Value parameter - * @v index Index parameter - * @v data Data buffer (if any) - * @v len Length of data - * @ret rc Return status code - */ -int usb_control ( struct usb_device *usb, unsigned int request, - unsigned int value, unsigned int index, void *data, - size_t len ) { - struct usb_bus *bus = usb->port->hub->bus; - struct usb_endpoint *ep = &usb->control; - struct io_buffer *iobuf; - struct io_buffer *cmplt; - union { - struct usb_setup_packet setup; - struct usb_control_pseudo_header pshdr; - } *headroom; - struct usb_control_pseudo_header *pshdr; - unsigned int i; - int rc; - - /* Allocate I/O buffer */ - iobuf = alloc_iob ( sizeof ( *headroom ) + len ); - if ( ! iobuf ) { - rc = -ENOMEM; - goto err_alloc; - } - iob_reserve ( iobuf, sizeof ( *headroom ) ); - iob_put ( iobuf, len ); - if ( request & USB_DIR_IN ) { - memset ( data, 0, len ); - } else { - memcpy ( iobuf->data, data, len ); - } - - /* Enqueue message */ - if ( ( rc = usb_message ( ep, request, value, index, iobuf ) ) != 0 ) - goto err_message; - - /* Wait for completion */ - for ( i = 0 ; i < USB_CONTROL_MAX_WAIT_MS ; i++ ) { - - /* Poll bus */ - usb_poll ( bus ); - - /* Check for completion */ - while ( ( cmplt = list_first_entry ( &usb->complete, - struct io_buffer, - list ) ) ) { - - /* Remove from completion list */ - list_del ( &cmplt->list ); - - /* Extract and strip completion status */ - pshdr = cmplt->data; - iob_pull ( cmplt, sizeof ( *pshdr ) ); - rc = pshdr->rc; - - /* Discard stale completions */ - if ( cmplt != iobuf ) { - DBGC ( usb, "USB %s stale control completion: " - "%s\n", usb->name, strerror ( rc ) ); - DBGC_HDA ( usb, 0, cmplt->data, - iob_len ( cmplt ) ); - free_iob ( cmplt ); - continue; - } - - /* Fail immediately if completion was in error */ - if ( rc != 0 ) { - DBGC ( usb, "USB %s control %04x:%04x:%04x " - "failed: %s\n", usb->name, request, - value, index, strerror ( rc ) ); - free_iob ( cmplt ); - return rc; - } - - /* Copy completion to data buffer, if applicable */ - assert ( iob_len ( cmplt ) <= len ); - if ( request & USB_DIR_IN ) - memcpy ( data, cmplt->data, iob_len ( cmplt ) ); - free_iob ( cmplt ); - return 0; - } - - /* Delay */ - mdelay ( 1 ); - } - - DBGC ( usb, "USB %s timed out waiting for control %04x:%04x:%04x\n", - usb->name, request, value, index ); - return -ETIMEDOUT; - - err_message: - free_iob ( iobuf ); - err_alloc: - return rc; -} - -/** - * Get USB string descriptor - * - * @v usb USB device - * @v index String index - * @v language Language ID - * @v buf Data buffer - * @v len Length of buffer - * @ret len String length (excluding NUL), or negative error - */ -int usb_get_string_descriptor ( struct usb_device *usb, unsigned int index, - unsigned int language, char *buf, size_t len ) { - size_t max = ( len ? ( len - 1 /* NUL */ ) : 0 ); - struct { - struct usb_descriptor_header header; - uint16_t character[max]; - } __attribute__ (( packed )) *desc; - unsigned int actual; - unsigned int i; - int rc; - - /* Allocate buffer for string */ - desc = malloc ( sizeof ( *desc ) ); - if ( ! desc ) { - rc = -ENOMEM; - goto err_alloc; - } - - /* Get descriptor */ - if ( ( rc = usb_get_descriptor ( usb, 0, USB_STRING_DESCRIPTOR, index, - language, &desc->header, - sizeof ( *desc ) ) ) != 0 ) - goto err_get_descriptor; - - /* Copy to buffer */ - actual = ( ( desc->header.len - sizeof ( desc->header ) ) / - sizeof ( desc->character[0] ) ); - for ( i = 0 ; ( ( i < actual ) && ( i < max ) ) ; i++ ) - buf[i] = le16_to_cpu ( desc->character[i] ); - if ( len ) - buf[i] = '\0'; - - /* Free buffer */ - free ( desc ); - - return actual; - - err_get_descriptor: - free ( desc ); - err_alloc: - return rc; -} - -/****************************************************************************** - * - * USB device driver - * - ****************************************************************************** - */ - -/** - * Describe USB function - * - * @v func USB function - * @v config Configuration descriptor - * @v first First interface number - * @ret rc Return status code - */ -static int usb_function ( struct usb_function *func, - struct usb_configuration_descriptor *config, - unsigned int first ) { - struct usb_device *usb = func->usb; - struct usb_interface_association_descriptor *association; - struct usb_interface_descriptor *interface; - struct cdc_union_descriptor *cdc_union; - unsigned int i; - - /* First, look for an interface association descriptor */ - association = usb_interface_association_descriptor ( config, first ); - if ( association ) { - - /* Sanity check */ - if ( association->count > config->interfaces ) { - DBGC ( usb, "USB %s has invalid association [%d-%d)\n", - func->name, association->first, - ( association->first + association->count ) ); - return -ERANGE; - } - - /* Describe function */ - memcpy ( &func->class, &association->class, - sizeof ( func->class ) ); - func->count = association->count; - for ( i = 0 ; i < association->count ; i++ ) - func->interface[i] = ( association->first + i ); - return 0; - } - - /* Next, look for an interface descriptor */ - interface = usb_interface_descriptor ( config, first, 0 ); - if ( ! interface ) { - DBGC ( usb, "USB %s has no interface descriptor\n", - func->name ); - return -ENOENT; - } - - /* Describe function */ - memcpy ( &func->class, &interface->class, sizeof ( func->class ) ); - func->count = 1; - func->interface[0] = first; - - /* Look for a CDC union descriptor, if applicable */ - if ( ( func->class.class == USB_CLASS_CDC ) && - ( cdc_union = cdc_union_descriptor ( config, interface ) ) ) { - - /* Determine interface count */ - func->count = ( ( cdc_union->header.len - - offsetof ( typeof ( *cdc_union ), - interface[0] ) ) / - sizeof ( cdc_union->interface[0] ) ); - if ( func->count > config->interfaces ) { - DBGC ( usb, "USB %s has invalid union functional " - "descriptor with %d interfaces\n", - func->name, func->count ); - return -ERANGE; - } - - /* Describe function */ - for ( i = 0 ; i < func->count ; i++ ) - func->interface[i] = cdc_union->interface[i]; - - return 0; - } - - return 0; -} - -/** - * Check for a USB device ID match - * - * @v func USB function - * @v id Device ID - * @ret matches Device ID matches - */ -static int -usb_device_id_matches ( struct usb_function *func, struct usb_device_id *id ) { - - return ( ( ( id->vendor == func->dev.desc.vendor ) || - ( id->vendor == USB_ANY_ID ) ) && - ( ( id->product == func->dev.desc.device ) || - ( id->product == USB_ANY_ID ) ) && - ( id->class.class == func->class.class ) && - ( id->class.subclass == func->class.subclass ) && - ( id->class.protocol == func->class.protocol ) ); -} - -/** - * Probe USB device driver - * - * @v func USB function - * @v config Configuration descriptor - * @ret rc Return status code - */ -static int usb_probe ( struct usb_function *func, - struct usb_configuration_descriptor *config ) { - struct usb_device *usb = func->usb; - struct usb_driver *driver; - struct usb_device_id *id; - unsigned int i; - int rc; - - /* Look for a matching driver */ - for_each_table_entry ( driver, USB_DRIVERS ) { - for ( i = 0 ; i < driver->id_count ; i++ ) { - - /* Check for a matching ID */ - id = &driver->ids[i]; - if ( ! usb_device_id_matches ( func, id ) ) - continue; - - /* Probe driver */ - if ( ( rc = driver->probe ( func, config ) ) != 0 ) { - DBGC ( usb, "USB %s failed to probe driver %s: " - "%s\n", func->name, id->name, - strerror ( rc ) ); - /* Continue trying other drivers */ - continue; - } - - /* Record driver */ - func->driver = driver; - func->dev.driver_name = id->name; - return 0; - } - } - - /* No driver found */ - DBGC ( usb, "USB %s %04x:%04x class %d:%d:%d has no driver\n", - func->name, func->dev.desc.vendor, func->dev.desc.device, - func->class.class, func->class.subclass, func->class.protocol ); - return -ENOENT; -} - -/** - * Remove USB device driver - * - * @v func USB function - */ -static void usb_remove ( struct usb_function *func ) { - - /* Remove driver */ - func->driver->remove ( func ); -} - -/** - * Probe all USB device drivers - * - * @v usb USB device - * @v config Configuration descriptor - */ -static void -usb_probe_all ( struct usb_device *usb, - struct usb_configuration_descriptor *config ) { - struct usb_bus *bus = usb->port->hub->bus; - struct usb_function *func; - uint8_t used[config->interfaces]; - unsigned int first; - unsigned int i; - int rc; - - /* Identify each function in turn */ - memset ( used, 0, sizeof ( used ) ); - for ( first = 0 ; first < config->interfaces ; first++ ) { - - /* Skip interfaces already used */ - if ( used[first] ) - continue; - - /* Allocate and initialise structure */ - func = zalloc ( sizeof ( *func ) + - ( config->interfaces * - sizeof ( func->interface[0] ) ) ); - if ( ! func ) - goto err_alloc; - func->name = func->dev.name; - func->usb = usb; - func->dev.desc.bus_type = BUS_TYPE_USB; - func->dev.desc.location = usb->address; - func->dev.desc.vendor = le16_to_cpu ( usb->device.vendor ); - func->dev.desc.device = le16_to_cpu ( usb->device.product ); - snprintf ( func->dev.name, sizeof ( func->dev.name ), - "%s-%d.%d", usb->name, config->config, first ); - INIT_LIST_HEAD ( &func->dev.children ); - func->dev.parent = bus->dev; - - /* Identify function */ - if ( ( rc = usb_function ( func, config, first ) ) != 0 ) - goto err_function; - assert ( func->count <= config->interfaces ); - - /* Mark interfaces as used */ - for ( i = 0 ; i < func->count ; i++ ) { - if ( func->interface[i] >= config->interfaces ) { - DBGC ( usb, "USB %s has invalid interface %d\n", - func->name, func->interface[i] ); - goto err_interface; - } - used[ func->interface[i] ] = 1; - } - - /* Probe device driver */ - if ( ( rc = usb_probe ( func, config ) ) != 0 ) - goto err_probe; - DBGC ( usb, "USB %s %04x:%04x class %d:%d:%d interfaces ", - func->name, func->dev.desc.vendor, func->dev.desc.device, - func->class.class, func->class.subclass, - func->class.protocol ); - for ( i = 0 ; i < func->count ; i++ ) - DBGC ( usb, "%s%d", ( i ? "," : "" ), - func->interface[i] ); - DBGC ( usb, " using driver %s\n", func->dev.driver_name ); - - /* Add to list of functions */ - list_add ( &func->list, &usb->functions ); - - /* Add to device hierarchy */ - list_add_tail ( &func->dev.siblings, &bus->dev->children ); - - continue; - - list_del ( &func->dev.siblings ); - list_del ( &func->list ); - usb_remove ( func ); - err_probe: - free ( func ); - err_alloc: - err_interface: - err_function: - /* Continue registering other functions */ - continue; - } -} - -/** - * Remove all device drivers - * - * @v usb USB device - */ -static void usb_remove_all ( struct usb_device *usb ) { - struct usb_function *func; - struct usb_function *tmp; - - /* Remove all functions */ - list_for_each_entry_safe ( func, tmp, &usb->functions, list ) { - - /* Remove device driver */ - usb_remove ( func ); - - /* Remove from device hierarchy */ - assert ( list_empty ( &func->dev.children ) ); - list_del ( &func->dev.siblings ); - - /* Remove from list of functions */ - list_del ( &func->list ); - - /* Free function */ - free ( func ); - } -} - -/** - * Select USB device configuration - * - * @v usb USB device - * @v index Configuration index - * @ret rc Return status code - */ -static int usb_configure ( struct usb_device *usb, unsigned int index ) { - struct usb_configuration_descriptor partial; - struct usb_configuration_descriptor *config; - size_t len; - int rc; - - /* Read first part of configuration descriptor to get size */ - if ( ( rc = usb_get_config_descriptor ( usb, index, &partial, - sizeof ( partial ) ) ) != 0 ) { - DBGC ( usb, "USB %s could not get configuration descriptor %d: " - "%s\n", usb->name, index, strerror ( rc ) ); - goto err_get_partial; - } - len = le16_to_cpu ( partial.len ); - if ( len < sizeof ( partial ) ) { - DBGC ( usb, "USB %s underlength configuraton descriptor %d\n", - usb->name, index ); - rc = -EINVAL; - goto err_partial_len; - } - - /* Allocate buffer for whole configuration descriptor */ - config = malloc ( len ); - if ( ! config ) { - rc = -ENOMEM; - goto err_alloc_config; - } - - /* Read whole configuration descriptor */ - if ( ( rc = usb_get_config_descriptor ( usb, index, config, - len ) ) != 0 ) { - DBGC ( usb, "USB %s could not get configuration descriptor %d: " - "%s\n", usb->name, index, strerror ( rc ) ); - goto err_get_config_descriptor; - } - if ( config->len != partial.len ) { - DBGC ( usb, "USB %s bad configuration descriptor %d length\n", - usb->name, index ); - rc = -EINVAL; - goto err_config_len; - } - - /* Set configuration */ - if ( ( rc = usb_set_configuration ( usb, config->config ) ) != 0){ - DBGC ( usb, "USB %s could not set configuration %d: %s\n", - usb->name, config->config, strerror ( rc ) ); - goto err_set_configuration; - } - - /* Probe USB device drivers */ - usb_probe_all ( usb, config ); - - /* Free configuration descriptor */ - free ( config ); - - return 0; - - usb_remove_all ( usb ); - usb_set_configuration ( usb, 0 ); - err_set_configuration: - err_config_len: - err_get_config_descriptor: - free ( config ); - err_alloc_config: - err_partial_len: - err_get_partial: - return rc; -} - -/** - * Clear USB device configuration - * - * @v usb USB device - */ -static void usb_deconfigure ( struct usb_device *usb ) { - unsigned int i; - - /* Remove device drivers */ - usb_remove_all ( usb ); - - /* Sanity checks */ - for ( i = 0 ; i < ( sizeof ( usb->ep ) / sizeof ( usb->ep[0] ) ) ; i++){ - if ( i != USB_ENDPOINT_IDX ( USB_EP0_ADDRESS ) ) - assert ( usb->ep[i] == NULL ); - } - - /* Clear device configuration */ - usb_set_configuration ( usb, 0 ); -} - -/** - * Find and select a supported USB device configuration - * - * @v usb USB device - * @ret rc Return status code - */ -static int usb_configure_any ( struct usb_device *usb ) { - unsigned int index; - int rc = -ENOENT; - - /* Attempt all configuration indexes */ - for ( index = 0 ; index < usb->device.configurations ; index++ ) { - - /* Attempt this configuration index */ - if ( ( rc = usb_configure ( usb, index ) ) != 0 ) - continue; - - /* If we have no drivers, then try the next configuration */ - if ( list_empty ( &usb->functions ) ) { - rc = -ENOTSUP; - usb_deconfigure ( usb ); - continue; - } - - return 0; - } - - return rc; -} - -/****************************************************************************** - * - * USB device - * - ****************************************************************************** - */ - -/** - * Allocate USB device - * - * @v port USB port - * @ret usb USB device, or NULL on allocation failure - */ -static struct usb_device * alloc_usb ( struct usb_port *port ) { - struct usb_hub *hub = port->hub; - struct usb_bus *bus = hub->bus; - struct usb_device *usb; - - /* Allocate and initialise structure */ - usb = zalloc ( sizeof ( *usb ) ); - if ( ! usb ) - return NULL; - snprintf ( usb->name, sizeof ( usb->name ), "%s%c%d", hub->name, - ( hub->usb ? '.' : '-' ), port->address ); - usb->port = port; - INIT_LIST_HEAD ( &usb->functions ); - usb->host = &bus->op->device; - usb_endpoint_init ( &usb->control, usb, &usb_control_operations ); - INIT_LIST_HEAD ( &usb->complete ); - - return usb; -} - -/** - * Register USB device - * - * @v usb USB device - * @ret rc Return status code - */ -static int register_usb ( struct usb_device *usb ) { - struct usb_port *port = usb->port; - struct usb_hub *hub = port->hub; - struct usb_bus *bus = hub->bus; - unsigned int protocol; - size_t mtu; - int rc; - - /* Add to port */ - if ( port->usb != NULL ) { - DBGC ( hub, "USB hub %s port %d is already registered to %s\n", - hub->name, port->address, port->usb->name ); - rc = -EALREADY; - goto err_already; - } - port->usb = usb; - - /* Add to bus device list */ - list_add_tail ( &usb->list, &bus->devices ); - - /* Enable device */ - if ( ( rc = hub->driver->enable ( hub, port ) ) != 0 ) { - DBGC ( hub, "USB hub %s port %d could not enable: %s\n", - hub->name, port->address, strerror ( rc ) ); - goto err_enable; - } - - /* Allow recovery interval since port may have been reset */ - mdelay ( USB_RESET_RECOVER_DELAY_MS ); - - /* Get device speed */ - if ( ( rc = hub->driver->speed ( hub, port ) ) != 0 ) { - DBGC ( hub, "USB hub %s port %d could not get speed: %s\n", - hub->name, port->address, strerror ( rc ) ); - goto err_speed; - } - DBGC2 ( usb, "USB %s attached as %s-speed device\n", - usb->name, usb_speed_name ( port->speed ) ); - - /* Open device */ - if ( ( rc = usb->host->open ( usb ) ) != 0 ) { - DBGC ( usb, "USB %s could not open: %s\n", - usb->name, strerror ( rc ) ); - goto err_open; - } - - /* Describe control endpoint */ - mtu = USB_EP0_DEFAULT_MTU ( port->speed ); - usb_endpoint_describe ( &usb->control, USB_EP0_ADDRESS, - USB_EP0_ATTRIBUTES, mtu, USB_EP0_BURST, - USB_EP0_INTERVAL ); - - /* Open control endpoint */ - if ( ( rc = usb_endpoint_open ( &usb->control ) ) != 0 ) - goto err_open_control; - assert ( usb_endpoint ( usb, USB_EP0_ADDRESS ) == &usb->control ); - - /* Assign device address */ - if ( ( rc = usb->host->address ( usb ) ) != 0 ) { - DBGC ( usb, "USB %s could not set address: %s\n", - usb->name, strerror ( rc ) ); - goto err_address; - } - DBGC2 ( usb, "USB %s assigned address %d\n", usb->name, usb->address ); - - /* Allow recovery interval after Set Address command */ - mdelay ( USB_SET_ADDRESS_RECOVER_DELAY_MS ); - - /* Read first part of device descriptor to get EP0 MTU */ - if ( ( rc = usb_get_mtu ( usb, &usb->device ) ) != 0 ) { - DBGC ( usb, "USB %s could not get MTU: %s\n", - usb->name, strerror ( rc ) ); - goto err_get_mtu; - } - - /* Calculate EP0 MTU */ - protocol = le16_to_cpu ( usb->device.protocol ); - mtu = ( ( protocol < USB_PROTO_3_0 ) ? - usb->device.mtu : ( 1 << usb->device.mtu ) ); - DBGC2 ( usb, "USB %s has control MTU %zd (guessed %zd)\n", - usb->name, mtu, usb->control.mtu ); - - /* Update MTU */ - if ( ( rc = usb_endpoint_mtu ( &usb->control, mtu ) ) != 0 ) - goto err_mtu; - - /* Read whole device descriptor */ - if ( ( rc = usb_get_device_descriptor ( usb, &usb->device ) ) != 0 ) { - DBGC ( usb, "USB %s could not get device descriptor: %s\n", - usb->name, strerror ( rc ) ); - goto err_get_device_descriptor; - } - DBGC ( usb, "USB %s addr %d %04x:%04x class %d:%d:%d (v%s, %s-speed, " - "MTU %zd)\n", usb->name, usb->address, - le16_to_cpu ( usb->device.vendor ), - le16_to_cpu ( usb->device.product ), usb->device.class.class, - usb->device.class.subclass, usb->device.class.protocol, - usb_bcd ( le16_to_cpu ( usb->device.protocol ) ), - usb_speed_name ( port->speed ), usb->control.mtu ); - - /* Configure device */ - if ( ( rc = usb_configure_any ( usb ) ) != 0 ) - goto err_configure_any; - - return 0; - - usb_deconfigure ( usb ); - err_configure_any: - err_get_device_descriptor: - err_mtu: - err_get_mtu: - err_address: - usb_endpoint_close ( &usb->control ); - err_open_control: - usb->host->close ( usb ); - err_open: - err_speed: - hub->driver->disable ( hub, port ); - err_enable: - list_del ( &usb->list ); - port->usb = NULL; - err_already: - return rc; -} - -/** - * Unregister USB device - * - * @v usb USB device - */ -static void unregister_usb ( struct usb_device *usb ) { - struct usb_port *port = usb->port; - struct usb_hub *hub = port->hub; - struct io_buffer *iobuf; - struct io_buffer *tmp; - - /* Sanity checks */ - assert ( port->usb == usb ); - - /* Clear device configuration */ - usb_deconfigure ( usb ); - - /* Close control endpoint */ - usb_endpoint_close ( &usb->control ); - - /* Discard any stale control completions */ - list_for_each_entry_safe ( iobuf, tmp, &usb->complete, list ) { - list_del ( &iobuf->list ); - free_iob ( iobuf ); - } - - /* Close device */ - usb->host->close ( usb ); - - /* Disable port */ - hub->driver->disable ( hub, port ); - - /* Remove from bus device list */ - list_del ( &usb->list ); - - /* Remove from port */ - port->usb = NULL; -} - -/** - * Free USB device - * - * @v usb USB device - */ -static void free_usb ( struct usb_device *usb ) { - unsigned int i; - - /* Sanity checks */ - for ( i = 0 ; i < ( sizeof ( usb->ep ) / sizeof ( usb->ep[0] ) ) ; i++ ) - assert ( usb->ep[i] == NULL ); - assert ( list_empty ( &usb->functions ) ); - assert ( list_empty ( &usb->complete ) ); - - /* Free device */ - free ( usb ); -} - -/****************************************************************************** - * - * USB device hotplug event handling - * - ****************************************************************************** - */ - -/** - * Handle newly attached USB device - * - * @v port USB port - * @ret rc Return status code - */ -static int usb_attached ( struct usb_port *port ) { - struct usb_device *usb; - int rc; - - /* Mark port as attached */ - port->attached = 1; - - /* Sanity checks */ - assert ( port->usb == NULL ); - - /* Allocate USB device */ - usb = alloc_usb ( port ); - if ( ! usb ) { - rc = -ENOMEM; - goto err_alloc; - } - - /* Register USB device */ - if ( ( rc = register_usb ( usb ) ) != 0 ) - goto err_register; - - return 0; - - unregister_usb ( usb ); - err_register: - free_usb ( usb ); - err_alloc: - return rc; -} - -/** - * Handle newly detached USB device - * - * @v port USB port - */ -static void usb_detached ( struct usb_port *port ) { - struct usb_device *usb = port->usb; - - /* Mark port as detached */ - port->attached = 0; - - /* Do nothing if we have no USB device */ - if ( ! usb ) - return; - - /* Unregister USB device */ - unregister_usb ( usb ); - - /* Free USB device */ - free_usb ( usb ); -} - -/** - * Handle newly attached or detached USB device - * - * @v port USB port - * @ret rc Return status code - */ -static int usb_hotplugged ( struct usb_port *port ) { - struct usb_hub *hub = port->hub; - int rc; - - /* Get current port speed */ - if ( ( rc = hub->driver->speed ( hub, port ) ) != 0 ) { - DBGC ( hub, "USB hub %s port %d could not get speed: %s\n", - hub->name, port->address, strerror ( rc ) ); - goto err_speed; - } - - /* Detach device, if applicable */ - if ( port->attached && ( port->disconnected || ! port->speed ) ) - usb_detached ( port ); - - /* Attach device, if applicable */ - if ( port->speed && ( ! port->attached ) && - ( ( rc = usb_attached ( port ) ) != 0 ) ) - goto err_attached; - - err_attached: - err_speed: - /* Clear any recorded disconnections */ - port->disconnected = 0; - return rc; -} - -/****************************************************************************** - * - * USB process - * - ****************************************************************************** - */ - -/** - * Report port status change - * - * @v port USB port - */ -void usb_port_changed ( struct usb_port *port ) { - - /* Record hub port status change */ - list_del ( &port->changed ); - list_add_tail ( &port->changed, &usb_changed ); -} - -/** - * Handle newly attached or detached USB device - * - */ -static void usb_hotplug ( void ) { - struct usb_port *port; - - /* Handle any changed ports, allowing for the fact that the - * port list may change as we perform hotplug actions. - */ - while ( ! list_empty ( &usb_changed ) ) { - - /* Get first changed port */ - port = list_first_entry ( &usb_changed, struct usb_port, - changed ); - assert ( port != NULL ); - - /* Remove from list of changed ports */ - list_del ( &port->changed ); - INIT_LIST_HEAD ( &port->changed ); - - /* Perform appropriate hotplug action */ - usb_hotplugged ( port ); - } -} - -/** - * USB process - * - * @v process USB process - */ -static void usb_step ( struct process *process __unused ) { - struct usb_bus *bus; - struct usb_endpoint *ep; - - /* Poll all buses */ - for_each_usb_bus ( bus ) - usb_poll ( bus ); - - /* Attempt to reset first halted endpoint in list, if any. We - * do not attempt to process the complete list, since this - * would require extra code to allow for the facts that the - * halted endpoint list may change as we do so, and that - * resetting an endpoint may fail. - */ - if ( ( ep = list_first_entry ( &usb_halted, struct usb_endpoint, - halted ) ) != NULL ) - usb_endpoint_reset ( ep ); - - /* Handle any changed ports */ - usb_hotplug(); -} - -/** USB process */ -PERMANENT_PROCESS ( usb_process, usb_step ); - -/****************************************************************************** - * - * USB hub - * - ****************************************************************************** - */ - -/** - * Allocate USB hub - * - * @v bus USB bus - * @v usb Underlying USB device, if any - * @v ports Number of ports - * @v driver Hub driver operations - * @ret hub USB hub, or NULL on allocation failure - */ -struct usb_hub * alloc_usb_hub ( struct usb_bus *bus, struct usb_device *usb, - unsigned int ports, - struct usb_hub_driver_operations *driver ) { - struct usb_hub *hub; - struct usb_port *port; - unsigned int i; - - /* Allocate and initialise structure */ - hub = zalloc ( sizeof ( *hub ) + ( ports * sizeof ( hub->port[0] ) ) ); - if ( ! hub ) - return NULL; - hub->name = ( usb ? usb->name : bus->name ); - hub->bus = bus; - hub->usb = usb; - if ( usb ) - hub->protocol = usb->port->protocol; - hub->ports = ports; - hub->driver = driver; - hub->host = &bus->op->hub; - - /* Initialise port list */ - for ( i = 1 ; i <= hub->ports ; i++ ) { - port = usb_port ( hub, i ); - port->hub = hub; - port->address = i; - if ( usb ) - port->protocol = usb->port->protocol; - INIT_LIST_HEAD ( &port->changed ); - } - - return hub; -} - -/** - * Register USB hub - * - * @v hub USB hub - * @ret rc Return status code - */ -int register_usb_hub ( struct usb_hub *hub ) { - struct usb_bus *bus = hub->bus; - struct usb_port *port; - unsigned int i; - int rc; - - /* Add to hub list */ - list_add_tail ( &hub->list, &bus->hubs ); - - /* Open hub (host controller) */ - if ( ( rc = hub->host->open ( hub ) ) != 0 ) { - DBGC ( hub, "USB hub %s could not open: %s\n", - hub->name, strerror ( rc ) ); - goto err_host_open; - } - - /* Open hub (driver) */ - if ( ( rc = hub->driver->open ( hub ) ) != 0 ) { - DBGC ( hub, "USB hub %s could not open: %s\n", - hub->name, strerror ( rc ) ); - goto err_driver_open; - } - - /* Delay to allow ports to stabilise */ - mdelay ( USB_PORT_DELAY_MS ); - - /* Mark all ports as changed */ - for ( i = 1 ; i <= hub->ports ; i++ ) { - port = usb_port ( hub, i ); - usb_port_changed ( port ); - } - - /* Some hubs seem to defer reporting device connections until - * their interrupt endpoint is polled for the first time. - * Poll the bus once now in order to pick up any such - * connections. - */ - usb_poll ( bus ); - - return 0; - - hub->driver->close ( hub ); - err_driver_open: - hub->host->close ( hub ); - err_host_open: - list_del ( &hub->list ); - return rc; -} - -/** - * Unregister USB hub - * - * @v hub USB hub - */ -void unregister_usb_hub ( struct usb_hub *hub ) { - struct usb_port *port; - unsigned int i; - - /* Detach all devices */ - for ( i = 1 ; i <= hub->ports ; i++ ) { - port = usb_port ( hub, i ); - if ( port->attached ) - usb_detached ( port ); - } - - /* Close hub (driver) */ - hub->driver->close ( hub ); - - /* Close hub (host controller) */ - hub->host->close ( hub ); - - /* Cancel any pending port status changes */ - for ( i = 1 ; i <= hub->ports ; i++ ) { - port = usb_port ( hub, i ); - list_del ( &port->changed ); - INIT_LIST_HEAD ( &port->changed ); - } - - /* Remove from hub list */ - list_del ( &hub->list ); -} - -/** - * Free USB hub - * - * @v hub USB hub - */ -void free_usb_hub ( struct usb_hub *hub ) { - struct usb_port *port; - unsigned int i; - - /* Sanity checks */ - for ( i = 1 ; i <= hub->ports ; i++ ) { - port = usb_port ( hub, i ); - assert ( ! port->attached ); - assert ( port->usb == NULL ); - assert ( list_empty ( &port->changed ) ); - } - - /* Free hub */ - free ( hub ); -} - -/****************************************************************************** - * - * USB bus - * - ****************************************************************************** - */ - -/** - * Allocate USB bus - * - * @v dev Underlying hardware device - * @v ports Number of root hub ports - * @v mtu Largest transfer allowed on the bus - * @v op Host controller operations - * @ret bus USB bus, or NULL on allocation failure - */ -struct usb_bus * alloc_usb_bus ( struct device *dev, unsigned int ports, - size_t mtu, struct usb_host_operations *op ) { - struct usb_bus *bus; - - /* Allocate and initialise structure */ - bus = zalloc ( sizeof ( *bus ) ); - if ( ! bus ) - goto err_alloc_bus; - bus->name = dev->name; - bus->dev = dev; - bus->mtu = mtu; - bus->op = op; - INIT_LIST_HEAD ( &bus->devices ); - INIT_LIST_HEAD ( &bus->hubs ); - bus->host = &bus->op->bus; - - /* Allocate root hub */ - bus->hub = alloc_usb_hub ( bus, NULL, ports, &op->root ); - if ( ! bus->hub ) - goto err_alloc_hub; - - return bus; - - free_usb_hub ( bus->hub ); - err_alloc_hub: - free ( bus ); - err_alloc_bus: - return NULL; -} - -/** - * Register USB bus - * - * @v bus USB bus - * @ret rc Return status code - */ -int register_usb_bus ( struct usb_bus *bus ) { - int rc; - - /* Sanity checks */ - assert ( bus->hub != NULL ); - - /* Open bus */ - if ( ( rc = bus->host->open ( bus ) ) != 0 ) - goto err_open; - - /* Add to list of USB buses */ - list_add_tail ( &bus->list, &usb_buses ); - - /* Register root hub */ - if ( ( rc = register_usb_hub ( bus->hub ) ) != 0 ) - goto err_register_hub; - - /* Attach any devices already present */ - usb_hotplug(); - - return 0; - - unregister_usb_hub ( bus->hub ); - err_register_hub: - list_del ( &bus->list ); - bus->host->close ( bus ); - err_open: - return rc; -} - -/** - * Unregister USB bus - * - * @v bus USB bus - */ -void unregister_usb_bus ( struct usb_bus *bus ) { - - /* Sanity checks */ - assert ( bus->hub != NULL ); - - /* Unregister root hub */ - unregister_usb_hub ( bus->hub ); - - /* Remove from list of USB buses */ - list_del ( &bus->list ); - - /* Close bus */ - bus->host->close ( bus ); - - /* Sanity checks */ - assert ( list_empty ( &bus->devices ) ); - assert ( list_empty ( &bus->hubs ) ); -} - -/** - * Free USB bus - * - * @v bus USB bus - */ -void free_usb_bus ( struct usb_bus *bus ) { - struct usb_endpoint *ep; - struct usb_port *port; - - /* Sanity checks */ - assert ( list_empty ( &bus->devices ) ); - assert ( list_empty ( &bus->hubs ) ); - list_for_each_entry ( ep, &usb_halted, halted ) - assert ( ep->usb->port->hub->bus != bus ); - list_for_each_entry ( port, &usb_changed, changed ) - assert ( port->hub->bus != bus ); - - /* Free root hub */ - free_usb_hub ( bus->hub ); - - /* Free bus */ - free ( bus ); -} - -/** - * Find USB bus by device location - * - * @v bus_type Bus type - * @v location Bus location - * @ret bus USB bus, or NULL - */ -struct usb_bus * find_usb_bus_by_location ( unsigned int bus_type, - unsigned int location ) { - struct usb_bus *bus; - - for_each_usb_bus ( bus ) { - if ( ( bus->dev->desc.bus_type == bus_type ) && - ( bus->dev->desc.location == location ) ) - return bus; - } - - return NULL; -} - -/****************************************************************************** - * - * USB address assignment - * - ****************************************************************************** - */ - -/** - * Allocate device address - * - * @v bus USB bus - * @ret address Device address, or negative error - */ -int usb_alloc_address ( struct usb_bus *bus ) { - unsigned int address; - - /* Find first free device address */ - address = ffsll ( ~bus->addresses ); - if ( ! address ) - return -ENOENT; - - /* Mark address as used */ - bus->addresses |= ( 1ULL << ( address - 1 ) ); - - return address; -} - -/** - * Free device address - * - * @v bus USB bus - * @v address Device address - */ -void usb_free_address ( struct usb_bus *bus, unsigned int address ) { - - /* Sanity check */ - assert ( address > 0 ); - assert ( bus->addresses & ( 1ULL << ( address - 1 ) ) ); - - /* Mark address as free */ - bus->addresses &= ~( 1ULL << ( address - 1 ) ); -} - -/****************************************************************************** - * - * USB bus topology - * - ****************************************************************************** - */ - -/** - * Get USB route string - * - * @v usb USB device - * @ret route USB route string - */ -unsigned int usb_route_string ( struct usb_device *usb ) { - struct usb_device *parent; - unsigned int route; - - /* Navigate up to root hub, constructing route string as we go */ - for ( route = 0 ; ( parent = usb->port->hub->usb ) ; usb = parent ) { - route <<= 4; - route |= ( ( usb->port->address > 0xf ) ? - 0xf : usb->port->address ); - } - - return route; -} - -/** - * Get USB depth - * - * @v usb USB device - * @ret depth Hub depth - */ -unsigned int usb_depth ( struct usb_device *usb ) { - struct usb_device *parent; - unsigned int depth; - - /* Navigate up to root hub, constructing depth as we go */ - for ( depth = 0 ; ( parent = usb->port->hub->usb ) ; usb = parent ) - depth++; - - return depth; -} - -/** - * Get USB root hub port - * - * @v usb USB device - * @ret port Root hub port - */ -struct usb_port * usb_root_hub_port ( struct usb_device *usb ) { - struct usb_device *parent; - - /* Navigate up to root hub */ - while ( ( parent = usb->port->hub->usb ) ) - usb = parent; - - return usb->port; -} - -/** - * Get USB transaction translator - * - * @v usb USB device - * @ret port Transaction translator port, or NULL - */ -struct usb_port * usb_transaction_translator ( struct usb_device *usb ) { - struct usb_device *parent; - - /* Navigate up to root hub. If we find a low-speed or - * full-speed port with a higher-speed parent device, then - * that port is the transaction translator. - */ - for ( ; ( parent = usb->port->hub->usb ) ; usb = parent ) { - if ( ( usb->port->speed <= USB_SPEED_FULL ) && - ( parent->port->speed > USB_SPEED_FULL ) ) - return usb->port; - } - - return NULL; -} - -/* Drag in objects via register_usb_bus() */ -REQUIRING_SYMBOL ( register_usb_bus ); - -/* Drag in USB configuration */ -REQUIRE_OBJECT ( config_usb ); - -/* Drag in hub driver */ -REQUIRE_OBJECT ( usbhub ); |