12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910 |
- /*
- * 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 <unistd.h>
- #include <errno.h>
- #include <assert.h>
- #include <byteswap.h>
- #include <ipxe/usb.h>
- #include <ipxe/cdc.h>
-
- /** @file
- *
- * Universal Serial Bus (USB)
- *
- */
-
- /******************************************************************************
- *
- * Utility functions
- *
- ******************************************************************************
- */
-
- /**
- * Get USB endpoint name (for debugging)
- *
- * @v address Endpoint address
- * @ret name Endpoint name
- */
- static inline const char * usb_endpoint_name ( unsigned int address ) {
- static char buf[ 9 /* "EPxx OUT" + NUL */ ];
-
- snprintf ( buf, sizeof ( buf ), "EP%d%s",
- ( address & USB_ENDPOINT_MAX ),
- ( address ?
- ( ( address & USB_ENDPOINT_IN ) ? " IN" : " OUT" ) : "" ));
- return buf;
- }
-
- /**
- * 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
- *
- ******************************************************************************
- */
-
- /**
- * 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_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->address ) );
- rc = -EALREADY;
- goto err_already;
- }
- usb->ep[idx] = ep;
-
- /* Clear any stale error status */
- ep->rc = 0;
-
- /* 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->address ), 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->address ), 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;
- }
-
- /**
- * 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;
-
- /* Discard any recycled buffers, if applicable */
- if ( ep->max )
- usb_flush ( 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;
-
- /* 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->address ),
- strerror ( rc ) );
- 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->address ),
- strerror ( rc ) );
- return rc;
- }
-
- /* Clear recorded error */
- ep->rc = 0;
-
- DBGC ( usb, "USB %s %s reset\n",
- usb->name, usb_endpoint_name ( ep->address ) );
- 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->address ),
- 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
- */
- 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;
-
- /* Fail immediately if device has been unplugged */
- if ( port->speed == USB_SPEED_NONE )
- return -ENODEV;
-
- /* Reset endpoint if required */
- if ( ( ep->rc != 0 ) && ( ( 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.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, &packet, iobuf ) ) != 0 ) {
- DBGC ( usb, "USB %s %s could not enqueue message transfer: "
- "%s\n", usb->name, usb_endpoint_name ( ep->address ),
- 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 ( ( ep->rc != 0 ) && ( ( 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->address ),
- 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--;
-
- /* Record error (if any) */
- ep->rc = rc;
- if ( ( rc != 0 ) && ep->open ) {
- DBGC ( usb, "USB %s %s completion failed: %s\n",
- usb->name, usb_endpoint_name ( ep->address ),
- strerror ( rc ) );
- }
-
- /* 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
- *
- ******************************************************************************
- */
-
- /**
- * 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;
-
- /* Check for failures */
- if ( rc != 0 ) {
- DBGC ( usb, "USB %s control transaction failed: %s\n",
- usb->name, strerror ( rc ) );
- free_iob ( iobuf );
- return;
- }
-
- /* 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;
- unsigned int i;
- int rc;
-
- /* Allocate I/O buffer */
- iobuf = alloc_iob ( len );
- if ( ! iobuf ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
- 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 );
-
- /* Discard stale completions */
- if ( cmplt != iobuf ) {
- DBGC ( usb, "USB %s stale control "
- "completion:\n", usb->name );
- DBGC_HDA ( usb, 0, cmplt->data,
- iob_len ( cmplt ) );
- free_iob ( cmplt );
- continue;
- }
-
- /* 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;
- }
-
- /* Fail immediately if endpoint is in an error state */
- if ( ep->rc )
- return ep->rc;
-
- /* Delay */
- mdelay ( 1 );
- }
-
- DBGC ( usb, "USB %s timed out waiting for control transaction\n",
- usb->name );
- 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;
- }
-
- /* 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 );
-
- /* 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 devices
- *
- * @v port USB port
- * @ret rc Return status code
- */
- static int usb_hotplug ( 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 ) );
- return rc;
- }
-
- /* Handle attached/detached device as applicable */
- if ( port->speed && ! port->attached ) {
- /* Newly attached device */
- return usb_attached ( port );
- } else if ( port->attached && ! port->speed ) {
- /* Newly detached device */
- usb_detached ( port );
- return 0;
- } else {
- /* Ignore */
- return 0;
- }
- }
-
- /******************************************************************************
- *
- * USB process
- *
- ******************************************************************************
- */
-
- /**
- * Report port status change
- *
- * @v port USB port
- */
- void usb_port_changed ( struct usb_port *port ) {
- struct usb_hub *hub = port->hub;
- struct usb_bus *bus = hub->bus;
-
- /* Record hub port status change */
- list_del ( &port->list );
- list_add_tail ( &port->list, &bus->changed );
- }
-
- /**
- * USB process
- *
- * @v bus USB bus
- */
- static void usb_step ( struct usb_bus *bus ) {
- struct usb_port *port;
-
- /* Poll bus */
- usb_poll ( bus );
-
- /* Handle any changed ports, allowing for the fact that the
- * port list may change as we perform hotplug actions.
- */
- while ( ! list_empty ( &bus->changed ) ) {
-
- /* Get first changed port */
- port = list_first_entry ( &bus->changed, struct usb_port, list);
- assert ( port != NULL );
-
- /* Remove from list of changed ports */
- list_del ( &port->list );
- INIT_LIST_HEAD ( &port->list );
-
- /* Perform appropriate hotplug action */
- usb_hotplug ( port );
- }
- }
-
- /** USB process */
- static struct process_descriptor usb_process_desc =
- PROC_DESC ( struct usb_bus, 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;
-
- /* 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->list );
- }
-
- 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 */
- if ( ( rc = hub->driver->open ( hub ) ) != 0 ) {
- DBGC ( hub, "USB hub %s could not open: %s\n",
- hub->name, strerror ( rc ) );
- goto err_open;
- }
-
- /* Delay to allow ports to stabilise */
- mdelay ( USB_PORT_DELAY_MS );
-
- /* Attach any devices already present */
- for ( i = 1 ; i <= hub->ports ; i++ ) {
- port = usb_port ( hub, i );
- usb_hotplug ( 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_step ( bus );
-
- return 0;
-
- hub->driver->close ( hub );
- err_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 */
- hub->driver->close ( hub );
-
- /* Cancel any pending port status changes */
- for ( i = 1 ; i <= hub->ports ; i++ ) {
- port = usb_port ( hub, i );
- list_del ( &port->list );
- INIT_LIST_HEAD ( &port->list );
- }
-
- /* 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->list ) );
- }
-
- /* Free hub */
- free ( hub );
- }
-
- /******************************************************************************
- *
- * USB bus
- *
- ******************************************************************************
- */
-
- /**
- * Allocate USB bus
- *
- * @v dev Underlying hardware device
- * @v ports Number of root hub ports
- * @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,
- 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->op = op;
- INIT_LIST_HEAD ( &bus->devices );
- INIT_LIST_HEAD ( &bus->hubs );
- INIT_LIST_HEAD ( &bus->changed );
- process_init_stopped ( &bus->process, &usb_process_desc, NULL );
- bus->host = &bus->op->bus;
-
- /* Allocate root hub */
- bus->hub = alloc_usb_hub ( bus, NULL, ports, &op->hub );
- 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;
-
- /* Register root hub */
- if ( ( rc = register_usb_hub ( bus->hub ) ) != 0 )
- goto err_register_hub;
-
- /* Start bus process */
- process_add ( &bus->process );
-
- return 0;
-
- unregister_usb_hub ( bus->hub );
- err_register_hub:
- 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 );
- assert ( process_running ( &bus->process ) );
-
- /* Stop bus process */
- process_del ( &bus->process );
-
- /* Unregister root hub */
- unregister_usb_hub ( bus->hub );
-
- /* Close bus */
- bus->host->close ( bus );
-
- /* Sanity checks */
- assert ( list_empty ( &bus->devices ) );
- assert ( list_empty ( &bus->hubs ) );
- assert ( ! process_running ( &bus->process ) );
- }
-
- /**
- * Free USB bus
- *
- * @v bus USB bus
- */
- void free_usb_bus ( struct usb_bus *bus ) {
-
- /* Sanity checks */
- assert ( list_empty ( &bus->devices ) );
- assert ( list_empty ( &bus->hubs ) );
- assert ( ! process_running ( &bus->process ) );
-
- /* Free root hub */
- free_usb_hub ( bus->hub );
-
- /* Free bus */
- free ( bus );
- }
-
- /******************************************************************************
- *
- * 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 ) {
- unsigned int route;
-
- /* Navigate up to root hub, constructing route string as we go */
- for ( route = 0 ; usb->port->hub->usb ; usb = usb->port->hub->usb ) {
- 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 ) {
- unsigned int depth;
-
- /* Navigate up to root hub, constructing depth as we go */
- for ( depth = 0 ; usb->port->hub->usb ; usb = usb->port->hub->usb )
- 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 ) {
-
- /* Navigate up to root hub */
- while ( usb->port->hub->usb )
- usb = usb->port->hub->usb;
-
- return usb->port;
- }
-
- /* Drag in USB configuration */
- REQUIRE_OBJECT ( config_usb );
-
- /* Drag in hub driver */
- REQUIRE_OBJECT ( usbhub );
|