1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261 |
- /*
- * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * You can also choose to distribute this program under the terms of
- * the Unmodified Binary Distribution Licence (as given in the file
- * COPYING.UBDL), provided that you have satisfied its requirements.
- */
-
- FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <unistd.h>
- #include <errno.h>
- #include <assert.h>
- #include <ipxe/efi/efi.h>
- #include <ipxe/efi/efi_utils.h>
- #include <ipxe/efi/efi_driver.h>
- #include <ipxe/efi/efi_usb.h>
- #include <ipxe/usb.h>
-
- /** @file
- *
- * EFI USB I/O PROTOCOL
- *
- */
-
- /**
- * Transcribe data direction (for debugging)
- *
- * @v direction Data direction
- * @ret text Transcribed data direction
- */
- static const char * efi_usb_direction_name ( EFI_USB_DATA_DIRECTION direction ){
-
- switch ( direction ) {
- case EfiUsbDataIn: return "in";
- case EfiUsbDataOut: return "out";
- case EfiUsbNoData: return "none";
- default: return "<UNKNOWN>";
- }
- }
-
- /******************************************************************************
- *
- * Endpoints
- *
- ******************************************************************************
- */
-
- /**
- * Poll USB bus (from endpoint event timer)
- *
- * @v event EFI event
- * @v context EFI USB endpoint
- */
- static VOID EFIAPI efi_usb_timer ( EFI_EVENT event __unused,
- VOID *context ) {
- struct efi_usb_endpoint *usbep = context;
- struct usb_bus *bus = usbep->usbintf->usbdev->usb->port->hub->bus;
-
- /* Poll bus */
- usb_poll ( bus );
-
- /* Refill endpoint */
- usb_refill ( &usbep->ep );
- }
-
- /**
- * Get endpoint MTU
- *
- * @v usbintf EFI USB interface
- * @v endpoint Endpoint address
- * @ret mtu Endpoint MTU, or negative error
- */
- static int efi_usb_mtu ( struct efi_usb_interface *usbintf,
- unsigned int endpoint ) {
- struct efi_usb_device *usbdev = usbintf->usbdev;
- struct usb_interface_descriptor *interface;
- struct usb_endpoint_descriptor *desc;
-
- /* Locate cached interface descriptor */
- interface = usb_interface_descriptor ( usbdev->config,
- usbintf->interface,
- usbintf->alternate );
- if ( ! interface ) {
- DBGC ( usbdev, "USBDEV %s alt %d has no interface descriptor\n",
- usbintf->name, usbintf->alternate );
- return -ENOENT;
- }
-
- /* Locate and copy cached endpoint descriptor */
- for_each_interface_descriptor ( desc, usbdev->config, interface ) {
- if ( ( desc->header.type == USB_ENDPOINT_DESCRIPTOR ) &&
- ( desc->endpoint == endpoint ) )
- return USB_ENDPOINT_MTU ( le16_to_cpu ( desc->sizes ) );
- }
-
- DBGC ( usbdev, "USBDEV %s alt %d ep %02x has no descriptor\n",
- usbintf->name, usbintf->alternate, endpoint );
- return -ENOENT;
- }
-
- /**
- * Open endpoint
- *
- * @v usbintf EFI USB interface
- * @v endpoint Endpoint address
- * @v attributes Endpoint attributes
- * @v interval Interval (in milliseconds)
- * @v driver Driver operations
- * @ret rc Return status code
- */
- static int efi_usb_open ( struct efi_usb_interface *usbintf,
- unsigned int endpoint, unsigned int attributes,
- unsigned int interval,
- struct usb_endpoint_driver_operations *driver ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
- struct efi_usb_device *usbdev = usbintf->usbdev;
- struct efi_usb_endpoint *usbep;
- unsigned int index = USB_ENDPOINT_IDX ( endpoint );
- int mtu;
- EFI_STATUS efirc;
- int rc;
-
- /* Get endpoint MTU */
- mtu = efi_usb_mtu ( usbintf, endpoint );
- if ( mtu < 0 ) {
- rc = mtu;
- goto err_mtu;
- }
-
- /* Allocate and initialise structure */
- usbep = zalloc ( sizeof ( *usbep ) );
- if ( ! usbep ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
- usbep->usbintf = usbintf;
- usb_endpoint_init ( &usbep->ep, usbdev->usb, driver );
- usb_endpoint_describe ( &usbep->ep, endpoint, attributes, mtu, 0,
- ( interval << 3 /* microframes */ ) );
-
- /* Open endpoint */
- if ( ( rc = usb_endpoint_open ( &usbep->ep ) ) != 0 ) {
- DBGC ( usbdev, "USBDEV %s %s could not open: %s\n",
- usbintf->name, usb_endpoint_name ( &usbep->ep ),
- strerror ( rc ) );
- goto err_open;
- }
-
- /* Record opened endpoint */
- usbintf->endpoint[index] = usbep;
- DBGC ( usbdev, "USBDEV %s %s opened\n",
- usbintf->name, usb_endpoint_name ( &usbep->ep ) );
-
- /* Create event */
- if ( ( efirc = bs->CreateEvent ( ( EVT_TIMER | EVT_NOTIFY_SIGNAL ),
- TPL_CALLBACK, efi_usb_timer, usbep,
- &usbep->event ) ) != 0 ) {
- rc = -EEFI ( efirc );
- DBGC ( usbdev, "USBDEV %s %s could not create event: %s\n",
- usbintf->name, usb_endpoint_name ( &usbep->ep ),
- strerror ( rc ) );
- goto err_event;
- }
-
- return 0;
-
- bs->CloseEvent ( usbep->event );
- err_event:
- usbintf->endpoint[index] = usbep;
- usb_endpoint_close ( &usbep->ep );
- err_open:
- free ( usbep );
- err_alloc:
- err_mtu:
- return rc;
- }
-
- /**
- * Close endpoint
- *
- * @v usbep EFI USB endpoint
- */
- static void efi_usb_close ( struct efi_usb_endpoint *usbep ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
- struct efi_usb_interface *usbintf = usbep->usbintf;
- struct efi_usb_device *usbdev = usbintf->usbdev;
- unsigned int index = USB_ENDPOINT_IDX ( usbep->ep.address );
-
- /* Sanity check */
- assert ( usbintf->endpoint[index] == usbep );
-
- /* Cancel timer (if applicable) and close event */
- bs->SetTimer ( usbep->event, TimerCancel, 0 );
- bs->CloseEvent ( usbep->event );
-
- /* Close endpoint */
- usb_endpoint_close ( &usbep->ep );
- DBGC ( usbdev, "USBDEV %s %s closed\n",
- usbintf->name, usb_endpoint_name ( &usbep->ep ) );
-
- /* Free endpoint */
- free ( usbep );
-
- /* Record closed endpoint */
- usbintf->endpoint[index] = NULL;
- }
-
- /**
- * Close all endpoints
- *
- * @v usbintf EFI USB interface
- */
- static void efi_usb_close_all ( struct efi_usb_interface *usbintf ) {
- struct efi_usb_endpoint *usbep;
- unsigned int i;
-
- for ( i = 0 ; i < ( sizeof ( usbintf->endpoint ) /
- sizeof ( usbintf->endpoint[0] ) ) ; i++ ) {
- usbep = usbintf->endpoint[i];
- if ( usbep )
- efi_usb_close ( usbep );
- }
- }
-
- /**
- * Complete synchronous transfer
- *
- * @v ep USB endpoint
- * @v iobuf I/O buffer
- * @v rc Completion status code
- */
- static void efi_usb_sync_complete ( struct usb_endpoint *ep,
- struct io_buffer *iobuf __unused, int rc ) {
- struct efi_usb_endpoint *usbep =
- container_of ( ep, struct efi_usb_endpoint, ep );
-
- /* Record completion status */
- usbep->rc = rc;
- }
-
- /** Synchronous endpoint operations */
- static struct usb_endpoint_driver_operations efi_usb_sync_driver = {
- .complete = efi_usb_sync_complete,
- };
-
- /**
- * Perform synchronous transfer
- *
- * @v usbintf USB endpoint
- * @v endpoint Endpoint address
- * @v attributes Endpoint attributes
- * @v timeout Timeout (in milliseconds)
- * @v data Data buffer
- * @v len Length of data buffer
- * @ret rc Return status code
- */
- static int efi_usb_sync_transfer ( struct efi_usb_interface *usbintf,
- unsigned int endpoint,
- unsigned int attributes,
- unsigned int timeout,
- void *data, size_t *len ) {
- struct efi_usb_device *usbdev = usbintf->usbdev;
- struct efi_usb_endpoint *usbep;
- struct io_buffer *iobuf;
- unsigned int index = USB_ENDPOINT_IDX ( endpoint );
- unsigned int i;
- int rc;
-
- /* Open endpoint, if applicable */
- if ( ( ! usbintf->endpoint[index] ) &&
- ( ( rc = efi_usb_open ( usbintf, endpoint, attributes, 0,
- &efi_usb_sync_driver ) ) != 0 ) ) {
- goto err_open;
- }
- usbep = usbintf->endpoint[index];
-
- /* Allocate and construct I/O buffer */
- iobuf = alloc_iob ( *len );
- if ( ! iobuf ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
- iob_put ( iobuf, *len );
- if ( ! ( endpoint & USB_ENDPOINT_IN ) )
- memcpy ( iobuf->data, data, *len );
-
- /* Initialise completion status */
- usbep->rc = -EINPROGRESS;
-
- /* Enqueue transfer */
- if ( ( rc = usb_stream ( &usbep->ep, iobuf, 0 ) ) != 0 ) {
- DBGC ( usbdev, "USBDEV %s %s could not enqueue: %s\n",
- usbintf->name, usb_endpoint_name ( &usbep->ep ),
- strerror ( rc ) );
- goto err_stream;
- }
-
- /* Wait for completion */
- rc = -ETIMEDOUT;
- for ( i = 0 ; ( ( timeout == 0 ) || ( i < timeout ) ) ; i++ ) {
-
- /* Poll bus */
- usb_poll ( usbdev->usb->port->hub->bus );
-
- /* Check for completion */
- if ( usbep->rc != -EINPROGRESS ) {
- rc = usbep->rc;
- break;
- }
-
- /* Delay */
- mdelay ( 1 );
- }
-
- /* Check for errors */
- if ( rc != 0 ) {
- DBGC ( usbdev, "USBDEV %s %s failed: %s\n", usbintf->name,
- usb_endpoint_name ( &usbep->ep ), strerror ( rc ) );
- goto err_completion;
- }
-
- /* Copy completion to data buffer, if applicable */
- assert ( iob_len ( iobuf ) <= *len );
- if ( endpoint & USB_ENDPOINT_IN )
- memcpy ( data, iobuf->data, iob_len ( iobuf ) );
- *len = iob_len ( iobuf );
-
- /* Free I/O buffer */
- free_iob ( iobuf );
-
- /* Leave endpoint open */
- return 0;
-
- err_completion:
- err_stream:
- free_iob ( iobuf );
- err_alloc:
- efi_usb_close ( usbep );
- err_open:
- return EFIRC ( rc );
- }
-
- /**
- * Complete asynchronous transfer
- *
- * @v ep USB endpoint
- * @v iobuf I/O buffer
- * @v rc Completion status code
- */
- static void efi_usb_async_complete ( struct usb_endpoint *ep,
- struct io_buffer *iobuf, int rc ) {
- struct efi_usb_endpoint *usbep =
- container_of ( ep, struct efi_usb_endpoint, ep );
- UINT32 status;
-
- /* Ignore packets cancelled when the endpoint closes */
- if ( ! ep->open )
- goto drop;
-
- /* Construct status */
- status = ( ( rc == 0 ) ? 0 : EFI_USB_ERR_STALL );
-
- /* Report completion */
- usbep->callback ( iobuf->data, iob_len ( iobuf ), usbep->context,
- status );
-
- drop:
- /* Recycle I/O buffer */
- usb_recycle ( &usbep->ep, iobuf );
- }
-
- /** Asynchronous endpoint operations */
- static struct usb_endpoint_driver_operations efi_usb_async_driver = {
- .complete = efi_usb_async_complete,
- };
-
- /**
- * Start asynchronous transfer
- *
- * @v usbintf EFI USB interface
- * @v endpoint Endpoint address
- * @v interval Interval (in milliseconds)
- * @v len Transfer length
- * @v callback Callback function
- * @v context Context for callback function
- * @ret rc Return status code
- */
- static int efi_usb_async_start ( struct efi_usb_interface *usbintf,
- unsigned int endpoint, unsigned int interval,
- size_t len,
- EFI_ASYNC_USB_TRANSFER_CALLBACK callback,
- void *context ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
- struct efi_usb_device *usbdev = usbintf->usbdev;
- struct efi_usb_endpoint *usbep;
- unsigned int index = USB_ENDPOINT_IDX ( endpoint );
- EFI_STATUS efirc;
- int rc;
-
- /* Open endpoint */
- if ( ( rc = efi_usb_open ( usbintf, endpoint,
- USB_ENDPOINT_ATTR_INTERRUPT, interval,
- &efi_usb_async_driver ) ) != 0 )
- goto err_open;
- usbep = usbintf->endpoint[index];
-
- /* Record callback parameters */
- usbep->callback = callback;
- usbep->context = context;
-
- /* Prefill endpoint */
- usb_refill_init ( &usbep->ep, 0, len, EFI_USB_ASYNC_FILL );
- if ( ( rc = usb_prefill ( &usbep->ep ) ) != 0 ) {
- DBGC ( usbdev, "USBDEV %s %s could not prefill: %s\n",
- usbintf->name, usb_endpoint_name ( &usbep->ep ),
- strerror ( rc ) );
- goto err_prefill;
- }
-
- /* Start timer */
- if ( ( efirc = bs->SetTimer ( usbep->event, TimerPeriodic,
- ( interval * 10000 ) ) ) != 0 ) {
- rc = -EEFI ( efirc );
- DBGC ( usbdev, "USBDEV %s %s could not set timer: %s\n",
- usbintf->name, usb_endpoint_name ( &usbep->ep ),
- strerror ( rc ) );
- goto err_timer;
- }
-
- return 0;
-
- bs->SetTimer ( usbep->event, TimerCancel, 0 );
- err_timer:
- err_prefill:
- efi_usb_close ( usbep );
- err_open:
- return rc;
- }
-
- /**
- * Stop asynchronous transfer
- *
- * @v usbintf EFI USB interface
- * @v endpoint Endpoint address
- */
- static void efi_usb_async_stop ( struct efi_usb_interface *usbintf,
- unsigned int endpoint ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
- struct efi_usb_endpoint *usbep;
- unsigned int index = USB_ENDPOINT_IDX ( endpoint );
-
- /* Do nothing if endpoint is already closed */
- usbep = usbintf->endpoint[index];
- if ( ! usbep )
- return;
-
- /* Stop timer */
- bs->SetTimer ( usbep->event, TimerCancel, 0 );
-
- /* Close endpoint */
- efi_usb_close ( usbep );
- }
-
- /******************************************************************************
- *
- * USB I/O protocol
- *
- ******************************************************************************
- */
-
- /**
- * Perform control transfer
- *
- * @v usbio USB I/O protocol
- * @v packet Setup packet
- * @v direction Data direction
- * @v timeout Timeout (in milliseconds)
- * @v data Data buffer
- * @v len Length of data
- * @ret status Transfer status
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_usb_control_transfer ( EFI_USB_IO_PROTOCOL *usbio,
- EFI_USB_DEVICE_REQUEST *packet,
- EFI_USB_DATA_DIRECTION direction,
- UINT32 timeout, VOID *data, UINTN len,
- UINT32 *status ) {
- struct efi_usb_interface *usbintf =
- container_of ( usbio, struct efi_usb_interface, usbio );
- struct efi_usb_device *usbdev = usbintf->usbdev;
- unsigned int request = ( packet->RequestType |
- USB_REQUEST_TYPE ( packet->Request ) );
- unsigned int value = le16_to_cpu ( packet->Value );
- unsigned int index = le16_to_cpu ( packet->Index );
- int rc;
-
- DBGC2 ( usbdev, "USBDEV %s control %04x:%04x:%04x:%04x %s %dms "
- "%p+%zx\n", usbintf->name, request, value, index,
- le16_to_cpu ( packet->Length ),
- efi_usb_direction_name ( direction ), timeout, data,
- ( ( size_t ) len ) );
-
- /* Clear status */
- *status = 0;
-
- /* Block attempts to change the device configuration, since
- * this is logically impossible to do given the constraints of
- * the EFI_USB_IO_PROTOCOL design.
- */
- if ( ( request == USB_SET_CONFIGURATION ) &&
- ( value != usbdev->config->config ) ) {
- DBGC ( usbdev, "USBDEV %s cannot set configuration %d: not "
- "logically possible\n", usbintf->name, index );
- rc = -ENOTSUP;
- goto err_change_config;
- }
-
- /* If we are selecting a new alternate setting then close all
- * open endpoints.
- */
- if ( ( request == USB_SET_INTERFACE ) &&
- ( value != usbintf->alternate ) )
- efi_usb_close_all ( usbintf );
-
- /* Issue control transfer */
- if ( ( rc = usb_control ( usbdev->usb, request, value, index,
- data, len ) ) != 0 ) {
- DBGC ( usbdev, "USBDEV %s control %04x:%04x:%04x:%04x %p+%zx "
- "failed: %s\n", usbintf->name, request, value, index,
- le16_to_cpu ( packet->Length ), data, ( ( size_t ) len ),
- strerror ( rc ) );
- /* Assume that any error represents a stall */
- *status = EFI_USB_ERR_STALL;
- goto err_control;
- }
-
- /* Update alternate setting, if applicable */
- if ( request == USB_SET_INTERFACE ) {
- usbintf->alternate = value;
- DBGC ( usbdev, "USBDEV %s alt %d selected\n",
- usbintf->name, usbintf->alternate );
- }
-
- err_control:
- err_change_config:
- return EFIRC ( rc );
- }
-
- /**
- * Perform bulk transfer
- *
- * @v usbio USB I/O protocol
- * @v endpoint Endpoint address
- * @v data Data buffer
- * @v len Length of data
- * @v timeout Timeout (in milliseconds)
- * @ret status Transfer status
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_usb_bulk_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, VOID *data,
- UINTN *len, UINTN timeout, UINT32 *status ) {
- struct efi_usb_interface *usbintf =
- container_of ( usbio, struct efi_usb_interface, usbio );
- struct efi_usb_device *usbdev = usbintf->usbdev;
- size_t actual = *len;
- int rc;
-
- DBGC2 ( usbdev, "USBDEV %s bulk %s %p+%zx %dms\n", usbintf->name,
- ( ( endpoint & USB_ENDPOINT_IN ) ? "IN" : "OUT" ), data,
- ( ( size_t ) *len ), ( ( unsigned int ) timeout ) );
-
- /* Clear status */
- *status = 0;
-
- /* Perform synchronous transfer */
- if ( ( rc = efi_usb_sync_transfer ( usbintf, endpoint,
- USB_ENDPOINT_ATTR_BULK, timeout,
- data, &actual ) ) != 0 ) {
- /* Assume that any error represents a timeout */
- *status = EFI_USB_ERR_TIMEOUT;
- return rc;
- }
-
- return 0;
- }
-
- /**
- * Perform synchronous interrupt transfer
- *
- * @v usbio USB I/O protocol
- * @v endpoint Endpoint address
- * @v data Data buffer
- * @v len Length of data
- * @v timeout Timeout (in milliseconds)
- * @ret status Transfer status
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_usb_sync_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint,
- VOID *data, UINTN *len, UINTN timeout,
- UINT32 *status ) {
- struct efi_usb_interface *usbintf =
- container_of ( usbio, struct efi_usb_interface, usbio );
- struct efi_usb_device *usbdev = usbintf->usbdev;
- size_t actual = *len;
- int rc;
-
- DBGC2 ( usbdev, "USBDEV %s sync intr %s %p+%zx %dms\n", usbintf->name,
- ( ( endpoint & USB_ENDPOINT_IN ) ? "IN" : "OUT" ), data,
- ( ( size_t ) *len ), ( ( unsigned int ) timeout ) );
-
- /* Clear status */
- *status = 0;
-
- /* Perform synchronous transfer */
- if ( ( rc = efi_usb_sync_transfer ( usbintf, endpoint,
- USB_ENDPOINT_ATTR_INTERRUPT,
- timeout, data, &actual ) ) != 0 ) {
- /* Assume that any error represents a timeout */
- *status = EFI_USB_ERR_TIMEOUT;
- return rc;
- }
-
- return 0;
- }
-
- /**
- * Perform asynchronous interrupt transfer
- *
- * @v usbio USB I/O protocol
- * @v endpoint Endpoint address
- * @v start Start (rather than stop) transfer
- * @v interval Polling interval (in milliseconds)
- * @v len Data length
- * @v callback Callback function
- * @v context Context for callback function
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_usb_async_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint,
- BOOLEAN start, UINTN interval, UINTN len,
- EFI_ASYNC_USB_TRANSFER_CALLBACK callback,
- VOID *context ) {
- struct efi_usb_interface *usbintf =
- container_of ( usbio, struct efi_usb_interface, usbio );
- struct efi_usb_device *usbdev = usbintf->usbdev;
- int rc;
-
- DBGC2 ( usbdev, "USBDEV %s async intr %s len %#zx int %d %p/%p\n",
- usbintf->name,
- ( ( endpoint & USB_ENDPOINT_IN ) ? "IN" : "OUT" ),
- ( ( size_t ) len ), ( ( unsigned int ) interval ),
- callback, context );
-
- /* Start/stop transfer as applicable */
- if ( start ) {
-
- /* Start new transfer */
- if ( ( rc = efi_usb_async_start ( usbintf, endpoint, interval,
- len, callback,
- context ) ) != 0 )
- goto err_start;
-
- } else {
-
- /* Stop transfer */
- efi_usb_async_stop ( usbintf, endpoint );
-
- }
-
- return 0;
-
- err_start:
- return EFIRC ( rc );
- }
-
- /**
- * Perform synchronous isochronous transfer
- *
- * @v usbio USB I/O protocol
- * @v endpoint Endpoint address
- * @v data Data buffer
- * @v len Length of data
- * @ret status Transfer status
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_usb_isochronous_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint,
- VOID *data, UINTN len, UINT32 *status ) {
- struct efi_usb_interface *usbintf =
- container_of ( usbio, struct efi_usb_interface, usbio );
- struct efi_usb_device *usbdev = usbintf->usbdev;
-
- DBGC2 ( usbdev, "USBDEV %s sync iso %s %p+%zx\n", usbintf->name,
- ( ( endpoint & USB_ENDPOINT_IN ) ? "IN" : "OUT" ), data,
- ( ( size_t ) len ) );
-
- /* Clear status */
- *status = 0;
-
- /* Not supported */
- return EFI_UNSUPPORTED;
- }
-
- /**
- * Perform asynchronous isochronous transfers
- *
- * @v usbio USB I/O protocol
- * @v endpoint Endpoint address
- * @v data Data buffer
- * @v len Length of data
- * @v callback Callback function
- * @v context Context for callback function
- * @ret status Transfer status
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_usb_async_isochronous_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint,
- VOID *data, UINTN len,
- EFI_ASYNC_USB_TRANSFER_CALLBACK callback,
- VOID *context ) {
- struct efi_usb_interface *usbintf =
- container_of ( usbio, struct efi_usb_interface, usbio );
- struct efi_usb_device *usbdev = usbintf->usbdev;
-
- DBGC2 ( usbdev, "USBDEV %s async iso %s %p+%zx %p/%p\n", usbintf->name,
- ( ( endpoint & USB_ENDPOINT_IN ) ? "IN" : "OUT" ), data,
- ( ( size_t ) len ), callback, context );
-
- /* Not supported */
- return EFI_UNSUPPORTED;
- }
-
- /**
- * Get device descriptor
- *
- * @v usbio USB I/O protocol
- * @ret efidesc EFI device descriptor
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_usb_get_device_descriptor ( EFI_USB_IO_PROTOCOL *usbio,
- EFI_USB_DEVICE_DESCRIPTOR *efidesc ) {
- struct efi_usb_interface *usbintf =
- container_of ( usbio, struct efi_usb_interface, usbio );
- struct efi_usb_device *usbdev = usbintf->usbdev;
-
- DBGC2 ( usbdev, "USBDEV %s get device descriptor\n", usbintf->name );
-
- /* Copy cached device descriptor */
- memcpy ( efidesc, &usbdev->usb->device, sizeof ( *efidesc ) );
-
- return 0;
- }
-
- /**
- * Get configuration descriptor
- *
- * @v usbio USB I/O protocol
- * @ret efidesc EFI interface descriptor
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_usb_get_config_descriptor ( EFI_USB_IO_PROTOCOL *usbio,
- EFI_USB_CONFIG_DESCRIPTOR *efidesc ) {
- struct efi_usb_interface *usbintf =
- container_of ( usbio, struct efi_usb_interface, usbio );
- struct efi_usb_device *usbdev = usbintf->usbdev;
-
- DBGC2 ( usbdev, "USBDEV %s get configuration descriptor\n",
- usbintf->name );
-
- /* Copy cached configuration descriptor */
- memcpy ( efidesc, usbdev->config, sizeof ( *efidesc ) );
-
- return 0;
- }
-
- /**
- * Get interface descriptor
- *
- * @v usbio USB I/O protocol
- * @ret efidesc EFI interface descriptor
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_usb_get_interface_descriptor ( EFI_USB_IO_PROTOCOL *usbio,
- EFI_USB_INTERFACE_DESCRIPTOR *efidesc ) {
- struct efi_usb_interface *usbintf =
- container_of ( usbio, struct efi_usb_interface, usbio );
- struct efi_usb_device *usbdev = usbintf->usbdev;
- struct usb_interface_descriptor *desc;
-
- DBGC2 ( usbdev, "USBDEV %s get interface descriptor\n", usbintf->name );
-
- /* Locate cached interface descriptor */
- desc = usb_interface_descriptor ( usbdev->config, usbintf->interface,
- usbintf->alternate );
- if ( ! desc ) {
- DBGC ( usbdev, "USBDEV %s alt %d has no interface descriptor\n",
- usbintf->name, usbintf->alternate );
- return -ENOENT;
- }
-
- /* Copy cached interface descriptor */
- memcpy ( efidesc, desc, sizeof ( *efidesc ) );
-
- return 0;
- }
-
- /**
- * Get endpoint descriptor
- *
- * @v usbio USB I/O protocol
- * @v address Endpoint index
- * @ret efidesc EFI interface descriptor
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_usb_get_endpoint_descriptor ( EFI_USB_IO_PROTOCOL *usbio, UINT8 index,
- EFI_USB_ENDPOINT_DESCRIPTOR *efidesc ) {
- struct efi_usb_interface *usbintf =
- container_of ( usbio, struct efi_usb_interface, usbio );
- struct efi_usb_device *usbdev = usbintf->usbdev;
- struct usb_interface_descriptor *interface;
- struct usb_endpoint_descriptor *desc;
-
- DBGC2 ( usbdev, "USBDEV %s get endpoint %d descriptor\n",
- usbintf->name, index );
-
- /* Locate cached interface descriptor */
- interface = usb_interface_descriptor ( usbdev->config,
- usbintf->interface,
- usbintf->alternate );
- if ( ! interface ) {
- DBGC ( usbdev, "USBDEV %s alt %d has no interface descriptor\n",
- usbintf->name, usbintf->alternate );
- return -ENOENT;
- }
-
- /* Locate and copy cached endpoint descriptor */
- for_each_interface_descriptor ( desc, usbdev->config, interface ) {
- if ( ( desc->header.type == USB_ENDPOINT_DESCRIPTOR ) &&
- ( index-- == 0 ) ) {
- memcpy ( efidesc, desc, sizeof ( *efidesc ) );
- return 0;
- }
- }
- return -ENOENT;
- }
-
- /**
- * Get string descriptor
- *
- * @v usbio USB I/O protocol
- * @v language Language ID
- * @v index String index
- * @ret string String
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_usb_get_string_descriptor ( EFI_USB_IO_PROTOCOL *usbio, UINT16 language,
- UINT8 index, CHAR16 **string ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
- struct efi_usb_interface *usbintf =
- container_of ( usbio, struct efi_usb_interface, usbio );
- struct efi_usb_device *usbdev = usbintf->usbdev;
- struct usb_descriptor_header header;
- VOID *buffer;
- size_t len;
- EFI_STATUS efirc;
- int rc;
-
- DBGC2 ( usbdev, "USBDEV %s get string %d:%d descriptor\n",
- usbintf->name, language, index );
-
- /* Read descriptor header */
- if ( ( rc = usb_get_descriptor ( usbdev->usb, 0, USB_STRING_DESCRIPTOR,
- index, language, &header,
- sizeof ( header ) ) ) != 0 ) {
- DBGC ( usbdev, "USBDEV %s could not get string %d:%d "
- "descriptor header: %s\n", usbintf->name, language,
- index, strerror ( rc ) );
- goto err_get_header;
- }
- len = header.len;
-
- /* Allocate buffer */
- if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, len,
- &buffer ) ) != 0 ) {
- rc = -EEFI ( efirc );
- goto err_alloc;
- }
-
- /* Read whole descriptor */
- if ( ( rc = usb_get_descriptor ( usbdev->usb, 0, USB_STRING_DESCRIPTOR,
- index, language, buffer,
- len ) ) != 0 ) {
- DBGC ( usbdev, "USBDEV %s could not get string %d:%d "
- "descriptor: %s\n", usbintf->name, language,
- index, strerror ( rc ) );
- goto err_get_descriptor;
- }
-
- /* Shuffle down and terminate string */
- memmove ( buffer, ( buffer + sizeof ( header ) ),
- ( len - sizeof ( header ) ) );
- memset ( ( buffer + len - sizeof ( header ) ), 0, sizeof ( **string ) );
-
- /* Return allocated string */
- *string = buffer;
- return 0;
-
- err_get_descriptor:
- bs->FreePool ( buffer );
- err_alloc:
- err_get_header:
- return EFIRC ( rc );
- }
-
- /**
- * Get supported languages
- *
- * @v usbio USB I/O protocol
- * @ret languages Language ID table
- * @ret len Length of language ID table
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_usb_get_supported_languages ( EFI_USB_IO_PROTOCOL *usbio,
- UINT16 **languages, UINT16 *len ) {
- struct efi_usb_interface *usbintf =
- container_of ( usbio, struct efi_usb_interface, usbio );
- struct efi_usb_device *usbdev = usbintf->usbdev;
-
- DBGC2 ( usbdev, "USBDEV %s get supported languages\n", usbintf->name );
-
- /* Return cached supported languages */
- *languages = ( ( ( void * ) usbdev->languages ) +
- sizeof ( *(usbdev->languages) ) );
- *len = usbdev->languages->len;
-
- return 0;
- }
-
- /**
- * Reset port
- *
- * @v usbio USB I/O protocol
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_usb_port_reset ( EFI_USB_IO_PROTOCOL *usbio ) {
- struct efi_usb_interface *usbintf =
- container_of ( usbio, struct efi_usb_interface, usbio );
- struct efi_usb_device *usbdev = usbintf->usbdev;
-
- DBGC2 ( usbdev, "USBDEV %s reset port\n", usbintf->name );
-
- /* This is logically impossible to do, since resetting the
- * port may destroy state belonging to other
- * EFI_USB_IO_PROTOCOL instances belonging to the same USB
- * device. (This is yet another artifact of the incredibly
- * poor design of the EFI_USB_IO_PROTOCOL.)
- */
- return EFI_INVALID_PARAMETER;
- }
-
- /** USB I/O protocol */
- static EFI_USB_IO_PROTOCOL efi_usb_io_protocol = {
- .UsbControlTransfer = efi_usb_control_transfer,
- .UsbBulkTransfer = efi_usb_bulk_transfer,
- .UsbAsyncInterruptTransfer = efi_usb_async_interrupt_transfer,
- .UsbSyncInterruptTransfer = efi_usb_sync_interrupt_transfer,
- .UsbIsochronousTransfer = efi_usb_isochronous_transfer,
- .UsbAsyncIsochronousTransfer = efi_usb_async_isochronous_transfer,
- .UsbGetDeviceDescriptor = efi_usb_get_device_descriptor,
- .UsbGetConfigDescriptor = efi_usb_get_config_descriptor,
- .UsbGetInterfaceDescriptor = efi_usb_get_interface_descriptor,
- .UsbGetEndpointDescriptor = efi_usb_get_endpoint_descriptor,
- .UsbGetStringDescriptor = efi_usb_get_string_descriptor,
- .UsbGetSupportedLanguages = efi_usb_get_supported_languages,
- .UsbPortReset = efi_usb_port_reset,
- };
-
- /******************************************************************************
- *
- * USB driver
- *
- ******************************************************************************
- */
-
- /**
- * Install interface
- *
- * @v usbdev EFI USB device
- * @v interface Interface number
- * @ret rc Return status code
- */
- static int efi_usb_install ( struct efi_usb_device *usbdev,
- unsigned int interface ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
- struct efi_device *efidev = usbdev->efidev;
- struct efi_usb_interface *usbintf;
- struct usb_device *usb;
- EFI_DEVICE_PATH_PROTOCOL *path_end;
- USB_DEVICE_PATH *usbpath;
- unsigned int path_count;
- size_t path_prefix_len;
- size_t path_len;
- EFI_STATUS efirc;
- int rc;
-
- /* Calculate device path length */
- path_count = ( usb_depth ( usbdev->usb ) + 1 );
- path_prefix_len = efi_devpath_len ( efidev->path );
- path_len = ( path_prefix_len + ( path_count * sizeof ( *usbpath ) ) +
- sizeof ( *path_end ) );
-
- /* Allocate and initialise structure */
- usbintf = zalloc ( sizeof ( *usbintf ) + path_len );
- if ( ! usbintf ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
- snprintf ( usbintf->name, sizeof ( usbintf->name ), "%s[%d]",
- usbdev->name, interface );
- usbintf->usbdev = usbdev;
- usbintf->interface = interface;
- memcpy ( &usbintf->usbio, &efi_usb_io_protocol,
- sizeof ( usbintf->usbio ) );
- usbintf->path = ( ( ( void * ) usbintf ) + sizeof ( *usbintf ) );
-
- /* Construct device path */
- memcpy ( usbintf->path, efidev->path, path_prefix_len );
- path_end = ( ( ( void * ) usbintf->path ) + path_len -
- sizeof ( *path_end ) );
- path_end->Type = END_DEVICE_PATH_TYPE;
- path_end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE;
- path_end->Length[0] = sizeof ( *path_end );
- usbpath = ( ( ( void * ) path_end ) - sizeof ( *usbpath ) );
- usbpath->InterfaceNumber = interface;
- for ( usb = usbdev->usb ; usb ; usbpath--, usb = usb->port->hub->usb ) {
- usbpath->Header.Type = MESSAGING_DEVICE_PATH;
- usbpath->Header.SubType = MSG_USB_DP;
- usbpath->Header.Length[0] = sizeof ( *usbpath );
- usbpath->ParentPortNumber = usb->port->address;
- }
-
- /* Add to list of interfaces */
- list_add_tail ( &usbintf->list, &usbdev->interfaces );
-
- /* Install protocols */
- if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
- &usbintf->handle,
- &efi_usb_io_protocol_guid, &usbintf->usbio,
- &efi_device_path_protocol_guid, usbintf->path,
- NULL ) ) != 0 ) {
- rc = -EEFI ( efirc );
- DBGC ( usbdev, "USBDEV %s could not install protocols: %s\n",
- usbintf->name, strerror ( rc ) );
- goto err_install_protocol;
- }
-
- DBGC ( usbdev, "USBDEV %s installed as %s\n",
- usbintf->name, efi_handle_name ( usbintf->handle ) );
- return 0;
-
- efi_usb_close_all ( usbintf );
- bs->UninstallMultipleProtocolInterfaces (
- usbintf->handle,
- &efi_usb_io_protocol_guid, &usbintf->usbio,
- &efi_device_path_protocol_guid, usbintf->path,
- NULL );
- err_install_protocol:
- list_del ( &usbintf->list );
- free ( usbintf );
- err_alloc:
- return rc;
- }
-
- /**
- * Uninstall interface
- *
- * @v usbintf EFI USB interface
- */
- static void efi_usb_uninstall ( struct efi_usb_interface *usbintf ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
-
- /* Close all endpoints */
- efi_usb_close_all ( usbintf );
-
- /* Uninstall protocols */
- bs->UninstallMultipleProtocolInterfaces (
- usbintf->handle,
- &efi_usb_io_protocol_guid, &usbintf->usbio,
- &efi_device_path_protocol_guid, usbintf->path,
- NULL );
-
- /* Remove from list of interfaces */
- list_del ( &usbintf->list );
-
- /* Free interface */
- free ( usbintf );
- }
-
- /**
- * Uninstall all interfaces
- *
- * @v usbdev EFI USB device
- */
- static void efi_usb_uninstall_all ( struct efi_usb_device *efiusb ) {
- struct efi_usb_interface *usbintf;
-
- /* Uninstall all interfaces */
- while ( ( usbintf = list_first_entry ( &efiusb->interfaces,
- struct efi_usb_interface,
- list ) ) ) {
- efi_usb_uninstall ( usbintf );
- }
- }
-
- /**
- * Probe device
- *
- * @v func USB function
- * @v config Configuration descriptor
- * @ret rc Return status code
- */
- static int efi_usb_probe ( struct usb_function *func,
- struct usb_configuration_descriptor *config ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
- struct usb_device *usb = func->usb;
- struct efi_usb_device *usbdev;
- struct efi_usb_interface *usbintf;
- struct efi_device *efidev;
- struct usb_descriptor_header header;
- size_t config_len;
- unsigned int i;
- int rc;
-
- /* Find parent EFI device */
- efidev = efidev_parent ( &func->dev );
- if ( ! efidev ) {
- rc = -ENOTTY;
- goto err_no_efidev;
- }
-
- /* Get configuration length */
- config_len = le16_to_cpu ( config->len );
-
- /* Get supported languages descriptor header */
- if ( ( rc = usb_get_descriptor ( usb, 0, USB_STRING_DESCRIPTOR, 0, 0,
- &header, sizeof ( header ) ) ) != 0 ) {
- /* Assume no strings are present */
- header.len = 0;
- }
-
- /* Allocate and initialise structure */
- usbdev = zalloc ( sizeof ( *usbdev ) + config_len + header.len );
- if ( ! usbdev ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
- usb_func_set_drvdata ( func, usbdev );
- usbdev->name = func->name;
- usbdev->usb = usb;
- usbdev->efidev = efidev;
- usbdev->config = ( ( ( void * ) usbdev ) + sizeof ( *usbdev ) );
- memcpy ( usbdev->config, config, config_len );
- usbdev->languages = ( ( ( void * ) usbdev->config ) + config_len );
- INIT_LIST_HEAD ( &usbdev->interfaces );
-
- /* Get supported languages descriptor */
- if ( header.len &&
- ( rc = usb_get_descriptor ( usb, 0, USB_STRING_DESCRIPTOR, 0, 0,
- usbdev->languages,
- header.len ) ) != 0 ) {
- DBGC ( usbdev, "USBDEV %s could not get supported languages: "
- "%s\n", usbdev->name, strerror ( rc ) );
- goto err_get_languages;
- }
-
- /* Install interfaces */
- for ( i = 0 ; i < func->desc.count ; i++ ) {
- if ( ( rc = efi_usb_install ( usbdev,
- func->interface[i] ) ) != 0 )
- goto err_install;
- }
-
- /* Connect any external drivers */
- list_for_each_entry ( usbintf, &usbdev->interfaces, list )
- bs->ConnectController ( usbintf->handle, NULL, NULL, TRUE );
-
- return 0;
-
- err_install:
- efi_usb_uninstall_all ( usbdev );
- assert ( list_empty ( &usbdev->interfaces ) );
- err_get_languages:
- free ( usbdev );
- err_alloc:
- err_no_efidev:
- return rc;
- }
-
- /**
- * Remove device
- *
- * @v func USB function
- */
- static void efi_usb_remove ( struct usb_function *func ) {
- struct efi_usb_device *usbdev = usb_func_get_drvdata ( func );
-
- /* Uninstall all interfaces */
- efi_usb_uninstall_all ( usbdev );
- assert ( list_empty ( &usbdev->interfaces ) );
-
- /* Free device */
- free ( usbdev );
- }
-
- /** USB I/O protocol device IDs */
- static struct usb_device_id efi_usb_ids[] = {
- {
- .name = "usbio",
- .vendor = USB_ANY_ID,
- .product = USB_ANY_ID,
- },
- };
-
- /** USB I/O protocol driver */
- struct usb_driver usbio_driver __usb_driver = {
- .ids = efi_usb_ids,
- .id_count = ( sizeof ( efi_usb_ids ) / sizeof ( efi_usb_ids[0] ) ),
- .class = USB_CLASS_ID ( USB_ANY_ID, USB_ANY_ID, USB_ANY_ID ),
- .score = USB_SCORE_FALLBACK,
- .probe = efi_usb_probe,
- .remove = efi_usb_remove,
- };
|