1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065 |
- /*
- * 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)
- *
- */
-
- /******************************************************************************
- *
- * 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;
- 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->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;
- }
-
- /**
- * 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->address ),
- 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->address ),
- 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->address ),
- 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->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 ( ( ! 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.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 ( ( ! list_empty ( &ep->halted ) ) &&
- ( ( rc = usb_endpoint_reset ( ep ) ) != 0 ) )
- return rc;
-
- /* Enqueue stream transfer */
- if ( ( rc = ep->host->stream ( ep, iobuf, terminate ) ) != 0 ) {
- DBGC ( usb, "USB %s %s could not enqueue stream transfer: %s\n",
- usb->name, usb_endpoint_name ( ep->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;
- struct usb_bus *bus = usb->port->hub->bus;
-
- /* 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->address ),
- strerror ( rc ) );
- list_del ( &ep->halted );
- list_add_tail ( &ep->halted, &bus->halted );
- }
-
- /* Report completion */
- ep->driver->complete ( ep, iobuf, rc );
- }
-
- /******************************************************************************
- *
- * Endpoint refilling
- *
- ******************************************************************************
- */
-
- /**
- * Prefill endpoint recycled buffer list
- *
- * @v ep USB endpoint
- * @ret rc Return status code
- */
- int usb_prefill ( struct usb_endpoint *ep ) {
- struct io_buffer *iobuf;
- size_t len = ( ep->len ? ep->len : ep->mtu );
- unsigned int fill;
- int rc;
-
- /* Sanity checks */
- assert ( ep->fill == 0 );
- assert ( ep->max > 0 );
- assert ( list_empty ( &ep->recycled ) );
-
- /* Fill recycled buffer list */
- for ( fill = 0 ; fill < ep->max ; fill++ ) {
-
- /* Allocate I/O buffer */
- iobuf = alloc_iob ( len );
- if ( ! iobuf ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
-
- /* Add to recycled buffer list */
- list_add_tail ( &iobuf->list, &ep->recycled );
- }
-
- return 0;
-
- err_alloc:
- usb_flush ( ep );
- return rc;
- }
-
- /**
- * Refill endpoint
- *
- * @v ep USB endpoint
- * @ret rc Return status code
- */
- int usb_refill ( struct usb_endpoint *ep ) {
- struct io_buffer *iobuf;
- size_t len = ( ep->len ? ep->len : ep->mtu );
- int rc;
-
- /* Sanity checks */
- assert ( ep->open );
- assert ( ep->max > 0 );
-
- /* Refill endpoint */
- while ( ep->fill < ep->max ) {
-
- /* Get or allocate buffer */
- if ( list_empty ( &ep->recycled ) ) {
- /* Recycled buffer list is empty; allocate new buffer */
- iobuf = alloc_iob ( len );
- if ( ! iobuf )
- return -ENOMEM;
- } else {
- /* Get buffer from recycled buffer list */
- iobuf = list_first_entry ( &ep->recycled,
- struct io_buffer, list );
- assert ( iobuf != NULL );
- list_del ( &iobuf->list );
- }
-
- /* Reset buffer to maximum size */
- assert ( iob_len ( iobuf ) <= len );
- iob_put ( iobuf, ( len - iob_len ( iobuf ) ) );
-
- /* Enqueue buffer */
- if ( ( rc = usb_stream ( ep, iobuf, 0 ) ) != 0 ) {
- list_add ( &iobuf->list, &ep->recycled );
- return rc;
- }
- }
-
- return 0;
- }
-
- /**
- * Discard endpoint recycled buffer list
- *
- * @v ep USB endpoint
- */
- void usb_flush ( struct usb_endpoint *ep ) {
- struct io_buffer *iobuf;
- struct io_buffer *tmp;
-
- /* Sanity checks */
- assert ( ! ep->open );
- assert ( ep->max > 0 );
-
- /* Free all I/O buffers */
- list_for_each_entry_safe ( iobuf, tmp, &ep->recycled, list ) {
- list_del ( &iobuf->list );
- free_iob ( iobuf );
- }
- }
-
- /******************************************************************************
- *
- * Control endpoint
- *
- ******************************************************************************
- */
-
- /** USB control transfer pseudo-header */
- struct usb_control_pseudo_header {
- /** Completion status */
- int rc;
- };
-
- /**
- * Complete USB control transfer
- *
- * @v ep USB endpoint
- * @v iobuf I/O buffer
- * @v rc Completion status code
- */
- static void usb_control_complete ( struct usb_endpoint *ep,
- struct io_buffer *iobuf, int rc ) {
- struct usb_device *usb = ep->usb;
- struct usb_control_pseudo_header *pshdr;
-
- /* Record completion status in buffer */
- pshdr = iob_push ( iobuf, sizeof ( *pshdr ) );
- pshdr->rc = rc;
- if ( rc != 0 ) {
- DBGC ( usb, "USB %s control transaction failed: %s\n",
- usb->name, strerror ( 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 usb_control_pseudo_header *pshdr;
- struct io_buffer *iobuf;
- struct io_buffer *cmplt;
- unsigned int i;
- int rc;
-
- /* Allocate I/O buffer */
- iobuf = alloc_iob ( sizeof ( *pshdr ) + len );
- if ( ! iobuf ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
- iob_reserve ( iobuf, sizeof ( *pshdr ) );
- 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 ) {
- 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 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;
- }
-
- /* Allow recovery interval since port may have been reset */
- mdelay ( USB_RESET_RECOVER_DELAY_MS );
-
- /* Get device speed */
- if ( ( rc = hub->driver->speed ( hub, port ) ) != 0 ) {
- DBGC ( hub, "USB hub %s port %d could not get speed: %s\n",
- hub->name, port->address, strerror ( rc ) );
- goto err_speed;
- }
- DBGC2 ( usb, "USB %s attached as %s-speed device\n",
- usb->name, usb_speed_name ( port->speed ) );
-
- /* Open device */
- if ( ( rc = usb->host->open ( usb ) ) != 0 ) {
- DBGC ( usb, "USB %s could not open: %s\n",
- usb->name, strerror ( rc ) );
- goto err_open;
- }
-
- /* Describe control endpoint */
- mtu = USB_EP0_DEFAULT_MTU ( port->speed );
- usb_endpoint_describe ( &usb->control, USB_EP0_ADDRESS,
- USB_EP0_ATTRIBUTES, mtu, USB_EP0_BURST,
- USB_EP0_INTERVAL );
-
- /* Open control endpoint */
- if ( ( rc = usb_endpoint_open ( &usb->control ) ) != 0 )
- goto err_open_control;
- assert ( usb_endpoint ( usb, USB_EP0_ADDRESS ) == &usb->control );
-
- /* Assign device address */
- if ( ( rc = usb->host->address ( usb ) ) != 0 ) {
- DBGC ( usb, "USB %s could not set address: %s\n",
- usb->name, strerror ( rc ) );
- goto err_address;
- }
- DBGC2 ( usb, "USB %s assigned address %d\n", usb->name, usb->address );
-
- /* Allow recovery interval after Set Address command */
- mdelay ( USB_SET_ADDRESS_RECOVER_DELAY_MS );
-
- /* Read first part of device descriptor to get EP0 MTU */
- if ( ( rc = usb_get_mtu ( usb, &usb->device ) ) != 0 ) {
- DBGC ( usb, "USB %s could not get MTU: %s\n",
- usb->name, strerror ( rc ) );
- goto err_get_mtu;
- }
-
- /* Calculate EP0 MTU */
- protocol = le16_to_cpu ( usb->device.protocol );
- mtu = ( ( protocol < USB_PROTO_3_0 ) ?
- usb->device.mtu : ( 1 << usb->device.mtu ) );
- DBGC2 ( usb, "USB %s has control MTU %zd (guessed %zd)\n",
- usb->name, mtu, usb->control.mtu );
-
- /* Update MTU */
- if ( ( rc = usb_endpoint_mtu ( &usb->control, mtu ) ) != 0 )
- goto err_mtu;
-
- /* Read whole device descriptor */
- if ( ( rc = usb_get_device_descriptor ( usb, &usb->device ) ) != 0 ) {
- DBGC ( usb, "USB %s could not get device descriptor: %s\n",
- usb->name, strerror ( rc ) );
- goto err_get_device_descriptor;
- }
- DBGC ( usb, "USB %s addr %d %04x:%04x class %d:%d:%d (v%s, %s-speed, "
- "MTU %zd)\n", usb->name, usb->address,
- le16_to_cpu ( usb->device.vendor ),
- le16_to_cpu ( usb->device.product ), usb->device.class.class,
- usb->device.class.subclass, usb->device.class.protocol,
- usb_bcd ( le16_to_cpu ( usb->device.protocol ) ),
- usb_speed_name ( port->speed ), usb->control.mtu );
-
- /* Configure device */
- if ( ( rc = usb_configure_any ( usb ) ) != 0 )
- goto err_configure_any;
-
- return 0;
-
- usb_deconfigure ( usb );
- err_configure_any:
- err_get_device_descriptor:
- err_mtu:
- err_get_mtu:
- err_address:
- usb_endpoint_close ( &usb->control );
- err_open_control:
- usb->host->close ( usb );
- err_open:
- err_speed:
- hub->driver->disable ( hub, port );
- err_enable:
- list_del ( &usb->list );
- port->usb = NULL;
- err_already:
- return rc;
- }
-
- /**
- * Unregister USB device
- *
- * @v usb USB device
- */
- static void unregister_usb ( struct usb_device *usb ) {
- struct usb_port *port = usb->port;
- struct usb_hub *hub = port->hub;
- struct io_buffer *iobuf;
- struct io_buffer *tmp;
-
- /* Sanity checks */
- assert ( port->usb == usb );
-
- /* Clear device configuration */
- usb_deconfigure ( usb );
-
- /* Close control endpoint */
- usb_endpoint_close ( &usb->control );
-
- /* Discard any stale control completions */
- list_for_each_entry_safe ( iobuf, tmp, &usb->complete, list ) {
- list_del ( &iobuf->list );
- free_iob ( iobuf );
- }
-
- /* Close device */
- usb->host->close ( usb );
-
- /* Disable port */
- hub->driver->disable ( hub, port );
-
- /* Remove from bus device list */
- list_del ( &usb->list );
-
- /* Remove from port */
- port->usb = NULL;
- }
-
- /**
- * Free USB device
- *
- * @v usb USB device
- */
- static void free_usb ( struct usb_device *usb ) {
- unsigned int i;
-
- /* Sanity checks */
- for ( i = 0 ; i < ( sizeof ( usb->ep ) / sizeof ( usb->ep[0] ) ) ; i++ )
- assert ( usb->ep[i] == NULL );
- assert ( list_empty ( &usb->functions ) );
- assert ( list_empty ( &usb->complete ) );
-
- /* Free device */
- free ( usb );
- }
-
- /******************************************************************************
- *
- * USB device hotplug event handling
- *
- ******************************************************************************
- */
-
- /**
- * Handle newly attached USB device
- *
- * @v port USB port
- * @ret rc Return status code
- */
- static int usb_attached ( struct usb_port *port ) {
- struct usb_device *usb;
- int rc;
-
- /* Mark port as attached */
- port->attached = 1;
-
- /* Sanity checks */
- assert ( port->usb == NULL );
-
- /* Allocate USB device */
- usb = alloc_usb ( port );
- if ( ! usb ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
-
- /* Register USB device */
- if ( ( rc = register_usb ( usb ) ) != 0 )
- goto err_register;
-
- return 0;
-
- unregister_usb ( usb );
- err_register:
- free_usb ( usb );
- err_alloc:
- return rc;
- }
-
- /**
- * Handle newly detached USB device
- *
- * @v port USB port
- */
- static void usb_detached ( struct usb_port *port ) {
- struct usb_device *usb = port->usb;
-
- /* Mark port as detached */
- port->attached = 0;
-
- /* Do nothing if we have no USB device */
- if ( ! usb )
- return;
-
- /* Unregister USB device */
- unregister_usb ( usb );
-
- /* Free USB device */
- free_usb ( usb );
- }
-
- /**
- * Handle newly attached or detached USB 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->changed );
- list_add_tail ( &port->changed, &bus->changed );
- }
-
- /**
- * USB process
- *
- * @v bus USB bus
- */
- static void usb_step ( struct usb_bus *bus ) {
- struct usb_endpoint *ep;
- struct usb_port *port;
-
- /* Poll 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 ( &bus->halted, struct usb_endpoint,
- halted ) ) != NULL )
- usb_endpoint_reset ( ep );
-
- /* 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,
- changed );
- assert ( port != NULL );
-
- /* Remove from list of changed ports */
- list_del ( &port->changed );
- INIT_LIST_HEAD ( &port->changed );
-
- /* 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->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 */
- 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->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 );
- INIT_LIST_HEAD ( &bus->changed );
- INIT_LIST_HEAD ( &bus->halted );
- 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 address assignment
- *
- ******************************************************************************
- */
-
- /**
- * Allocate device address
- *
- * @v bus USB bus
- * @ret address Device address, or negative error
- */
- int usb_alloc_address ( struct usb_bus *bus ) {
- unsigned int address;
-
- /* Find first free device address */
- address = ffsll ( ~bus->addresses );
- if ( ! address )
- return -ENOENT;
-
- /* Mark address as used */
- bus->addresses |= ( 1ULL << ( address - 1 ) );
-
- return address;
- }
-
- /**
- * Free device address
- *
- * @v bus USB bus
- * @v address Device address
- */
- void usb_free_address ( struct usb_bus *bus, unsigned int address ) {
-
- /* Sanity check */
- assert ( address > 0 );
- assert ( bus->addresses & ( 1ULL << ( address - 1 ) ) );
-
- /* Mark address as free */
- bus->addresses &= ~( 1ULL << ( address - 1 ) );
- }
-
- /******************************************************************************
- *
- * USB bus topology
- *
- ******************************************************************************
- */
-
- /**
- * Get USB route string
- *
- * @v usb USB device
- * @ret route USB route string
- */
- unsigned int usb_route_string ( struct usb_device *usb ) {
- struct usb_device *parent;
- unsigned int route;
-
- /* Navigate up to root hub, constructing route string as we go */
- for ( route = 0 ; ( parent = usb->port->hub->usb ) ; usb = parent ) {
- route <<= 4;
- route |= ( ( usb->port->address > 0xf ) ?
- 0xf : usb->port->address );
- }
-
- return route;
- }
-
- /**
- * Get USB depth
- *
- * @v usb USB device
- * @ret depth Hub depth
- */
- unsigned int usb_depth ( struct usb_device *usb ) {
- struct usb_device *parent;
- unsigned int depth;
-
- /* Navigate up to root hub, constructing depth as we go */
- for ( depth = 0 ; ( parent = usb->port->hub->usb ) ; usb = parent )
- depth++;
-
- return depth;
- }
-
- /**
- * Get USB root hub port
- *
- * @v usb USB device
- * @ret port Root hub port
- */
- struct usb_port * usb_root_hub_port ( struct usb_device *usb ) {
- struct usb_device *parent;
-
- /* Navigate up to root hub */
- while ( ( parent = usb->port->hub->usb ) )
- usb = parent;
-
- return usb->port;
- }
-
- /**
- * Get USB transaction translator
- *
- * @v usb USB device
- * @ret port Transaction translator port, or NULL
- */
- struct usb_port * usb_transaction_translator ( struct usb_device *usb ) {
- struct usb_device *parent;
-
- /* Navigate up to root hub. If we find a low-speed or
- * full-speed port with a higher-speed parent device, then
- * that port is the transaction translator.
- */
- for ( ; ( parent = usb->port->hub->usb ) ; usb = parent ) {
- if ( ( usb->port->speed <= USB_SPEED_FULL ) &&
- ( parent->port->speed > USB_SPEED_FULL ) )
- return usb->port;
- }
-
- return NULL;
- }
-
- /* Drag in objects via register_usb_bus() */
- REQUIRING_SYMBOL ( register_usb_bus );
-
- /* Drag in USB configuration */
- REQUIRE_OBJECT ( config_usb );
-
- /* Drag in hub driver */
- REQUIRE_OBJECT ( usbhub );
|