12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297 |
- /*
- * 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_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 ( usb->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->disconnected )
- 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 zlp;
- int rc;
-
- /* Fail immediately if device has been unplugged */
- if ( port->disconnected )
- return -ENODEV;
-
- /* Reset endpoint if required */
- if ( ( ! list_empty ( &ep->halted ) ) &&
- ( ( rc = usb_endpoint_reset ( ep ) ) != 0 ) )
- return rc;
-
- /* Append a zero-length packet if necessary */
- zlp = terminate;
- if ( iob_len ( iobuf ) & ( ep->mtu - 1 ) )
- zlp = 0;
-
- /* Enqueue stream transfer */
- if ( ( rc = ep->host->stream ( ep, iobuf, zlp ) ) != 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 reserve = ep->reserve;
- 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 ( reserve + len );
- if ( ! iobuf ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
- iob_reserve ( iobuf, reserve );
-
- /* 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 reserve = ep->reserve;
- 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 ( reserve + len );
- if ( ! iobuf )
- return -ENOMEM;
- iob_reserve ( iobuf, reserve );
- } 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 default language ID
- *
- * @v usb USB device
- * @ret language Language ID
- */
- static unsigned int usb_get_default_language ( struct usb_device *usb ) {
- struct {
- struct usb_descriptor_header header;
- uint16_t language[1];
- } __attribute__ (( packed )) desc;
- unsigned int language;
- int rc;
-
- /* Get descriptor */
- if ( ( rc = usb_get_descriptor ( usb, 0, USB_STRING_DESCRIPTOR, 0, 0,
- &desc.header, sizeof ( desc ) ) ) !=0){
- DBGC ( usb, "USB %s has no default language: %s\n",
- usb->name, strerror ( rc ) );
- return USB_LANG_ENGLISH;
- }
-
- /* Use first language ID */
- language = le16_to_cpu ( desc.language[0] );
- DBGC2 ( usb, "USB %s default language %#04x\n", usb->name, language );
- return language;
- }
-
- /**
- * Get USB string descriptor
- *
- * @v usb USB device
- * @v index String index
- * @v language Language ID, or 0 to use default
- * @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;
-
- /* Use default language ID, if applicable */
- if ( ( language == 0 ) && ( index != 0 ) ) {
- if ( ! usb->language )
- usb->language = usb_get_default_language ( usb );
- language = usb->language;
- }
-
- /* 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
- *
- ******************************************************************************
- */
-
- /**
- * Get USB configuration descriptor
- *
- * @v usb USB device
- * @v index Configuration index
- * @ret config Configuration descriptor
- * @ret rc Return status code
- *
- * The configuration descriptor is dynamically allocated and must
- * eventually be freed by the caller.
- */
- static int
- usb_config_descriptor ( struct usb_device *usb, unsigned int index,
- struct usb_configuration_descriptor **config ) {
- struct usb_configuration_descriptor partial;
- 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;
- }
-
- return 0;
-
- err_config_len:
- err_get_config_descriptor:
- free ( *config );
- err_alloc_config:
- err_partial_len:
- err_get_partial:
- return rc;
- }
-
- /**
- * Describe USB function
- *
- * @v usb USB device
- * @v config Configuration descriptor
- * @v first First interface number
- * @v interfaces Interface list to fill in
- * @v desc Function descriptor to fill in
- * @ret rc Return status code
- */
- static int usb_describe ( struct usb_device *usb,
- struct usb_configuration_descriptor *config,
- unsigned int first, uint8_t *interfaces,
- struct usb_function_descriptor *desc ) {
- struct usb_interface_association_descriptor *association;
- struct usb_interface_descriptor *interface;
- struct cdc_union_descriptor *cdc_union;
- unsigned int i;
-
- /* Fill in vendor and product ID */
- memset ( desc, 0, sizeof ( *desc ) );
- desc->vendor = le16_to_cpu ( usb->device.vendor );
- desc->product = le16_to_cpu ( usb->device.product );
-
- /* First, look for an interface association descriptor */
- association = usb_interface_association_descriptor ( config, first );
- if ( association ) {
-
- /* Sanity check */
- assert ( association->first == first );
- if ( ( first + association->count ) > config->interfaces ) {
- DBGC ( usb, "USB %s has invalid association [%d-%d)\n",
- usb->name, first, ( first + association->count));
- return -ERANGE;
- }
-
- /* Describe function */
- memcpy ( &desc->class.class, &association->class,
- sizeof ( desc->class.class ) );
- desc->count = association->count;
- for ( i = 0 ; i < association->count ; i++ )
- interfaces[i] = ( 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 descriptor for interface %d\n",
- usb->name, first );
- return -ENOENT;
- }
-
- /* Describe function */
- memcpy ( &desc->class.class, &interface->class,
- sizeof ( desc->class.class ) );
- desc->count = 1;
- interfaces[0] = first;
-
- /* Look for a CDC union descriptor, if applicable */
- if ( ( desc->class.class.class == USB_CLASS_CDC ) &&
- ( cdc_union = cdc_union_descriptor ( config, interface ) ) ) {
-
- /* Determine interface count */
- desc->count = ( ( cdc_union->header.len -
- offsetof ( typeof ( *cdc_union ),
- interface[0] ) ) /
- sizeof ( cdc_union->interface[0] ) );
- if ( desc->count > config->interfaces ) {
- DBGC ( usb, "USB %s has invalid union functional "
- "descriptor with %d interfaces\n",
- usb->name, desc->count );
- return -ERANGE;
- }
-
- /* Describe function */
- for ( i = 0 ; i < desc->count ; i++ ) {
- if ( cdc_union->interface[i] >= config->interfaces ) {
- DBGC ( usb, "USB %s has invalid union "
- "functional descriptor covering "
- "interface %d\n", usb->name,
- cdc_union->interface[i] );
- return -ERANGE;
- }
- interfaces[i] = cdc_union->interface[i];
- }
-
- return 0;
- }
-
- return 0;
- }
-
- /**
- * Update list of used interface
- *
- * @v usb USB device
- * @v count Number of interfaces
- * @v interface List of interfaces
- * @v used List of already-used interfaces
- * @ret rc Return status code
- */
- static int usb_used ( struct usb_device *usb, unsigned int count,
- uint8_t *interface, uint8_t *used ) {
- unsigned int i;
-
- for ( i = 0 ; i < count ; i++ ) {
- if ( used[interface[i]] ) {
- DBGC ( usb, "USB %s interface %d already in use\n",
- usb->name, interface[i] );
- return -EINVAL;
- }
- used[interface[i]] = 1;
- }
- return 0;
- }
-
- /**
- * Find USB device driver
- *
- * @v desc Function descriptor
- * @ret id USB device ID, or NULL
- * @ret driver USB device driver, or NULL
- */
- struct usb_driver * usb_find_driver ( struct usb_function_descriptor *desc,
- struct usb_device_id **id ) {
- struct usb_driver *driver;
- unsigned int i;
-
- /* Look for a matching driver */
- for_each_table_entry ( driver, USB_DRIVERS ) {
- for ( i = 0 ; i < driver->id_count ; i++ ) {
-
- /* Ignore non-matching driver class */
- if ( ( driver->class.class.scalar ^ desc->class.scalar )
- & driver->class.mask.scalar )
- continue;
-
- /* Look for a matching ID */
- *id = &driver->ids[i];
- if ( ( ( (*id)->vendor == desc->vendor ) ||
- ( (*id)->vendor == USB_ANY_ID ) ) &&
- ( ( (*id)->product == desc->product ) ||
- ( (*id)->product == USB_ANY_ID ) ) )
- return driver;
- }
- }
-
- /* Not found */
- *id = NULL;
- return NULL;
- }
-
- /**
- * Get USB device configuration score
- *
- * @v usb USB device
- * @v config Configuration descriptor
- * @ret score Device configuration score, or negative error
- */
- static int usb_score ( struct usb_device *usb,
- struct usb_configuration_descriptor *config ) {
- uint8_t used[config->interfaces];
- uint8_t interface[config->interfaces];
- struct usb_function_descriptor desc;
- struct usb_driver *driver;
- struct usb_device_id *id;
- unsigned int first;
- unsigned int score = 0;
- 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;
-
- /* Describe function */
- if ( ( rc = usb_describe ( usb, config, first, interface,
- &desc ) ) != 0 )
- return rc;
-
- /* Update used interfaces */
- if ( ( rc = usb_used ( usb, desc.count, interface,
- used ) ) != 0 )
- return rc;
-
- /* Look for a driver for this function */
- driver = usb_find_driver ( &desc, &id );
- if ( driver )
- score += driver->score;
- }
-
- return score;
- }
-
- /**
- * 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;
- int rc;
-
- /* Identify driver */
- driver = usb_find_driver ( &func->desc, &id );
- if ( ! driver ) {
- DBGC ( usb, "USB %s %04x:%04x class %d:%d:%d has no driver\n",
- func->name, func->desc.vendor, func->desc.product,
- func->desc.class.class.class,
- func->desc.class.class.subclass,
- func->desc.class.class.protocol );
- return -ENOENT;
- }
-
- /* Record driver */
- func->driver = driver;
- func->id = id;
- func->dev.driver_name = id->name;
-
- /* 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 ) );
- return rc;
- }
-
- return 0;
- }
-
- /**
- * 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;
- list_add_tail ( &func->list, &usb->functions );
-
- /* Identify function */
- if ( ( rc = usb_describe ( usb, config, first, func->interface,
- &func->desc ) ) != 0 )
- goto err_describe;
- assert ( func->desc.count <= config->interfaces );
-
- /* Mark interfaces as used */
- if ( ( rc = usb_used ( usb, func->desc.count, func->interface,
- used ) ) != 0 )
- goto err_used;
-
- /* 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->desc.vendor, func->desc.product,
- func->desc.class.class.class,
- func->desc.class.class.subclass,
- func->desc.class.class.protocol );
- for ( i = 0 ; i < func->desc.count ; i++ )
- DBGC ( usb, "%s%d", ( i ? "," : "" ),
- func->interface[i] );
- DBGC ( usb, " using driver %s\n", func->dev.driver_name );
-
- /* Add to device hierarchy */
- list_add_tail ( &func->dev.siblings, &bus->dev->children );
-
- continue;
-
- list_del ( &func->dev.siblings );
- usb_remove ( func );
- err_probe:
- err_used:
- err_describe:
- list_del ( &func->list );
- free ( func );
- err_alloc:
- /* 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 );
- }
- }
-
- /**
- * 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 );
- }
-
- /**
- * Choose our preferred USB device configuration
- *
- * @v usb USB device
- * @ret rc Return status code
- */
- static int usb_autoconfigure ( struct usb_device *usb ) {
- struct usb_configuration_descriptor *config;
- unsigned int preferred = 0;
- unsigned int index;
- int score;
- int best = 0;
- int rc;
-
- /* Calculate driver score for each configuration index */
- for ( index = 0 ; index < usb->device.configurations ; index++ ) {
-
- /* Read configuration descriptor */
- if ( ( rc = usb_config_descriptor ( usb, index,
- &config ) ) != 0 )
- goto err_config;
-
- /* Get score for this configuration */
- score = usb_score ( usb, config );
- if ( score < 0 ) {
- rc = score;
- goto err_score;
- }
- DBGC2 ( usb, "USB %s configuration %d score %d\n",
- usb->name, config->config, score );
-
- /* Record as preferred configuration, if applicable */
- if ( score > best ) {
- best = score;
- preferred = index;
- }
-
- /* Free configuration descriptor */
- free ( config );
- config = NULL;
- }
-
- /* Read preferred configuration descriptor */
- if ( ( rc = usb_config_descriptor ( usb, preferred, &config ) ) != 0 )
- goto err_preferred;
-
- /* 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:
- free ( config );
- err_preferred:
- return rc;
-
- err_score:
- free ( config );
- err_config:
- 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;
- }
- usb->speed = port->speed;
- DBGC2 ( usb, "USB %s attached as %s-speed device\n",
- usb->name, usb_speed_name ( usb->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 ( usb->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 ( usb->speed ), usb->control.mtu );
-
- /* Configure device */
- if ( ( rc = usb_autoconfigure ( usb ) ) != 0 )
- goto err_autoconfigure;
-
- return 0;
-
- usb_deconfigure ( usb );
- err_autoconfigure:
- 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 ) );
- /* Treat as a disconnection */
- port->disconnected = 1;
- port->speed = USB_SPEED_NONE;
- }
-
- /* Detach device, if applicable */
- if ( port->attached && ( port->disconnected || ! port->speed ) )
- usb_detached ( port );
-
- /* Clear any recorded disconnections */
- port->disconnected = 0;
-
- /* Attach device, if applicable */
- if ( port->speed && ( ! port->attached ) &&
- ( ( rc = usb_attached ( port ) ) != 0 ) )
- return rc;
-
- return 0;
- }
-
- /******************************************************************************
- *
- * 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 device with a higher-speed parent hub, then that
- * device's port is the transaction translator.
- */
- for ( ; ( parent = usb->port->hub->usb ) ; usb = parent ) {
- if ( ( usb->speed <= USB_SPEED_FULL ) &&
- ( parent->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 );
|