diff options
author | 2017-04-25 03:31:15 -0700 | |
---|---|---|
committer | 2017-05-22 06:48:08 +0000 | |
commit | bb756eebdac6fd24e8919e2c43f7d2c8c4091f59 (patch) | |
tree | ca11e03542edf2d8f631efeca5e1626d211107e3 /qemu/roms/ipxe/src/drivers/usb | |
parent | a14b48d18a9ed03ec191cf16b162206998a895ce (diff) |
Adding qemu as a submodule of KVMFORNFV
This Patch includes the changes to add qemu as a submodule to
kvmfornfv repo and make use of the updated latest qemu for the
execution of all testcase
Change-Id: I1280af507a857675c7f81d30c95255635667bdd7
Signed-off-by:RajithaY<rajithax.yerrumsetty@intel.com>
Diffstat (limited to 'qemu/roms/ipxe/src/drivers/usb')
-rw-r--r-- | qemu/roms/ipxe/src/drivers/usb/ehci.c | 1994 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/drivers/usb/ehci.h | 544 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/drivers/usb/uhci.c | 1577 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/drivers/usb/uhci.h | 350 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/drivers/usb/usbhid.c | 151 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/drivers/usb/usbhub.c | 547 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/drivers/usb/usbhub.h | 279 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/drivers/usb/usbkbd.c | 509 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/drivers/usb/usbkbd.h | 154 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/drivers/usb/usbnet.c | 284 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/drivers/usb/xhci.c | 3321 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/drivers/usb/xhci.h | 1150 |
12 files changed, 0 insertions, 10860 deletions
diff --git a/qemu/roms/ipxe/src/drivers/usb/ehci.c b/qemu/roms/ipxe/src/drivers/usb/ehci.c deleted file mode 100644 index 4124692a6..000000000 --- a/qemu/roms/ipxe/src/drivers/usb/ehci.c +++ /dev/null @@ -1,1994 +0,0 @@ -/* - * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * You can also choose to distribute this program under the terms of - * the Unmodified Binary Distribution Licence (as given in the file - * COPYING.UBDL), provided that you have satisfied its requirements. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <strings.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/malloc.h> -#include <ipxe/pci.h> -#include <ipxe/usb.h> -#include <ipxe/init.h> -#include "ehci.h" - -/** @file - * - * USB Enhanced Host Controller Interface (EHCI) driver - * - */ - -/** - * Construct error code from transfer descriptor status - * - * @v status Transfer descriptor status - * @ret rc Error code - * - * Bits 2-5 of the status code provide some indication as to the root - * cause of the error. We incorporate these into the error code as - * reported to usb_complete_err(). - */ -#define EIO_STATUS( status ) EUNIQ ( EINFO_EIO, ( ( (status) >> 2 ) & 0xf ) ) - -/****************************************************************************** - * - * Register access - * - ****************************************************************************** - */ - -/** - * Initialise device - * - * @v ehci EHCI device - * @v regs MMIO registers - */ -static void ehci_init ( struct ehci_device *ehci, void *regs ) { - uint32_t hcsparams; - uint32_t hccparams; - size_t caplength; - - /* Locate capability and operational registers */ - ehci->cap = regs; - caplength = readb ( ehci->cap + EHCI_CAP_CAPLENGTH ); - ehci->op = ( ehci->cap + caplength ); - DBGC2 ( ehci, "EHCI %s cap %08lx op %08lx\n", ehci->name, - virt_to_phys ( ehci->cap ), virt_to_phys ( ehci->op ) ); - - /* Read structural parameters */ - hcsparams = readl ( ehci->cap + EHCI_CAP_HCSPARAMS ); - ehci->ports = EHCI_HCSPARAMS_PORTS ( hcsparams ); - DBGC ( ehci, "EHCI %s has %d ports\n", ehci->name, ehci->ports ); - - /* Read capability parameters 1 */ - hccparams = readl ( ehci->cap + EHCI_CAP_HCCPARAMS ); - ehci->addr64 = EHCI_HCCPARAMS_ADDR64 ( hccparams ); - ehci->flsize = ( EHCI_HCCPARAMS_FLSIZE ( hccparams ) ? - EHCI_FLSIZE_SMALL : EHCI_FLSIZE_DEFAULT ); - ehci->eecp = EHCI_HCCPARAMS_EECP ( hccparams ); - DBGC2 ( ehci, "EHCI %s %d-bit flsize %d\n", ehci->name, - ( ehci->addr64 ? 64 : 32 ), ehci->flsize ); -} - -/** - * Find extended capability - * - * @v ehci EHCI device - * @v pci PCI device - * @v id Capability ID - * @v offset Offset to previous extended capability instance, or zero - * @ret offset Offset to extended capability, or zero if not found - */ -static unsigned int ehci_extended_capability ( struct ehci_device *ehci, - struct pci_device *pci, - unsigned int id, - unsigned int offset ) { - uint32_t eecp; - - /* Locate the extended capability */ - while ( 1 ) { - - /* Locate first or next capability as applicable */ - if ( offset ) { - pci_read_config_dword ( pci, offset, &eecp ); - offset = EHCI_EECP_NEXT ( eecp ); - } else { - offset = ehci->eecp; - } - if ( ! offset ) - return 0; - - /* Check if this is the requested capability */ - pci_read_config_dword ( pci, offset, &eecp ); - if ( EHCI_EECP_ID ( eecp ) == id ) - return offset; - } -} - -/** - * Calculate buffer alignment - * - * @v len Length - * @ret align Buffer alignment - * - * Determine alignment required for a buffer which must be aligned to - * at least EHCI_MIN_ALIGN and which must not cross a page boundary. - */ -static inline size_t ehci_align ( size_t len ) { - size_t align; - - /* Align to own length (rounded up to a power of two) */ - align = ( 1 << fls ( len - 1 ) ); - - /* Round up to EHCI_MIN_ALIGN if needed */ - if ( align < EHCI_MIN_ALIGN ) - align = EHCI_MIN_ALIGN; - - return align; -} - -/** - * Check control data structure reachability - * - * @v ehci EHCI device - * @v ptr Data structure pointer - * @ret rc Return status code - */ -static int ehci_ctrl_reachable ( struct ehci_device *ehci, void *ptr ) { - physaddr_t phys = virt_to_phys ( ptr ); - uint32_t segment; - - /* Always reachable in a 32-bit build */ - if ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) ) - return 0; - - /* Reachable only if control segment matches in a 64-bit build */ - segment = ( ( ( uint64_t ) phys ) >> 32 ); - if ( segment == ehci->ctrldssegment ) - return 0; - - return -ENOTSUP; -} - -/****************************************************************************** - * - * USB legacy support - * - ****************************************************************************** - */ - -/** Prevent the release of ownership back to BIOS */ -static int ehci_legacy_prevent_release; - -/** - * Initialise USB legacy support - * - * @v ehci EHCI device - * @v pci PCI device - */ -static void ehci_legacy_init ( struct ehci_device *ehci, - struct pci_device *pci ) { - unsigned int legacy; - uint8_t bios; - - /* Locate USB legacy support capability (if present) */ - legacy = ehci_extended_capability ( ehci, pci, EHCI_EECP_ID_LEGACY, 0 ); - if ( ! legacy ) { - /* Not an error; capability may not be present */ - DBGC ( ehci, "EHCI %s has no USB legacy support capability\n", - ehci->name ); - return; - } - - /* Check if legacy USB support is enabled */ - pci_read_config_byte ( pci, ( legacy + EHCI_USBLEGSUP_BIOS ), &bios ); - if ( ! ( bios & EHCI_USBLEGSUP_BIOS_OWNED ) ) { - /* Not an error; already owned by OS */ - DBGC ( ehci, "EHCI %s USB legacy support already disabled\n", - ehci->name ); - return; - } - - /* Record presence of USB legacy support capability */ - ehci->legacy = legacy; -} - -/** - * Claim ownership from BIOS - * - * @v ehci EHCI device - * @v pci PCI device - */ -static void ehci_legacy_claim ( struct ehci_device *ehci, - struct pci_device *pci ) { - unsigned int legacy = ehci->legacy; - uint32_t ctlsts; - uint8_t bios; - unsigned int i; - - /* Do nothing unless legacy support capability is present */ - if ( ! legacy ) - return; - - /* Claim ownership */ - pci_write_config_byte ( pci, ( legacy + EHCI_USBLEGSUP_OS ), - EHCI_USBLEGSUP_OS_OWNED ); - - /* Wait for BIOS to release ownership */ - for ( i = 0 ; i < EHCI_USBLEGSUP_MAX_WAIT_MS ; i++ ) { - - /* Check if BIOS has released ownership */ - pci_read_config_byte ( pci, ( legacy + EHCI_USBLEGSUP_BIOS ), - &bios ); - if ( ! ( bios & EHCI_USBLEGSUP_BIOS_OWNED ) ) { - DBGC ( ehci, "EHCI %s claimed ownership from BIOS\n", - ehci->name ); - pci_read_config_dword ( pci, ( legacy + - EHCI_USBLEGSUP_CTLSTS ), - &ctlsts ); - if ( ctlsts ) { - DBGC ( ehci, "EHCI %s warning: BIOS retained " - "SMIs: %08x\n", ehci->name, ctlsts ); - } - return; - } - - /* Delay */ - mdelay ( 1 ); - } - - /* BIOS did not release ownership. Claim it forcibly by - * disabling all SMIs. - */ - DBGC ( ehci, "EHCI %s could not claim ownership from BIOS: forcibly " - "disabling SMIs\n", ehci->name ); - pci_write_config_dword ( pci, ( legacy + EHCI_USBLEGSUP_CTLSTS ), 0 ); -} - -/** - * Release ownership back to BIOS - * - * @v ehci EHCI device - * @v pci PCI device - */ -static void ehci_legacy_release ( struct ehci_device *ehci, - struct pci_device *pci ) { - - /* Do nothing unless legacy support capability is present */ - if ( ! ehci->legacy ) - return; - - /* Do nothing if releasing ownership is prevented */ - if ( ehci_legacy_prevent_release ) { - DBGC ( ehci, "EHCI %s not releasing ownership to BIOS\n", - ehci->name ); - return; - } - - /* Release ownership */ - pci_write_config_byte ( pci, ( ehci->legacy + EHCI_USBLEGSUP_OS ), 0 ); - DBGC ( ehci, "EHCI %s released ownership to BIOS\n", ehci->name ); -} - -/****************************************************************************** - * - * Companion controllers - * - ****************************************************************************** - */ - -/** - * Poll child companion controllers - * - * @v ehci EHCI device - */ -static void ehci_poll_companions ( struct ehci_device *ehci ) { - struct usb_bus *bus; - struct device_description *desc; - - /* Poll any USB buses belonging to child companion controllers */ - for_each_usb_bus ( bus ) { - - /* Get underlying devices description */ - desc = &bus->dev->desc; - - /* Skip buses that are not PCI devices */ - if ( desc->bus_type != BUS_TYPE_PCI ) - continue; - - /* Skip buses that are not part of the same PCI device */ - if ( PCI_FIRST_FUNC ( desc->location ) != - PCI_FIRST_FUNC ( ehci->bus->dev->desc.location ) ) - continue; - - /* Skip buses that are not UHCI or OHCI PCI devices */ - if ( ( desc->class != PCI_CLASS ( PCI_CLASS_SERIAL, - PCI_CLASS_SERIAL_USB, - PCI_CLASS_SERIAL_USB_UHCI ))&& - ( desc->class != PCI_CLASS ( PCI_CLASS_SERIAL, - PCI_CLASS_SERIAL_USB, - PCI_CLASS_SERIAL_USB_OHCI ) )) - continue; - - /* Poll child companion controller bus */ - DBGC2 ( ehci, "EHCI %s polling companion %s\n", - ehci->name, bus->name ); - usb_poll ( bus ); - } -} - -/** - * Locate EHCI companion controller - * - * @v pci PCI device - * @ret busdevfn EHCI companion controller bus:dev.fn (if any) - */ -unsigned int ehci_companion ( struct pci_device *pci ) { - struct pci_device tmp; - unsigned int busdevfn; - int rc; - - /* Look for an EHCI function on the same PCI device */ - busdevfn = pci->busdevfn; - while ( ++busdevfn <= PCI_LAST_FUNC ( pci->busdevfn ) ) { - pci_init ( &tmp, busdevfn ); - if ( ( rc = pci_read_config ( &tmp ) ) != 0 ) - continue; - if ( tmp.class == PCI_CLASS ( PCI_CLASS_SERIAL, - PCI_CLASS_SERIAL_USB, - PCI_CLASS_SERIAL_USB_EHCI ) ) - return busdevfn; - } - - return 0; -} - -/****************************************************************************** - * - * Run / stop / reset - * - ****************************************************************************** - */ - -/** - * Start EHCI device - * - * @v ehci EHCI device - */ -static void ehci_run ( struct ehci_device *ehci ) { - uint32_t usbcmd; - - /* Set run/stop bit */ - usbcmd = readl ( ehci->op + EHCI_OP_USBCMD ); - usbcmd &= ~EHCI_USBCMD_FLSIZE_MASK; - usbcmd |= ( EHCI_USBCMD_RUN | EHCI_USBCMD_FLSIZE ( ehci->flsize ) | - EHCI_USBCMD_PERIODIC | EHCI_USBCMD_ASYNC ); - writel ( usbcmd, ehci->op + EHCI_OP_USBCMD ); -} - -/** - * Stop EHCI device - * - * @v ehci EHCI device - * @ret rc Return status code - */ -static int ehci_stop ( struct ehci_device *ehci ) { - uint32_t usbcmd; - uint32_t usbsts; - unsigned int i; - - /* Clear run/stop bit */ - usbcmd = readl ( ehci->op + EHCI_OP_USBCMD ); - usbcmd &= ~( EHCI_USBCMD_RUN | EHCI_USBCMD_PERIODIC | - EHCI_USBCMD_ASYNC ); - writel ( usbcmd, ehci->op + EHCI_OP_USBCMD ); - - /* Wait for device to stop */ - for ( i = 0 ; i < EHCI_STOP_MAX_WAIT_MS ; i++ ) { - - /* Check if device is stopped */ - usbsts = readl ( ehci->op + EHCI_OP_USBSTS ); - if ( usbsts & EHCI_USBSTS_HCH ) - return 0; - - /* Delay */ - mdelay ( 1 ); - } - - DBGC ( ehci, "EHCI %s timed out waiting for stop\n", ehci->name ); - return -ETIMEDOUT; -} - -/** - * Reset EHCI device - * - * @v ehci EHCI device - * @ret rc Return status code - */ -static int ehci_reset ( struct ehci_device *ehci ) { - uint32_t usbcmd; - unsigned int i; - int rc; - - /* The EHCI specification states that resetting a running - * device may result in undefined behaviour, so try stopping - * it first. - */ - if ( ( rc = ehci_stop ( ehci ) ) != 0 ) { - /* Ignore errors and attempt to reset the device anyway */ - } - - /* Reset device */ - writel ( EHCI_USBCMD_HCRST, ehci->op + EHCI_OP_USBCMD ); - - /* Wait for reset to complete */ - for ( i = 0 ; i < EHCI_RESET_MAX_WAIT_MS ; i++ ) { - - /* Check if reset is complete */ - usbcmd = readl ( ehci->op + EHCI_OP_USBCMD ); - if ( ! ( usbcmd & EHCI_USBCMD_HCRST ) ) - return 0; - - /* Delay */ - mdelay ( 1 ); - } - - DBGC ( ehci, "EHCI %s timed out waiting for reset\n", ehci->name ); - return -ETIMEDOUT; -} - -/****************************************************************************** - * - * Transfer descriptor rings - * - ****************************************************************************** - */ - -/** - * Allocate transfer descriptor ring - * - * @v ehci EHCI device - * @v ring Transfer descriptor ring - * @ret rc Return status code - */ -static int ehci_ring_alloc ( struct ehci_device *ehci, - struct ehci_ring *ring ) { - struct ehci_transfer_descriptor *desc; - struct ehci_transfer_descriptor *next; - unsigned int i; - size_t len; - uint32_t link; - int rc; - - /* Initialise structure */ - memset ( ring, 0, sizeof ( *ring ) ); - - /* Allocate I/O buffers */ - ring->iobuf = zalloc ( EHCI_RING_COUNT * sizeof ( ring->iobuf[0] ) ); - if ( ! ring->iobuf ) { - rc = -ENOMEM; - goto err_alloc_iobuf; - } - - /* Allocate queue head */ - ring->head = malloc_dma ( sizeof ( *ring->head ), - ehci_align ( sizeof ( *ring->head ) ) ); - if ( ! ring->head ) { - rc = -ENOMEM; - goto err_alloc_queue; - } - if ( ( rc = ehci_ctrl_reachable ( ehci, ring->head ) ) != 0 ) { - DBGC ( ehci, "EHCI %s queue head unreachable\n", ehci->name ); - goto err_unreachable_queue; - } - memset ( ring->head, 0, sizeof ( *ring->head ) ); - - /* Allocate transfer descriptors */ - len = ( EHCI_RING_COUNT * sizeof ( ring->desc[0] ) ); - ring->desc = malloc_dma ( len, sizeof ( ring->desc[0] ) ); - if ( ! ring->desc ) { - rc = -ENOMEM; - goto err_alloc_desc; - } - memset ( ring->desc, 0, len ); - - /* Initialise transfer descriptors */ - for ( i = 0 ; i < EHCI_RING_COUNT ; i++ ) { - desc = &ring->desc[i]; - if ( ( rc = ehci_ctrl_reachable ( ehci, desc ) ) != 0 ) { - DBGC ( ehci, "EHCI %s descriptor unreachable\n", - ehci->name ); - goto err_unreachable_desc; - } - next = &ring->desc[ ( i + 1 ) % EHCI_RING_COUNT ]; - link = virt_to_phys ( next ); - desc->next = cpu_to_le32 ( link ); - desc->alt = cpu_to_le32 ( link ); - } - - /* Initialise queue head */ - link = virt_to_phys ( &ring->desc[0] ); - ring->head->cache.next = cpu_to_le32 ( link ); - - return 0; - - err_unreachable_desc: - free_dma ( ring->desc, len ); - err_alloc_desc: - err_unreachable_queue: - free_dma ( ring->head, sizeof ( *ring->head ) ); - err_alloc_queue: - free ( ring->iobuf ); - err_alloc_iobuf: - return rc; -} - -/** - * Free transfer descriptor ring - * - * @v ring Transfer descriptor ring - */ -static void ehci_ring_free ( struct ehci_ring *ring ) { - unsigned int i; - - /* Sanity checks */ - assert ( ehci_ring_fill ( ring ) == 0 ); - for ( i = 0 ; i < EHCI_RING_COUNT ; i++ ) - assert ( ring->iobuf[i] == NULL ); - - /* Free transfer descriptors */ - free_dma ( ring->desc, ( EHCI_RING_COUNT * sizeof ( ring->desc[0] ) ) ); - - /* Free queue head */ - free_dma ( ring->head, sizeof ( *ring->head ) ); - - /* Free I/O buffers */ - free ( ring->iobuf ); -} - -/** - * Enqueue transfer descriptors - * - * @v ehci EHCI device - * @v ring Transfer descriptor ring - * @v iobuf I/O buffer - * @v xfers Transfers - * @v count Number of transfers - * @ret rc Return status code - */ -static int ehci_enqueue ( struct ehci_device *ehci, struct ehci_ring *ring, - struct io_buffer *iobuf, - const struct ehci_transfer *xfer, - unsigned int count ) { - struct ehci_transfer_descriptor *desc; - physaddr_t phys; - void *data; - size_t len; - size_t offset; - size_t frag_len; - unsigned int toggle; - unsigned int index; - unsigned int i; - - /* Sanity check */ - assert ( iobuf != NULL ); - assert ( count > 0 ); - - /* Fail if ring does not have sufficient space */ - if ( ehci_ring_remaining ( ring ) < count ) - return -ENOBUFS; - - /* Fail if any portion is unreachable */ - for ( i = 0 ; i < count ; i++ ) { - phys = ( virt_to_phys ( xfer[i].data ) + xfer[i].len - 1 ); - if ( ( phys > 0xffffffffUL ) && ( ! ehci->addr64 ) ) - return -ENOTSUP; - } - - /* Enqueue each transfer, recording the I/O buffer with the last */ - for ( ; count ; ring->prod++, xfer++ ) { - - /* Populate descriptor header */ - index = ( ring->prod % EHCI_RING_COUNT ); - desc = &ring->desc[index]; - toggle = ( xfer->flags & EHCI_FL_TOGGLE ); - assert ( xfer->len <= EHCI_LEN_MASK ); - assert ( EHCI_FL_TOGGLE == EHCI_LEN_TOGGLE ); - desc->len = cpu_to_le16 ( xfer->len | toggle ); - desc->flags = ( xfer->flags | EHCI_FL_CERR_MAX ); - - /* Populate buffer pointers */ - data = xfer->data; - len = xfer->len; - for ( i = 0 ; len ; i++ ) { - - /* Calculate length of this fragment */ - phys = virt_to_phys ( data ); - offset = ( phys & ( EHCI_PAGE_ALIGN - 1 ) ); - frag_len = ( EHCI_PAGE_ALIGN - offset ); - if ( frag_len > len ) - frag_len = len; - - /* Sanity checks */ - assert ( ( i == 0 ) || ( offset == 0 ) ); - assert ( i < ( sizeof ( desc->low ) / - sizeof ( desc->low[0] ) ) ); - - /* Populate buffer pointer */ - desc->low[i] = cpu_to_le32 ( phys ); - if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) { - desc->high[i] = - cpu_to_le32 ( ((uint64_t) phys) >> 32 ); - } - - /* Move to next fragment */ - data += frag_len; - len -= frag_len; - } - - /* Ensure everything is valid before activating descriptor */ - wmb(); - desc->status = EHCI_STATUS_ACTIVE; - - /* Record I/O buffer against last ring index */ - if ( --count == 0 ) - ring->iobuf[index] = iobuf; - } - - return 0; -} - -/** - * Dequeue a transfer descriptor - * - * @v ring Transfer descriptor ring - * @ret iobuf I/O buffer (or NULL) - */ -static struct io_buffer * ehci_dequeue ( struct ehci_ring *ring ) { - struct ehci_transfer_descriptor *desc; - struct io_buffer *iobuf; - unsigned int index = ( ring->cons % EHCI_RING_COUNT ); - - /* Sanity check */ - assert ( ehci_ring_fill ( ring ) > 0 ); - - /* Mark descriptor as inactive (and not halted) */ - desc = &ring->desc[index]; - desc->status = 0; - - /* Retrieve I/O buffer */ - iobuf = ring->iobuf[index]; - ring->iobuf[index] = NULL; - - /* Update consumer counter */ - ring->cons++; - - return iobuf; -} - -/****************************************************************************** - * - * Schedule management - * - ****************************************************************************** - */ - -/** - * Get link value for a queue head - * - * @v queue Queue head - * @ret link Link value - */ -static inline uint32_t ehci_link_qh ( struct ehci_queue_head *queue ) { - - return ( virt_to_phys ( queue ) | EHCI_LINK_TYPE_QH ); -} - -/** - * (Re)build asynchronous schedule - * - * @v ehci EHCI device - */ -static void ehci_async_schedule ( struct ehci_device *ehci ) { - struct ehci_endpoint *endpoint; - struct ehci_queue_head *queue; - uint32_t link; - - /* Build schedule in reverse order of execution. Provided - * that we only ever add or remove single endpoints, this can - * safely run concurrently with hardware execution of the - * schedule. - */ - link = ehci_link_qh ( ehci->head ); - list_for_each_entry_reverse ( endpoint, &ehci->async, schedule ) { - queue = endpoint->ring.head; - queue->link = cpu_to_le32 ( link ); - wmb(); - link = ehci_link_qh ( queue ); - } - ehci->head->link = cpu_to_le32 ( link ); - wmb(); -} - -/** - * Add endpoint to asynchronous schedule - * - * @v endpoint Endpoint - */ -static void ehci_async_add ( struct ehci_endpoint *endpoint ) { - struct ehci_device *ehci = endpoint->ehci; - - /* Add to end of schedule */ - list_add_tail ( &endpoint->schedule, &ehci->async ); - - /* Rebuild schedule */ - ehci_async_schedule ( ehci ); -} - -/** - * Remove endpoint from asynchronous schedule - * - * @v endpoint Endpoint - * @ret rc Return status code - */ -static int ehci_async_del ( struct ehci_endpoint *endpoint ) { - struct ehci_device *ehci = endpoint->ehci; - uint32_t usbcmd; - uint32_t usbsts; - unsigned int i; - - /* Remove from schedule */ - list_check_contains_entry ( endpoint, &ehci->async, schedule ); - list_del ( &endpoint->schedule ); - - /* Rebuild schedule */ - ehci_async_schedule ( ehci ); - - /* Request notification when asynchronous schedule advances */ - usbcmd = readl ( ehci->op + EHCI_OP_USBCMD ); - usbcmd |= EHCI_USBCMD_ASYNC_ADVANCE; - writel ( usbcmd, ehci->op + EHCI_OP_USBCMD ); - - /* Wait for asynchronous schedule to advance */ - for ( i = 0 ; i < EHCI_ASYNC_ADVANCE_MAX_WAIT_MS ; i++ ) { - - /* Check for asynchronous schedule advancing */ - usbsts = readl ( ehci->op + EHCI_OP_USBSTS ); - if ( usbsts & EHCI_USBSTS_ASYNC_ADVANCE ) { - usbsts &= ~EHCI_USBSTS_CHANGE; - usbsts |= EHCI_USBSTS_ASYNC_ADVANCE; - writel ( usbsts, ehci->op + EHCI_OP_USBSTS ); - return 0; - } - - /* Delay */ - mdelay ( 1 ); - } - - /* Bad things will probably happen now */ - DBGC ( ehci, "EHCI %s timed out waiting for asynchronous schedule " - "to advance\n", ehci->name ); - return -ETIMEDOUT; -} - -/** - * (Re)build periodic schedule - * - * @v ehci EHCI device - */ -static void ehci_periodic_schedule ( struct ehci_device *ehci ) { - struct ehci_endpoint *endpoint; - struct ehci_queue_head *queue; - uint32_t link; - unsigned int frames; - unsigned int max_interval; - unsigned int i; - - /* Build schedule in reverse order of execution. Provided - * that we only ever add or remove single endpoints, this can - * safely run concurrently with hardware execution of the - * schedule. - */ - DBGCP ( ehci, "EHCI %s periodic schedule: ", ehci->name ); - link = EHCI_LINK_TERMINATE; - list_for_each_entry_reverse ( endpoint, &ehci->periodic, schedule ) { - queue = endpoint->ring.head; - queue->link = cpu_to_le32 ( link ); - wmb(); - DBGCP ( ehci, "%s%d", - ( ( link == EHCI_LINK_TERMINATE ) ? "" : "<-" ), - endpoint->ep->interval ); - link = ehci_link_qh ( queue ); - } - DBGCP ( ehci, "\n" ); - - /* Populate periodic frame list */ - DBGCP ( ehci, "EHCI %s periodic frame list:", ehci->name ); - frames = EHCI_PERIODIC_FRAMES ( ehci->flsize ); - for ( i = 0 ; i < frames ; i++ ) { - - /* Calculate maximum interval (in microframes) which - * may appear as part of this frame list. - */ - if ( i == 0 ) { - /* Start of list: include all endpoints */ - max_interval = -1U; - } else { - /* Calculate highest power-of-two frame interval */ - max_interval = ( 1 << ( ffs ( i ) - 1 ) ); - /* Convert to microframes */ - max_interval <<= 3; - /* Round up to nearest 2^n-1 */ - max_interval = ( ( max_interval << 1 ) - 1 ); - } - - /* Find first endpoint in schedule satisfying this - * maximum interval constraint. - */ - link = EHCI_LINK_TERMINATE; - list_for_each_entry ( endpoint, &ehci->periodic, schedule ) { - if ( endpoint->ep->interval <= max_interval ) { - queue = endpoint->ring.head; - link = ehci_link_qh ( queue ); - DBGCP ( ehci, " %d:%d", - i, endpoint->ep->interval ); - break; - } - } - ehci->frame[i].link = cpu_to_le32 ( link ); - } - wmb(); - DBGCP ( ehci, "\n" ); -} - -/** - * Add endpoint to periodic schedule - * - * @v endpoint Endpoint - */ -static void ehci_periodic_add ( struct ehci_endpoint *endpoint ) { - struct ehci_device *ehci = endpoint->ehci; - struct ehci_endpoint *before; - unsigned int interval = endpoint->ep->interval; - - /* Find first endpoint with a smaller interval */ - list_for_each_entry ( before, &ehci->periodic, schedule ) { - if ( before->ep->interval < interval ) - break; - } - list_add_tail ( &endpoint->schedule, &before->schedule ); - - /* Rebuild schedule */ - ehci_periodic_schedule ( ehci ); -} - -/** - * Remove endpoint from periodic schedule - * - * @v endpoint Endpoint - * @ret rc Return status code - */ -static int ehci_periodic_del ( struct ehci_endpoint *endpoint ) { - struct ehci_device *ehci = endpoint->ehci; - - /* Remove from schedule */ - list_check_contains_entry ( endpoint, &ehci->periodic, schedule ); - list_del ( &endpoint->schedule ); - - /* Rebuild schedule */ - ehci_periodic_schedule ( ehci ); - - /* Delay for a whole USB frame (with a 100% safety margin) */ - mdelay ( 2 ); - - return 0; -} - -/** - * Add endpoint to appropriate schedule - * - * @v endpoint Endpoint - */ -static void ehci_schedule_add ( struct ehci_endpoint *endpoint ) { - struct usb_endpoint *ep = endpoint->ep; - unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); - - if ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) { - ehci_periodic_add ( endpoint ); - } else { - ehci_async_add ( endpoint ); - } -} - -/** - * Remove endpoint from appropriate schedule - * - * @v endpoint Endpoint - * @ret rc Return status code - */ -static int ehci_schedule_del ( struct ehci_endpoint *endpoint ) { - struct usb_endpoint *ep = endpoint->ep; - unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); - - if ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) { - return ehci_periodic_del ( endpoint ); - } else { - return ehci_async_del ( endpoint ); - } -} - -/****************************************************************************** - * - * Endpoint operations - * - ****************************************************************************** - */ - -/** - * Determine endpoint characteristics - * - * @v ep USB endpoint - * @ret chr Endpoint characteristics - */ -static uint32_t ehci_endpoint_characteristics ( struct usb_endpoint *ep ) { - struct usb_device *usb = ep->usb; - unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); - uint32_t chr; - - /* Determine basic characteristics */ - chr = ( EHCI_CHR_ADDRESS ( usb->address ) | - EHCI_CHR_ENDPOINT ( ep->address ) | - EHCI_CHR_MAX_LEN ( ep->mtu ) ); - - /* Control endpoints require manual control of the data toggle */ - if ( attr == USB_ENDPOINT_ATTR_CONTROL ) - chr |= EHCI_CHR_TOGGLE; - - /* Determine endpoint speed */ - if ( usb->port->speed == USB_SPEED_HIGH ) { - chr |= EHCI_CHR_EPS_HIGH; - } else { - if ( usb->port->speed == USB_SPEED_FULL ) { - chr |= EHCI_CHR_EPS_FULL; - } else { - chr |= EHCI_CHR_EPS_LOW; - } - if ( attr == USB_ENDPOINT_ATTR_CONTROL ) - chr |= EHCI_CHR_CONTROL; - } - - return chr; -} - -/** - * Determine endpoint capabilities - * - * @v ep USB endpoint - * @ret cap Endpoint capabilities - */ -static uint32_t ehci_endpoint_capabilities ( struct usb_endpoint *ep ) { - struct usb_device *usb = ep->usb; - struct usb_port *tt = usb_transaction_translator ( usb ); - unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); - uint32_t cap; - unsigned int i; - - /* Determine basic capabilities */ - cap = EHCI_CAP_MULT ( ep->burst + 1 ); - - /* Determine interrupt schedule mask, if applicable */ - if ( ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) && - ( ( ep->interval != 0 ) /* avoid infinite loop */ ) ) { - for ( i = 0 ; i < 8 /* microframes per frame */ ; - i += ep->interval ) { - cap |= EHCI_CAP_INTR_SCHED ( i ); - } - } - - /* Set transaction translator hub address and port, if applicable */ - if ( tt ) { - assert ( tt->hub->usb ); - cap |= ( EHCI_CAP_TT_HUB ( tt->hub->usb->address ) | - EHCI_CAP_TT_PORT ( tt->address ) ); - if ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) - cap |= EHCI_CAP_SPLIT_SCHED_DEFAULT; - } - - return cap; -} - -/** - * Update endpoint characteristics and capabilities - * - * @v ep USB endpoint - */ -static void ehci_endpoint_update ( struct usb_endpoint *ep ) { - struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); - struct ehci_queue_head *head; - - /* Update queue characteristics and capabilities */ - head = endpoint->ring.head; - head->chr = cpu_to_le32 ( ehci_endpoint_characteristics ( ep ) ); - head->cap = cpu_to_le32 ( ehci_endpoint_capabilities ( ep ) ); -} - -/** - * Open endpoint - * - * @v ep USB endpoint - * @ret rc Return status code - */ -static int ehci_endpoint_open ( struct usb_endpoint *ep ) { - struct usb_device *usb = ep->usb; - struct ehci_device *ehci = usb_get_hostdata ( usb ); - struct ehci_endpoint *endpoint; - int rc; - - /* Allocate and initialise structure */ - endpoint = zalloc ( sizeof ( *endpoint ) ); - if ( ! endpoint ) { - rc = -ENOMEM; - goto err_alloc; - } - endpoint->ehci = ehci; - endpoint->ep = ep; - usb_endpoint_set_hostdata ( ep, endpoint ); - - /* Initialise descriptor ring */ - if ( ( rc = ehci_ring_alloc ( ehci, &endpoint->ring ) ) != 0 ) - goto err_ring_alloc; - - /* Update queue characteristics and capabilities */ - ehci_endpoint_update ( ep ); - - /* Add to list of endpoints */ - list_add_tail ( &endpoint->list, &ehci->endpoints ); - - /* Add to schedule */ - ehci_schedule_add ( endpoint ); - - return 0; - - ehci_ring_free ( &endpoint->ring ); - err_ring_alloc: - free ( endpoint ); - err_alloc: - return rc; -} - -/** - * Close endpoint - * - * @v ep USB endpoint - */ -static void ehci_endpoint_close ( struct usb_endpoint *ep ) { - struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); - struct ehci_device *ehci = endpoint->ehci; - struct usb_device *usb = ep->usb; - struct io_buffer *iobuf; - int rc; - - /* Remove from schedule */ - if ( ( rc = ehci_schedule_del ( endpoint ) ) != 0 ) { - /* No way to prevent hardware from continuing to - * access the memory, so leak it. - */ - DBGC ( ehci, "EHCI %s %s could not unschedule: %s\n", - usb->name, usb_endpoint_name ( ep ), strerror ( rc ) ); - return; - } - - /* Cancel any incomplete transfers */ - while ( ehci_ring_fill ( &endpoint->ring ) ) { - iobuf = ehci_dequeue ( &endpoint->ring ); - if ( iobuf ) - usb_complete_err ( ep, iobuf, -ECANCELED ); - } - - /* Remove from list of endpoints */ - list_del ( &endpoint->list ); - - /* Free descriptor ring */ - ehci_ring_free ( &endpoint->ring ); - - /* Free endpoint */ - free ( endpoint ); -} - -/** - * Reset endpoint - * - * @v ep USB endpoint - * @ret rc Return status code - */ -static int ehci_endpoint_reset ( struct usb_endpoint *ep ) { - struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); - struct ehci_ring *ring = &endpoint->ring; - struct ehci_transfer_descriptor *cache = &ring->head->cache; - uint32_t link; - - /* Sanity checks */ - assert ( ! ( cache->status & EHCI_STATUS_ACTIVE ) ); - assert ( cache->status & EHCI_STATUS_HALTED ); - - /* Reset residual count */ - ring->residual = 0; - - /* Reset data toggle */ - cache->len = 0; - - /* Prepare to restart at next unconsumed descriptor */ - link = virt_to_phys ( &ring->desc[ ring->cons % EHCI_RING_COUNT ] ); - cache->next = cpu_to_le32 ( link ); - - /* Restart ring */ - wmb(); - cache->status = 0; - - return 0; -} - -/** - * Update MTU - * - * @v ep USB endpoint - * @ret rc Return status code - */ -static int ehci_endpoint_mtu ( struct usb_endpoint *ep ) { - - /* Update endpoint characteristics and capabilities */ - ehci_endpoint_update ( ep ); - - return 0; -} - -/** - * Enqueue message transfer - * - * @v ep USB endpoint - * @v iobuf I/O buffer - * @ret rc Return status code - */ -static int ehci_endpoint_message ( struct usb_endpoint *ep, - struct io_buffer *iobuf ) { - struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); - struct ehci_device *ehci = endpoint->ehci; - struct usb_setup_packet *packet; - unsigned int input; - struct ehci_transfer xfers[3]; - struct ehci_transfer *xfer = xfers; - size_t len; - int rc; - - /* Construct setup stage */ - assert ( iob_len ( iobuf ) >= sizeof ( *packet ) ); - packet = iobuf->data; - iob_pull ( iobuf, sizeof ( *packet ) ); - xfer->data = packet; - xfer->len = sizeof ( *packet ); - xfer->flags = EHCI_FL_PID_SETUP; - xfer++; - - /* Construct data stage, if applicable */ - len = iob_len ( iobuf ); - input = ( packet->request & cpu_to_le16 ( USB_DIR_IN ) ); - if ( len ) { - xfer->data = iobuf->data; - xfer->len = len; - xfer->flags = ( EHCI_FL_TOGGLE | - ( input ? EHCI_FL_PID_IN : EHCI_FL_PID_OUT ) ); - xfer++; - } - - /* Construct status stage */ - xfer->data = NULL; - xfer->len = 0; - xfer->flags = ( EHCI_FL_TOGGLE | EHCI_FL_IOC | - ( ( len && input ) ? EHCI_FL_PID_OUT : EHCI_FL_PID_IN)); - xfer++; - - /* Enqueue transfer */ - if ( ( rc = ehci_enqueue ( ehci, &endpoint->ring, iobuf, xfers, - ( xfer - xfers ) ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Enqueue stream transfer - * - * @v ep USB endpoint - * @v iobuf I/O buffer - * @v terminate Terminate using a short packet - * @ret rc Return status code - */ -static int ehci_endpoint_stream ( struct usb_endpoint *ep, - struct io_buffer *iobuf, int terminate ) { - struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); - struct ehci_device *ehci = endpoint->ehci; - unsigned int input = ( ep->address & USB_DIR_IN ); - struct ehci_transfer xfers[2]; - struct ehci_transfer *xfer = xfers; - size_t len = iob_len ( iobuf ); - int rc; - - /* Create transfer */ - xfer->data = iobuf->data; - xfer->len = len; - xfer->flags = ( EHCI_FL_IOC | - ( input ? EHCI_FL_PID_IN : EHCI_FL_PID_OUT ) ); - xfer++; - if ( terminate && ( ( len & ( ep->mtu - 1 ) ) == 0 ) ) { - xfer->data = NULL; - xfer->len = 0; - assert ( ! input ); - xfer->flags = ( EHCI_FL_IOC | EHCI_FL_PID_OUT ); - xfer++; - } - - /* Enqueue transfer */ - if ( ( rc = ehci_enqueue ( ehci, &endpoint->ring, iobuf, xfers, - ( xfer - xfers ) ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Poll for completions - * - * @v endpoint Endpoint - */ -static void ehci_endpoint_poll ( struct ehci_endpoint *endpoint ) { - struct ehci_device *ehci = endpoint->ehci; - struct ehci_ring *ring = &endpoint->ring; - struct ehci_transfer_descriptor *desc; - struct usb_endpoint *ep = endpoint->ep; - struct usb_device *usb = ep->usb; - struct io_buffer *iobuf; - unsigned int index; - unsigned int status; - int rc; - - /* Consume all completed descriptors */ - while ( ehci_ring_fill ( &endpoint->ring ) ) { - - /* Stop if we reach an uncompleted descriptor */ - rmb(); - index = ( ring->cons % EHCI_RING_COUNT ); - desc = &ring->desc[index]; - status = desc->status; - if ( status & EHCI_STATUS_ACTIVE ) - break; - - /* Consume this descriptor */ - iobuf = ehci_dequeue ( ring ); - - /* If we have encountered an error, then consume all - * remaining descriptors in this transaction, report - * the error to the USB core, and stop further - * processing. - */ - if ( status & EHCI_STATUS_HALTED ) { - rc = -EIO_STATUS ( status ); - DBGC ( ehci, "EHCI %s %s completion %d failed (status " - "%02x): %s\n", usb->name, - usb_endpoint_name ( ep ), index, status, - strerror ( rc ) ); - while ( ! iobuf ) - iobuf = ehci_dequeue ( ring ); - usb_complete_err ( endpoint->ep, iobuf, rc ); - return; - } - - /* Accumulate residual data count */ - ring->residual += ( le16_to_cpu ( desc->len ) & EHCI_LEN_MASK ); - - /* If this is not the end of a transaction (i.e. has - * no I/O buffer), then continue to next descriptor. - */ - if ( ! iobuf ) - continue; - - /* Update I/O buffer length */ - iob_unput ( iobuf, ring->residual ); - ring->residual = 0; - - /* Report completion to USB core */ - usb_complete ( endpoint->ep, iobuf ); - } -} - -/****************************************************************************** - * - * Device operations - * - ****************************************************************************** - */ - -/** - * Open device - * - * @v usb USB device - * @ret rc Return status code - */ -static int ehci_device_open ( struct usb_device *usb ) { - struct ehci_device *ehci = usb_bus_get_hostdata ( usb->port->hub->bus ); - - usb_set_hostdata ( usb, ehci ); - return 0; -} - -/** - * Close device - * - * @v usb USB device - */ -static void ehci_device_close ( struct usb_device *usb ) { - struct ehci_device *ehci = usb_get_hostdata ( usb ); - struct usb_bus *bus = ehci->bus; - - /* Free device address, if assigned */ - if ( usb->address ) - usb_free_address ( bus, usb->address ); -} - -/** - * Assign device address - * - * @v usb USB device - * @ret rc Return status code - */ -static int ehci_device_address ( struct usb_device *usb ) { - struct ehci_device *ehci = usb_get_hostdata ( usb ); - struct usb_bus *bus = ehci->bus; - struct usb_endpoint *ep0 = usb_endpoint ( usb, USB_EP0_ADDRESS ); - int address; - int rc; - - /* Sanity checks */ - assert ( usb->address == 0 ); - assert ( ep0 != NULL ); - - /* Allocate device address */ - address = usb_alloc_address ( bus ); - if ( address < 0 ) { - rc = address; - DBGC ( ehci, "EHCI %s could not allocate address: %s\n", - usb->name, strerror ( rc ) ); - goto err_alloc_address; - } - - /* Set address */ - if ( ( rc = usb_set_address ( usb, address ) ) != 0 ) - goto err_set_address; - - /* Update device address */ - usb->address = address; - - /* Update control endpoint characteristics and capabilities */ - ehci_endpoint_update ( ep0 ); - - return 0; - - err_set_address: - usb_free_address ( bus, address ); - err_alloc_address: - return rc; -} - -/****************************************************************************** - * - * Hub operations - * - ****************************************************************************** - */ - -/** - * Open hub - * - * @v hub USB hub - * @ret rc Return status code - */ -static int ehci_hub_open ( struct usb_hub *hub __unused ) { - - /* Nothing to do */ - return 0; -} - -/** - * Close hub - * - * @v hub USB hub - */ -static void ehci_hub_close ( struct usb_hub *hub __unused ) { - - /* Nothing to do */ -} - -/****************************************************************************** - * - * Root hub operations - * - ****************************************************************************** - */ - -/** - * Open root hub - * - * @v hub USB hub - * @ret rc Return status code - */ -static int ehci_root_open ( struct usb_hub *hub ) { - struct usb_bus *bus = hub->bus; - struct ehci_device *ehci = usb_bus_get_hostdata ( bus ); - uint32_t portsc; - unsigned int i; - - /* Route all ports to EHCI controller */ - writel ( EHCI_CONFIGFLAG_CF, ehci->op + EHCI_OP_CONFIGFLAG ); - - /* Enable power to all ports */ - for ( i = 1 ; i <= ehci->ports ; i++ ) { - portsc = readl ( ehci->op + EHCI_OP_PORTSC ( i ) ); - portsc &= ~EHCI_PORTSC_CHANGE; - portsc |= EHCI_PORTSC_PP; - writel ( portsc, ehci->op + EHCI_OP_PORTSC ( i ) ); - } - - /* Wait 20ms after potentially enabling power to a port */ - mdelay ( EHCI_PORT_POWER_DELAY_MS ); - - /* Record hub driver private data */ - usb_hub_set_drvdata ( hub, ehci ); - - return 0; -} - -/** - * Close root hub - * - * @v hub USB hub - */ -static void ehci_root_close ( struct usb_hub *hub ) { - struct ehci_device *ehci = usb_hub_get_drvdata ( hub ); - - /* Route all ports back to companion controllers */ - writel ( 0, ehci->op + EHCI_OP_CONFIGFLAG ); - - /* Clear hub driver private data */ - usb_hub_set_drvdata ( hub, NULL ); -} - -/** - * Enable port - * - * @v hub USB hub - * @v port USB port - * @ret rc Return status code - */ -static int ehci_root_enable ( struct usb_hub *hub, struct usb_port *port ) { - struct ehci_device *ehci = usb_hub_get_drvdata ( hub ); - uint32_t portsc; - unsigned int line; - unsigned int i; - - /* Check for a low-speed device */ - portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) ); - line = EHCI_PORTSC_LINE_STATUS ( portsc ); - if ( line == EHCI_PORTSC_LINE_STATUS_LOW ) { - DBGC ( ehci, "EHCI %s-%d detected low-speed device: " - "disowning\n", ehci->name, port->address ); - goto disown; - } - - /* Reset port */ - portsc &= ~( EHCI_PORTSC_PED | EHCI_PORTSC_CHANGE ); - portsc |= EHCI_PORTSC_PR; - writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) ); - mdelay ( USB_RESET_DELAY_MS ); - portsc &= ~EHCI_PORTSC_PR; - writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) ); - - /* Wait for reset to complete */ - for ( i = 0 ; i < EHCI_PORT_RESET_MAX_WAIT_MS ; i++ ) { - - /* Check port status */ - portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) ); - if ( ! ( portsc & EHCI_PORTSC_PR ) ) { - if ( portsc & EHCI_PORTSC_PED ) - return 0; - DBGC ( ehci, "EHCI %s-%d not enabled after reset: " - "disowning\n", ehci->name, port->address ); - goto disown; - } - - /* Delay */ - mdelay ( 1 ); - } - - DBGC ( ehci, "EHCI %s-%d timed out waiting for port to reset\n", - ehci->name, port->address ); - return -ETIMEDOUT; - - disown: - /* Disown port */ - portsc &= ~EHCI_PORTSC_CHANGE; - portsc |= EHCI_PORTSC_OWNER; - writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) ); - - /* Delay to allow child companion controllers to settle */ - mdelay ( EHCI_DISOWN_DELAY_MS ); - - /* Poll child companion controllers */ - ehci_poll_companions ( ehci ); - - return -ENODEV; -} - -/** - * Disable port - * - * @v hub USB hub - * @v port USB port - * @ret rc Return status code - */ -static int ehci_root_disable ( struct usb_hub *hub, struct usb_port *port ) { - struct ehci_device *ehci = usb_hub_get_drvdata ( hub ); - uint32_t portsc; - - /* Disable port */ - portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) ); - portsc &= ~( EHCI_PORTSC_PED | EHCI_PORTSC_CHANGE ); - writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) ); - - return 0; -} - -/** - * Update root hub port speed - * - * @v hub USB hub - * @v port USB port - * @ret rc Return status code - */ -static int ehci_root_speed ( struct usb_hub *hub, struct usb_port *port ) { - struct ehci_device *ehci = usb_hub_get_drvdata ( hub ); - uint32_t portsc; - unsigned int speed; - unsigned int line; - int ccs; - int csc; - int ped; - - /* Read port status */ - portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) ); - DBGC2 ( ehci, "EHCI %s-%d status is %08x\n", - ehci->name, port->address, portsc ); - ccs = ( portsc & EHCI_PORTSC_CCS ); - csc = ( portsc & EHCI_PORTSC_CSC ); - ped = ( portsc & EHCI_PORTSC_PED ); - line = EHCI_PORTSC_LINE_STATUS ( portsc ); - - /* Record disconnections and clear changes */ - port->disconnected |= csc; - writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) ); - - /* Determine port speed */ - if ( ! ccs ) { - /* Port not connected */ - speed = USB_SPEED_NONE; - } else if ( line == EHCI_PORTSC_LINE_STATUS_LOW ) { - /* Detected as low-speed */ - speed = USB_SPEED_LOW; - } else if ( ped ) { - /* Port already enabled: must be high-speed */ - speed = USB_SPEED_HIGH; - } else { - /* Not low-speed and not yet enabled. Could be either - * full-speed or high-speed; we can't yet tell. - */ - speed = USB_SPEED_FULL; - } - port->speed = speed; - return 0; -} - -/** - * Clear transaction translator buffer - * - * @v hub USB hub - * @v port USB port - * @v ep USB endpoint - * @ret rc Return status code - */ -static int ehci_root_clear_tt ( struct usb_hub *hub, struct usb_port *port, - struct usb_endpoint *ep ) { - struct ehci_device *ehci = usb_hub_get_drvdata ( hub ); - - /* Should never be called; this is a root hub */ - DBGC ( ehci, "EHCI %s-%d nonsensical CLEAR_TT for %s %s\n", ehci->name, - port->address, ep->usb->name, usb_endpoint_name ( ep ) ); - - return -ENOTSUP; -} - -/** - * Poll for port status changes - * - * @v hub USB hub - * @v port USB port - */ -static void ehci_root_poll ( struct usb_hub *hub, struct usb_port *port ) { - struct ehci_device *ehci = usb_hub_get_drvdata ( hub ); - uint32_t portsc; - uint32_t change; - - /* Do nothing unless something has changed */ - portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) ); - change = ( portsc & EHCI_PORTSC_CHANGE ); - if ( ! change ) - return; - - /* Record disconnections and clear changes */ - port->disconnected |= ( portsc & EHCI_PORTSC_CSC ); - writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) ); - - /* Report port status change */ - usb_port_changed ( port ); -} - -/****************************************************************************** - * - * Bus operations - * - ****************************************************************************** - */ - -/** - * Open USB bus - * - * @v bus USB bus - * @ret rc Return status code - */ -static int ehci_bus_open ( struct usb_bus *bus ) { - struct ehci_device *ehci = usb_bus_get_hostdata ( bus ); - unsigned int frames; - size_t len; - int rc; - - /* Sanity checks */ - assert ( list_empty ( &ehci->async ) ); - assert ( list_empty ( &ehci->periodic ) ); - - /* Allocate and initialise asynchronous queue head */ - ehci->head = malloc_dma ( sizeof ( *ehci->head ), - ehci_align ( sizeof ( *ehci->head ) ) ); - if ( ! ehci->head ) { - rc = -ENOMEM; - goto err_alloc_head; - } - memset ( ehci->head, 0, sizeof ( *ehci->head ) ); - ehci->head->chr = cpu_to_le32 ( EHCI_CHR_HEAD ); - ehci->head->cache.next = cpu_to_le32 ( EHCI_LINK_TERMINATE ); - ehci->head->cache.status = EHCI_STATUS_HALTED; - ehci_async_schedule ( ehci ); - writel ( virt_to_phys ( ehci->head ), - ehci->op + EHCI_OP_ASYNCLISTADDR ); - - /* Use async queue head to determine control data structure segment */ - ehci->ctrldssegment = - ( ( ( uint64_t ) virt_to_phys ( ehci->head ) ) >> 32 ); - if ( ehci->addr64 ) { - writel ( ehci->ctrldssegment, ehci->op + EHCI_OP_CTRLDSSEGMENT); - } else if ( ehci->ctrldssegment ) { - DBGC ( ehci, "EHCI %s CTRLDSSEGMENT not supported\n", - ehci->name ); - rc = -ENOTSUP; - goto err_ctrldssegment; - } - - /* Allocate periodic frame list */ - frames = EHCI_PERIODIC_FRAMES ( ehci->flsize ); - len = ( frames * sizeof ( ehci->frame[0] ) ); - ehci->frame = malloc_dma ( len, EHCI_PAGE_ALIGN ); - if ( ! ehci->frame ) { - rc = -ENOMEM; - goto err_alloc_frame; - } - if ( ( rc = ehci_ctrl_reachable ( ehci, ehci->frame ) ) != 0 ) { - DBGC ( ehci, "EHCI %s frame list unreachable\n", ehci->name ); - goto err_unreachable_frame; - } - ehci_periodic_schedule ( ehci ); - writel ( virt_to_phys ( ehci->frame ), - ehci->op + EHCI_OP_PERIODICLISTBASE ); - - /* Start controller */ - ehci_run ( ehci ); - - return 0; - - ehci_stop ( ehci ); - err_unreachable_frame: - free_dma ( ehci->frame, len ); - err_alloc_frame: - err_ctrldssegment: - free_dma ( ehci->head, sizeof ( *ehci->head ) ); - err_alloc_head: - return rc; -} - -/** - * Close USB bus - * - * @v bus USB bus - */ -static void ehci_bus_close ( struct usb_bus *bus ) { - struct ehci_device *ehci = usb_bus_get_hostdata ( bus ); - unsigned int frames = EHCI_PERIODIC_FRAMES ( ehci->flsize ); - - /* Sanity checks */ - assert ( list_empty ( &ehci->async ) ); - assert ( list_empty ( &ehci->periodic ) ); - - /* Stop controller */ - ehci_stop ( ehci ); - - /* Free periodic frame list */ - free_dma ( ehci->frame, ( frames * sizeof ( ehci->frame[0] ) ) ); - - /* Free asynchronous schedule */ - free_dma ( ehci->head, sizeof ( *ehci->head ) ); -} - -/** - * Poll USB bus - * - * @v bus USB bus - */ -static void ehci_bus_poll ( struct usb_bus *bus ) { - struct ehci_device *ehci = usb_bus_get_hostdata ( bus ); - struct usb_hub *hub = bus->hub; - struct ehci_endpoint *endpoint; - unsigned int i; - uint32_t usbsts; - uint32_t change; - - /* Do nothing unless something has changed */ - usbsts = readl ( ehci->op + EHCI_OP_USBSTS ); - assert ( usbsts & EHCI_USBSTS_ASYNC ); - assert ( usbsts & EHCI_USBSTS_PERIODIC ); - assert ( ! ( usbsts & EHCI_USBSTS_HCH ) ); - change = ( usbsts & EHCI_USBSTS_CHANGE ); - if ( ! change ) - return; - - /* Acknowledge changes */ - writel ( usbsts, ehci->op + EHCI_OP_USBSTS ); - - /* Process completions, if applicable */ - if ( change & ( EHCI_USBSTS_USBINT | EHCI_USBSTS_USBERRINT ) ) { - - /* Iterate over all endpoints looking for completed - * descriptors. We trust that completion handlers are - * minimal and will not do anything that could - * plausibly affect the endpoint list itself. - */ - list_for_each_entry ( endpoint, &ehci->endpoints, list ) - ehci_endpoint_poll ( endpoint ); - } - - /* Process port status changes, if applicable */ - if ( change & EHCI_USBSTS_PORT ) { - - /* Iterate over all ports looking for status changes */ - for ( i = 1 ; i <= ehci->ports ; i++ ) - ehci_root_poll ( hub, usb_port ( hub, i ) ); - } - - /* Report fatal errors */ - if ( change & EHCI_USBSTS_SYSERR ) - DBGC ( ehci, "EHCI %s host system error\n", ehci->name ); -} - -/****************************************************************************** - * - * PCI interface - * - ****************************************************************************** - */ - -/** USB host controller operations */ -static struct usb_host_operations ehci_operations = { - .endpoint = { - .open = ehci_endpoint_open, - .close = ehci_endpoint_close, - .reset = ehci_endpoint_reset, - .mtu = ehci_endpoint_mtu, - .message = ehci_endpoint_message, - .stream = ehci_endpoint_stream, - }, - .device = { - .open = ehci_device_open, - .close = ehci_device_close, - .address = ehci_device_address, - }, - .bus = { - .open = ehci_bus_open, - .close = ehci_bus_close, - .poll = ehci_bus_poll, - }, - .hub = { - .open = ehci_hub_open, - .close = ehci_hub_close, - }, - .root = { - .open = ehci_root_open, - .close = ehci_root_close, - .enable = ehci_root_enable, - .disable = ehci_root_disable, - .speed = ehci_root_speed, - .clear_tt = ehci_root_clear_tt, - }, -}; - -/** - * Probe PCI device - * - * @v pci PCI device - * @ret rc Return status code - */ -static int ehci_probe ( struct pci_device *pci ) { - struct ehci_device *ehci; - struct usb_port *port; - unsigned long bar_start; - size_t bar_size; - unsigned int i; - int rc; - - /* Allocate and initialise structure */ - ehci = zalloc ( sizeof ( *ehci ) ); - if ( ! ehci ) { - rc = -ENOMEM; - goto err_alloc; - } - ehci->name = pci->dev.name; - INIT_LIST_HEAD ( &ehci->endpoints ); - INIT_LIST_HEAD ( &ehci->async ); - INIT_LIST_HEAD ( &ehci->periodic ); - - /* Fix up PCI device */ - adjust_pci_device ( pci ); - - /* Map registers */ - bar_start = pci_bar_start ( pci, EHCI_BAR ); - bar_size = pci_bar_size ( pci, EHCI_BAR ); - ehci->regs = ioremap ( bar_start, bar_size ); - if ( ! ehci->regs ) { - rc = -ENODEV; - goto err_ioremap; - } - - /* Initialise EHCI device */ - ehci_init ( ehci, ehci->regs ); - - /* Initialise USB legacy support and claim ownership */ - ehci_legacy_init ( ehci, pci ); - ehci_legacy_claim ( ehci, pci ); - - /* Reset device */ - if ( ( rc = ehci_reset ( ehci ) ) != 0 ) - goto err_reset; - - /* Allocate USB bus */ - ehci->bus = alloc_usb_bus ( &pci->dev, ehci->ports, EHCI_MTU, - &ehci_operations ); - if ( ! ehci->bus ) { - rc = -ENOMEM; - goto err_alloc_bus; - } - usb_bus_set_hostdata ( ehci->bus, ehci ); - usb_hub_set_drvdata ( ehci->bus->hub, ehci ); - - /* Set port protocols */ - for ( i = 1 ; i <= ehci->ports ; i++ ) { - port = usb_port ( ehci->bus->hub, i ); - port->protocol = USB_PROTO_2_0; - } - - /* Register USB bus */ - if ( ( rc = register_usb_bus ( ehci->bus ) ) != 0 ) - goto err_register; - - pci_set_drvdata ( pci, ehci ); - return 0; - - unregister_usb_bus ( ehci->bus ); - err_register: - free_usb_bus ( ehci->bus ); - err_alloc_bus: - ehci_reset ( ehci ); - err_reset: - ehci_legacy_release ( ehci, pci ); - iounmap ( ehci->regs ); - err_ioremap: - free ( ehci ); - err_alloc: - return rc; -} - -/** - * Remove PCI device - * - * @v pci PCI device - */ -static void ehci_remove ( struct pci_device *pci ) { - struct ehci_device *ehci = pci_get_drvdata ( pci ); - struct usb_bus *bus = ehci->bus; - - unregister_usb_bus ( bus ); - assert ( list_empty ( &ehci->async ) ); - assert ( list_empty ( &ehci->periodic ) ); - free_usb_bus ( bus ); - ehci_reset ( ehci ); - ehci_legacy_release ( ehci, pci ); - iounmap ( ehci->regs ); - free ( ehci ); -} - -/** EHCI PCI device IDs */ -static struct pci_device_id ehci_ids[] = { - PCI_ROM ( 0xffff, 0xffff, "ehci", "EHCI", 0 ), -}; - -/** EHCI PCI driver */ -struct pci_driver ehci_driver __pci_driver = { - .ids = ehci_ids, - .id_count = ( sizeof ( ehci_ids ) / sizeof ( ehci_ids[0] ) ), - .class = PCI_CLASS_ID ( PCI_CLASS_SERIAL, PCI_CLASS_SERIAL_USB, - PCI_CLASS_SERIAL_USB_EHCI ), - .probe = ehci_probe, - .remove = ehci_remove, -}; - -/** - * Prepare for exit - * - * @v booting System is shutting down for OS boot - */ -static void ehci_shutdown ( int booting ) { - /* If we are shutting down to boot an OS, then prevent the - * release of ownership back to BIOS. - */ - ehci_legacy_prevent_release = booting; -} - -/** Startup/shutdown function */ -struct startup_fn ehci_startup __startup_fn ( STARTUP_LATE ) = { - .shutdown = ehci_shutdown, -}; diff --git a/qemu/roms/ipxe/src/drivers/usb/ehci.h b/qemu/roms/ipxe/src/drivers/usb/ehci.h deleted file mode 100644 index 42e282e92..000000000 --- a/qemu/roms/ipxe/src/drivers/usb/ehci.h +++ /dev/null @@ -1,544 +0,0 @@ -#ifndef _IPXE_EHCI_H -#define _IPXE_EHCI_H - -/** @file - * - * USB Enhanced Host Controller Interface (EHCI) driver - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <ipxe/pci.h> -#include <ipxe/usb.h> - -/** Minimum alignment required for data structures - * - * With the exception of the periodic frame list (which is - * page-aligned), data structures used by EHCI generally require - * 32-byte alignment and must not cross a 4kB page boundary. We - * simplify this requirement by aligning each structure on its own - * size, with a minimum of a 32 byte alignment. - */ -#define EHCI_MIN_ALIGN 32 - -/** Maximum transfer size - * - * EHCI allows for transfers of up to 20kB with page-alignment, or - * 16kB with arbitrary alignment. - */ -#define EHCI_MTU 16384 - -/** Page-alignment required for some data structures */ -#define EHCI_PAGE_ALIGN 4096 - -/** EHCI PCI BAR */ -#define EHCI_BAR PCI_BASE_ADDRESS_0 - -/** Capability register length */ -#define EHCI_CAP_CAPLENGTH 0x00 - -/** Host controller interface version number */ -#define EHCI_CAP_HCIVERSION 0x02 - -/** Structural parameters */ -#define EHCI_CAP_HCSPARAMS 0x04 - -/** Number of ports */ -#define EHCI_HCSPARAMS_PORTS(params) ( ( (params) >> 0 ) & 0x0f ) - -/** Capability parameters */ -#define EHCI_CAP_HCCPARAMS 0x08 - -/** 64-bit addressing capability */ -#define EHCI_HCCPARAMS_ADDR64(params) ( ( (params) >> 0 ) & 0x1 ) - -/** Programmable frame list flag */ -#define EHCI_HCCPARAMS_FLSIZE(params) ( ( (params) >> 1 ) & 0x1 ) - -/** EHCI extended capabilities pointer */ -#define EHCI_HCCPARAMS_EECP(params) ( ( ( (params) >> 8 ) & 0xff ) ) - -/** EHCI extended capability ID */ -#define EHCI_EECP_ID(eecp) ( ( (eecp) >> 0 ) & 0xff ) - -/** Next EHCI extended capability pointer */ -#define EHCI_EECP_NEXT(eecp) ( ( ( (eecp) >> 8 ) & 0xff ) ) - -/** USB legacy support extended capability */ -#define EHCI_EECP_ID_LEGACY 1 - -/** USB legacy support BIOS owned semaphore */ -#define EHCI_USBLEGSUP_BIOS 0x02 - -/** USB legacy support BIOS ownership flag */ -#define EHCI_USBLEGSUP_BIOS_OWNED 0x01 - -/** USB legacy support OS owned semaphore */ -#define EHCI_USBLEGSUP_OS 0x03 - -/** USB legacy support OS ownership flag */ -#define EHCI_USBLEGSUP_OS_OWNED 0x01 - -/** USB legacy support control/status */ -#define EHCI_USBLEGSUP_CTLSTS 0x04 - -/** USB command register */ -#define EHCI_OP_USBCMD 0x00 - -/** Run/stop */ -#define EHCI_USBCMD_RUN 0x00000001UL - -/** Host controller reset */ -#define EHCI_USBCMD_HCRST 0x00000002UL - -/** Frame list size */ -#define EHCI_USBCMD_FLSIZE(flsize) ( (flsize) << 2 ) - -/** Frame list size mask */ -#define EHCI_USBCMD_FLSIZE_MASK EHCI_USBCMD_FLSIZE ( 3 ) - -/** Default frame list size */ -#define EHCI_FLSIZE_DEFAULT 0 - -/** Smallest allowed frame list size */ -#define EHCI_FLSIZE_SMALL 2 - -/** Number of elements in frame list */ -#define EHCI_PERIODIC_FRAMES(flsize) ( 1024 >> (flsize) ) - -/** Periodic schedule enable */ -#define EHCI_USBCMD_PERIODIC 0x00000010UL - -/** Asynchronous schedule enable */ -#define EHCI_USBCMD_ASYNC 0x00000020UL - -/** Asyncchronous schedule advance doorbell */ -#define EHCI_USBCMD_ASYNC_ADVANCE 0x000040UL - -/** USB status register */ -#define EHCI_OP_USBSTS 0x04 - -/** USB interrupt */ -#define EHCI_USBSTS_USBINT 0x00000001UL - -/** USB error interrupt */ -#define EHCI_USBSTS_USBERRINT 0x00000002UL - -/** Port change detect */ -#define EHCI_USBSTS_PORT 0x00000004UL - -/** Frame list rollover */ -#define EHCI_USBSTS_ROLLOVER 0x00000008UL - -/** Host system error */ -#define EHCI_USBSTS_SYSERR 0x00000010UL - -/** Asynchronous schedule advanced */ -#define EHCI_USBSTS_ASYNC_ADVANCE 0x00000020UL - -/** Periodic schedule enabled */ -#define EHCI_USBSTS_PERIODIC 0x00004000UL - -/** Asynchronous schedule enabled */ -#define EHCI_USBSTS_ASYNC 0x00008000UL - -/** Host controller halted */ -#define EHCI_USBSTS_HCH 0x00001000UL - -/** USB status change mask */ -#define EHCI_USBSTS_CHANGE \ - ( EHCI_USBSTS_USBINT | EHCI_USBSTS_USBERRINT | \ - EHCI_USBSTS_PORT | EHCI_USBSTS_ROLLOVER | \ - EHCI_USBSTS_SYSERR | EHCI_USBSTS_ASYNC_ADVANCE ) - -/** USB interrupt enable register */ -#define EHCI_OP_USBINTR 0x08 - -/** Frame index register */ -#define EHCI_OP_FRINDEX 0x0c - -/** Control data structure segment register */ -#define EHCI_OP_CTRLDSSEGMENT 0x10 - -/** Periodic frame list base address register */ -#define EHCI_OP_PERIODICLISTBASE 0x14 - -/** Current asynchronous list address register */ -#define EHCI_OP_ASYNCLISTADDR 0x18 - -/** Configure flag register */ -#define EHCI_OP_CONFIGFLAG 0x40 - -/** Configure flag */ -#define EHCI_CONFIGFLAG_CF 0x00000001UL - -/** Port status and control register */ -#define EHCI_OP_PORTSC(port) ( 0x40 + ( (port) << 2 ) ) - -/** Current connect status */ -#define EHCI_PORTSC_CCS 0x00000001UL - -/** Connect status change */ -#define EHCI_PORTSC_CSC 0x00000002UL - -/** Port enabled */ -#define EHCI_PORTSC_PED 0x00000004UL - -/** Port enabled/disabled change */ -#define EHCI_PORTSC_PEC 0x00000008UL - -/** Over-current change */ -#define EHCI_PORTSC_OCC 0x00000020UL - -/** Port reset */ -#define EHCI_PORTSC_PR 0x00000100UL - -/** Line status */ -#define EHCI_PORTSC_LINE_STATUS(portsc) ( ( (portsc) >> 10 ) & 0x3 ) - -/** Line status: low-speed device */ -#define EHCI_PORTSC_LINE_STATUS_LOW 0x1 - -/** Port power */ -#define EHCI_PORTSC_PP 0x00001000UL - -/** Port owner */ -#define EHCI_PORTSC_OWNER 0x00002000UL - -/** Port status change mask */ -#define EHCI_PORTSC_CHANGE \ - ( EHCI_PORTSC_CSC | EHCI_PORTSC_PEC | EHCI_PORTSC_OCC ) - -/** List terminator */ -#define EHCI_LINK_TERMINATE 0x00000001UL - -/** Frame list type */ -#define EHCI_LINK_TYPE(type) ( (type) << 1 ) - -/** Queue head type */ -#define EHCI_LINK_TYPE_QH EHCI_LINK_TYPE ( 1 ) - -/** A periodic frame list entry */ -struct ehci_periodic_frame { - /** First queue head */ - uint32_t link; -} __attribute__ (( packed )); - -/** A transfer descriptor */ -struct ehci_transfer_descriptor { - /** Next transfer descriptor */ - uint32_t next; - /** Alternate next transfer descriptor */ - uint32_t alt; - /** Status */ - uint8_t status; - /** Flags */ - uint8_t flags; - /** Transfer length */ - uint16_t len; - /** Buffer pointers (low 32 bits) */ - uint32_t low[5]; - /** Extended buffer pointers (high 32 bits) */ - uint32_t high[5]; - /** Reserved */ - uint8_t reserved[12]; -} __attribute__ (( packed )); - -/** Transaction error */ -#define EHCI_STATUS_XACT_ERR 0x08 - -/** Babble detected */ -#define EHCI_STATUS_BABBLE 0x10 - -/** Data buffer error */ -#define EHCI_STATUS_BUFFER 0x20 - -/** Halted */ -#define EHCI_STATUS_HALTED 0x40 - -/** Active */ -#define EHCI_STATUS_ACTIVE 0x80 - -/** PID code */ -#define EHCI_FL_PID(code) ( (code) << 0 ) - -/** OUT token */ -#define EHCI_FL_PID_OUT EHCI_FL_PID ( 0 ) - -/** IN token */ -#define EHCI_FL_PID_IN EHCI_FL_PID ( 1 ) - -/** SETUP token */ -#define EHCI_FL_PID_SETUP EHCI_FL_PID ( 2 ) - -/** Error counter */ -#define EHCI_FL_CERR( count ) ( (count) << 2 ) - -/** Error counter maximum value */ -#define EHCI_FL_CERR_MAX EHCI_FL_CERR ( 3 ) - -/** Interrupt on completion */ -#define EHCI_FL_IOC 0x80 - -/** Length mask */ -#define EHCI_LEN_MASK 0x7fff - -/** Data toggle */ -#define EHCI_LEN_TOGGLE 0x8000 - -/** A queue head */ -struct ehci_queue_head { - /** Horizontal link pointer */ - uint32_t link; - /** Endpoint characteristics */ - uint32_t chr; - /** Endpoint capabilities */ - uint32_t cap; - /** Current transfer descriptor */ - uint32_t current; - /** Transfer descriptor cache */ - struct ehci_transfer_descriptor cache; -} __attribute__ (( packed )); - -/** Device address */ -#define EHCI_CHR_ADDRESS( address ) ( (address) << 0 ) - -/** Endpoint number */ -#define EHCI_CHR_ENDPOINT( address ) ( ( (address) & 0xf ) << 8 ) - -/** Endpoint speed */ -#define EHCI_CHR_EPS( eps ) ( (eps) << 12 ) - -/** Full-speed endpoint */ -#define EHCI_CHR_EPS_FULL EHCI_CHR_EPS ( 0 ) - -/** Low-speed endpoint */ -#define EHCI_CHR_EPS_LOW EHCI_CHR_EPS ( 1 ) - -/** High-speed endpoint */ -#define EHCI_CHR_EPS_HIGH EHCI_CHR_EPS ( 2 ) - -/** Explicit data toggles */ -#define EHCI_CHR_TOGGLE 0x00004000UL - -/** Head of reclamation list flag */ -#define EHCI_CHR_HEAD 0x00008000UL - -/** Maximum packet length */ -#define EHCI_CHR_MAX_LEN( len ) ( (len) << 16 ) - -/** Control endpoint flag */ -#define EHCI_CHR_CONTROL 0x08000000UL - -/** Interrupt schedule mask */ -#define EHCI_CAP_INTR_SCHED( uframe ) ( 1 << ( (uframe) + 0 ) ) - -/** Split completion schedule mask */ -#define EHCI_CAP_SPLIT_SCHED( uframe ) ( 1 << ( (uframe) + 8 ) ) - -/** Default split completion schedule mask - * - * We schedule all split starts in microframe 0, on the assumption - * that we will never have to deal with more than sixteen actively - * interrupting devices via the same transaction translator. We - * schedule split completions for all remaining microframes after - * microframe 1 (in which the low-speed or full-speed transaction is - * assumed to execute). This is a very crude approximation designed - * to avoid the need for calculating exactly when low-speed and - * full-speed transactions will execute. Since we only ever deal with - * interrupt endpoints (rather than isochronous endpoints), the volume - * of periodic traffic is extremely low, and this approximation should - * remain valid. - */ -#define EHCI_CAP_SPLIT_SCHED_DEFAULT \ - ( EHCI_CAP_SPLIT_SCHED ( 2 ) | EHCI_CAP_SPLIT_SCHED ( 3 ) | \ - EHCI_CAP_SPLIT_SCHED ( 4 ) | EHCI_CAP_SPLIT_SCHED ( 5 ) | \ - EHCI_CAP_SPLIT_SCHED ( 6 ) | EHCI_CAP_SPLIT_SCHED ( 7 ) ) - -/** Transaction translator hub address */ -#define EHCI_CAP_TT_HUB( address ) ( (address) << 16 ) - -/** Transaction translator port number */ -#define EHCI_CAP_TT_PORT( port ) ( (port) << 23 ) - -/** High-bandwidth pipe multiplier */ -#define EHCI_CAP_MULT( mult ) ( (mult) << 30 ) - -/** A transfer descriptor ring */ -struct ehci_ring { - /** Producer counter */ - unsigned int prod; - /** Consumer counter */ - unsigned int cons; - - /** Residual untransferred data */ - size_t residual; - - /** I/O buffers */ - struct io_buffer **iobuf; - - /** Queue head */ - struct ehci_queue_head *head; - /** Transfer descriptors */ - struct ehci_transfer_descriptor *desc; -}; - -/** Number of transfer descriptors in a ring - * - * This is a policy decision. - */ -#define EHCI_RING_COUNT 64 - -/** - * Calculate space used in transfer descriptor ring - * - * @v ring Transfer descriptor ring - * @ret fill Number of entries used - */ -static inline __attribute__ (( always_inline )) unsigned int -ehci_ring_fill ( struct ehci_ring *ring ) { - unsigned int fill; - - fill = ( ring->prod - ring->cons ); - assert ( fill <= EHCI_RING_COUNT ); - return fill; -} - -/** - * Calculate space remaining in transfer descriptor ring - * - * @v ring Transfer descriptor ring - * @ret remaining Number of entries remaining - */ -static inline __attribute__ (( always_inline )) unsigned int -ehci_ring_remaining ( struct ehci_ring *ring ) { - unsigned int fill = ehci_ring_fill ( ring ); - - return ( EHCI_RING_COUNT - fill ); -} - -/** Time to delay after enabling power to a port - * - * This is not mandated by EHCI; we use the value given for xHCI. - */ -#define EHCI_PORT_POWER_DELAY_MS 20 - -/** Time to delay after releasing ownership of a port - * - * This is a policy decision. - */ -#define EHCI_DISOWN_DELAY_MS 100 - -/** Maximum time to wait for BIOS to release ownership - * - * This is a policy decision. - */ -#define EHCI_USBLEGSUP_MAX_WAIT_MS 100 - -/** Maximum time to wait for asynchronous schedule to advance - * - * This is a policy decision. - */ -#define EHCI_ASYNC_ADVANCE_MAX_WAIT_MS 100 - -/** Maximum time to wait for host controller to stop - * - * This is a policy decision. - */ -#define EHCI_STOP_MAX_WAIT_MS 100 - -/** Maximum time to wait for reset to complete - * - * This is a policy decision. - */ -#define EHCI_RESET_MAX_WAIT_MS 500 - -/** Maximum time to wait for a port reset to complete - * - * This is a policy decision. - */ -#define EHCI_PORT_RESET_MAX_WAIT_MS 500 - -/** An EHCI transfer */ -struct ehci_transfer { - /** Data buffer */ - void *data; - /** Length */ - size_t len; - /** Flags - * - * This is the bitwise OR of zero or more EHCI_FL_XXX values. - * The low 8 bits are copied to the flags byte within the - * transfer descriptor; the remaining bits hold flags - * meaningful only to our driver code. - */ - unsigned int flags; -}; - -/** Set initial data toggle */ -#define EHCI_FL_TOGGLE 0x8000 - -/** An EHCI device */ -struct ehci_device { - /** Registers */ - void *regs; - /** Name */ - const char *name; - - /** Capability registers */ - void *cap; - /** Operational registers */ - void *op; - - /** Number of ports */ - unsigned int ports; - /** 64-bit addressing capability */ - int addr64; - /** Frame list size */ - unsigned int flsize; - /** EHCI extended capabilities offset */ - unsigned int eecp; - - /** USB legacy support capability (if present and enabled) */ - unsigned int legacy; - - /** Control data structure segment */ - uint32_t ctrldssegment; - /** Asynchronous queue head */ - struct ehci_queue_head *head; - /** Periodic frame list */ - struct ehci_periodic_frame *frame; - - /** List of all endpoints */ - struct list_head endpoints; - /** Asynchronous schedule */ - struct list_head async; - /** Periodic schedule - * - * Listed in decreasing order of endpoint interval. - */ - struct list_head periodic; - - /** USB bus */ - struct usb_bus *bus; -}; - -/** An EHCI endpoint */ -struct ehci_endpoint { - /** EHCI device */ - struct ehci_device *ehci; - /** USB endpoint */ - struct usb_endpoint *ep; - /** List of all endpoints */ - struct list_head list; - /** Endpoint schedule */ - struct list_head schedule; - - /** Transfer descriptor ring */ - struct ehci_ring ring; -}; - -extern unsigned int ehci_companion ( struct pci_device *pci ); - -#endif /* _IPXE_EHCI_H */ diff --git a/qemu/roms/ipxe/src/drivers/usb/uhci.c b/qemu/roms/ipxe/src/drivers/usb/uhci.c deleted file mode 100644 index b6bb92560..000000000 --- a/qemu/roms/ipxe/src/drivers/usb/uhci.c +++ /dev/null @@ -1,1577 +0,0 @@ -/* - * 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 <strings.h> -#include <unistd.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/malloc.h> -#include <ipxe/pci.h> -#include <ipxe/usb.h> -#include "ehci.h" -#include "uhci.h" - -/** @file - * - * USB Universal Host Controller Interface (UHCI) driver - * - */ - -/****************************************************************************** - * - * Register access - * - ****************************************************************************** - */ - -/** - * Check that address is reachable - * - * @v addr Address - * @v len Length - * @ret rc Return status code - */ -static inline __attribute__ (( always_inline)) int -uhci_reachable ( void *addr, size_t len ) { - physaddr_t phys = virt_to_phys ( addr ); - - /* Always reachable in a 32-bit build */ - if ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) ) - return 0; - - /* Reachable if below 4GB */ - if ( ( ( phys + len - 1 ) & ~0xffffffffULL ) == 0 ) - return 0; - - return -ENOTSUP; -} - -/****************************************************************************** - * - * Run / stop / reset - * - ****************************************************************************** - */ - -/** - * Start UHCI device - * - * @v uhci UHCI device - */ -static void uhci_run ( struct uhci_device *uhci ) { - uint16_t usbcmd; - - /* Set run/stop bit */ - usbcmd = inw ( uhci->regs + UHCI_USBCMD ); - usbcmd |= ( UHCI_USBCMD_RUN | UHCI_USBCMD_MAX64 ); - outw ( usbcmd, uhci->regs + UHCI_USBCMD ); -} - -/** - * Stop UHCI device - * - * @v uhci UHCI device - * @ret rc Return status code - */ -static int uhci_stop ( struct uhci_device *uhci ) { - uint16_t usbcmd; - uint16_t usbsts; - unsigned int i; - - /* Clear run/stop bit */ - usbcmd = inw ( uhci->regs + UHCI_USBCMD ); - usbcmd &= ~UHCI_USBCMD_RUN; - outw ( usbcmd, uhci->regs + UHCI_USBCMD ); - - /* Wait for device to stop */ - for ( i = 0 ; i < UHCI_STOP_MAX_WAIT_MS ; i++ ) { - - /* Check if device is stopped */ - usbsts = inw ( uhci->regs + UHCI_USBSTS ); - if ( usbsts & UHCI_USBSTS_HCHALTED ) - return 0; - - /* Delay */ - mdelay ( 1 ); - } - - DBGC ( uhci, "UHCI %s timed out waiting for stop\n", uhci->name ); - return -ETIMEDOUT; -} - -/** - * Reset UHCI device - * - * @v uhci UHCI device - * @ret rc Return status code - */ -static int uhci_reset ( struct uhci_device *uhci ) { - uint16_t usbcmd; - unsigned int i; - int rc; - - /* The UHCI specification states that resetting a running - * device may result in undefined behaviour, so try stopping - * it first. - */ - if ( ( rc = uhci_stop ( uhci ) ) != 0 ) { - /* Ignore errors and attempt to reset the device anyway */ - } - - /* Reset device */ - outw ( UHCI_USBCMD_HCRESET, uhci->regs + UHCI_USBCMD ); - - /* Wait for reset to complete */ - for ( i = 0 ; i < UHCI_RESET_MAX_WAIT_MS ; i++ ) { - - /* Check if reset is complete */ - usbcmd = inw ( uhci->regs + UHCI_USBCMD ); - if ( ! ( usbcmd & UHCI_USBCMD_HCRESET ) ) - return 0; - - /* Delay */ - mdelay ( 1 ); - } - - DBGC ( uhci, "UHCI %s timed out waiting for reset\n", uhci->name ); - return -ETIMEDOUT; -} - -/****************************************************************************** - * - * Transfer descriptor rings - * - ****************************************************************************** - */ - -/** - * Allocate transfer ring - * - * @v ring Transfer ring - * @ret rc Return status code - */ -static int uhci_ring_alloc ( struct uhci_ring *ring ) { - int rc; - - /* Initialise structure */ - memset ( ring, 0, sizeof ( *ring ) ); - - /* Allocate queue head */ - ring->head = malloc_dma ( sizeof ( *ring->head ), UHCI_ALIGN ); - if ( ! ring->head ) { - rc = -ENOMEM; - goto err_alloc; - } - if ( ( rc = uhci_reachable ( ring->head, - sizeof ( *ring->head ) ) ) != 0 ) - goto err_unreachable; - - /* Initialise queue head */ - ring->head->current = cpu_to_le32 ( UHCI_LINK_TERMINATE ); - - return 0; - - err_unreachable: - free_dma ( ring->head, sizeof ( *ring->head ) ); - err_alloc: - return rc; -} - -/** - * Free transfer ring - * - * @v ring Transfer ring - */ -static void uhci_ring_free ( struct uhci_ring *ring ) { - unsigned int i; - - /* Sanity checks */ - assert ( uhci_ring_fill ( ring ) == 0 ); - for ( i = 0 ; i < UHCI_RING_COUNT ; i++ ) - assert ( ring->xfer[i] == NULL ); - - /* Free queue head */ - free_dma ( ring->head, sizeof ( *ring->head ) ); -} - -/** - * Enqueue new transfer - * - * @v ring Transfer ring - * @v iobuf I/O buffer - * @v count Number of descriptors - * @ret rc Return status code - */ -static int uhci_enqueue ( struct uhci_ring *ring, struct io_buffer *iobuf, - unsigned int count ) { - struct uhci_transfer *xfer; - struct uhci_transfer *end; - struct uhci_transfer_descriptor *desc; - unsigned int index = ( ring->prod % UHCI_RING_COUNT ); - uint32_t link; - size_t len; - int rc; - - /* Sanity check */ - assert ( count > 0 ); - assert ( iobuf != NULL ); - - /* Check for space in ring */ - if ( ! uhci_ring_remaining ( ring ) ) { - rc = -ENOBUFS; - goto err_ring_full; - } - - /* Check for reachability of I/O buffer */ - if ( ( rc = uhci_reachable ( iobuf->data, iob_len ( iobuf ) ) ) != 0 ) - goto err_unreachable_iobuf; - - /* Allocate transfer */ - xfer = malloc ( sizeof ( *xfer ) ); - if ( ! xfer ) { - rc = -ENOMEM; - goto err_alloc_xfer; - } - - /* Initialise transfer */ - xfer->prod = 0; - xfer->cons = 0; - xfer->len = 0; - xfer->iobuf = iobuf; - - /* Allocate transfer descriptors */ - len = ( count * sizeof ( xfer->desc[0] ) ); - xfer->desc = malloc_dma ( len, UHCI_ALIGN ); - if ( ! xfer->desc ) { - rc = -ENOMEM; - goto err_alloc_desc; - } - if ( ( rc = uhci_reachable ( xfer->desc, len ) ) != 0 ) - goto err_unreachable_desc; - - /* Initialise transfer descriptors */ - memset ( xfer->desc, 0, len ); - desc = xfer->desc; - for ( ; --count ; desc++ ) { - link = ( virt_to_phys ( desc + 1 ) | UHCI_LINK_DEPTH_FIRST ); - desc->link = cpu_to_le32 ( link ); - desc->flags = ring->flags; - } - desc->link = cpu_to_le32 ( UHCI_LINK_TERMINATE ); - desc->flags = ( ring->flags | UHCI_FL_IOC ); - - /* Add to ring */ - wmb(); - link = virt_to_phys ( xfer->desc ); - if ( uhci_ring_fill ( ring ) > 0 ) { - end = ring->end; - end->desc[ end->prod - 1 ].link = cpu_to_le32 ( link ); - } else { - ring->head->current = cpu_to_le32 ( link ); - } - assert ( ring->xfer[index] == NULL ); - ring->xfer[index] = xfer; - ring->end = xfer; - ring->prod++; - - return 0; - - err_unreachable_desc: - free_dma ( xfer->desc, len ); - err_alloc_desc: - free ( xfer ); - err_alloc_xfer: - err_unreachable_iobuf: - err_ring_full: - return rc; -} - -/** - * Describe transfer - * - * @v ring Transfer ring - * @v data Data - * @v len Length of data - * @v pid Packet ID - */ -static void uhci_describe ( struct uhci_ring *ring, void *data, - size_t len, uint8_t pid ) { - struct uhci_transfer *xfer = ring->end; - struct uhci_transfer_descriptor *desc; - size_t frag_len; - uint32_t control; - - do { - /* Calculate fragment length */ - frag_len = len; - if ( frag_len > ring->mtu ) - frag_len = ring->mtu; - - /* Populate descriptor */ - desc = &xfer->desc[xfer->prod++]; - if ( pid == USB_PID_IN ) - desc->flags |= UHCI_FL_SPD; - control = ( ring->control | UHCI_CONTROL_PID ( pid ) | - UHCI_CONTROL_LEN ( frag_len ) ); - desc->control = cpu_to_le32 ( control ); - if ( data ) - desc->data = virt_to_phys ( data ); - wmb(); - desc->status = UHCI_STATUS_ACTIVE; - - /* Update data toggle */ - ring->control ^= UHCI_CONTROL_TOGGLE; - - /* Move to next descriptor */ - data += frag_len; - len -= frag_len; - - } while ( len ); -} - -/** - * Dequeue transfer - * - * @v ring Transfer ring - * @ret iobuf I/O buffer - */ -static struct io_buffer * uhci_dequeue ( struct uhci_ring *ring ) { - unsigned int index = ( ring->cons % UHCI_RING_COUNT ); - struct io_buffer *iobuf; - struct uhci_transfer *xfer; - size_t len; - - /* Sanity checks */ - assert ( uhci_ring_fill ( ring ) > 0 ); - - /* Consume transfer */ - xfer = ring->xfer[index]; - assert ( xfer != NULL ); - assert ( xfer->desc != NULL ); - iobuf = xfer->iobuf; - assert ( iobuf != NULL ); - ring->xfer[index] = NULL; - ring->cons++; - - /* Free transfer descriptors */ - len = ( xfer->prod * sizeof ( xfer->desc[0] ) ); - free_dma ( xfer->desc, len ); - - /* Free transfer */ - free ( xfer ); - - return iobuf; -} - -/** - * Restart ring - * - * @v ring Transfer ring - * @v toggle Expected data toggle for next descriptor - */ -static void uhci_restart ( struct uhci_ring *ring, uint32_t toggle ) { - struct uhci_transfer *xfer; - struct uhci_transfer_descriptor *desc; - struct uhci_transfer_descriptor *first; - uint32_t link; - unsigned int i; - unsigned int j; - - /* Sanity check */ - assert ( ring->head->current == cpu_to_le32 ( UHCI_LINK_TERMINATE ) ); - - /* If ring is empty, then just update the data toggle for the - * next descriptor. - */ - if ( uhci_ring_fill ( ring ) == 0 ) { - ring->control &= ~UHCI_CONTROL_TOGGLE; - ring->control |= toggle; - return; - } - - /* If expected toggle does not match the toggle in the first - * unconsumed descriptor, then invert all toggles. - */ - xfer = ring->xfer[ ring->cons % UHCI_RING_COUNT ]; - assert ( xfer != NULL ); - assert ( xfer->cons == 0 ); - first = &xfer->desc[0]; - if ( ( le32_to_cpu ( first->control ) ^ toggle ) & UHCI_CONTROL_TOGGLE){ - - /* Invert toggle on all unconsumed transfer descriptors */ - for ( i = ring->cons ; i != ring->prod ; i++ ) { - xfer = ring->xfer[ i % UHCI_RING_COUNT ]; - assert ( xfer != NULL ); - assert ( xfer->cons == 0 ); - for ( j = 0 ; j < xfer->prod ; j++ ) { - desc = &xfer->desc[j]; - desc->control ^= - cpu_to_le32 ( UHCI_CONTROL_TOGGLE ); - } - } - - /* Invert toggle for next descriptor to be enqueued */ - ring->control ^= UHCI_CONTROL_TOGGLE; - } - - /* Restart ring at first unconsumed transfer */ - link = virt_to_phys ( first ); - wmb(); - ring->head->current = cpu_to_le32 ( link ); -} - -/****************************************************************************** - * - * Schedule management - * - ****************************************************************************** - */ - -/** - * Get link value for a queue head - * - * @v queue Queue head - * @ret link Link value - */ -static inline uint32_t uhci_link_qh ( struct uhci_queue_head *queue ) { - - return ( virt_to_phys ( queue ) | UHCI_LINK_TYPE_QH ); -} - -/** - * (Re)build asynchronous schedule - * - * @v uhci UHCI device - */ -static void uhci_async_schedule ( struct uhci_device *uhci ) { - struct uhci_endpoint *endpoint; - struct uhci_queue_head *queue; - uint32_t end; - uint32_t link; - - /* Build schedule in reverse order of execution. Provided - * that we only ever add or remove single endpoints, this can - * safely run concurrently with hardware execution of the - * schedule. - */ - link = end = uhci_link_qh ( uhci->head ); - list_for_each_entry_reverse ( endpoint, &uhci->async, schedule ) { - queue = endpoint->ring.head; - queue->link = cpu_to_le32 ( link ); - wmb(); - link = uhci_link_qh ( queue ); - } - if ( link == end ) - link = UHCI_LINK_TERMINATE; - uhci->head->link = cpu_to_le32 ( link ); - wmb(); -} - -/** - * Add endpoint to asynchronous schedule - * - * @v endpoint Endpoint - */ -static void uhci_async_add ( struct uhci_endpoint *endpoint ) { - struct uhci_device *uhci = endpoint->uhci; - - /* Add to end of schedule */ - list_add_tail ( &endpoint->schedule, &uhci->async ); - - /* Rebuild schedule */ - uhci_async_schedule ( uhci ); -} - -/** - * Remove endpoint from asynchronous schedule - * - * @v endpoint Endpoint - */ -static void uhci_async_del ( struct uhci_endpoint *endpoint ) { - struct uhci_device *uhci = endpoint->uhci; - - /* Remove from schedule */ - list_check_contains_entry ( endpoint, &uhci->async, schedule ); - list_del ( &endpoint->schedule ); - - /* Rebuild schedule */ - uhci_async_schedule ( uhci ); - - /* Delay for a whole USB frame (with a 100% safety margin) */ - mdelay ( 2 ); -} - -/** - * (Re)build periodic schedule - * - * @v uhci UHCI device - */ -static void uhci_periodic_schedule ( struct uhci_device *uhci ) { - struct uhci_endpoint *endpoint; - struct uhci_queue_head *queue; - uint32_t link; - uint32_t end; - unsigned int max_interval; - unsigned int i; - - /* Build schedule in reverse order of execution. Provided - * that we only ever add or remove single endpoints, this can - * safely run concurrently with hardware execution of the - * schedule. - */ - DBGCP ( uhci, "UHCI %s periodic schedule: ", uhci->name ); - link = end = uhci_link_qh ( uhci->head ); - list_for_each_entry_reverse ( endpoint, &uhci->periodic, schedule ) { - queue = endpoint->ring.head; - queue->link = cpu_to_le32 ( link ); - wmb(); - DBGCP ( uhci, "%s%d", ( ( link == end ) ? "" : "<-" ), - endpoint->ep->interval ); - link = uhci_link_qh ( queue ); - } - DBGCP ( uhci, "\n" ); - - /* Populate periodic frame list */ - DBGCP ( uhci, "UHCI %s periodic frame list:", uhci->name ); - for ( i = 0 ; i < UHCI_FRAMES ; i++ ) { - - /* Calculate maximum interval (in microframes) which - * may appear as part of this frame list. - */ - if ( i == 0 ) { - /* Start of list: include all endpoints */ - max_interval = -1U; - } else { - /* Calculate highest power-of-two frame interval */ - max_interval = ( 1 << ( ffs ( i ) - 1 ) ); - /* Convert to microframes */ - max_interval <<= 3; - /* Round up to nearest 2^n-1 */ - max_interval = ( ( max_interval << 1 ) - 1 ); - } - - /* Find first endpoint in schedule satisfying this - * maximum interval constraint. - */ - link = uhci_link_qh ( uhci->head ); - list_for_each_entry ( endpoint, &uhci->periodic, schedule ) { - if ( endpoint->ep->interval <= max_interval ) { - queue = endpoint->ring.head; - link = uhci_link_qh ( queue ); - DBGCP ( uhci, " %d:%d", - i, endpoint->ep->interval ); - break; - } - } - uhci->frame->link[i] = cpu_to_le32 ( link ); - } - wmb(); - DBGCP ( uhci, "\n" ); -} - -/** - * Add endpoint to periodic schedule - * - * @v endpoint Endpoint - */ -static void uhci_periodic_add ( struct uhci_endpoint *endpoint ) { - struct uhci_device *uhci = endpoint->uhci; - struct uhci_endpoint *before; - unsigned int interval = endpoint->ep->interval; - - /* Find first endpoint with a smaller interval */ - list_for_each_entry ( before, &uhci->periodic, schedule ) { - if ( before->ep->interval < interval ) - break; - } - list_add_tail ( &endpoint->schedule, &before->schedule ); - - /* Rebuild schedule */ - uhci_periodic_schedule ( uhci ); -} - -/** - * Remove endpoint from periodic schedule - * - * @v endpoint Endpoint - */ -static void uhci_periodic_del ( struct uhci_endpoint *endpoint ) { - struct uhci_device *uhci = endpoint->uhci; - - /* Remove from schedule */ - list_check_contains_entry ( endpoint, &uhci->periodic, schedule ); - list_del ( &endpoint->schedule ); - - /* Rebuild schedule */ - uhci_periodic_schedule ( uhci ); - - /* Delay for a whole USB frame (with a 100% safety margin) */ - mdelay ( 2 ); -} - -/** - * Add endpoint to appropriate schedule - * - * @v endpoint Endpoint - */ -static void uhci_schedule_add ( struct uhci_endpoint *endpoint ) { - struct usb_endpoint *ep = endpoint->ep; - unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); - - if ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) { - uhci_periodic_add ( endpoint ); - } else { - uhci_async_add ( endpoint ); - } -} - -/** - * Remove endpoint from appropriate schedule - * - * @v endpoint Endpoint - */ -static void uhci_schedule_del ( struct uhci_endpoint *endpoint ) { - struct usb_endpoint *ep = endpoint->ep; - unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); - - if ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) { - uhci_periodic_del ( endpoint ); - } else { - uhci_async_del ( endpoint ); - } -} - -/****************************************************************************** - * - * Endpoint operations - * - ****************************************************************************** - */ - -/** - * Open endpoint - * - * @v ep USB endpoint - * @ret rc Return status code - */ -static int uhci_endpoint_open ( struct usb_endpoint *ep ) { - struct usb_device *usb = ep->usb; - struct uhci_device *uhci = usb_get_hostdata ( usb ); - struct uhci_endpoint *endpoint; - int rc; - - /* Allocate and initialise structure */ - endpoint = zalloc ( sizeof ( *endpoint ) ); - if ( ! endpoint ) { - rc = -ENOMEM; - goto err_alloc; - } - endpoint->uhci = uhci; - endpoint->ep = ep; - usb_endpoint_set_hostdata ( ep, endpoint ); - - /* Initialise descriptor ring */ - if ( ( rc = uhci_ring_alloc ( &endpoint->ring ) ) != 0 ) - goto err_ring_alloc; - endpoint->ring.mtu = ep->mtu; - endpoint->ring.flags = UHCI_FL_CERR_MAX; - if ( usb->port->speed < USB_SPEED_FULL ) - endpoint->ring.flags |= UHCI_FL_LS; - endpoint->ring.control = ( UHCI_CONTROL_DEVICE ( usb->address ) | - UHCI_CONTROL_ENDPOINT ( ep->address ) ); - - /* Add to list of endpoints */ - list_add_tail ( &endpoint->list, &uhci->endpoints ); - - /* Add to schedule */ - uhci_schedule_add ( endpoint ); - - return 0; - - uhci_ring_free ( &endpoint->ring ); - err_ring_alloc: - free ( endpoint ); - err_alloc: - return rc; -} - -/** - * Close endpoint - * - * @v ep USB endpoint - */ -static void uhci_endpoint_close ( struct usb_endpoint *ep ) { - struct uhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); - struct io_buffer *iobuf; - - /* Remove from schedule */ - uhci_schedule_del ( endpoint ); - - /* Cancel any incomplete transfers */ - while ( uhci_ring_fill ( &endpoint->ring ) ) { - iobuf = uhci_dequeue ( &endpoint->ring ); - if ( iobuf ) - usb_complete_err ( ep, iobuf, -ECANCELED ); - } - - /* Remove from list of endpoints */ - list_del ( &endpoint->list ); - - /* Free descriptor ring */ - uhci_ring_free ( &endpoint->ring ); - - /* Free endpoint */ - free ( endpoint ); -} - -/** - * Reset endpoint - * - * @v ep USB endpoint - * @ret rc Return status code - */ -static int uhci_endpoint_reset ( struct usb_endpoint *ep ) { - struct uhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); - struct uhci_ring *ring = &endpoint->ring; - - /* Restart ring */ - uhci_restart ( ring, 0 ); - - return 0; -} - -/** - * Update MTU - * - * @v ep USB endpoint - * @ret rc Return status code - */ -static int uhci_endpoint_mtu ( struct usb_endpoint *ep ) { - struct uhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); - - /* Update endpoint MTU */ - endpoint->ring.mtu = ep->mtu; - - return 0; -} - -/** - * Enqueue message transfer - * - * @v ep USB endpoint - * @v iobuf I/O buffer - * @ret rc Return status code - */ -static int uhci_endpoint_message ( struct usb_endpoint *ep, - struct io_buffer *iobuf ) { - struct uhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); - struct uhci_ring *ring = &endpoint->ring; - struct usb_setup_packet *packet; - unsigned int count; - size_t len; - int input; - int rc; - - /* Calculate number of descriptors */ - assert ( iob_len ( iobuf ) >= sizeof ( *packet ) ); - len = ( iob_len ( iobuf ) - sizeof ( *packet ) ); - count = ( 1 /* setup stage */ + - ( ( len + ring->mtu - 1 ) / ring->mtu ) /* data stage */ + - 1 /* status stage */ ); - - /* Enqueue transfer */ - if ( ( rc = uhci_enqueue ( ring, iobuf, count ) ) != 0 ) - return rc; - - /* Describe setup stage */ - packet = iobuf->data; - ring->control &= ~UHCI_CONTROL_TOGGLE; - uhci_describe ( ring, packet, sizeof ( *packet ), USB_PID_SETUP ); - iob_pull ( iobuf, sizeof ( *packet ) ); - - /* Describe data stage, if applicable */ - assert ( ring->control & UHCI_CONTROL_TOGGLE ); - input = ( packet->request & cpu_to_le16 ( USB_DIR_IN ) ); - if ( len ) { - uhci_describe ( ring, iobuf->data, len, - ( input ? USB_PID_IN : USB_PID_OUT ) ); - } - - /* Describe status stage */ - ring->control |= UHCI_CONTROL_TOGGLE; - uhci_describe ( ring, NULL, 0, - ( ( len && input ) ? USB_PID_OUT : USB_PID_IN ) ); - - /* Sanity check */ - assert ( ring->end->prod == count ); - - return 0; -} - -/** - * Enqueue stream transfer - * - * @v ep USB endpoint - * @v iobuf I/O buffer - * @v terminate Terminate using a short packet - * @ret rc Return status code - */ -static int uhci_endpoint_stream ( struct usb_endpoint *ep, - struct io_buffer *iobuf, int terminate ) { - struct uhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); - struct uhci_ring *ring = &endpoint->ring; - unsigned int count; - size_t len; - int input; - int zlp; - int rc; - - /* Calculate number of descriptors */ - len = iob_len ( iobuf ); - zlp = ( terminate && ( ( len & ( ring->mtu - 1 ) ) == 0 ) ); - count = ( ( ( len + ring->mtu - 1 ) / ring->mtu ) + ( zlp ? 1 : 0 ) ); - - /* Enqueue transfer */ - if ( ( rc = uhci_enqueue ( ring, iobuf, count ) ) != 0 ) - return rc; - - /* Describe data packet */ - input = ( ep->address & USB_DIR_IN ); - uhci_describe ( ring, iobuf->data, len, - ( input ? USB_PID_IN : USB_PID_OUT ) ); - - /* Describe zero-length packet, if applicable */ - if ( zlp ) - uhci_describe ( ring, NULL, 0, USB_PID_OUT ); - - /* Sanity check */ - assert ( ring->end->prod == count ); - - return 0; -} - -/** - * Check if transfer is a message transfer - * - * @v xfer UHCI transfer - * @ret is_message Transfer is a message transfer - */ -static inline int uhci_is_message ( struct uhci_transfer *xfer ) { - struct uhci_transfer_descriptor *desc = &xfer->desc[0]; - - return ( ( desc->control & cpu_to_le32 ( UHCI_CONTROL_PID_MASK ) ) == - cpu_to_le32 ( UHCI_CONTROL_PID ( USB_PID_SETUP ) ) ); -} - -/** - * Poll for completions - * - * @v endpoint Endpoint - */ -static void uhci_endpoint_poll ( struct uhci_endpoint *endpoint ) { - struct uhci_ring *ring = &endpoint->ring; - struct uhci_device *uhci = endpoint->uhci; - struct usb_endpoint *ep = endpoint->ep; - struct usb_device *usb = ep->usb; - struct uhci_transfer *xfer; - struct uhci_transfer_descriptor *desc; - struct io_buffer *iobuf; - unsigned int index; - uint32_t link; - uint32_t toggle; - uint32_t control; - uint16_t actual; - size_t len; - - /* Consume all completed descriptors */ - while ( uhci_ring_fill ( ring ) ) { - - /* Stop if we reach an uncompleted descriptor */ - index = ( ring->cons % UHCI_RING_COUNT ); - xfer = ring->xfer[index]; - assert ( xfer != NULL ); - assert ( xfer->cons < xfer->prod ); - desc = &xfer->desc[xfer->cons]; - rmb(); - if ( desc->status & UHCI_STATUS_ACTIVE ) - break; - control = le32_to_cpu ( desc->control ); - actual = le16_to_cpu ( desc->actual ); - - /* Update data length, if applicable */ - if ( UHCI_DATA_PACKET ( control ) ) - xfer->len += UHCI_ACTUAL_LEN ( actual ); - - /* If we have encountered an error, then deactivate - * the queue head (to prevent further hardware - * accesses to this transfer), consume the transfer, - * and report the error to the USB core. - */ - if ( desc->status & UHCI_STATUS_STALLED ) { - DBGC ( uhci, "UHCI %s %s completion %d.%d failed " - "(status %02x)\n", usb->name, - usb_endpoint_name ( ep ), index, - xfer->cons, desc->status ); - link = UHCI_LINK_TERMINATE; - ring->head->current = cpu_to_le32 ( link ); - wmb(); - iobuf = uhci_dequeue ( ring ); - usb_complete_err ( ep, iobuf, -EIO ); - break; - } - - /* Consume this descriptor */ - xfer->cons++; - - /* Check for short packets */ - if ( UHCI_SHORT_PACKET ( control, actual ) ) { - - /* Sanity checks */ - assert ( desc->flags & UHCI_FL_SPD ); - link = virt_to_phys ( desc ); - assert ( ( le32_to_cpu ( ring->head->current ) & - ~( UHCI_ALIGN - 1 ) ) == link ); - - /* If this is a message transfer, then restart - * at the status stage. - */ - if ( uhci_is_message ( xfer ) ) { - xfer->cons = ( xfer->prod - 1 ); - link = virt_to_phys ( &xfer->desc[xfer->cons] ); - ring->head->current = cpu_to_le32 ( link ); - break; - } - - /* Otherwise, this is a stream transfer. - * First, prevent further hardware access to - * this transfer. - */ - link = UHCI_LINK_TERMINATE; - ring->head->current = cpu_to_le32 ( link ); - wmb(); - - /* Determine expected data toggle for next descriptor */ - toggle = ( ( control ^ UHCI_CONTROL_TOGGLE ) & - UHCI_CONTROL_TOGGLE ); - - /* Consume this transfer */ - len = xfer->len; - iobuf = uhci_dequeue ( ring ); - - /* Update packet length */ - assert ( len <= iob_len ( iobuf ) ); - iob_unput ( iobuf, ( iob_len ( iobuf ) - len ) ); - - /* Restart ring */ - uhci_restart ( ring, toggle ); - - } else if ( xfer->cons == xfer->prod ) { - - /* Completed a transfer: consume it */ - len = xfer->len; - iobuf = uhci_dequeue ( ring ); - assert ( len == iob_len ( iobuf ) ); - - } else { - - /* Not a short packet and not yet complete: - * continue processing. - */ - continue; - } - - /* Report completion to USB core */ - usb_complete ( ep, iobuf ); - } -} - -/****************************************************************************** - * - * Device operations - * - ****************************************************************************** - */ - -/** - * Open device - * - * @v usb USB device - * @ret rc Return status code - */ -static int uhci_device_open ( struct usb_device *usb ) { - struct uhci_device *uhci = usb_bus_get_hostdata ( usb->port->hub->bus ); - - usb_set_hostdata ( usb, uhci ); - return 0; -} - -/** - * Close device - * - * @v usb USB device - */ -static void uhci_device_close ( struct usb_device *usb ) { - struct uhci_device *uhci = usb_get_hostdata ( usb ); - struct usb_bus *bus = uhci->bus; - - /* Free device address, if assigned */ - if ( usb->address ) - usb_free_address ( bus, usb->address ); -} - -/** - * Assign device address - * - * @v usb USB device - * @ret rc Return status code - */ -static int uhci_device_address ( struct usb_device *usb ) { - struct uhci_device *uhci = usb_get_hostdata ( usb ); - struct usb_bus *bus = uhci->bus; - struct usb_endpoint *ep0 = usb_endpoint ( usb, USB_EP0_ADDRESS ); - struct uhci_endpoint *endpoint0 = usb_endpoint_get_hostdata ( ep0 ); - int address; - int rc; - - /* Sanity checks */ - assert ( usb->address == 0 ); - assert ( ep0 != NULL ); - - /* Allocate device address */ - address = usb_alloc_address ( bus ); - if ( address < 0 ) { - rc = address; - DBGC ( uhci, "UHCI %s could not allocate address: %s\n", - usb->name, strerror ( rc ) ); - goto err_alloc_address; - } - - /* Set address */ - if ( ( rc = usb_set_address ( usb, address ) ) != 0 ) - goto err_set_address; - - /* Update device address */ - usb->address = address; - endpoint0->ring.control |= UHCI_CONTROL_DEVICE ( address ); - - return 0; - - err_set_address: - usb_free_address ( bus, address ); - err_alloc_address: - return rc; -} - -/****************************************************************************** - * - * Hub operations - * - ****************************************************************************** - */ - -/** - * Open hub - * - * @v hub USB hub - * @ret rc Return status code - */ -static int uhci_hub_open ( struct usb_hub *hub __unused ) { - - /* Nothing to do */ - return 0; -} - -/** - * Close hub - * - * @v hub USB hub - */ -static void uhci_hub_close ( struct usb_hub *hub __unused ) { - - /* Nothing to do */ -} - -/****************************************************************************** - * - * Root hub operations - * - ****************************************************************************** - */ - -/** - * Open root hub - * - * @v hub USB hub - * @ret rc Return status code - */ -static int uhci_root_open ( struct usb_hub *hub ) { - struct usb_bus *bus = hub->bus; - struct uhci_device *uhci = usb_bus_get_hostdata ( bus ); - - /* Record hub driver private data */ - usb_hub_set_drvdata ( hub, uhci ); - - return 0; -} - -/** - * Close root hub - * - * @v hub USB hub - */ -static void uhci_root_close ( struct usb_hub *hub ) { - - /* Clear hub driver private data */ - usb_hub_set_drvdata ( hub, NULL ); -} - -/** - * Enable port - * - * @v hub USB hub - * @v port USB port - * @ret rc Return status code - */ -static int uhci_root_enable ( struct usb_hub *hub, struct usb_port *port ) { - struct uhci_device *uhci = usb_hub_get_drvdata ( hub ); - uint16_t portsc; - unsigned int i; - - /* Reset port */ - portsc = inw ( uhci->regs + UHCI_PORTSC ( port->address ) ); - portsc |= UHCI_PORTSC_PR; - outw ( portsc, uhci->regs + UHCI_PORTSC ( port->address ) ); - mdelay ( USB_RESET_DELAY_MS ); - portsc &= ~UHCI_PORTSC_PR; - outw ( portsc, uhci->regs + UHCI_PORTSC ( port->address ) ); - mdelay ( USB_RESET_RECOVER_DELAY_MS ); - - /* Enable port */ - portsc |= UHCI_PORTSC_PED; - outw ( portsc, uhci->regs + UHCI_PORTSC ( port->address ) ); - mdelay ( USB_RESET_RECOVER_DELAY_MS ); - - /* Wait for port to become enabled */ - for ( i = 0 ; i < UHCI_PORT_ENABLE_MAX_WAIT_MS ; i++ ) { - - /* Check port status */ - portsc = inw ( uhci->regs + UHCI_PORTSC ( port->address ) ); - if ( portsc & UHCI_PORTSC_PED ) - return 0; - - /* Delay */ - mdelay ( 1 ); - } - - DBGC ( uhci, "UHCI %s-%d timed out waiting for port to enable " - "(status %04x)\n", uhci->name, port->address, portsc ); - return -ETIMEDOUT; -} - -/** - * Disable port - * - * @v hub USB hub - * @v port USB port - * @ret rc Return status code - */ -static int uhci_root_disable ( struct usb_hub *hub, struct usb_port *port ) { - struct uhci_device *uhci = usb_hub_get_drvdata ( hub ); - uint16_t portsc; - - /* Disable port */ - portsc = inw ( uhci->regs + UHCI_PORTSC ( port->address ) ); - portsc &= ~UHCI_PORTSC_PED; - outw ( portsc, uhci->regs + UHCI_PORTSC ( port->address ) ); - - return 0; -} - -/** - * Update root hub port speed - * - * @v hub USB hub - * @v port USB port - * @ret rc Return status code - */ -static int uhci_root_speed ( struct usb_hub *hub, struct usb_port *port ) { - struct uhci_device *uhci = usb_hub_get_drvdata ( hub ); - struct pci_device pci; - uint16_t portsc; - unsigned int speed; - - /* Read port status */ - portsc = inw ( uhci->regs + UHCI_PORTSC ( port->address ) ); - if ( ! ( portsc & UHCI_PORTSC_CCS ) ) { - /* Port not connected */ - speed = USB_SPEED_NONE; - } else if ( uhci->companion && - ! find_usb_bus_by_location ( BUS_TYPE_PCI, - uhci->companion ) ) { - /* Defer connection detection until companion - * controller has been enumerated. - */ - pci_init ( &pci, uhci->companion ); - DBGC ( uhci, "UHCI %s-%d deferring for companion " PCI_FMT "\n", - uhci->name, port->address, PCI_ARGS ( &pci ) ); - speed = USB_SPEED_NONE; - } else if ( portsc & UHCI_PORTSC_LS ) { - /* Low-speed device */ - speed = USB_SPEED_LOW; - } else { - /* Full-speed device */ - speed = USB_SPEED_FULL; - } - port->speed = speed; - - /* Record disconnections and clear changes */ - port->disconnected |= ( portsc & UHCI_PORTSC_CSC ); - outw ( portsc, uhci->regs + UHCI_PORTSC ( port->address ) ); - - return 0; -} - -/** - * Clear transaction translator buffer - * - * @v hub USB hub - * @v port USB port - * @v ep USB endpoint - * @ret rc Return status code - */ -static int uhci_root_clear_tt ( struct usb_hub *hub, struct usb_port *port, - struct usb_endpoint *ep ) { - struct uhci_device *uhci = usb_hub_get_drvdata ( hub ); - - /* Should never be called; this is a root hub */ - DBGC ( uhci, "UHCI %s-%d nonsensical CLEAR_TT for %s %s\n", uhci->name, - port->address, ep->usb->name, usb_endpoint_name ( ep ) ); - - return -ENOTSUP; -} - -/** - * Poll for port status changes - * - * @v hub USB hub - * @v port USB port - */ -static void uhci_root_poll ( struct usb_hub *hub, struct usb_port *port ) { - struct uhci_device *uhci = usb_hub_get_drvdata ( hub ); - uint16_t portsc; - uint16_t change; - - /* Do nothing unless something has changed */ - portsc = inw ( uhci->regs + UHCI_PORTSC ( port->address ) ); - change = ( portsc & UHCI_PORTSC_CHANGE ); - if ( ! change ) - return; - - /* Record disconnections and clear changes */ - port->disconnected |= ( portsc & UHCI_PORTSC_CSC ); - outw ( portsc, uhci->regs + UHCI_PORTSC ( port->address ) ); - - /* Report port status change */ - usb_port_changed ( port ); -} - -/****************************************************************************** - * - * Bus operations - * - ****************************************************************************** - */ - -/** - * Open USB bus - * - * @v bus USB bus - * @ret rc Return status code - */ -static int uhci_bus_open ( struct usb_bus *bus ) { - struct uhci_device *uhci = usb_bus_get_hostdata ( bus ); - int rc; - - /* Sanity checks */ - assert ( list_empty ( &uhci->async ) ); - assert ( list_empty ( &uhci->periodic ) ); - - /* Allocate and initialise asynchronous queue head */ - uhci->head = malloc_dma ( sizeof ( *uhci->head ), UHCI_ALIGN ); - if ( ! uhci->head ) { - rc = -ENOMEM; - goto err_alloc_head; - } - if ( ( rc = uhci_reachable ( uhci->head, sizeof ( *uhci->head ) ) ) !=0) - goto err_unreachable_head; - memset ( uhci->head, 0, sizeof ( *uhci->head ) ); - uhci->head->current = cpu_to_le32 ( UHCI_LINK_TERMINATE ); - uhci_async_schedule ( uhci ); - - /* Allocate periodic frame list */ - uhci->frame = malloc_dma ( sizeof ( *uhci->frame ), - sizeof ( *uhci->frame ) ); - if ( ! uhci->frame ) { - rc = -ENOMEM; - goto err_alloc_frame; - } - if ( ( rc = uhci_reachable ( uhci->frame, - sizeof ( *uhci->frame ) ) ) != 0 ) - goto err_unreachable_frame; - uhci_periodic_schedule ( uhci ); - outl ( virt_to_phys ( uhci->frame ), uhci->regs + UHCI_FLBASEADD ); - - /* Start controller */ - uhci_run ( uhci ); - - return 0; - - uhci_stop ( uhci ); - err_unreachable_frame: - free_dma ( uhci->frame, sizeof ( *uhci->frame ) ); - err_alloc_frame: - err_unreachable_head: - free_dma ( uhci->head, sizeof ( *uhci->head ) ); - err_alloc_head: - return rc; -} - -/** - * Close USB bus - * - * @v bus USB bus - */ -static void uhci_bus_close ( struct usb_bus *bus ) { - struct uhci_device *uhci = usb_bus_get_hostdata ( bus ); - - /* Sanity checks */ - assert ( list_empty ( &uhci->async ) ); - assert ( list_empty ( &uhci->periodic ) ); - - /* Stop controller */ - uhci_stop ( uhci ); - - /* Free periodic frame list */ - free_dma ( uhci->frame, sizeof ( *uhci->frame ) ); - - /* Free asynchronous schedule */ - free_dma ( uhci->head, sizeof ( *uhci->head ) ); -} - -/** - * Poll USB bus - * - * @v bus USB bus - */ -static void uhci_bus_poll ( struct usb_bus *bus ) { - struct uhci_device *uhci = usb_bus_get_hostdata ( bus ); - struct usb_hub *hub = bus->hub; - struct uhci_endpoint *endpoint; - unsigned int i; - - /* UHCI defers interrupts (including short packet detection) - * until the end of the frame. This can result in bulk IN - * endpoints remaining halted for much of the time, waiting - * for software action to reset the data toggles. We - * therefore ignore USBSTS and unconditionally poll all - * endpoints for completed transfer descriptors. - * - * As with EHCI, we trust that completion handlers are minimal - * and will not do anything that could plausibly affect the - * endpoint list itself. - */ - list_for_each_entry ( endpoint, &uhci->endpoints, list ) - uhci_endpoint_poll ( endpoint ); - - /* UHCI provides no single bit to indicate that a port status - * change has occurred. We therefore unconditionally iterate - * over all ports looking for status changes. - */ - for ( i = 1 ; i <= UHCI_PORTS ; i++ ) - uhci_root_poll ( hub, usb_port ( hub, i ) ); -} - -/****************************************************************************** - * - * PCI interface - * - ****************************************************************************** - */ - -/** USB host controller operations */ -static struct usb_host_operations uhci_operations = { - .endpoint = { - .open = uhci_endpoint_open, - .close = uhci_endpoint_close, - .reset = uhci_endpoint_reset, - .mtu = uhci_endpoint_mtu, - .message = uhci_endpoint_message, - .stream = uhci_endpoint_stream, - }, - .device = { - .open = uhci_device_open, - .close = uhci_device_close, - .address = uhci_device_address, - }, - .bus = { - .open = uhci_bus_open, - .close = uhci_bus_close, - .poll = uhci_bus_poll, - }, - .hub = { - .open = uhci_hub_open, - .close = uhci_hub_close, - }, - .root = { - .open = uhci_root_open, - .close = uhci_root_close, - .enable = uhci_root_enable, - .disable = uhci_root_disable, - .speed = uhci_root_speed, - .clear_tt = uhci_root_clear_tt, - }, -}; - -/** - * Locate EHCI companion controller (when no EHCI support is present) - * - * @v pci PCI device - * @ret busdevfn EHCI companion controller bus:dev.fn (if any) - */ -__weak unsigned int ehci_companion ( struct pci_device *pci __unused ) { - return 0; -} - -/** - * Probe PCI device - * - * @v pci PCI device - * @ret rc Return status code - */ -static int uhci_probe ( struct pci_device *pci ) { - struct uhci_device *uhci; - struct usb_port *port; - unsigned int i; - int rc; - - /* Allocate and initialise structure */ - uhci = zalloc ( sizeof ( *uhci ) ); - if ( ! uhci ) { - rc = -ENOMEM; - goto err_alloc; - } - uhci->name = pci->dev.name; - INIT_LIST_HEAD ( &uhci->endpoints ); - INIT_LIST_HEAD ( &uhci->async ); - INIT_LIST_HEAD ( &uhci->periodic ); - - /* Fix up PCI device */ - adjust_pci_device ( pci ); - - /* Identify EHCI companion controller, if any */ - uhci->companion = ehci_companion ( pci ); - - /* Claim ownership from BIOS. (There is no release mechanism - * for UHCI.) - */ - pci_write_config_word ( pci, UHCI_USBLEGSUP, UHCI_USBLEGSUP_DEFAULT ); - - /* Map registers */ - uhci->regs = pci->ioaddr; - if ( ! uhci->regs ) { - rc = -ENODEV; - goto err_ioremap; - } - - /* Reset device */ - if ( ( rc = uhci_reset ( uhci ) ) != 0 ) - goto err_reset; - - /* Allocate USB bus */ - uhci->bus = alloc_usb_bus ( &pci->dev, UHCI_PORTS, UHCI_MTU, - &uhci_operations ); - if ( ! uhci->bus ) { - rc = -ENOMEM; - goto err_alloc_bus; - } - usb_bus_set_hostdata ( uhci->bus, uhci ); - usb_hub_set_drvdata ( uhci->bus->hub, uhci ); - - /* Set port protocols */ - for ( i = 1 ; i <= UHCI_PORTS ; i++ ) { - port = usb_port ( uhci->bus->hub, i ); - port->protocol = USB_PROTO_2_0; - } - - /* Register USB bus */ - if ( ( rc = register_usb_bus ( uhci->bus ) ) != 0 ) - goto err_register; - - pci_set_drvdata ( pci, uhci ); - return 0; - - unregister_usb_bus ( uhci->bus ); - err_register: - free_usb_bus ( uhci->bus ); - err_alloc_bus: - uhci_reset ( uhci ); - err_reset: - err_ioremap: - free ( uhci ); - err_alloc: - return rc; -} - -/** - * Remove PCI device - * - * @v pci PCI device - */ -static void uhci_remove ( struct pci_device *pci ) { - struct uhci_device *uhci = pci_get_drvdata ( pci ); - struct usb_bus *bus = uhci->bus; - - unregister_usb_bus ( bus ); - assert ( list_empty ( &uhci->async ) ); - assert ( list_empty ( &uhci->periodic ) ); - free_usb_bus ( bus ); - uhci_reset ( uhci ); - free ( uhci ); -} - -/** UHCI PCI device IDs */ -static struct pci_device_id uhci_ids[] = { - PCI_ROM ( 0xffff, 0xffff, "uhci", "UHCI", 0 ), -}; - -/** UHCI PCI driver */ -struct pci_driver uhci_driver __pci_driver = { - .ids = uhci_ids, - .id_count = ( sizeof ( uhci_ids ) / sizeof ( uhci_ids[0] ) ), - .class = PCI_CLASS_ID ( PCI_CLASS_SERIAL, PCI_CLASS_SERIAL_USB, - PCI_CLASS_SERIAL_USB_UHCI ), - .probe = uhci_probe, - .remove = uhci_remove, -}; diff --git a/qemu/roms/ipxe/src/drivers/usb/uhci.h b/qemu/roms/ipxe/src/drivers/usb/uhci.h deleted file mode 100644 index ba4c28f7e..000000000 --- a/qemu/roms/ipxe/src/drivers/usb/uhci.h +++ /dev/null @@ -1,350 +0,0 @@ -#ifndef _IPXE_UHCI_H -#define _IPXE_UHCI_H - -/** @file - * - * USB Universal Host Controller Interface (UHCI) driver - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <assert.h> -#include <ipxe/pci.h> -#include <ipxe/usb.h> - -/** Minimum alignment required for data structures - * - * With the exception of the frame list (which is page-aligned), data - * structures used by UHCI generally require 16-byte alignment. - */ -#define UHCI_ALIGN 16 - -/** Number of ports */ -#define UHCI_PORTS 2 - -/** Maximum transfer size */ -#define UHCI_MTU 1280 - -/** I/O BAR size */ -#define UHCI_BAR_SIZE 0x14 - -/** USB command register */ -#define UHCI_USBCMD 0x00 - -/** Max packet is 64 bytes */ -#define UHCI_USBCMD_MAX64 0x0080 - -/** Host controller reset */ -#define UHCI_USBCMD_HCRESET 0x0002 - -/** Run/stop */ -#define UHCI_USBCMD_RUN 0x0001 - -/** USB status register */ -#define UHCI_USBSTS 0x02 - -/** Host controller halted */ -#define UHCI_USBSTS_HCHALTED 0x0020 - -/** USB interrupt */ -#define UHCI_USBSTS_USBINT 0x0001 - -/** Frame list base address register */ -#define UHCI_FLBASEADD 0x08 - -/** Port status and control register */ -#define UHCI_PORTSC(port) ( 0x0e + ( (port) << 1 ) ) - -/** Port reset */ -#define UHCI_PORTSC_PR 0x0200 - -/** Low-speed device attached */ -#define UHCI_PORTSC_LS 0x0100 - -/** Port enabled/disabled change */ -#define UHCI_PORTSC_PEC 0x0008 - -/** Port enabled */ -#define UHCI_PORTSC_PED 0x0004 - -/** Connect status change */ -#define UHCI_PORTSC_CSC 0x0002 - -/** Current connect status */ -#define UHCI_PORTSC_CCS 0x0001 - -/** Port status change mask */ -#define UHCI_PORTSC_CHANGE ( UHCI_PORTSC_CSC | UHCI_PORTSC_PEC ) - -/** Depth-first processing */ -#define UHCI_LINK_DEPTH_FIRST 0x00000004UL - -/** Queue head type */ -#define UHCI_LINK_TYPE_QH 0x00000002UL - -/** List terminator */ -#define UHCI_LINK_TERMINATE 0x00000001UL - -/** Number of frames in frame list */ -#define UHCI_FRAMES 1024 - -/** A frame list */ -struct uhci_frame_list { - /** Link pointer */ - uint32_t link[UHCI_FRAMES]; -} __attribute__ (( packed )); - -/** A transfer descriptor */ -struct uhci_transfer_descriptor { - /** Link pointer */ - uint32_t link; - /** Actual length */ - uint16_t actual; - /** Status */ - uint8_t status; - /** Flags */ - uint8_t flags; - /** Control */ - uint32_t control; - /** Buffer pointer */ - uint32_t data; -} __attribute__ (( packed )); - -/** Length mask */ -#define UHCI_LEN_MASK 0x7ff - -/** Actual length */ -#define UHCI_ACTUAL_LEN( actual ) ( ( (actual) + 1 ) & UHCI_LEN_MASK ) - -/** Active */ -#define UHCI_STATUS_ACTIVE 0x80 - -/** Stalled */ -#define UHCI_STATUS_STALLED 0x40 - -/** Data buffer error */ -#define UHCI_STATUS_BUFFER 0x20 - -/** Babble detected */ -#define UHCI_STATUS_BABBLE 0x10 - -/** NAK received */ -#define UHCI_STATUS_NAK 0x08 - -/** CRC/timeout error */ -#define UHCI_STATUS_CRC_TIMEOUT 0x04 - -/** Bitstuff error */ -#define UHCI_STATUS_BITSTUFF 0x02 - -/** Short packet detect */ -#define UHCI_FL_SPD 0x20 - -/** Error counter */ -#define UHCI_FL_CERR( count ) ( (count) << 3 ) - -/** Error counter maximum value */ -#define UHCI_FL_CERR_MAX UHCI_FL_CERR ( 3 ) - -/** Low speed device */ -#define UHCI_FL_LS 0x04 - -/** Interrupt on completion */ -#define UHCI_FL_IOC 0x01 - -/** Packet ID */ -#define UHCI_CONTROL_PID( pid ) ( (pid) << 0 ) - -/** Packet ID mask */ -#define UHCI_CONTROL_PID_MASK UHCI_CONTROL_PID ( 0xff ) - -/** Device address */ -#define UHCI_CONTROL_DEVICE( address ) ( (address) << 8 ) - -/** Endpoint address */ -#define UHCI_CONTROL_ENDPOINT( address ) ( (address) << 15 ) - -/** Data toggle */ -#define UHCI_CONTROL_TOGGLE ( 1 << 19 ) - -/** Data length */ -#define UHCI_CONTROL_LEN( len ) ( ( ( (len) - 1 ) & UHCI_LEN_MASK ) << 21 ) - -/** Check for data packet - * - * This check is based on the fact that only USB_PID_SETUP has bit 2 - * set. - */ -#define UHCI_DATA_PACKET( control ) ( ! ( control & 0x04 ) ) - -/** Check for short packet */ -#define UHCI_SHORT_PACKET( control, actual ) \ - ( ( ( (control) >> 21 ) ^ (actual) ) & UHCI_LEN_MASK ) - -/** USB legacy support register (in PCI configuration space) */ -#define UHCI_USBLEGSUP 0xc0 - -/** USB legacy support default value */ -#define UHCI_USBLEGSUP_DEFAULT 0x2000 - -/** A queue head */ -struct uhci_queue_head { - /** Horizontal link pointer */ - uint32_t link; - /** Current transfer descriptor */ - uint32_t current; -} __attribute__ (( packed )); - -/** A single UHCI transfer - * - * UHCI hardware is extremely simple, and requires software to build - * the entire packet schedule (including manually handling all of the - * data toggles). The hardware requires at least 16 bytes of transfer - * descriptors per 64 bytes of transmitted/received data. We allocate - * the transfer descriptors at the time that the transfer is enqueued, - * to avoid the need to allocate unreasonably large blocks when the - * endpoint is opened. - */ -struct uhci_transfer { - /** Producer counter */ - unsigned int prod; - /** Consumer counter */ - unsigned int cons; - /** Completed data length */ - size_t len; - - /** Transfer descriptors */ - struct uhci_transfer_descriptor *desc; - - /** I/O buffer */ - struct io_buffer *iobuf; -}; - -/** Number of transfer descriptors in a ring - * - * This is a policy decision. - */ -#define UHCI_RING_COUNT 16 - -/** A transfer ring */ -struct uhci_ring { - /** Producer counter */ - unsigned int prod; - /** Consumer counter */ - unsigned int cons; - - /** Maximum packet length */ - size_t mtu; - /** Base flags - * - * This incorporates the CERR and LS bits - */ - uint8_t flags; - /** Base control word - * - * This incorporates the device address, the endpoint address, - * and the data toggle for the next descriptor to be enqueued. - */ - uint32_t control; - - /** Transfers */ - struct uhci_transfer *xfer[UHCI_RING_COUNT]; - /** End of transfer ring (if non-empty) */ - struct uhci_transfer *end; - - /** Queue head */ - struct uhci_queue_head *head; -}; - -/** - * Calculate space used in transfer ring - * - * @v ring Transfer ring - * @ret fill Number of entries used - */ -static inline __attribute__ (( always_inline )) unsigned int -uhci_ring_fill ( struct uhci_ring *ring ) { - unsigned int fill; - - fill = ( ring->prod - ring->cons ); - assert ( fill <= UHCI_RING_COUNT ); - return fill; -} - -/** - * Calculate space remaining in transfer ring - * - * @v ring Transfer ring - * @ret remaining Number of entries remaining - */ -static inline __attribute__ (( always_inline )) unsigned int -uhci_ring_remaining ( struct uhci_ring *ring ) { - unsigned int fill = uhci_ring_fill ( ring ); - - return ( UHCI_RING_COUNT - fill ); -} - -/** Maximum time to wait for host controller to stop - * - * This is a policy decision. - */ -#define UHCI_STOP_MAX_WAIT_MS 100 - -/** Maximum time to wait for reset to complete - * - * This is a policy decision. - */ -#define UHCI_RESET_MAX_WAIT_MS 500 - -/** Maximum time to wait for a port to be enabled - * - * This is a policy decision. - */ -#define UHCI_PORT_ENABLE_MAX_WAIT_MS 500 - -/** A UHCI device */ -struct uhci_device { - /** Registers */ - unsigned long regs; - /** Name */ - const char *name; - - /** EHCI companion controller bus:dev.fn address (if any) */ - unsigned int companion; - - /** Asynchronous queue head */ - struct uhci_queue_head *head; - /** Frame list */ - struct uhci_frame_list *frame; - - /** List of all endpoints */ - struct list_head endpoints; - /** Asynchronous schedule */ - struct list_head async; - /** Periodic schedule - * - * Listed in decreasing order of endpoint interval. - */ - struct list_head periodic; - - /** USB bus */ - struct usb_bus *bus; -}; - -/** A UHCI endpoint */ -struct uhci_endpoint { - /** UHCI device */ - struct uhci_device *uhci; - /** USB endpoint */ - struct usb_endpoint *ep; - /** List of all endpoints */ - struct list_head list; - /** Endpoint schedule */ - struct list_head schedule; - - /** Transfer ring */ - struct uhci_ring ring; -}; - -#endif /* _IPXE_UHCI_H */ diff --git a/qemu/roms/ipxe/src/drivers/usb/usbhid.c b/qemu/roms/ipxe/src/drivers/usb/usbhid.c deleted file mode 100644 index c74535a05..000000000 --- a/qemu/roms/ipxe/src/drivers/usb/usbhid.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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 <string.h> -#include <errno.h> -#include <ipxe/usb.h> -#include <ipxe/usbhid.h> - -/** @file - * - * USB human interface devices (HID) - * - */ - -/** - * Open USB human interface device - * - * @v hid USB human interface device - * @ret rc Return status code - */ -int usbhid_open ( struct usb_hid *hid ) { - int rc; - - /* Open interrupt IN endpoint */ - if ( ( rc = usb_endpoint_open ( &hid->in ) ) != 0 ) { - DBGC ( hid, "HID %s could not open interrupt IN: %s\n", - hid->func->name, strerror ( rc ) ); - goto err_open_in; - } - - /* Refill interrupt IN endpoint */ - if ( ( rc = usb_refill ( &hid->in ) ) != 0 ) { - DBGC ( hid, "HID %s could not refill interrupt IN: %s\n", - hid->func->name, strerror ( rc ) ); - goto err_refill_in; - } - - /* Open interrupt OUT endpoint, if applicable */ - if ( hid->out.usb && - ( ( rc = usb_endpoint_open ( &hid->out ) ) != 0 ) ) { - DBGC ( hid, "HID %s could not open interrupt OUT: %s\n", - hid->func->name, strerror ( rc ) ); - goto err_open_out; - } - - return 0; - - usb_endpoint_close ( &hid->out ); - err_open_out: - err_refill_in: - usb_endpoint_close ( &hid->in ); - err_open_in: - return rc; -} - -/** - * Close USB human interface device - * - * @v hid USB human interface device - */ -void usbhid_close ( struct usb_hid *hid ) { - - /* Close interrupt OUT endpoint, if applicable */ - if ( hid->out.usb ) - usb_endpoint_close ( &hid->out ); - - /* Close interrupt IN endpoint */ - usb_endpoint_close ( &hid->in ); -} - -/** - * Refill USB human interface device endpoints - * - * @v hid USB human interface device - * @ret rc Return status code - */ -int usbhid_refill ( struct usb_hid *hid ) { - int rc; - - /* Refill interrupt IN endpoint */ - if ( ( rc = usb_refill ( &hid->in ) ) != 0 ) - return rc; - - /* Refill interrupt OUT endpoint, if applicable */ - if ( hid->out.usb && ( ( rc = usb_refill ( &hid->out ) ) != 0 ) ) - return rc; - - return 0; -} - -/** - * Describe USB human interface device - * - * @v hid USB human interface device - * @v config Configuration descriptor - * @ret rc Return status code - */ -int usbhid_describe ( struct usb_hid *hid, - struct usb_configuration_descriptor *config ) { - struct usb_interface_descriptor *desc; - int rc; - - /* Locate interface descriptor */ - desc = usb_interface_descriptor ( config, hid->func->interface[0], 0 ); - if ( ! desc ) { - DBGC ( hid, "HID %s has no interface descriptor\n", - hid->func->name ); - return -EINVAL; - } - - /* Describe interrupt IN endpoint */ - if ( ( rc = usb_endpoint_described ( &hid->in, config, desc, - USB_INTERRUPT_IN, 0 ) ) != 0 ) { - DBGC ( hid, "HID %s could not describe interrupt IN: %s\n", - hid->func->name, strerror ( rc ) ); - return rc; - } - - /* Describe interrupt OUT endpoint, if applicable */ - if ( hid->out.usb && - ( ( rc = usb_endpoint_described ( &hid->out, config, desc, - USB_INTERRUPT_OUT, 0 ) ) != 0 )){ - DBGC ( hid, "HID %s could not describe interrupt OUT: %s\n", - hid->func->name, strerror ( rc ) ); - return rc; - } - - return 0; -} diff --git a/qemu/roms/ipxe/src/drivers/usb/usbhub.c b/qemu/roms/ipxe/src/drivers/usb/usbhub.c deleted file mode 100644 index bf2a20005..000000000 --- a/qemu/roms/ipxe/src/drivers/usb/usbhub.c +++ /dev/null @@ -1,547 +0,0 @@ -/* - * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * You can also choose to distribute this program under the terms of - * the Unmodified Binary Distribution Licence (as given in the file - * COPYING.UBDL), provided that you have satisfied its requirements. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> -#include <assert.h> -#include <byteswap.h> -#include <ipxe/usb.h> -#include "usbhub.h" - -/** @file - * - * USB hub driver - * - */ - -/** - * Refill interrupt ring - * - * @v hubdev Hub device - */ -static void hub_refill ( struct usb_hub_device *hubdev ) { - int rc; - - /* Refill interrupt endpoint */ - if ( ( rc = usb_refill ( &hubdev->intr ) ) != 0 ) { - DBGC ( hubdev, "HUB %s could not refill interrupt: %s\n", - hubdev->name, strerror ( rc ) ); - /* Continue attempting to refill */ - return; - } - - /* Stop refill process */ - process_del ( &hubdev->refill ); -} - -/** Refill process descriptor */ -static struct process_descriptor hub_refill_desc = - PROC_DESC ( struct usb_hub_device, refill, hub_refill ); - -/** - * Complete interrupt transfer - * - * @v ep USB endpoint - * @v iobuf I/O buffer - * @v rc Completion status code - */ -static void hub_complete ( struct usb_endpoint *ep, - struct io_buffer *iobuf, int rc ) { - struct usb_hub_device *hubdev = - container_of ( ep, struct usb_hub_device, intr ); - struct usb_hub *hub = hubdev->hub; - uint8_t *data = iobuf->data; - unsigned int bits = ( 8 * iob_len ( iobuf ) ); - unsigned int i; - - /* Ignore packets cancelled when the endpoint closes */ - if ( ! ep->open ) - goto done; - - /* Ignore packets with errors */ - if ( rc != 0 ) { - DBGC ( hubdev, "HUB %s interrupt failed: %s\n", - hubdev->name, strerror ( rc ) ); - DBGC_HDA ( hubdev, 0, iobuf->data, iob_len ( iobuf ) ); - goto done; - } - - /* Report any port status changes */ - for ( i = 1 ; i <= hub->ports ; i++ ) { - - /* Sanity check */ - if ( i > bits ) { - DBGC ( hubdev, "HUB %s underlength interrupt:\n", - hubdev->name ); - DBGC_HDA ( hubdev, 0, iobuf->data, iob_len ( iobuf ) ); - goto done; - } - - /* Report port status change if applicable */ - if ( data[ i / 8 ] & ( 1 << ( i % 8 ) ) ) { - DBGC2 ( hubdev, "HUB %s port %d status changed\n", - hubdev->name, i ); - usb_port_changed ( usb_port ( hub, i ) ); - } - } - - done: - /* Start refill process */ - process_add ( &hubdev->refill ); -} - -/** Interrupt endpoint operations */ -static struct usb_endpoint_driver_operations usb_hub_intr_operations = { - .complete = hub_complete, -}; - -/** - * Open hub - * - * @v hub USB hub - * @ret rc Return status code - */ -static int hub_open ( struct usb_hub *hub ) { - struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub ); - struct usb_device *usb = hubdev->usb; - unsigned int i; - int rc; - - /* Ensure ports are powered */ - for ( i = 1 ; i <= hub->ports ; i++ ) { - if ( ( rc = usb_hub_set_port_feature ( usb, i, - USB_HUB_PORT_POWER, - 0 ) ) != 0 ) { - DBGC ( hubdev, "HUB %s port %d could not apply power: " - "%s\n", hubdev->name, i, strerror ( rc ) ); - goto err_power; - } - } - - /* Open interrupt endpoint */ - if ( ( rc = usb_endpoint_open ( &hubdev->intr ) ) != 0 ) { - DBGC ( hubdev, "HUB %s could not register interrupt: %s\n", - hubdev->name, strerror ( rc ) ); - goto err_open; - } - - /* Start refill process */ - process_add ( &hubdev->refill ); - - /* Refill interrupt ring */ - hub_refill ( hubdev ); - - return 0; - - usb_endpoint_close ( &hubdev->intr ); - err_open: - err_power: - return rc; -} - -/** - * Close hub - * - * @v hub USB hub - */ -static void hub_close ( struct usb_hub *hub ) { - struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub ); - - /* Close interrupt endpoint */ - usb_endpoint_close ( &hubdev->intr ); - - /* Stop refill process */ - process_del ( &hubdev->refill ); -} - -/** - * Enable port - * - * @v hub USB hub - * @v port USB port - * @ret rc Return status code - */ -static int hub_enable ( struct usb_hub *hub, struct usb_port *port ) { - struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub ); - struct usb_device *usb = hubdev->usb; - struct usb_hub_port_status status; - unsigned int current; - unsigned int i; - int rc; - - /* Initiate reset if applicable */ - if ( ( hub->protocol < USB_PROTO_3_0 ) && - ( ( rc = usb_hub_set_port_feature ( usb, port->address, - USB_HUB_PORT_RESET, 0 ) )!=0)){ - DBGC ( hubdev, "HUB %s port %d could not initiate reset: %s\n", - hubdev->name, port->address, strerror ( rc ) ); - return rc; - } - - /* Wait for port to become enabled */ - for ( i = 0 ; i < USB_HUB_ENABLE_MAX_WAIT_MS ; i++ ) { - - /* Check for port being enabled */ - if ( ( rc = usb_hub_get_port_status ( usb, port->address, - &status ) ) != 0 ) { - DBGC ( hubdev, "HUB %s port %d could not get status: " - "%s\n", hubdev->name, port->address, - strerror ( rc ) ); - return rc; - } - current = le16_to_cpu ( status.current ); - if ( current & ( 1 << USB_HUB_PORT_ENABLE ) ) - return 0; - - /* Delay */ - mdelay ( 1 ); - } - - DBGC ( hubdev, "HUB %s port %d timed out waiting for enable\n", - hubdev->name, port->address ); - return -ETIMEDOUT; -} - -/** - * Disable port - * - * @v hub USB hub - * @v port USB port - * @ret rc Return status code - */ -static int hub_disable ( struct usb_hub *hub, struct usb_port *port ) { - struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub ); - struct usb_device *usb = hubdev->usb; - int rc; - - /* Disable port */ - if ( ( rc = usb_hub_clear_port_feature ( usb, port->address, - USB_HUB_PORT_ENABLE, 0 ) )!=0){ - DBGC ( hubdev, "HUB %s port %d could not disable: %s\n", - hubdev->name, port->address, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Clear port status change bits - * - * @v hubdev USB hub device - * @v port Port number - * @v changed Port status change bits - * @ret rc Return status code - */ -static int hub_clear_changes ( struct usb_hub_device *hubdev, - unsigned int port, uint16_t changed ) { - struct usb_device *usb = hubdev->usb; - unsigned int bit; - unsigned int feature; - int rc; - - /* Clear each set bit */ - for ( bit = 0 ; bit < 16 ; bit++ ) { - - /* Skip unset bits */ - if ( ! ( changed & ( 1 << bit ) ) ) - continue; - - /* Skip unused features */ - feature = USB_HUB_C_FEATURE ( bit ); - if ( ! ( hubdev->features & ( 1 << feature ) ) ) - continue; - - /* Clear bit */ - if ( ( rc = usb_hub_clear_port_feature ( usb, port, - feature, 0 ) ) != 0 ) { - DBGC ( hubdev, "HUB %s port %d could not clear feature " - "%d: %s\n", hubdev->name, port, feature, - strerror ( rc ) ); - return rc; - } - } - - return 0; -} - -/** - * Update port speed - * - * @v hub USB hub - * @v port USB port - * @ret rc Return status code - */ -static int hub_speed ( struct usb_hub *hub, struct usb_port *port ) { - struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub ); - struct usb_device *usb = hubdev->usb; - struct usb_hub_port_status status; - unsigned int current; - unsigned int changed; - int rc; - - /* Get port status */ - if ( ( rc = usb_hub_get_port_status ( usb, port->address, - &status ) ) != 0 ) { - DBGC ( hubdev, "HUB %s port %d could not get status: %s\n", - hubdev->name, port->address, strerror ( rc ) ); - return rc; - } - current = le16_to_cpu ( status.current ); - changed = le16_to_cpu ( status.changed ); - DBGC2 ( hubdev, "HUB %s port %d status is %04x:%04x\n", - hubdev->name, port->address, changed, current ); - - /* Update port speed */ - if ( current & ( 1 << USB_HUB_PORT_CONNECTION ) ) { - if ( hub->protocol >= USB_PROTO_3_0 ) { - port->speed = USB_SPEED_SUPER; - } else if ( current & ( 1 << USB_HUB_PORT_LOW_SPEED ) ) { - port->speed = USB_SPEED_LOW; - } else if ( current & ( 1 << USB_HUB_PORT_HIGH_SPEED ) ) { - port->speed = USB_SPEED_HIGH; - } else { - port->speed = USB_SPEED_FULL; - } - } else { - port->speed = USB_SPEED_NONE; - } - - /* Record disconnections */ - port->disconnected |= ( changed & ( 1 << USB_HUB_PORT_CONNECTION ) ); - - /* Clear port status change bits */ - if ( ( rc = hub_clear_changes ( hubdev, port->address, changed ) ) != 0) - return rc; - - return 0; -} - -/** - * Clear transaction translator buffer - * - * @v hub USB hub - * @v port USB port - * @v ep USB endpoint - * @ret rc Return status code - */ -static int hub_clear_tt ( struct usb_hub *hub, struct usb_port *port, - struct usb_endpoint *ep ) { - struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub ); - struct usb_device *usb = hubdev->usb; - int rc; - - /* Clear transaction translator buffer. All hubs must support - * single-TT operation; we simplify our code by supporting - * only this configuration. - */ - if ( ( rc = usb_hub_clear_tt_buffer ( usb, ep->usb->address, - ep->address, ep->attributes, - USB_HUB_TT_SINGLE ) ) != 0 ) { - DBGC ( hubdev, "HUB %s port %d could not clear TT buffer: %s\n", - hubdev->name, port->address, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** USB hub operations */ -static struct usb_hub_driver_operations hub_operations = { - .open = hub_open, - .close = hub_close, - .enable = hub_enable, - .disable = hub_disable, - .speed = hub_speed, - .clear_tt = hub_clear_tt, -}; - -/** - * Probe USB hub - * - * @v func USB function - * @v config Configuration descriptor - * @ret rc Return status code - */ -static int hub_probe ( struct usb_function *func, - struct usb_configuration_descriptor *config ) { - struct usb_device *usb = func->usb; - struct usb_bus *bus = usb->port->hub->bus; - struct usb_hub_device *hubdev; - struct usb_interface_descriptor *interface; - union usb_hub_descriptor desc; - unsigned int depth; - unsigned int ports; - int enhanced; - int rc; - - /* Allocate and initialise structure */ - hubdev = zalloc ( sizeof ( *hubdev ) ); - if ( ! hubdev ) { - rc = -ENOMEM; - goto err_alloc; - } - enhanced = ( usb->port->protocol >= USB_PROTO_3_0 ); - hubdev->name = func->name; - hubdev->usb = usb; - hubdev->features = - ( enhanced ? USB_HUB_FEATURES_ENHANCED : USB_HUB_FEATURES ); - usb_endpoint_init ( &hubdev->intr, usb, &usb_hub_intr_operations ); - usb_refill_init ( &hubdev->intr, 0, USB_HUB_INTR_FILL ); - process_init_stopped ( &hubdev->refill, &hub_refill_desc, NULL ); - - /* Locate hub interface descriptor */ - interface = usb_interface_descriptor ( config, func->interface[0], 0 ); - if ( ! interface ) { - DBGC ( hubdev, "HUB %s has no interface descriptor\n", - hubdev->name ); - rc = -EINVAL; - goto err_interface; - } - - /* Locate interrupt endpoint descriptor */ - if ( ( rc = usb_endpoint_described ( &hubdev->intr, config, interface, - USB_INTERRUPT_IN, 0 ) ) != 0 ) { - DBGC ( hubdev, "HUB %s could not describe interrupt endpoint: " - "%s\n", hubdev->name, strerror ( rc ) ); - goto err_endpoint; - } - - /* Set hub depth */ - depth = usb_depth ( usb ); - if ( enhanced ) { - if ( ( rc = usb_hub_set_hub_depth ( usb, depth ) ) != 0 ) { - DBGC ( hubdev, "HUB %s could not set hub depth to %d: " - "%s\n", hubdev->name, depth, strerror ( rc ) ); - goto err_set_hub_depth; - } - } - - /* Get hub descriptor */ - if ( ( rc = usb_hub_get_descriptor ( usb, enhanced, &desc ) ) != 0 ) { - DBGC ( hubdev, "HUB %s could not get hub descriptor: %s\n", - hubdev->name, strerror ( rc ) ); - goto err_hub_descriptor; - } - ports = desc.basic.ports; - DBGC ( hubdev, "HUB %s has %d ports at depth %d%s\n", hubdev->name, - ports, depth, ( enhanced ? " (enhanced)" : "" ) ); - - /* Allocate hub */ - hubdev->hub = alloc_usb_hub ( bus, usb, ports, &hub_operations ); - if ( ! hubdev->hub ) { - rc = -ENOMEM; - goto err_alloc_hub; - } - usb_hub_set_drvdata ( hubdev->hub, hubdev ); - - /* Register hub */ - if ( ( rc = register_usb_hub ( hubdev->hub ) ) != 0 ) { - DBGC ( hubdev, "HUB %s could not register: %s\n", - hubdev->name, strerror ( rc ) ); - goto err_register_hub; - } - - usb_func_set_drvdata ( func, hubdev ); - return 0; - - unregister_usb_hub ( hubdev->hub ); - err_register_hub: - free_usb_hub ( hubdev->hub ); - err_alloc_hub: - err_hub_descriptor: - err_set_hub_depth: - err_endpoint: - err_interface: - free ( hubdev ); - err_alloc: - return rc; -} - -/** - * Remove USB hub - * - * @v func USB function - * @ret rc Return status code - */ -static void hub_remove ( struct usb_function *func ) { - struct usb_hub_device *hubdev = usb_func_get_drvdata ( func ); - struct usb_hub *hub = hubdev->hub; - struct usb_device *usb = hubdev->usb; - struct usb_port *port; - unsigned int i; - - /* If hub has been unplugged, mark all ports as unplugged */ - if ( usb->port->speed == USB_SPEED_NONE ) { - for ( i = 1 ; i <= hub->ports ; i++ ) { - port = usb_port ( hub, i ); - port->speed = USB_SPEED_NONE; - } - } - - /* Unregister hub */ - unregister_usb_hub ( hubdev->hub ); - assert ( ! process_running ( &hubdev->refill ) ); - - /* Free hub */ - free_usb_hub ( hubdev->hub ); - - /* Free hub device */ - free ( hubdev ); -} - -/** USB hub device IDs */ -static struct usb_device_id hub_ids[] = { - { - .name = "hub-1", - .vendor = USB_ANY_ID, - .product = USB_ANY_ID, - .class = { - .class = USB_CLASS_HUB, - .subclass = 0, - .protocol = 0, - }, - }, - { - .name = "hub-2", - .vendor = USB_ANY_ID, - .product = USB_ANY_ID, - .class = { - .class = USB_CLASS_HUB, - .subclass = 0, - .protocol = 1, - }, - }, -}; - -/** USB hub driver */ -struct usb_driver usb_hub_driver __usb_driver = { - .ids = hub_ids, - .id_count = ( sizeof ( hub_ids ) / sizeof ( hub_ids[0] ) ), - .probe = hub_probe, - .remove = hub_remove, -}; diff --git a/qemu/roms/ipxe/src/drivers/usb/usbhub.h b/qemu/roms/ipxe/src/drivers/usb/usbhub.h deleted file mode 100644 index d7d8f9610..000000000 --- a/qemu/roms/ipxe/src/drivers/usb/usbhub.h +++ /dev/null @@ -1,279 +0,0 @@ -#ifndef _USBHUB_H -#define _USBHUB_H - -/** @file - * - * USB hubs - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <ipxe/usb.h> -#include <ipxe/list.h> -#include <ipxe/process.h> - -/** Request recipient is a port */ -#define USB_HUB_RECIP_PORT ( 3 << 0 ) - -/** A basic USB hub descriptor */ -struct usb_hub_descriptor_basic { - /** Descriptor header */ - struct usb_descriptor_header header; - /** Number of ports */ - uint8_t ports; - /** Characteristics */ - uint16_t characteristics; - /** Power-on delay (in 2ms intervals */ - uint8_t delay; - /** Controller current (in mA) */ - uint8_t current; -} __attribute__ (( packed )); - -/** A basic USB hub descriptor */ -#define USB_HUB_DESCRIPTOR 41 - -/** An enhanced USB hub descriptor */ -struct usb_hub_descriptor_enhanced { - /** Basic USB hub descriptor */ - struct usb_hub_descriptor_basic basic; - /** Header decode latency */ - uint8_t latency; - /** Maximum delay */ - uint16_t delay; - /** Removable device bitmask */ - uint16_t removable; -} __attribute__ (( packed )); - -/** An enhanced USB hub descriptor */ -#define USB_HUB_DESCRIPTOR_ENHANCED 42 - -/** A USB hub descriptor */ -union usb_hub_descriptor { - /** Descriptor header */ - struct usb_descriptor_header header; - /** Basic hub descriptor */ - struct usb_hub_descriptor_basic basic; - /** Enhanced hub descriptor */ - struct usb_hub_descriptor_enhanced enhanced; -} __attribute__ (( packed )); - -/** Port status */ -struct usb_hub_port_status { - /** Current status */ - uint16_t current; - /** Changed status */ - uint16_t changed; -} __attribute__ (( packed )); - -/** Current connect status feature */ -#define USB_HUB_PORT_CONNECTION 0 - -/** Port enabled/disabled feature */ -#define USB_HUB_PORT_ENABLE 1 - -/** Port reset feature */ -#define USB_HUB_PORT_RESET 4 - -/** Port power feature */ -#define USB_HUB_PORT_POWER 8 - -/** Low-speed device attached */ -#define USB_HUB_PORT_LOW_SPEED 9 - -/** High-speed device attached */ -#define USB_HUB_PORT_HIGH_SPEED 10 - -/** Connect status changed */ -#define USB_HUB_C_PORT_CONNECTION 16 - -/** Port enable/disable changed */ -#define USB_HUB_C_PORT_ENABLE 17 - -/** Suspend changed */ -#define USB_HUB_C_PORT_SUSPEND 18 - -/** Over-current indicator changed */ -#define USB_HUB_C_PORT_OVER_CURRENT 19 - -/** Reset changed */ -#define USB_HUB_C_PORT_RESET 20 - -/** Link state changed */ -#define USB_HUB_C_PORT_LINK_STATE 25 - -/** Configuration error */ -#define USB_HUB_C_PORT_CONFIG_ERROR 26 - -/** Calculate feature from change bit number */ -#define USB_HUB_C_FEATURE( bit ) ( 16 + (bit) ) - -/** USB features */ -#define USB_HUB_FEATURES \ - ( ( 1 << USB_HUB_C_PORT_CONNECTION ) | \ - ( 1 << USB_HUB_C_PORT_ENABLE ) | \ - ( 1 << USB_HUB_C_PORT_SUSPEND ) | \ - ( 1 << USB_HUB_C_PORT_OVER_CURRENT ) | \ - ( 1 << USB_HUB_C_PORT_RESET ) ) - -/** USB features for enhanced hubs */ -#define USB_HUB_FEATURES_ENHANCED \ - ( ( 1 << USB_HUB_C_PORT_CONNECTION ) | \ - ( 1 << USB_HUB_C_PORT_OVER_CURRENT ) | \ - ( 1 << USB_HUB_C_PORT_RESET ) | \ - ( 1 << USB_HUB_C_PORT_LINK_STATE ) | \ - ( 1 << USB_HUB_C_PORT_CONFIG_ERROR ) ) - -/** Set hub depth */ -#define USB_HUB_SET_HUB_DEPTH \ - ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE | \ - USB_REQUEST_TYPE ( 12 ) ) - -/** Clear transaction translator buffer */ -#define USB_HUB_CLEAR_TT_BUFFER \ - ( USB_DIR_OUT | USB_TYPE_CLASS | USB_HUB_RECIP_PORT | \ - USB_REQUEST_TYPE ( 8 ) ) - -/** - * Get hub descriptor - * - * @v usb USB device - * @v enhanced Hub is an enhanced hub - * @v data Hub descriptor to fill in - * @ret rc Return status code - */ -static inline __attribute__ (( always_inline )) int -usb_hub_get_descriptor ( struct usb_device *usb, int enhanced, - union usb_hub_descriptor *data ) { - unsigned int desc; - size_t len; - - /* Determine descriptor type and length */ - desc = ( enhanced ? USB_HUB_DESCRIPTOR_ENHANCED : USB_HUB_DESCRIPTOR ); - len = ( enhanced ? sizeof ( data->enhanced ) : sizeof ( data->basic ) ); - - return usb_get_descriptor ( usb, USB_TYPE_CLASS, desc, 0, 0, - &data->header, len ); -} - -/** - * Get port status - * - * @v usb USB device - * @v port Port address - * @v status Port status descriptor to fill in - * @ret rc Return status code - */ -static inline __attribute__ (( always_inline )) int -usb_hub_get_port_status ( struct usb_device *usb, unsigned int port, - struct usb_hub_port_status *status ) { - - return usb_get_status ( usb, ( USB_TYPE_CLASS | USB_HUB_RECIP_PORT ), - port, status, sizeof ( *status ) ); -} - -/** - * Clear port feature - * - * @v usb USB device - * @v port Port address - * @v feature Feature to clear - * @v index Index (when clearing a port indicator) - * @ret rc Return status code - */ -static inline __attribute__ (( always_inline )) int -usb_hub_clear_port_feature ( struct usb_device *usb, unsigned int port, - unsigned int feature, unsigned int index ) { - - return usb_clear_feature ( usb, ( USB_TYPE_CLASS | USB_HUB_RECIP_PORT ), - feature, ( ( index << 8 ) | port ) ); -} - -/** - * Set port feature - * - * @v usb USB device - * @v port Port address - * @v feature Feature to clear - * @v index Index (when clearing a port indicator) - * @ret rc Return status code - */ -static inline __attribute__ (( always_inline )) int -usb_hub_set_port_feature ( struct usb_device *usb, unsigned int port, - unsigned int feature, unsigned int index ) { - - return usb_set_feature ( usb, ( USB_TYPE_CLASS | USB_HUB_RECIP_PORT ), - feature, ( ( index << 8 ) | port ) ); -} - -/** - * Set hub depth - * - * @v usb USB device - * @v depth Hub depth - * @ret rc Return status code - */ -static inline __attribute__ (( always_inline )) int -usb_hub_set_hub_depth ( struct usb_device *usb, unsigned int depth ) { - - return usb_control ( usb, USB_HUB_SET_HUB_DEPTH, depth, 0, NULL, 0 ); -} - -/** - * Clear transaction translator buffer - * - * @v usb USB device - * @v device Device address - * @v endpoint Endpoint address - * @v attributes Endpoint attributes - * @v tt_port Transaction translator port (or 1 for single-TT hubs) - * @ret rc Return status code - */ -static inline __attribute__ (( always_inline )) int -usb_hub_clear_tt_buffer ( struct usb_device *usb, unsigned int device, - unsigned int endpoint, unsigned int attributes, - unsigned int tt_port ) { - unsigned int value; - - /* Calculate value */ - value = ( ( ( endpoint & USB_ENDPOINT_MAX ) << 0 ) | ( device << 4 ) | - ( ( attributes & USB_ENDPOINT_ATTR_TYPE_MASK ) << 11 ) | - ( ( endpoint & USB_ENDPOINT_IN ) << 8 ) ); - - return usb_control ( usb, USB_HUB_CLEAR_TT_BUFFER, value, - tt_port, NULL, 0 ); -} - -/** Transaction translator port value for single-TT hubs */ -#define USB_HUB_TT_SINGLE 1 - -/** A USB hub device */ -struct usb_hub_device { - /** Name */ - const char *name; - /** USB device */ - struct usb_device *usb; - /** USB hub */ - struct usb_hub *hub; - /** Features */ - unsigned int features; - - /** Interrupt endpoint */ - struct usb_endpoint intr; - /** Interrupt endpoint refill process */ - struct process refill; -}; - -/** Interrupt ring fill level - * - * This is a policy decision. - */ -#define USB_HUB_INTR_FILL 4 - -/** Maximum time to wait for port to become enabled - * - * This is a policy decision. - */ -#define USB_HUB_ENABLE_MAX_WAIT_MS 100 - -#endif /* _USBHUB_H */ diff --git a/qemu/roms/ipxe/src/drivers/usb/usbkbd.c b/qemu/roms/ipxe/src/drivers/usb/usbkbd.c deleted file mode 100644 index ea94f2e63..000000000 --- a/qemu/roms/ipxe/src/drivers/usb/usbkbd.c +++ /dev/null @@ -1,509 +0,0 @@ -/* - * 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 <string.h> -#include <errno.h> -#include <assert.h> -#include <ipxe/console.h> -#include <ipxe/keys.h> -#include <ipxe/usb.h> -#include "usbkbd.h" - -/** @file - * - * USB keyboard driver - * - */ - -/** List of USB keyboards */ -static LIST_HEAD ( usb_keyboards ); - -/****************************************************************************** - * - * Keyboard map - * - ****************************************************************************** - */ - -/** - * Map USB keycode to iPXE key - * - * @v keycode Keycode - * @v modifiers Modifiers - * @ret key iPXE key - * - * Key codes are defined in the USB HID Usage Tables Keyboard/Keypad - * page. - */ -static unsigned int usbkbd_map ( unsigned int keycode, - unsigned int modifiers ) { - unsigned int key; - - if ( keycode < USBKBD_KEY_A ) { - /* Not keys */ - key = 0; - } else if ( keycode <= USBKBD_KEY_Z ) { - /* Alphabetic keys */ - key = ( keycode - USBKBD_KEY_A + 'a' ); - if ( modifiers & USBKBD_CTRL ) { - key -= ( 'a' - CTRL_A ); - } else if ( modifiers & USBKBD_SHIFT ) { - key -= ( 'a' - 'A' ); - } - } else if ( keycode <= USBKBD_KEY_0 ) { - /* Numeric key row */ - if ( modifiers & USBKBD_SHIFT ) { - key = "!@#$%^&*()" [ keycode - USBKBD_KEY_1 ]; - } else { - key = ( ( ( keycode - USBKBD_KEY_1 + 1 ) % 10 ) + '0' ); - } - } else if ( keycode <= USBKBD_KEY_SPACE ) { - /* Unmodifiable keys */ - static const uint8_t unmodifable[] = - { LF, ESC, BACKSPACE, TAB, ' ' }; - key = unmodifable[ keycode - USBKBD_KEY_ENTER ]; - } else if ( keycode <= USBKBD_KEY_SLASH ) { - /* Punctuation keys */ - if ( modifiers & USBKBD_SHIFT ) { - key = "_+{}|~:\"~<>?" [ keycode - USBKBD_KEY_MINUS ]; - } else { - key = "-=[]\\#;'`,./" [ keycode - USBKBD_KEY_MINUS ]; - } - } else if ( keycode <= USBKBD_KEY_UP ) { - /* Special keys */ - static const uint16_t special[] = { - 0, 0, 0, 0, 0, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, - KEY_F10, KEY_F11, KEY_F12, 0, 0, 0, KEY_IC, KEY_HOME, - KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT, - KEY_LEFT, KEY_DOWN, KEY_UP - }; - key = special[ keycode - USBKBD_KEY_CAPSLOCK ]; - } else { - key = 0; - } - - return key; -} - -/****************************************************************************** - * - * Keyboard buffer - * - ****************************************************************************** - */ - -/** - * Insert keypress into keyboard buffer - * - * @v kbd USB keyboard - * @v keycode Keycode - * @v modifiers Modifiers - */ -static void usbkbd_produce ( struct usb_keyboard *kbd, unsigned int keycode, - unsigned int modifiers ) { - unsigned int key; - - /* Map to iPXE key */ - key = usbkbd_map ( keycode, modifiers ); - - /* Do nothing if this keycode has no corresponding iPXE key */ - if ( ! key ) { - DBGC ( kbd, "KBD %s has no key for keycode %#02x:%#02x\n", - kbd->name, modifiers, keycode ); - return; - } - - /* Check for buffer overrun */ - if ( usbkbd_fill ( kbd ) >= USBKBD_BUFSIZE ) { - DBGC ( kbd, "KBD %s buffer overrun (key %#02x)\n", - kbd->name, key ); - return; - } - - /* Insert into buffer */ - kbd->key[ ( kbd->prod++ ) % USBKBD_BUFSIZE ] = key; - DBGC2 ( kbd, "KBD %s key %#02x produced\n", kbd->name, key ); -} - -/** - * Consume character from keyboard buffer - * - * @v kbd USB keyboard - * @ret character Character - */ -static unsigned int usbkbd_consume ( struct usb_keyboard *kbd ) { - static char buf[] = "\x1b[xx~"; - char *tmp = &buf[2]; - unsigned int key; - unsigned int character; - unsigned int ansi_n; - unsigned int len; - - /* Sanity check */ - assert ( usbkbd_fill ( kbd ) > 0 ); - - /* Get current keypress */ - key = kbd->key[ kbd->cons % USBKBD_BUFSIZE ]; - - /* If this is a straightforward key, just consume and return it */ - if ( key < KEY_MIN ) { - kbd->cons++; - DBGC2 ( kbd, "KBD %s key %#02x consumed\n", kbd->name, key ); - return key; - } - - /* Construct ANSI sequence */ - ansi_n = KEY_ANSI_N ( key ); - if ( ansi_n ) - tmp += sprintf ( tmp, "%d", ansi_n ); - *(tmp++) = KEY_ANSI_TERMINATOR ( key ); - *tmp = '\0'; - len = ( tmp - buf ); - assert ( len < sizeof ( buf ) ); - if ( kbd->subcons == 0 ) { - DBGC2 ( kbd, "KBD %s key %#02x consumed as ^[%s\n", - kbd->name, key, &buf[1] ); - } - - /* Extract character from ANSI sequence */ - assert ( kbd->subcons < len ); - character = buf[ kbd->subcons++ ]; - - /* Consume key if applicable */ - if ( kbd->subcons == len ) { - kbd->cons++; - kbd->subcons = 0; - } - - return character; -} - -/****************************************************************************** - * - * Keyboard report - * - ****************************************************************************** - */ - -/** - * Check for presence of keycode in report - * - * @v report Keyboard report - * @v keycode Keycode (must be non-zero) - * @ret has_keycode Keycode is present in report - */ -static int usbkbd_has_keycode ( struct usb_keyboard_report *report, - unsigned int keycode ) { - unsigned int i; - - /* Check for keycode */ - for ( i = 0 ; i < ( sizeof ( report->keycode ) / - sizeof ( report->keycode[0] ) ) ; i++ ) { - if ( report->keycode[i] == keycode ) - return keycode; - } - - return 0; -} - -/** - * Handle keyboard report - * - * @v kbd USB keyboard - * @v new New keyboard report - */ -static void usbkbd_report ( struct usb_keyboard *kbd, - struct usb_keyboard_report *new ) { - struct usb_keyboard_report *old = &kbd->report; - unsigned int keycode; - unsigned int i; - - /* Check if current key has been released */ - if ( kbd->keycode && ! usbkbd_has_keycode ( new, kbd->keycode ) ) { - DBGC2 ( kbd, "KBD %s keycode %#02x released\n", - kbd->name, kbd->keycode ); - kbd->keycode = 0; - } - - /* Decrement auto-repeat hold-off timer, if applicable */ - if ( kbd->holdoff ) - kbd->holdoff--; - - /* Check if a new key has been pressed */ - for ( i = 0 ; i < ( sizeof ( new->keycode ) / - sizeof ( new->keycode[0] ) ) ; i++ ) { - - /* Ignore keys present in the previous report */ - keycode = new->keycode[i]; - if ( ( keycode == 0 ) || usbkbd_has_keycode ( old, keycode ) ) - continue; - DBGC2 ( kbd, "KBD %s keycode %#02x pressed\n", - kbd->name, keycode ); - - /* Insert keypress into keyboard buffer */ - usbkbd_produce ( kbd, keycode, new->modifiers ); - - /* Record as most recent keycode */ - kbd->keycode = keycode; - - /* Start auto-repeat hold-off timer */ - kbd->holdoff = USBKBD_HOLDOFF; - } - - /* Insert auto-repeated keypress into keyboard buffer, if applicable */ - if ( kbd->keycode && ! kbd->holdoff ) - usbkbd_produce ( kbd, kbd->keycode, new->modifiers ); - - /* Record report */ - memcpy ( old, new, sizeof ( *old ) ); -} - -/****************************************************************************** - * - * Interrupt endpoint - * - ****************************************************************************** - */ - -/** - * Complete interrupt transfer - * - * @v ep USB endpoint - * @v iobuf I/O buffer - * @v rc Completion status code - */ -static void usbkbd_complete ( struct usb_endpoint *ep, - struct io_buffer *iobuf, int rc ) { - struct usb_keyboard *kbd = container_of ( ep, struct usb_keyboard, - hid.in ); - struct usb_keyboard_report *report; - - /* Ignore packets cancelled when the endpoint closes */ - if ( ! ep->open ) - goto drop; - - /* Ignore packets with errors */ - if ( rc != 0 ) { - DBGC ( kbd, "KBD %s interrupt IN failed: %s\n", - kbd->name, strerror ( rc ) ); - goto drop; - } - - /* Ignore underlength packets */ - if ( iob_len ( iobuf ) < sizeof ( *report ) ) { - DBGC ( kbd, "KBD %s underlength report:\n", kbd->name ); - DBGC_HDA ( kbd, 0, iobuf->data, iob_len ( iobuf ) ); - goto drop; - } - report = iobuf->data; - - /* Handle keyboard report */ - usbkbd_report ( kbd, report ); - - drop: - /* Recycle I/O buffer */ - usb_recycle ( &kbd->hid.in, iobuf ); -} - -/** Interrupt endpoint operations */ -static struct usb_endpoint_driver_operations usbkbd_operations = { - .complete = usbkbd_complete, -}; - -/****************************************************************************** - * - * USB interface - * - ****************************************************************************** - */ - -/** - * Probe device - * - * @v func USB function - * @v config Configuration descriptor - * @ret rc Return status code - */ -static int usbkbd_probe ( struct usb_function *func, - struct usb_configuration_descriptor *config ) { - struct usb_device *usb = func->usb; - struct usb_keyboard *kbd; - int rc; - - /* Allocate and initialise structure */ - kbd = zalloc ( sizeof ( *kbd ) ); - if ( ! kbd ) { - rc = -ENOMEM; - goto err_alloc; - } - kbd->name = func->name; - kbd->bus = usb->port->hub->bus; - usbhid_init ( &kbd->hid, func, &usbkbd_operations, NULL ); - usb_refill_init ( &kbd->hid.in, sizeof ( kbd->report ), - USBKBD_INTR_MAX_FILL ); - - /* Describe USB human interface device */ - if ( ( rc = usbhid_describe ( &kbd->hid, config ) ) != 0 ) { - DBGC ( kbd, "KBD %s could not describe: %s\n", - kbd->name, strerror ( rc ) ); - goto err_describe; - } - DBGC ( kbd, "KBD %s using %s (len %zd)\n", - kbd->name, usb_endpoint_name ( &kbd->hid.in ), kbd->hid.in.mtu ); - - /* Set boot protocol */ - if ( ( rc = usbhid_set_protocol ( usb, func->interface[0], - USBHID_PROTOCOL_BOOT ) ) != 0 ) { - DBGC ( kbd, "KBD %s could not set boot protocol: %s\n", - kbd->name, strerror ( rc ) ); - goto err_set_protocol; - } - - /* Set idle time */ - if ( ( rc = usbhid_set_idle ( usb, func->interface[0], 0, - USBKBD_IDLE_DURATION ) ) != 0 ) { - DBGC ( kbd, "KBD %s could not set idle time: %s\n", - kbd->name, strerror ( rc ) ); - goto err_set_idle; - } - - /* Open USB human interface device */ - if ( ( rc = usbhid_open ( &kbd->hid ) ) != 0 ) { - DBGC ( kbd, "KBD %s could not open: %s\n", - kbd->name, strerror ( rc ) ); - goto err_open; - } - - /* Add to list of USB keyboards */ - list_add_tail ( &kbd->list, &usb_keyboards ); - - usb_func_set_drvdata ( func, kbd ); - return 0; - - usbhid_close ( &kbd->hid ); - err_open: - err_set_idle: - err_set_protocol: - err_describe: - free ( kbd ); - err_alloc: - return rc; -} - -/** - * Remove device - * - * @v func USB function - */ -static void usbkbd_remove ( struct usb_function *func ) { - struct usb_keyboard *kbd = usb_func_get_drvdata ( func ); - - /* Remove from list of USB keyboards */ - list_del ( &kbd->list ); - - /* Close USB human interface device */ - usbhid_close ( &kbd->hid ); - - /* Free device */ - free ( kbd ); -} - -/** USB keyboard device IDs */ -static struct usb_device_id usbkbd_ids[] = { - { - .name = "kbd", - .vendor = USB_ANY_ID, - .product = USB_ANY_ID, - .class = { - .class = USB_CLASS_HID, - .subclass = USB_SUBCLASS_HID_BOOT, - .protocol = USBKBD_PROTOCOL, - }, - }, -}; - -/** USB keyboard driver */ -struct usb_driver usbkbd_driver __usb_driver = { - .ids = usbkbd_ids, - .id_count = ( sizeof ( usbkbd_ids ) / sizeof ( usbkbd_ids[0] ) ), - .probe = usbkbd_probe, - .remove = usbkbd_remove, -}; - -/****************************************************************************** - * - * Console interface - * - ****************************************************************************** - */ - -/** - * Read a character from the console - * - * @ret character Character read - */ -static int usbkbd_getchar ( void ) { - struct usb_keyboard *kbd; - - /* Consume first available key */ - list_for_each_entry ( kbd, &usb_keyboards, list ) { - if ( usbkbd_fill ( kbd ) ) - return usbkbd_consume ( kbd ); - } - - return 0; -} - -/** - * Check for available input - * - * @ret is_available Input is available - */ -static int usbkbd_iskey ( void ) { - struct usb_keyboard *kbd; - unsigned int fill; - - /* Poll all USB keyboards and refill endpoints */ - list_for_each_entry ( kbd, &usb_keyboards, list ) { - usb_poll ( kbd->bus ); - usb_refill ( &kbd->hid.in ); - } - - /* Check for a non-empty keyboard buffer */ - list_for_each_entry ( kbd, &usb_keyboards, list ) { - fill = usbkbd_fill ( kbd ); - if ( fill ) - return fill; - } - - return 0; -} - -/** USB keyboard console */ -struct console_driver usbkbd_console __console_driver = { - .getchar = usbkbd_getchar, - .iskey = usbkbd_iskey, -}; diff --git a/qemu/roms/ipxe/src/drivers/usb/usbkbd.h b/qemu/roms/ipxe/src/drivers/usb/usbkbd.h deleted file mode 100644 index 7eab24e46..000000000 --- a/qemu/roms/ipxe/src/drivers/usb/usbkbd.h +++ /dev/null @@ -1,154 +0,0 @@ -#ifndef _USBKBD_H -#define _USBKBD_H - -/** @file - * - * USB keyboard driver - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <assert.h> -#include <ipxe/usb.h> -#include <ipxe/usbhid.h> - -/** Keyboard protocol */ -#define USBKBD_PROTOCOL 1 - -/** A USB keyboard report */ -struct usb_keyboard_report { - /** Modifier keys */ - uint8_t modifiers; - /** Reserved */ - uint8_t reserved; - /** Keycodes */ - uint8_t keycode[6]; -} __attribute__ (( packed )); - -/** USB modifier keys */ -enum usb_keyboard_modifier { - /** Left Ctrl key */ - USBKBD_CTRL_LEFT = 0x01, - /** Left Shift key */ - USBKBD_SHIFT_LEFT = 0x02, - /** Left Alt key */ - USBKBD_ALT_LEFT = 0x04, - /** Left GUI key */ - USBKBD_GUI_LEFT = 0x08, - /** Right Ctrl key */ - USBKBD_CTRL_RIGHT = 0x10, - /** Right Shift key */ - USBKBD_SHIFT_RIGHT = 0x20, - /** Right Alt key */ - USBKBD_ALT_RIGHT = 0x40, - /** Right GUI key */ - USBKBD_GUI_RIGHT = 0x80, -}; - -/** Either Ctrl key */ -#define USBKBD_CTRL ( USBKBD_CTRL_LEFT | USBKBD_CTRL_RIGHT ) - -/** Either Shift key */ -#define USBKBD_SHIFT ( USBKBD_SHIFT_LEFT | USBKBD_SHIFT_RIGHT ) - -/** Either Alt key */ -#define USBKBD_ALT ( USBKBD_ALT_LEFT | USBKBD_ALT_RIGHT ) - -/** Either GUI key */ -#define USBKBD_GUI ( USBKBD_GUI_LEFT | USBKBD_GUI_RIGHT ) - -/** USB keycodes */ -enum usb_keycode { - USBKBD_KEY_A = 0x04, - USBKBD_KEY_Z = 0x1d, - USBKBD_KEY_1 = 0x1e, - USBKBD_KEY_0 = 0x27, - USBKBD_KEY_ENTER = 0x28, - USBKBD_KEY_SPACE = 0x2c, - USBKBD_KEY_MINUS = 0x2d, - USBKBD_KEY_SLASH = 0x38, - USBKBD_KEY_CAPSLOCK = 0x39, - USBKBD_KEY_UP = 0x52, -}; - -/** Keyboard idle duration (in 4ms units) - * - * This is a policy decision. We choose to use an autorepeat rate of - * approximately 40ms. - */ -#define USBKBD_IDLE_DURATION 10 /* 10 x 4ms = 40ms */ - -/** Keyboard auto-repeat hold-off (in units of USBKBD_IDLE_DURATION) - * - * This is a policy decision. We choose to use an autorepeat delay of - * approximately 500ms. - */ -#define USBKBD_HOLDOFF 12 /* 12 x 40ms = 480ms */ - -/** Interrupt endpoint maximum fill level - * - * When idling, we are likely to poll the USB endpoint at only the - * 18.2Hz system timer tick rate. With a typical observed bInterval - * of 10ms (which will be rounded down to 8ms by the HCI drivers), - * this gives approximately 7 completions per poll. - */ -#define USBKBD_INTR_MAX_FILL 8 - -/** Keyboard buffer size - * - * Must be a power of two. - */ -#define USBKBD_BUFSIZE 8 - -/** A USB keyboard device */ -struct usb_keyboard { - /** Name */ - const char *name; - /** List of all USB keyboards */ - struct list_head list; - - /** USB bus */ - struct usb_bus *bus; - /** USB human interface device */ - struct usb_hid hid; - - /** Most recent keyboard report */ - struct usb_keyboard_report report; - /** Most recently pressed non-modifier key (if any) */ - unsigned int keycode; - /** Autorepeat hold-off time (in number of completions reported) */ - unsigned int holdoff; - - /** Keyboard buffer - * - * This stores iPXE key values. - */ - unsigned int key[USBKBD_BUFSIZE]; - /** Keyboard buffer producer counter */ - unsigned int prod; - /** Keyboard buffer consumer counter */ - unsigned int cons; - /** Keyboard buffer sub-consumer counter - * - * This represents the index within the ANSI escape sequence - * corresponding to an iPXE key value. - */ - unsigned int subcons; -}; - -/** - * Calculate keyboard buffer fill level - * - * @v kbd USB keyboard - * @ret fill Keyboard buffer fill level - */ -static inline __attribute__ (( always_inline )) unsigned int -usbkbd_fill ( struct usb_keyboard *kbd ) { - unsigned int fill = ( kbd->prod - kbd->cons ); - - assert ( fill <= USBKBD_BUFSIZE ); - return fill; -} - -#endif /* _USBKBD_H */ diff --git a/qemu/roms/ipxe/src/drivers/usb/usbnet.c b/qemu/roms/ipxe/src/drivers/usb/usbnet.c deleted file mode 100644 index b92336d05..000000000 --- a/qemu/roms/ipxe/src/drivers/usb/usbnet.c +++ /dev/null @@ -1,284 +0,0 @@ -/* - * 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 <string.h> -#include <errno.h> -#include <ipxe/usb.h> -#include <ipxe/usbnet.h> - -/** @file - * - * USB network devices - * - * USB network devices use a variety of packet formats and interface - * descriptors, but tend to have several features in common: - * - * - a single interrupt endpoint using the generic refill mechanism - * - * - a single bulk IN endpoint using the generic refill mechanism - * - * - a single bulk OUT endpoint - * - * - optional use of an alternate setting to enable the data interface - * - */ - -/** - * Open USB network device - * - * @v usbnet USB network device - * @ret rc Return status code - */ -int usbnet_open ( struct usbnet_device *usbnet ) { - struct usb_device *usb = usbnet->func->usb; - int rc; - - /* Open interrupt endpoint */ - if ( ( rc = usb_endpoint_open ( &usbnet->intr ) ) != 0 ) { - DBGC ( usbnet, "USBNET %s could not open interrupt: %s\n", - usbnet->func->name, strerror ( rc ) ); - goto err_open_intr; - } - - /* Refill interrupt endpoint */ - if ( ( rc = usb_refill ( &usbnet->intr ) ) != 0 ) { - DBGC ( usbnet, "USBNET %s could not refill interrupt: %s\n", - usbnet->func->name, strerror ( rc ) ); - goto err_refill_intr; - } - - /* Select alternate setting for data interface, if applicable */ - if ( usbnet->alternate && - ( ( rc = usb_set_interface ( usb, usbnet->data, - usbnet->alternate ) ) != 0 ) ) { - DBGC ( usbnet, "USBNET %s could not set alternate interface " - "%d: %s\n", usbnet->func->name, usbnet->alternate, - strerror ( rc ) ); - goto err_set_interface; - } - - /* Open bulk IN endpoint */ - if ( ( rc = usb_endpoint_open ( &usbnet->in ) ) != 0 ) { - DBGC ( usbnet, "USBNET %s could not open bulk IN: %s\n", - usbnet->func->name, strerror ( rc ) ); - goto err_open_in; - } - - /* Open bulk OUT endpoint */ - if ( ( rc = usb_endpoint_open ( &usbnet->out ) ) != 0 ) { - DBGC ( usbnet, "USBNET %s could not open bulk OUT: %s\n", - usbnet->func->name, strerror ( rc ) ); - goto err_open_out; - } - - /* Refill bulk IN endpoint */ - if ( ( rc = usb_refill ( &usbnet->in ) ) != 0 ) { - DBGC ( usbnet, "USBNET %s could not refill bulk IN: %s\n", - usbnet->func->name, strerror ( rc ) ); - goto err_refill_in; - } - - return 0; - - err_refill_in: - usb_endpoint_close ( &usbnet->out ); - err_open_out: - usb_endpoint_close ( &usbnet->in ); - err_open_in: - if ( usbnet->alternate ) - usb_set_interface ( usb, usbnet->data, 0 ); - err_set_interface: - err_refill_intr: - usb_endpoint_close ( &usbnet->intr ); - err_open_intr: - return rc; -} - -/** - * Close USB network device - * - * @v usbnet USB network device - */ -void usbnet_close ( struct usbnet_device *usbnet ) { - struct usb_device *usb = usbnet->func->usb; - - /* Close bulk OUT endpoint */ - usb_endpoint_close ( &usbnet->out ); - - /* Close bulk IN endpoint */ - usb_endpoint_close ( &usbnet->in ); - - /* Reset alternate setting for data interface, if applicable */ - if ( usbnet->alternate ) - usb_set_interface ( usb, usbnet->data, 0 ); - - /* Close interrupt endpoint */ - usb_endpoint_close ( &usbnet->intr ); -} - -/** - * Refill USB network device bulk IN and interrupt endpoints - * - * @v usbnet USB network device - * @ret rc Return status code - */ -int usbnet_refill ( struct usbnet_device *usbnet ) { - int rc; - - /* Refill bulk IN endpoint */ - if ( ( rc = usb_refill ( &usbnet->in ) ) != 0 ) - return rc; - - /* Refill interrupt endpoint */ - if ( ( rc = usb_refill ( &usbnet->intr ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Describe communications interface and interrupt endpoint - * - * @v usbnet USB network device - * @v config Configuration descriptor - * @ret rc Return status code - */ -static int usbnet_comms_describe ( struct usbnet_device *usbnet, - struct usb_configuration_descriptor *config){ - struct usb_interface_descriptor *desc; - unsigned int comms; - unsigned int i; - int rc; - - /* Iterate over all available interfaces */ - for ( i = 0 ; i < usbnet->func->count ; i++ ) { - - /* Get interface number */ - comms = usbnet->func->interface[i]; - - /* Locate interface descriptor */ - desc = usb_interface_descriptor ( config, comms, 0 ); - if ( ! desc ) - continue; - - /* Describe interrupt endpoint */ - if ( ( rc = usb_endpoint_described ( &usbnet->intr, config, - desc, USB_INTERRUPT_IN, - 0 ) ) != 0 ) - continue; - - /* Record communications interface */ - usbnet->comms = comms; - DBGC ( usbnet, "USBNET %s found communications interface %d\n", - usbnet->func->name, comms ); - return 0; - } - - DBGC ( usbnet, "USBNET %s found no communications interface\n", - usbnet->func->name ); - return -ENOENT; -} - -/** - * Describe data interface and bulk endpoints - * - * @v usbnet USB network device - * @v config Configuration descriptor - * @ret rc Return status code - */ -static int usbnet_data_describe ( struct usbnet_device *usbnet, - struct usb_configuration_descriptor *config ){ - struct usb_interface_descriptor *desc; - unsigned int data; - unsigned int alt; - unsigned int i; - int rc; - - /* Iterate over all available interfaces */ - for ( i = 0 ; i < usbnet->func->count ; i++ ) { - - /* Get interface number */ - data = usbnet->func->interface[i]; - - /* Iterate over all existent alternate settings */ - for ( alt = 0 ; ; alt++ ) { - - /* Locate interface descriptor */ - desc = usb_interface_descriptor ( config, data, alt ); - if ( ! desc ) - break; - - /* Describe bulk IN endpoint */ - if ( ( rc = usb_endpoint_described ( &usbnet->in, - config, desc, - USB_BULK_IN, - 0 ) ) != 0 ) - continue; - - /* Describe bulk OUT endpoint */ - if ( ( rc = usb_endpoint_described ( &usbnet->out, - config, desc, - USB_BULK_OUT, - 0 ) ) != 0 ) - continue; - - /* Record data interface and alternate setting */ - usbnet->data = data; - usbnet->alternate = alt; - DBGC ( usbnet, "USBNET %s found data interface %d", - usbnet->func->name, data ); - if ( alt ) - DBGC ( usbnet, " using alternate %d", alt ); - DBGC ( usbnet, "\n" ); - return 0; - } - } - - DBGC ( usbnet, "USBNET %s found no data interface\n", - usbnet->func->name ); - return -ENOENT; -} - -/** - * Describe USB network device interfaces - * - * @v usbnet USB network device - * @v config Configuration descriptor - * @ret rc Return status code - */ -int usbnet_describe ( struct usbnet_device *usbnet, - struct usb_configuration_descriptor *config ) { - int rc; - - /* Describe communications interface */ - if ( ( rc = usbnet_comms_describe ( usbnet, config ) ) != 0 ) - return rc; - - /* Describe data interface */ - if ( ( rc = usbnet_data_describe ( usbnet, config ) ) != 0 ) - return rc; - - return 0; -} diff --git a/qemu/roms/ipxe/src/drivers/usb/xhci.c b/qemu/roms/ipxe/src/drivers/usb/xhci.c deleted file mode 100644 index 49e67316b..000000000 --- a/qemu/roms/ipxe/src/drivers/usb/xhci.c +++ /dev/null @@ -1,3321 +0,0 @@ -/* - * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * You can also choose to distribute this program under the terms of - * the Unmodified Binary Distribution Licence (as given in the file - * COPYING.UBDL), provided that you have satisfied its requirements. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <strings.h> -#include <errno.h> -#include <byteswap.h> -#include <ipxe/malloc.h> -#include <ipxe/umalloc.h> -#include <ipxe/pci.h> -#include <ipxe/usb.h> -#include <ipxe/init.h> -#include <ipxe/profile.h> -#include "xhci.h" - -/** @file - * - * USB eXtensible Host Controller Interface (xHCI) driver - * - */ - -/** Message transfer profiler */ -static struct profiler xhci_message_profiler __profiler = - { .name = "xhci.message" }; - -/** Stream transfer profiler */ -static struct profiler xhci_stream_profiler __profiler = - { .name = "xhci.stream" }; - -/** Event ring profiler */ -static struct profiler xhci_event_profiler __profiler = - { .name = "xhci.event" }; - -/** Transfer event profiler */ -static struct profiler xhci_transfer_profiler __profiler = - { .name = "xhci.transfer" }; - -/* Disambiguate the various error causes */ -#define EIO_DATA \ - __einfo_error ( EINFO_EIO_DATA ) -#define EINFO_EIO_DATA \ - __einfo_uniqify ( EINFO_EIO, ( 2 - 0 ), \ - "Data buffer error" ) -#define EIO_BABBLE \ - __einfo_error ( EINFO_EIO_BABBLE ) -#define EINFO_EIO_BABBLE \ - __einfo_uniqify ( EINFO_EIO, ( 3 - 0 ), \ - "Babble detected" ) -#define EIO_USB \ - __einfo_error ( EINFO_EIO_USB ) -#define EINFO_EIO_USB \ - __einfo_uniqify ( EINFO_EIO, ( 4 - 0 ), \ - "USB transaction error" ) -#define EIO_TRB \ - __einfo_error ( EINFO_EIO_TRB ) -#define EINFO_EIO_TRB \ - __einfo_uniqify ( EINFO_EIO, ( 5 - 0 ), \ - "TRB error" ) -#define EIO_STALL \ - __einfo_error ( EINFO_EIO_STALL ) -#define EINFO_EIO_STALL \ - __einfo_uniqify ( EINFO_EIO, ( 6 - 0 ), \ - "Stall error" ) -#define EIO_RESOURCE \ - __einfo_error ( EINFO_EIO_RESOURCE ) -#define EINFO_EIO_RESOURCE \ - __einfo_uniqify ( EINFO_EIO, ( 7 - 0 ), \ - "Resource error" ) -#define EIO_BANDWIDTH \ - __einfo_error ( EINFO_EIO_BANDWIDTH ) -#define EINFO_EIO_BANDWIDTH \ - __einfo_uniqify ( EINFO_EIO, ( 8 - 0 ), \ - "Bandwidth error" ) -#define EIO_NO_SLOTS \ - __einfo_error ( EINFO_EIO_NO_SLOTS ) -#define EINFO_EIO_NO_SLOTS \ - __einfo_uniqify ( EINFO_EIO, ( 9 - 0 ), \ - "No slots available" ) -#define EIO_STREAM_TYPE \ - __einfo_error ( EINFO_EIO_STREAM_TYPE ) -#define EINFO_EIO_STREAM_TYPE \ - __einfo_uniqify ( EINFO_EIO, ( 10 - 0 ), \ - "Invalid stream type" ) -#define EIO_SLOT \ - __einfo_error ( EINFO_EIO_SLOT ) -#define EINFO_EIO_SLOT \ - __einfo_uniqify ( EINFO_EIO, ( 11 - 0 ), \ - "Slot not enabled" ) -#define EIO_ENDPOINT \ - __einfo_error ( EINFO_EIO_ENDPOINT ) -#define EINFO_EIO_ENDPOINT \ - __einfo_uniqify ( EINFO_EIO, ( 12 - 0 ), \ - "Endpoint not enabled" ) -#define EIO_SHORT \ - __einfo_error ( EINFO_EIO_SHORT ) -#define EINFO_EIO_SHORT \ - __einfo_uniqify ( EINFO_EIO, ( 13 - 0 ), \ - "Short packet" ) -#define EIO_UNDERRUN \ - __einfo_error ( EINFO_EIO_UNDERRUN ) -#define EINFO_EIO_UNDERRUN \ - __einfo_uniqify ( EINFO_EIO, ( 14 - 0 ), \ - "Ring underrun" ) -#define EIO_OVERRUN \ - __einfo_error ( EINFO_EIO_OVERRUN ) -#define EINFO_EIO_OVERRUN \ - __einfo_uniqify ( EINFO_EIO, ( 15 - 0 ), \ - "Ring overrun" ) -#define EIO_VF_RING_FULL \ - __einfo_error ( EINFO_EIO_VF_RING_FULL ) -#define EINFO_EIO_VF_RING_FULL \ - __einfo_uniqify ( EINFO_EIO, ( 16 - 0 ), \ - "Virtual function event ring full" ) -#define EIO_PARAMETER \ - __einfo_error ( EINFO_EIO_PARAMETER ) -#define EINFO_EIO_PARAMETER \ - __einfo_uniqify ( EINFO_EIO, ( 17 - 0 ), \ - "Parameter error" ) -#define EIO_BANDWIDTH_OVERRUN \ - __einfo_error ( EINFO_EIO_BANDWIDTH_OVERRUN ) -#define EINFO_EIO_BANDWIDTH_OVERRUN \ - __einfo_uniqify ( EINFO_EIO, ( 18 - 0 ), \ - "Bandwidth overrun" ) -#define EIO_CONTEXT \ - __einfo_error ( EINFO_EIO_CONTEXT ) -#define EINFO_EIO_CONTEXT \ - __einfo_uniqify ( EINFO_EIO, ( 19 - 0 ), \ - "Context state error" ) -#define EIO_NO_PING \ - __einfo_error ( EINFO_EIO_NO_PING ) -#define EINFO_EIO_NO_PING \ - __einfo_uniqify ( EINFO_EIO, ( 20 - 0 ), \ - "No ping response" ) -#define EIO_RING_FULL \ - __einfo_error ( EINFO_EIO_RING_FULL ) -#define EINFO_EIO_RING_FULL \ - __einfo_uniqify ( EINFO_EIO, ( 21 - 0 ), \ - "Event ring full" ) -#define EIO_INCOMPATIBLE \ - __einfo_error ( EINFO_EIO_INCOMPATIBLE ) -#define EINFO_EIO_INCOMPATIBLE \ - __einfo_uniqify ( EINFO_EIO, ( 22 - 0 ), \ - "Incompatible device" ) -#define EIO_MISSED \ - __einfo_error ( EINFO_EIO_MISSED ) -#define EINFO_EIO_MISSED \ - __einfo_uniqify ( EINFO_EIO, ( 23 - 0 ), \ - "Missed service error" ) -#define EIO_CMD_STOPPED \ - __einfo_error ( EINFO_EIO_CMD_STOPPED ) -#define EINFO_EIO_CMD_STOPPED \ - __einfo_uniqify ( EINFO_EIO, ( 24 - 0 ), \ - "Command ring stopped" ) -#define EIO_CMD_ABORTED \ - __einfo_error ( EINFO_EIO_CMD_ABORTED ) -#define EINFO_EIO_CMD_ABORTED \ - __einfo_uniqify ( EINFO_EIO, ( 25 - 0 ), \ - "Command aborted" ) -#define EIO_STOP \ - __einfo_error ( EINFO_EIO_STOP ) -#define EINFO_EIO_STOP \ - __einfo_uniqify ( EINFO_EIO, ( 26 - 0 ), \ - "Stopped" ) -#define EIO_STOP_LEN \ - __einfo_error ( EINFO_EIO_STOP_LEN ) -#define EINFO_EIO_STOP_LEN \ - __einfo_uniqify ( EINFO_EIO, ( 27 - 0 ), \ - "Stopped - length invalid" ) -#define EIO_STOP_SHORT \ - __einfo_error ( EINFO_EIO_STOP_SHORT ) -#define EINFO_EIO_STOP_SHORT \ - __einfo_uniqify ( EINFO_EIO, ( 28 - 0 ), \ - "Stopped - short packet" ) -#define EIO_LATENCY \ - __einfo_error ( EINFO_EIO_LATENCY ) -#define EINFO_EIO_LATENCY \ - __einfo_uniqify ( EINFO_EIO, ( 29 - 0 ), \ - "Maximum exit latency too large" ) -#define EIO_ISOCH \ - __einfo_error ( EINFO_EIO_ISOCH ) -#define EINFO_EIO_ISOCH \ - __einfo_uniqify ( EINFO_EIO, ( 31 - 0 ), \ - "Isochronous buffer overrun" ) -#define EPROTO_LOST \ - __einfo_error ( EINFO_EPROTO_LOST ) -#define EINFO_EPROTO_LOST \ - __einfo_uniqify ( EINFO_EPROTO, ( 32 - 32 ), \ - "Event lost" ) -#define EPROTO_UNDEFINED \ - __einfo_error ( EINFO_EPROTO_UNDEFINED ) -#define EINFO_EPROTO_UNDEFINED \ - __einfo_uniqify ( EINFO_EPROTO, ( 33 - 32 ), \ - "Undefined error" ) -#define EPROTO_STREAM_ID \ - __einfo_error ( EINFO_EPROTO_STREAM_ID ) -#define EINFO_EPROTO_STREAM_ID \ - __einfo_uniqify ( EINFO_EPROTO, ( 34 - 32 ), \ - "Invalid stream ID" ) -#define EPROTO_SECONDARY \ - __einfo_error ( EINFO_EPROTO_SECONDARY ) -#define EINFO_EPROTO_SECONDARY \ - __einfo_uniqify ( EINFO_EPROTO, ( 35 - 32 ), \ - "Secondary bandwidth error" ) -#define EPROTO_SPLIT \ - __einfo_error ( EINFO_EPROTO_SPLIT ) -#define EINFO_EPROTO_SPLIT \ - __einfo_uniqify ( EINFO_EPROTO, ( 36 - 32 ), \ - "Split transaction error" ) -#define ECODE(code) \ - ( ( (code) < 32 ) ? \ - EUNIQ ( EINFO_EIO, ( (code) & 31 ), EIO_DATA, EIO_BABBLE, \ - EIO_USB, EIO_TRB, EIO_STALL, EIO_RESOURCE, \ - EIO_BANDWIDTH, EIO_NO_SLOTS, EIO_STREAM_TYPE, \ - EIO_SLOT, EIO_ENDPOINT, EIO_SHORT, EIO_UNDERRUN, \ - EIO_OVERRUN, EIO_VF_RING_FULL, EIO_PARAMETER, \ - EIO_BANDWIDTH_OVERRUN, EIO_CONTEXT, EIO_NO_PING, \ - EIO_RING_FULL, EIO_INCOMPATIBLE, EIO_MISSED, \ - EIO_CMD_STOPPED, EIO_CMD_ABORTED, EIO_STOP, \ - EIO_STOP_LEN, EIO_STOP_SHORT, EIO_LATENCY, \ - EIO_ISOCH ) : \ - ( (code) < 64 ) ? \ - EUNIQ ( EINFO_EPROTO, ( (code) & 31 ), EPROTO_LOST, \ - EPROTO_UNDEFINED, EPROTO_STREAM_ID, \ - EPROTO_SECONDARY, EPROTO_SPLIT ) : \ - EFAULT ) - -/****************************************************************************** - * - * Register access - * - ****************************************************************************** - */ - -/** - * Initialise device - * - * @v xhci xHCI device - * @v regs MMIO registers - */ -static void xhci_init ( struct xhci_device *xhci, void *regs ) { - uint32_t hcsparams1; - uint32_t hcsparams2; - uint32_t hccparams1; - uint32_t pagesize; - size_t caplength; - size_t rtsoff; - size_t dboff; - - /* Locate capability, operational, runtime, and doorbell registers */ - xhci->cap = regs; - caplength = readb ( xhci->cap + XHCI_CAP_CAPLENGTH ); - rtsoff = readl ( xhci->cap + XHCI_CAP_RTSOFF ); - dboff = readl ( xhci->cap + XHCI_CAP_DBOFF ); - xhci->op = ( xhci->cap + caplength ); - xhci->run = ( xhci->cap + rtsoff ); - xhci->db = ( xhci->cap + dboff ); - DBGC2 ( xhci, "XHCI %s cap %08lx op %08lx run %08lx db %08lx\n", - xhci->name, virt_to_phys ( xhci->cap ), - virt_to_phys ( xhci->op ), virt_to_phys ( xhci->run ), - virt_to_phys ( xhci->db ) ); - - /* Read structural parameters 1 */ - hcsparams1 = readl ( xhci->cap + XHCI_CAP_HCSPARAMS1 ); - xhci->slots = XHCI_HCSPARAMS1_SLOTS ( hcsparams1 ); - xhci->intrs = XHCI_HCSPARAMS1_INTRS ( hcsparams1 ); - xhci->ports = XHCI_HCSPARAMS1_PORTS ( hcsparams1 ); - DBGC ( xhci, "XHCI %s has %d slots %d intrs %d ports\n", - xhci->name, xhci->slots, xhci->intrs, xhci->ports ); - - /* Read structural parameters 2 */ - hcsparams2 = readl ( xhci->cap + XHCI_CAP_HCSPARAMS2 ); - xhci->scratchpads = XHCI_HCSPARAMS2_SCRATCHPADS ( hcsparams2 ); - DBGC2 ( xhci, "XHCI %s needs %d scratchpads\n", - xhci->name, xhci->scratchpads ); - - /* Read capability parameters 1 */ - hccparams1 = readl ( xhci->cap + XHCI_CAP_HCCPARAMS1 ); - xhci->addr64 = XHCI_HCCPARAMS1_ADDR64 ( hccparams1 ); - xhci->csz_shift = XHCI_HCCPARAMS1_CSZ_SHIFT ( hccparams1 ); - xhci->xecp = XHCI_HCCPARAMS1_XECP ( hccparams1 ); - - /* Read page size */ - pagesize = readl ( xhci->op + XHCI_OP_PAGESIZE ); - xhci->pagesize = XHCI_PAGESIZE ( pagesize ); - assert ( xhci->pagesize != 0 ); - assert ( ( ( xhci->pagesize ) & ( xhci->pagesize - 1 ) ) == 0 ); - DBGC2 ( xhci, "XHCI %s page size %zd bytes\n", - xhci->name, xhci->pagesize ); -} - -/** - * Find extended capability - * - * @v xhci xHCI device - * @v id Capability ID - * @v offset Offset to previous extended capability instance, or zero - * @ret offset Offset to extended capability, or zero if not found - */ -static unsigned int xhci_extended_capability ( struct xhci_device *xhci, - unsigned int id, - unsigned int offset ) { - uint32_t xecp; - unsigned int next; - - /* Locate the extended capability */ - while ( 1 ) { - - /* Locate first or next capability as applicable */ - if ( offset ) { - xecp = readl ( xhci->cap + offset ); - next = XHCI_XECP_NEXT ( xecp ); - } else { - next = xhci->xecp; - } - if ( ! next ) - return 0; - offset += next; - - /* Check if this is the requested capability */ - xecp = readl ( xhci->cap + offset ); - if ( XHCI_XECP_ID ( xecp ) == id ) - return offset; - } -} - -/** - * Write potentially 64-bit register - * - * @v xhci xHCI device - * @v value Value - * @v reg Register address - * @ret rc Return status code - */ -static inline __attribute__ (( always_inline )) int -xhci_writeq ( struct xhci_device *xhci, physaddr_t value, void *reg ) { - - /* If this is a 32-bit build, then this can never fail - * (allowing the compiler to optimise out the error path). - */ - if ( sizeof ( value ) <= sizeof ( uint32_t ) ) { - writel ( value, reg ); - writel ( 0, ( reg + sizeof ( uint32_t ) ) ); - return 0; - } - - /* If the device does not support 64-bit addresses and this - * address is outside the 32-bit address space, then fail. - */ - if ( ( value & ~0xffffffffULL ) && ! xhci->addr64 ) { - DBGC ( xhci, "XHCI %s cannot access address %lx\n", - xhci->name, value ); - return -ENOTSUP; - } - - /* If this is a 64-bit build, then writeq() is available */ - writeq ( value, reg ); - return 0; -} - -/** - * Calculate buffer alignment - * - * @v len Length - * @ret align Buffer alignment - * - * Determine alignment required for a buffer which must be aligned to - * at least XHCI_MIN_ALIGN and which must not cross a page boundary. - */ -static inline size_t xhci_align ( size_t len ) { - size_t align; - - /* Align to own length (rounded up to a power of two) */ - align = ( 1 << fls ( len - 1 ) ); - - /* Round up to XHCI_MIN_ALIGN if needed */ - if ( align < XHCI_MIN_ALIGN ) - align = XHCI_MIN_ALIGN; - - return align; -} - -/** - * Calculate device context offset - * - * @v xhci xHCI device - * @v ctx Context index - */ -static inline size_t xhci_device_context_offset ( struct xhci_device *xhci, - unsigned int ctx ) { - - return ( XHCI_DCI ( ctx ) << xhci->csz_shift ); -} - -/** - * Calculate input context offset - * - * @v xhci xHCI device - * @v ctx Context index - */ -static inline size_t xhci_input_context_offset ( struct xhci_device *xhci, - unsigned int ctx ) { - - return ( XHCI_ICI ( ctx ) << xhci->csz_shift ); -} - -/****************************************************************************** - * - * Diagnostics - * - ****************************************************************************** - */ - -/** - * Dump host controller registers - * - * @v xhci xHCI device - */ -static inline void xhci_dump ( struct xhci_device *xhci ) { - uint32_t usbcmd; - uint32_t usbsts; - uint32_t pagesize; - uint32_t dnctrl; - uint32_t config; - - /* Do nothing unless debugging is enabled */ - if ( ! DBG_LOG ) - return; - - /* Dump USBCMD */ - usbcmd = readl ( xhci->op + XHCI_OP_USBCMD ); - DBGC ( xhci, "XHCI %s USBCMD %08x%s%s\n", xhci->name, usbcmd, - ( ( usbcmd & XHCI_USBCMD_RUN ) ? " run" : "" ), - ( ( usbcmd & XHCI_USBCMD_HCRST ) ? " hcrst" : "" ) ); - - /* Dump USBSTS */ - usbsts = readl ( xhci->op + XHCI_OP_USBSTS ); - DBGC ( xhci, "XHCI %s USBSTS %08x%s\n", xhci->name, usbsts, - ( ( usbsts & XHCI_USBSTS_HCH ) ? " hch" : "" ) ); - - /* Dump PAGESIZE */ - pagesize = readl ( xhci->op + XHCI_OP_PAGESIZE ); - DBGC ( xhci, "XHCI %s PAGESIZE %08x\n", xhci->name, pagesize ); - - /* Dump DNCTRL */ - dnctrl = readl ( xhci->op + XHCI_OP_DNCTRL ); - DBGC ( xhci, "XHCI %s DNCTRL %08x\n", xhci->name, dnctrl ); - - /* Dump CONFIG */ - config = readl ( xhci->op + XHCI_OP_CONFIG ); - DBGC ( xhci, "XHCI %s CONFIG %08x\n", xhci->name, config ); -} - -/** - * Dump port registers - * - * @v xhci xHCI device - * @v port Port number - */ -static inline void xhci_dump_port ( struct xhci_device *xhci, - unsigned int port ) { - uint32_t portsc; - uint32_t portpmsc; - uint32_t portli; - uint32_t porthlpmc; - - /* Do nothing unless debugging is enabled */ - if ( ! DBG_LOG ) - return; - - /* Dump PORTSC */ - portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port ) ); - DBGC ( xhci, "XHCI %s-%d PORTSC %08x%s%s%s%s psiv=%d\n", - xhci->name, port, portsc, - ( ( portsc & XHCI_PORTSC_CCS ) ? " ccs" : "" ), - ( ( portsc & XHCI_PORTSC_PED ) ? " ped" : "" ), - ( ( portsc & XHCI_PORTSC_PR ) ? " pr" : "" ), - ( ( portsc & XHCI_PORTSC_PP ) ? " pp" : "" ), - XHCI_PORTSC_PSIV ( portsc ) ); - - /* Dump PORTPMSC */ - portpmsc = readl ( xhci->op + XHCI_OP_PORTPMSC ( port ) ); - DBGC ( xhci, "XHCI %s-%d PORTPMSC %08x\n", xhci->name, port, portpmsc ); - - /* Dump PORTLI */ - portli = readl ( xhci->op + XHCI_OP_PORTLI ( port ) ); - DBGC ( xhci, "XHCI %s-%d PORTLI %08x\n", xhci->name, port, portli ); - - /* Dump PORTHLPMC */ - porthlpmc = readl ( xhci->op + XHCI_OP_PORTHLPMC ( port ) ); - DBGC ( xhci, "XHCI %s-%d PORTHLPMC %08x\n", - xhci->name, port, porthlpmc ); -} - -/****************************************************************************** - * - * USB legacy support - * - ****************************************************************************** - */ - -/** Prevent the release of ownership back to BIOS */ -static int xhci_legacy_prevent_release; - -/** - * Initialise USB legacy support - * - * @v xhci xHCI device - */ -static void xhci_legacy_init ( struct xhci_device *xhci ) { - unsigned int legacy; - uint8_t bios; - - /* Locate USB legacy support capability (if present) */ - legacy = xhci_extended_capability ( xhci, XHCI_XECP_ID_LEGACY, 0 ); - if ( ! legacy ) { - /* Not an error; capability may not be present */ - DBGC ( xhci, "XHCI %s has no USB legacy support capability\n", - xhci->name ); - return; - } - - /* Check if legacy USB support is enabled */ - bios = readb ( xhci->cap + legacy + XHCI_USBLEGSUP_BIOS ); - if ( ! ( bios & XHCI_USBLEGSUP_BIOS_OWNED ) ) { - /* Not an error; already owned by OS */ - DBGC ( xhci, "XHCI %s USB legacy support already disabled\n", - xhci->name ); - return; - } - - /* Record presence of USB legacy support capability */ - xhci->legacy = legacy; -} - -/** - * Claim ownership from BIOS - * - * @v xhci xHCI device - */ -static void xhci_legacy_claim ( struct xhci_device *xhci ) { - uint32_t ctlsts; - uint8_t bios; - unsigned int i; - - /* Do nothing unless legacy support capability is present */ - if ( ! xhci->legacy ) - return; - - /* Claim ownership */ - writeb ( XHCI_USBLEGSUP_OS_OWNED, - xhci->cap + xhci->legacy + XHCI_USBLEGSUP_OS ); - - /* Wait for BIOS to release ownership */ - for ( i = 0 ; i < XHCI_USBLEGSUP_MAX_WAIT_MS ; i++ ) { - - /* Check if BIOS has released ownership */ - bios = readb ( xhci->cap + xhci->legacy + XHCI_USBLEGSUP_BIOS ); - if ( ! ( bios & XHCI_USBLEGSUP_BIOS_OWNED ) ) { - DBGC ( xhci, "XHCI %s claimed ownership from BIOS\n", - xhci->name ); - ctlsts = readl ( xhci->cap + xhci->legacy + - XHCI_USBLEGSUP_CTLSTS ); - if ( ctlsts ) { - DBGC ( xhci, "XHCI %s warning: BIOS retained " - "SMIs: %08x\n", xhci->name, ctlsts ); - } - return; - } - - /* Delay */ - mdelay ( 1 ); - } - - /* BIOS did not release ownership. Claim it forcibly by - * disabling all SMIs. - */ - DBGC ( xhci, "XHCI %s could not claim ownership from BIOS: forcibly " - "disabling SMIs\n", xhci->name ); - writel ( 0, xhci->cap + xhci->legacy + XHCI_USBLEGSUP_CTLSTS ); -} - -/** - * Release ownership back to BIOS - * - * @v xhci xHCI device - */ -static void xhci_legacy_release ( struct xhci_device *xhci ) { - - /* Do nothing unless legacy support capability is present */ - if ( ! xhci->legacy ) - return; - - /* Do nothing if releasing ownership is prevented */ - if ( xhci_legacy_prevent_release ) { - DBGC ( xhci, "XHCI %s not releasing ownership to BIOS\n", - xhci->name ); - return; - } - - /* Release ownership */ - writeb ( 0, xhci->cap + xhci->legacy + XHCI_USBLEGSUP_OS ); - DBGC ( xhci, "XHCI %s released ownership to BIOS\n", xhci->name ); -} - -/****************************************************************************** - * - * Supported protocols - * - ****************************************************************************** - */ - -/** - * Transcribe port speed (for debugging) - * - * @v psi Protocol speed ID - * @ret speed Transcribed speed - */ -static inline const char * xhci_speed_name ( uint32_t psi ) { - 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 = XHCI_SUPPORTED_PSI_MANTISSA ( psi ); - exponent = XHCI_SUPPORTED_PSI_EXPONENT ( psi ); - - /* Transcribe speed */ - snprintf ( buf, sizeof ( buf ), "%d%sbps", - mantissa, exponents[exponent] ); - return buf; -} - -/** - * Find supported protocol extended capability for a port - * - * @v xhci xHCI device - * @v port Port number - * @ret supported Offset to extended capability, or zero if not found - */ -static unsigned int xhci_supported_protocol ( struct xhci_device *xhci, - unsigned int port ) { - unsigned int supported = 0; - unsigned int offset; - unsigned int count; - uint32_t ports; - - /* Iterate over all supported protocol structures */ - while ( ( supported = xhci_extended_capability ( xhci, - XHCI_XECP_ID_SUPPORTED, - supported ) ) ) { - - /* Determine port range */ - ports = readl ( xhci->cap + supported + XHCI_SUPPORTED_PORTS ); - offset = XHCI_SUPPORTED_PORTS_OFFSET ( ports ); - count = XHCI_SUPPORTED_PORTS_COUNT ( ports ); - - /* Check if port lies within this range */ - if ( ( port - offset ) < count ) - return supported; - } - - DBGC ( xhci, "XHCI %s-%d has no supported protocol\n", - xhci->name, port ); - return 0; -} - -/** - * Find port protocol - * - * @v xhci xHCI device - * @v port Port number - * @ret protocol USB protocol, or zero if not found - */ -static unsigned int xhci_port_protocol ( struct xhci_device *xhci, - unsigned int port ) { - unsigned int supported = xhci_supported_protocol ( xhci, port ); - union { - uint32_t raw; - char text[5]; - } name; - unsigned int protocol; - unsigned int type; - unsigned int psic; - unsigned int psiv; - unsigned int i; - uint32_t revision; - uint32_t ports; - uint32_t slot; - uint32_t psi; - - /* Fail if there is no supported protocol */ - if ( ! supported ) - return 0; - - /* Determine protocol version */ - revision = readl ( xhci->cap + supported + XHCI_SUPPORTED_REVISION ); - protocol = XHCI_SUPPORTED_REVISION_VER ( revision ); - - /* Describe port protocol */ - if ( DBG_EXTRA ) { - name.raw = cpu_to_le32 ( readl ( xhci->cap + supported + - XHCI_SUPPORTED_NAME ) ); - name.text[4] = '\0'; - slot = readl ( xhci->cap + supported + XHCI_SUPPORTED_SLOT ); - type = XHCI_SUPPORTED_SLOT_TYPE ( slot ); - DBGC2 ( xhci, "XHCI %s-%d %sv%04x type %d", - xhci->name, port, name.text, protocol, type ); - ports = readl ( xhci->cap + supported + XHCI_SUPPORTED_PORTS ); - psic = XHCI_SUPPORTED_PORTS_PSIC ( ports ); - if ( psic ) { - DBGC2 ( xhci, " speeds" ); - for ( i = 0 ; i < psic ; i++ ) { - psi = readl ( xhci->cap + supported + - XHCI_SUPPORTED_PSI ( i ) ); - psiv = XHCI_SUPPORTED_PSI_VALUE ( psi ); - DBGC2 ( xhci, " %d:%s", psiv, - xhci_speed_name ( psi ) ); - } - } - if ( xhci->quirks & XHCI_BAD_PSIV ) - DBGC2 ( xhci, " (ignored)" ); - DBGC2 ( xhci, "\n" ); - } - - return protocol; -} - -/** - * Find port slot type - * - * @v xhci xHCI device - * @v port Port number - * @ret type Slot type, or negative error - */ -static int xhci_port_slot_type ( struct xhci_device *xhci, unsigned int port ) { - unsigned int supported = xhci_supported_protocol ( xhci, port ); - unsigned int type; - uint32_t slot; - - /* Fail if there is no supported protocol */ - if ( ! supported ) - return -ENOTSUP; - - /* Get slot type */ - slot = readl ( xhci->cap + supported + XHCI_SUPPORTED_SLOT ); - type = XHCI_SUPPORTED_SLOT_TYPE ( slot ); - - return type; -} - -/** - * Find port speed - * - * @v xhci xHCI device - * @v port Port number - * @v psiv Protocol speed ID value - * @ret speed Port speed, or negative error - */ -static int xhci_port_speed ( struct xhci_device *xhci, unsigned int port, - unsigned int psiv ) { - unsigned int supported = xhci_supported_protocol ( xhci, port ); - unsigned int psic; - unsigned int mantissa; - unsigned int exponent; - unsigned int speed; - unsigned int i; - uint32_t ports; - uint32_t psi; - - /* Fail if there is no supported protocol */ - if ( ! supported ) - return -ENOTSUP; - - /* Get protocol speed ID count */ - ports = readl ( xhci->cap + supported + XHCI_SUPPORTED_PORTS ); - psic = XHCI_SUPPORTED_PORTS_PSIC ( ports ); - - /* Use the default mappings if applicable */ - if ( ( psic == 0 ) || ( xhci->quirks & XHCI_BAD_PSIV ) ) { - switch ( psiv ) { - case XHCI_SPEED_LOW : return USB_SPEED_LOW; - case XHCI_SPEED_FULL : return USB_SPEED_FULL; - case XHCI_SPEED_HIGH : return USB_SPEED_HIGH; - case XHCI_SPEED_SUPER : return USB_SPEED_SUPER; - default: - DBGC ( xhci, "XHCI %s-%d non-standard PSI value %d\n", - xhci->name, port, psiv ); - return -ENOTSUP; - } - } - - /* Iterate over PSI dwords looking for a match */ - for ( i = 0 ; i < psic ; i++ ) { - psi = readl ( xhci->cap + supported + XHCI_SUPPORTED_PSI ( i )); - if ( psiv == XHCI_SUPPORTED_PSI_VALUE ( psi ) ) { - mantissa = XHCI_SUPPORTED_PSI_MANTISSA ( psi ); - exponent = XHCI_SUPPORTED_PSI_EXPONENT ( psi ); - speed = USB_SPEED ( mantissa, exponent ); - return speed; - } - } - - DBGC ( xhci, "XHCI %s-%d spurious PSI value %d\n", - xhci->name, port, psiv ); - return -ENOENT; -} - -/** - * Find protocol speed ID value - * - * @v xhci xHCI device - * @v port Port number - * @v speed USB speed - * @ret psiv Protocol speed ID value, or negative error - */ -static int xhci_port_psiv ( struct xhci_device *xhci, unsigned int port, - unsigned int speed ) { - unsigned int supported = xhci_supported_protocol ( xhci, port ); - unsigned int psic; - unsigned int mantissa; - unsigned int exponent; - unsigned int psiv; - unsigned int i; - uint32_t ports; - uint32_t psi; - - /* Fail if there is no supported protocol */ - if ( ! supported ) - return -ENOTSUP; - - /* Get protocol speed ID count */ - ports = readl ( xhci->cap + supported + XHCI_SUPPORTED_PORTS ); - psic = XHCI_SUPPORTED_PORTS_PSIC ( ports ); - - /* Use the default mappings if applicable */ - if ( ( psic == 0 ) || ( xhci->quirks & XHCI_BAD_PSIV ) ) { - switch ( speed ) { - case USB_SPEED_LOW : return XHCI_SPEED_LOW; - case USB_SPEED_FULL : return XHCI_SPEED_FULL; - case USB_SPEED_HIGH : return XHCI_SPEED_HIGH; - case USB_SPEED_SUPER : return XHCI_SPEED_SUPER; - default: - DBGC ( xhci, "XHCI %s-%d non-standard speed %d\n", - xhci->name, port, speed ); - return -ENOTSUP; - } - } - - /* Iterate over PSI dwords looking for a match */ - for ( i = 0 ; i < psic ; i++ ) { - psi = readl ( xhci->cap + supported + XHCI_SUPPORTED_PSI ( i )); - mantissa = XHCI_SUPPORTED_PSI_MANTISSA ( psi ); - exponent = XHCI_SUPPORTED_PSI_EXPONENT ( psi ); - if ( speed == USB_SPEED ( mantissa, exponent ) ) { - psiv = XHCI_SUPPORTED_PSI_VALUE ( psi ); - return psiv; - } - } - - DBGC ( xhci, "XHCI %s-%d unrepresentable speed %#x\n", - xhci->name, port, speed ); - return -ENOENT; -} - -/****************************************************************************** - * - * Device context base address array - * - ****************************************************************************** - */ - -/** - * Allocate device context base address array - * - * @v xhci xHCI device - * @ret rc Return status code - */ -static int xhci_dcbaa_alloc ( struct xhci_device *xhci ) { - size_t len; - physaddr_t dcbaap; - int rc; - - /* Allocate and initialise structure. Must be at least - * 64-byte aligned and must not cross a page boundary, so - * align on its own size (rounded up to a power of two and - * with a minimum of 64 bytes). - */ - len = ( ( xhci->slots + 1 ) * sizeof ( xhci->dcbaa[0] ) ); - xhci->dcbaa = malloc_dma ( len, xhci_align ( len ) ); - if ( ! xhci->dcbaa ) { - DBGC ( xhci, "XHCI %s could not allocate DCBAA\n", xhci->name ); - rc = -ENOMEM; - goto err_alloc; - } - memset ( xhci->dcbaa, 0, len ); - - /* Program DCBAA pointer */ - dcbaap = virt_to_phys ( xhci->dcbaa ); - if ( ( rc = xhci_writeq ( xhci, dcbaap, - xhci->op + XHCI_OP_DCBAAP ) ) != 0 ) - goto err_writeq; - - DBGC2 ( xhci, "XHCI %s DCBAA at [%08lx,%08lx)\n", - xhci->name, dcbaap, ( dcbaap + len ) ); - return 0; - - err_writeq: - free_dma ( xhci->dcbaa, len ); - err_alloc: - return rc; -} - -/** - * Free device context base address array - * - * @v xhci xHCI device - */ -static void xhci_dcbaa_free ( struct xhci_device *xhci ) { - size_t len; - unsigned int i; - - /* Sanity check */ - for ( i = 0 ; i <= xhci->slots ; i++ ) - assert ( xhci->dcbaa[i] == 0 ); - - /* Clear DCBAA pointer */ - xhci_writeq ( xhci, 0, xhci->op + XHCI_OP_DCBAAP ); - - /* Free DCBAA */ - len = ( ( xhci->slots + 1 ) * sizeof ( xhci->dcbaa[0] ) ); - free_dma ( xhci->dcbaa, len ); -} - -/****************************************************************************** - * - * Scratchpad buffers - * - ****************************************************************************** - */ - -/** - * Allocate scratchpad buffers - * - * @v xhci xHCI device - * @ret rc Return status code - */ -static int xhci_scratchpad_alloc ( struct xhci_device *xhci ) { - size_t array_len; - size_t len; - physaddr_t phys; - unsigned int i; - int rc; - - /* Do nothing if no scratchpad buffers are used */ - if ( ! xhci->scratchpads ) - return 0; - - /* Allocate scratchpads */ - len = ( xhci->scratchpads * xhci->pagesize ); - xhci->scratchpad = umalloc ( len ); - if ( ! xhci->scratchpad ) { - DBGC ( xhci, "XHCI %s could not allocate scratchpad buffers\n", - xhci->name ); - rc = -ENOMEM; - goto err_alloc; - } - memset_user ( xhci->scratchpad, 0, 0, len ); - - /* Allocate scratchpad array */ - array_len = ( xhci->scratchpads * sizeof ( xhci->scratchpad_array[0] )); - xhci->scratchpad_array = - malloc_dma ( array_len, xhci_align ( array_len ) ); - if ( ! xhci->scratchpad_array ) { - DBGC ( xhci, "XHCI %s could not allocate scratchpad buffer " - "array\n", xhci->name ); - rc = -ENOMEM; - goto err_alloc_array; - } - - /* Populate scratchpad array */ - for ( i = 0 ; i < xhci->scratchpads ; i++ ) { - phys = user_to_phys ( xhci->scratchpad, ( i * xhci->pagesize )); - xhci->scratchpad_array[i] = phys; - } - - /* Set scratchpad array pointer */ - assert ( xhci->dcbaa != NULL ); - xhci->dcbaa[0] = cpu_to_le64 ( virt_to_phys ( xhci->scratchpad_array )); - - DBGC2 ( xhci, "XHCI %s scratchpad [%08lx,%08lx) array [%08lx,%08lx)\n", - xhci->name, user_to_phys ( xhci->scratchpad, 0 ), - user_to_phys ( xhci->scratchpad, len ), - virt_to_phys ( xhci->scratchpad_array ), - ( virt_to_phys ( xhci->scratchpad_array ) + array_len ) ); - return 0; - - free_dma ( xhci->scratchpad_array, array_len ); - err_alloc_array: - ufree ( xhci->scratchpad ); - err_alloc: - return rc; -} - -/** - * Free scratchpad buffers - * - * @v xhci xHCI device - */ -static void xhci_scratchpad_free ( struct xhci_device *xhci ) { - size_t array_len; - - /* Do nothing if no scratchpad buffers are used */ - if ( ! xhci->scratchpads ) - return; - - /* Clear scratchpad array pointer */ - assert ( xhci->dcbaa != NULL ); - xhci->dcbaa[0] = 0; - - /* Free scratchpad array */ - array_len = ( xhci->scratchpads * sizeof ( xhci->scratchpad_array[0] )); - free_dma ( xhci->scratchpad_array, array_len ); - - /* Free scratchpads */ - ufree ( xhci->scratchpad ); -} - -/****************************************************************************** - * - * Run / stop / reset - * - ****************************************************************************** - */ - -/** - * Start xHCI device - * - * @v xhci xHCI device - */ -static void xhci_run ( struct xhci_device *xhci ) { - uint32_t config; - uint32_t usbcmd; - - /* Configure number of device slots */ - config = readl ( xhci->op + XHCI_OP_CONFIG ); - config &= ~XHCI_CONFIG_MAX_SLOTS_EN_MASK; - config |= XHCI_CONFIG_MAX_SLOTS_EN ( xhci->slots ); - writel ( config, xhci->op + XHCI_OP_CONFIG ); - - /* Set run/stop bit */ - usbcmd = readl ( xhci->op + XHCI_OP_USBCMD ); - usbcmd |= XHCI_USBCMD_RUN; - writel ( usbcmd, xhci->op + XHCI_OP_USBCMD ); -} - -/** - * Stop xHCI device - * - * @v xhci xHCI device - * @ret rc Return status code - */ -static int xhci_stop ( struct xhci_device *xhci ) { - uint32_t usbcmd; - uint32_t usbsts; - unsigned int i; - - /* Clear run/stop bit */ - usbcmd = readl ( xhci->op + XHCI_OP_USBCMD ); - usbcmd &= ~XHCI_USBCMD_RUN; - writel ( usbcmd, xhci->op + XHCI_OP_USBCMD ); - - /* Wait for device to stop */ - for ( i = 0 ; i < XHCI_STOP_MAX_WAIT_MS ; i++ ) { - - /* Check if device is stopped */ - usbsts = readl ( xhci->op + XHCI_OP_USBSTS ); - if ( usbsts & XHCI_USBSTS_HCH ) - return 0; - - /* Delay */ - mdelay ( 1 ); - } - - DBGC ( xhci, "XHCI %s timed out waiting for stop\n", xhci->name ); - return -ETIMEDOUT; -} - -/** - * Reset xHCI device - * - * @v xhci xHCI device - * @ret rc Return status code - */ -static int xhci_reset ( struct xhci_device *xhci ) { - uint32_t usbcmd; - unsigned int i; - int rc; - - /* The xHCI specification states that resetting a running - * device may result in undefined behaviour, so try stopping - * it first. - */ - if ( ( rc = xhci_stop ( xhci ) ) != 0 ) { - /* Ignore errors and attempt to reset the device anyway */ - } - - /* Reset device */ - writel ( XHCI_USBCMD_HCRST, xhci->op + XHCI_OP_USBCMD ); - - /* Wait for reset to complete */ - for ( i = 0 ; i < XHCI_RESET_MAX_WAIT_MS ; i++ ) { - - /* Check if reset is complete */ - usbcmd = readl ( xhci->op + XHCI_OP_USBCMD ); - if ( ! ( usbcmd & XHCI_USBCMD_HCRST ) ) - return 0; - - /* Delay */ - mdelay ( 1 ); - } - - DBGC ( xhci, "XHCI %s timed out waiting for reset\n", xhci->name ); - return -ETIMEDOUT; -} - -/****************************************************************************** - * - * Transfer request blocks - * - ****************************************************************************** - */ - -/** - * Allocate transfer request block ring - * - * @v xhci xHCI device - * @v ring TRB ring - * @v shift Ring size (log2) - * @v slot Device slot - * @v target Doorbell target - * @v stream Doorbell stream ID - * @ret rc Return status code - */ -static int xhci_ring_alloc ( struct xhci_device *xhci, - struct xhci_trb_ring *ring, - unsigned int shift, unsigned int slot, - unsigned int target, unsigned int stream ) { - struct xhci_trb_link *link; - unsigned int count; - int rc; - - /* Sanity check */ - assert ( shift > 0 ); - - /* Initialise structure */ - memset ( ring, 0, sizeof ( *ring ) ); - ring->shift = shift; - count = ( 1U << shift ); - ring->mask = ( count - 1 ); - ring->len = ( ( count + 1 /* Link TRB */ ) * sizeof ( ring->trb[0] ) ); - ring->db = ( xhci->db + ( slot * sizeof ( ring->dbval ) ) ); - ring->dbval = XHCI_DBVAL ( target, stream ); - - /* Allocate I/O buffers */ - ring->iobuf = zalloc ( count * sizeof ( ring->iobuf[0] ) ); - if ( ! ring->iobuf ) { - rc = -ENOMEM; - goto err_alloc_iobuf; - } - - /* Allocate TRBs */ - ring->trb = malloc_dma ( ring->len, xhci_align ( ring->len ) ); - if ( ! ring->trb ) { - rc = -ENOMEM; - goto err_alloc_trb; - } - memset ( ring->trb, 0, ring->len ); - - /* Initialise Link TRB */ - link = &ring->trb[count].link; - link->next = cpu_to_le64 ( virt_to_phys ( ring->trb ) ); - link->flags = XHCI_TRB_TC; - link->type = XHCI_TRB_LINK; - ring->link = link; - - return 0; - - free_dma ( ring->trb, ring->len ); - err_alloc_trb: - free ( ring->iobuf ); - err_alloc_iobuf: - return rc; -} - -/** - * Reset transfer request block ring - * - * @v ring TRB ring - */ -static void xhci_ring_reset ( struct xhci_trb_ring *ring ) { - unsigned int count = ( 1U << ring->shift ); - - /* Reset producer and consumer counters */ - ring->prod = 0; - ring->cons = 0; - - /* Reset TRBs (except Link TRB) */ - memset ( ring->trb, 0, ( count * sizeof ( ring->trb[0] ) ) ); -} - -/** - * Free transfer request block ring - * - * @v ring TRB ring - */ -static void xhci_ring_free ( struct xhci_trb_ring *ring ) { - unsigned int count = ( 1U << ring->shift ); - unsigned int i; - - /* Sanity checks */ - assert ( ring->cons == ring->prod ); - for ( i = 0 ; i < count ; i++ ) - assert ( ring->iobuf[i] == NULL ); - - /* Free TRBs */ - free_dma ( ring->trb, ring->len ); - - /* Free I/O buffers */ - free ( ring->iobuf ); -} - -/** - * Enqueue a transfer request block - * - * @v ring TRB ring - * @v iobuf I/O buffer (if any) - * @v trb Transfer request block (with empty Cycle flag) - * @ret rc Return status code - * - * This operation does not implicitly ring the doorbell register. - */ -static int xhci_enqueue ( struct xhci_trb_ring *ring, struct io_buffer *iobuf, - const union xhci_trb *trb ) { - union xhci_trb *dest; - unsigned int prod; - unsigned int mask; - unsigned int index; - unsigned int cycle; - - /* Sanity check */ - assert ( ! ( trb->common.flags & XHCI_TRB_C ) ); - - /* Fail if ring is full */ - if ( ! xhci_ring_remaining ( ring ) ) - return -ENOBUFS; - - /* Update producer counter (and link TRB, if applicable) */ - prod = ring->prod++; - mask = ring->mask; - cycle = ( ( ~( prod >> ring->shift ) ) & XHCI_TRB_C ); - index = ( prod & mask ); - if ( index == 0 ) - ring->link->flags = ( XHCI_TRB_TC | ( cycle ^ XHCI_TRB_C ) ); - - /* Record I/O buffer */ - ring->iobuf[index] = iobuf; - - /* Enqueue TRB */ - dest = &ring->trb[index]; - dest->template.parameter = trb->template.parameter; - dest->template.status = trb->template.status; - wmb(); - dest->template.control = ( trb->template.control | - cpu_to_le32 ( cycle ) ); - - return 0; -} - -/** - * Dequeue a transfer request block - * - * @v ring TRB ring - * @ret iobuf I/O buffer - */ -static struct io_buffer * xhci_dequeue ( struct xhci_trb_ring *ring ) { - struct io_buffer *iobuf; - unsigned int cons; - unsigned int mask; - unsigned int index; - - /* Sanity check */ - assert ( xhci_ring_fill ( ring ) != 0 ); - - /* Update consumer counter */ - cons = ring->cons++; - mask = ring->mask; - index = ( cons & mask ); - - /* Retrieve I/O buffer */ - iobuf = ring->iobuf[index]; - ring->iobuf[index] = NULL; - - return iobuf; -} - -/** - * Enqueue multiple transfer request blocks - * - * @v ring TRB ring - * @v iobuf I/O buffer - * @v trbs Transfer request blocks (with empty Cycle flag) - * @v count Number of transfer request blocks - * @ret rc Return status code - * - * This operation does not implicitly ring the doorbell register. - */ -static int xhci_enqueue_multi ( struct xhci_trb_ring *ring, - struct io_buffer *iobuf, - const union xhci_trb *trbs, - unsigned int count ) { - const union xhci_trb *trb = trbs; - int rc; - - /* Sanity check */ - assert ( iobuf != NULL ); - - /* Fail if ring does not have sufficient space */ - if ( xhci_ring_remaining ( ring ) < count ) - return -ENOBUFS; - - /* Enqueue each TRB, recording the I/O buffer with the final TRB */ - while ( count-- ) { - rc = xhci_enqueue ( ring, ( count ? NULL : iobuf ), trb++ ); - assert ( rc == 0 ); /* Should never be able to fail */ - } - - return 0; -} - -/** - * Dequeue multiple transfer request blocks - * - * @v ring TRB ring - * @ret iobuf I/O buffer - */ -static struct io_buffer * xhci_dequeue_multi ( struct xhci_trb_ring *ring ) { - struct io_buffer *iobuf; - - /* Dequeue TRBs until we reach the final TRB for an I/O buffer */ - do { - iobuf = xhci_dequeue ( ring ); - } while ( iobuf == NULL ); - - return iobuf; -} - -/** - * Ring doorbell register - * - * @v ring TRB ring - */ -static inline __attribute__ (( always_inline )) void -xhci_doorbell ( struct xhci_trb_ring *ring ) { - - wmb(); - writel ( ring->dbval, ring->db ); -} - -/****************************************************************************** - * - * Command and event rings - * - ****************************************************************************** - */ - -/** - * Allocate command ring - * - * @v xhci xHCI device - * @ret rc Return status code - */ -static int xhci_command_alloc ( struct xhci_device *xhci ) { - physaddr_t crp; - int rc; - - /* Allocate TRB ring */ - if ( ( rc = xhci_ring_alloc ( xhci, &xhci->command, XHCI_CMD_TRBS_LOG2, - 0, 0, 0 ) ) != 0 ) - goto err_ring_alloc; - - /* Program command ring control register */ - crp = virt_to_phys ( xhci->command.trb ); - if ( ( rc = xhci_writeq ( xhci, ( crp | XHCI_CRCR_RCS ), - xhci->op + XHCI_OP_CRCR ) ) != 0 ) - goto err_writeq; - - DBGC2 ( xhci, "XHCI %s CRCR at [%08lx,%08lx)\n", - xhci->name, crp, ( crp + xhci->command.len ) ); - return 0; - - err_writeq: - xhci_ring_free ( &xhci->command ); - err_ring_alloc: - return rc; -} - -/** - * Free command ring - * - * @v xhci xHCI device - */ -static void xhci_command_free ( struct xhci_device *xhci ) { - - /* Sanity check */ - assert ( ( readl ( xhci->op + XHCI_OP_CRCR ) & XHCI_CRCR_CRR ) == 0 ); - - /* Clear command ring control register */ - xhci_writeq ( xhci, 0, xhci->op + XHCI_OP_CRCR ); - - /* Free TRB ring */ - xhci_ring_free ( &xhci->command ); -} - -/** - * Allocate event ring - * - * @v xhci xHCI device - * @ret rc Return status code - */ -static int xhci_event_alloc ( struct xhci_device *xhci ) { - struct xhci_event_ring *event = &xhci->event; - unsigned int count; - size_t len; - int rc; - - /* Allocate event ring */ - count = ( 1 << XHCI_EVENT_TRBS_LOG2 ); - len = ( count * sizeof ( event->trb[0] ) ); - event->trb = malloc_dma ( len, xhci_align ( len ) ); - if ( ! event->trb ) { - rc = -ENOMEM; - goto err_alloc_trb; - } - memset ( event->trb, 0, len ); - - /* Allocate event ring segment table */ - event->segment = malloc_dma ( sizeof ( event->segment[0] ), - xhci_align ( sizeof (event->segment[0]))); - if ( ! event->segment ) { - rc = -ENOMEM; - goto err_alloc_segment; - } - memset ( event->segment, 0, sizeof ( event->segment[0] ) ); - event->segment[0].base = cpu_to_le64 ( virt_to_phys ( event->trb ) ); - event->segment[0].count = cpu_to_le32 ( count ); - - /* Program event ring registers */ - writel ( 1, xhci->run + XHCI_RUN_ERSTSZ ( 0 ) ); - if ( ( rc = xhci_writeq ( xhci, virt_to_phys ( event->trb ), - xhci->run + XHCI_RUN_ERDP ( 0 ) ) ) != 0 ) - goto err_writeq_erdp; - if ( ( rc = xhci_writeq ( xhci, virt_to_phys ( event->segment ), - xhci->run + XHCI_RUN_ERSTBA ( 0 ) ) ) != 0 ) - goto err_writeq_erstba; - - DBGC2 ( xhci, "XHCI %s event ring [%08lx,%08lx) table [%08lx,%08lx)\n", - xhci->name, virt_to_phys ( event->trb ), - ( virt_to_phys ( event->trb ) + len ), - virt_to_phys ( event->segment ), - ( virt_to_phys ( event->segment ) + - sizeof (event->segment[0] ) ) ); - return 0; - - xhci_writeq ( xhci, 0, xhci->run + XHCI_RUN_ERSTBA ( 0 ) ); - err_writeq_erstba: - xhci_writeq ( xhci, 0, xhci->run + XHCI_RUN_ERDP ( 0 ) ); - err_writeq_erdp: - free_dma ( event->trb, len ); - err_alloc_segment: - free_dma ( event->segment, sizeof ( event->segment[0] ) ); - err_alloc_trb: - return rc; -} - -/** - * Free event ring - * - * @v xhci xHCI device - */ -static void xhci_event_free ( struct xhci_device *xhci ) { - struct xhci_event_ring *event = &xhci->event; - unsigned int count; - size_t len; - - /* Clear event ring registers */ - writel ( 0, xhci->run + XHCI_RUN_ERSTSZ ( 0 ) ); - xhci_writeq ( xhci, 0, xhci->run + XHCI_RUN_ERSTBA ( 0 ) ); - xhci_writeq ( xhci, 0, xhci->run + XHCI_RUN_ERDP ( 0 ) ); - - /* Free event ring segment table */ - free_dma ( event->segment, sizeof ( event->segment[0] ) ); - - /* Free event ring */ - count = ( 1 << XHCI_EVENT_TRBS_LOG2 ); - len = ( count * sizeof ( event->trb[0] ) ); - free_dma ( event->trb, len ); -} - -/** - * Handle transfer event - * - * @v xhci xHCI device - * @v trb Transfer event TRB - */ -static void xhci_transfer ( struct xhci_device *xhci, - struct xhci_trb_transfer *trb ) { - struct xhci_slot *slot; - struct xhci_endpoint *endpoint; - struct io_buffer *iobuf; - int rc; - - /* Profile transfer events */ - profile_start ( &xhci_transfer_profiler ); - - /* Identify slot */ - if ( ( trb->slot > xhci->slots ) || - ( ( slot = xhci->slot[trb->slot] ) == NULL ) ) { - DBGC ( xhci, "XHCI %s transfer event invalid slot %d:\n", - xhci->name, trb->slot ); - DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) ); - return; - } - - /* Identify endpoint */ - if ( ( trb->endpoint > XHCI_CTX_END ) || - ( ( endpoint = slot->endpoint[trb->endpoint] ) == NULL ) ) { - DBGC ( xhci, "XHCI %s slot %d transfer event invalid epid " - "%d:\n", xhci->name, slot->id, trb->endpoint ); - DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) ); - return; - } - - /* Dequeue TRB(s) */ - iobuf = xhci_dequeue_multi ( &endpoint->ring ); - assert ( iobuf != NULL ); - - /* Check for errors */ - if ( ! ( ( trb->code == XHCI_CMPLT_SUCCESS ) || - ( trb->code == XHCI_CMPLT_SHORT ) ) ) { - - /* Construct error */ - rc = -ECODE ( trb->code ); - DBGC ( xhci, "XHCI %s slot %d ctx %d failed (code %d): %s\n", - xhci->name, slot->id, endpoint->ctx, trb->code, - strerror ( rc ) ); - DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) ); - - /* Sanity check */ - assert ( ( endpoint->context->state & XHCI_ENDPOINT_STATE_MASK ) - != XHCI_ENDPOINT_RUNNING ); - - /* Report failure to USB core */ - usb_complete_err ( endpoint->ep, iobuf, rc ); - return; - } - - /* Record actual transfer size */ - iob_unput ( iobuf, le16_to_cpu ( trb->residual ) ); - - /* Sanity check (for successful completions only) */ - assert ( xhci_ring_consumed ( &endpoint->ring ) == - le64_to_cpu ( trb->transfer ) ); - - /* Report completion to USB core */ - usb_complete ( endpoint->ep, iobuf ); - profile_stop ( &xhci_transfer_profiler ); -} - -/** - * Handle command completion event - * - * @v xhci xHCI device - * @v trb Command completion event - */ -static void xhci_complete ( struct xhci_device *xhci, - struct xhci_trb_complete *trb ) { - int rc; - - /* Ignore "command ring stopped" notifications */ - if ( trb->code == XHCI_CMPLT_CMD_STOPPED ) { - DBGC2 ( xhci, "XHCI %s command ring stopped\n", xhci->name ); - return; - } - - /* Ignore unexpected completions */ - if ( ! xhci->pending ) { - rc = -ECODE ( trb->code ); - DBGC ( xhci, "XHCI %s unexpected completion (code %d): %s\n", - xhci->name, trb->code, strerror ( rc ) ); - DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) ); - return; - } - - /* Dequeue command TRB */ - xhci_dequeue ( &xhci->command ); - - /* Sanity check */ - assert ( xhci_ring_consumed ( &xhci->command ) == - le64_to_cpu ( trb->command ) ); - - /* Record completion */ - memcpy ( xhci->pending, trb, sizeof ( *xhci->pending ) ); - xhci->pending = NULL; -} - -/** - * Handle port status event - * - * @v xhci xHCI device - * @v trb Port status event - */ -static void xhci_port_status ( struct xhci_device *xhci, - struct xhci_trb_port_status *trb ) { - struct usb_port *port = usb_port ( xhci->bus->hub, trb->port ); - uint32_t portsc; - - /* Sanity check */ - assert ( ( trb->port > 0 ) && ( trb->port <= xhci->ports ) ); - - /* Record disconnections and clear changes */ - portsc = readl ( xhci->op + XHCI_OP_PORTSC ( trb->port ) ); - port->disconnected |= ( portsc & XHCI_PORTSC_CSC ); - portsc &= ( XHCI_PORTSC_PRESERVE | XHCI_PORTSC_CHANGE ); - writel ( portsc, xhci->op + XHCI_OP_PORTSC ( trb->port ) ); - - /* Report port status change */ - usb_port_changed ( port ); -} - -/** - * Handle host controller event - * - * @v xhci xHCI device - * @v trb Host controller event - */ -static void xhci_host_controller ( struct xhci_device *xhci, - struct xhci_trb_host_controller *trb ) { - int rc; - - /* Construct error */ - rc = -ECODE ( trb->code ); - DBGC ( xhci, "XHCI %s host controller event (code %d): %s\n", - xhci->name, trb->code, strerror ( rc ) ); -} - -/** - * Poll event ring - * - * @v xhci xHCI device - */ -static void xhci_event_poll ( struct xhci_device *xhci ) { - struct xhci_event_ring *event = &xhci->event; - union xhci_trb *trb; - unsigned int shift = XHCI_EVENT_TRBS_LOG2; - unsigned int count = ( 1 << shift ); - unsigned int mask = ( count - 1 ); - unsigned int consumed; - unsigned int type; - - /* Poll for events */ - profile_start ( &xhci_event_profiler ); - for ( consumed = 0 ; ; consumed++ ) { - - /* Stop if we reach an empty TRB */ - rmb(); - trb = &event->trb[ event->cons & mask ]; - if ( ! ( ( trb->common.flags ^ - ( event->cons >> shift ) ) & XHCI_TRB_C ) ) - break; - - /* Handle TRB */ - type = ( trb->common.type & XHCI_TRB_TYPE_MASK ); - switch ( type ) { - - case XHCI_TRB_TRANSFER : - xhci_transfer ( xhci, &trb->transfer ); - break; - - case XHCI_TRB_COMPLETE : - xhci_complete ( xhci, &trb->complete ); - break; - - case XHCI_TRB_PORT_STATUS: - xhci_port_status ( xhci, &trb->port ); - break; - - case XHCI_TRB_HOST_CONTROLLER: - xhci_host_controller ( xhci, &trb->host ); - break; - - default: - DBGC ( xhci, "XHCI %s unrecognised event %#x\n:", - xhci->name, event->cons ); - DBGC_HDA ( xhci, virt_to_phys ( trb ), - trb, sizeof ( *trb ) ); - break; - } - - /* Consume this TRB */ - event->cons++; - } - - /* Update dequeue pointer if applicable */ - if ( consumed ) { - xhci_writeq ( xhci, virt_to_phys ( trb ), - xhci->run + XHCI_RUN_ERDP ( 0 ) ); - profile_stop ( &xhci_event_profiler ); - } -} - -/** - * Abort command - * - * @v xhci xHCI device - */ -static void xhci_abort ( struct xhci_device *xhci ) { - physaddr_t crp; - - /* Abort the command */ - DBGC2 ( xhci, "XHCI %s aborting command\n", xhci->name ); - xhci_writeq ( xhci, XHCI_CRCR_CA, xhci->op + XHCI_OP_CRCR ); - - /* Allow time for command to abort */ - mdelay ( XHCI_COMMAND_ABORT_DELAY_MS ); - - /* Sanity check */ - assert ( ( readl ( xhci->op + XHCI_OP_CRCR ) & XHCI_CRCR_CRR ) == 0 ); - - /* Consume (and ignore) any final command status */ - xhci_event_poll ( xhci ); - - /* Reset the command ring control register */ - xhci_ring_reset ( &xhci->command ); - crp = virt_to_phys ( xhci->command.trb ); - xhci_writeq ( xhci, ( crp | XHCI_CRCR_RCS ), xhci->op + XHCI_OP_CRCR ); -} - -/** - * Issue command and wait for completion - * - * @v xhci xHCI device - * @v trb Transfer request block (with empty Cycle flag) - * @ret rc Return status code - * - * On a successful completion, the TRB will be overwritten with the - * completion. - */ -static int xhci_command ( struct xhci_device *xhci, union xhci_trb *trb ) { - struct xhci_trb_complete *complete = &trb->complete; - unsigned int i; - int rc; - - /* Record the pending command */ - xhci->pending = trb; - - /* Enqueue the command */ - if ( ( rc = xhci_enqueue ( &xhci->command, NULL, trb ) ) != 0 ) - goto err_enqueue; - - /* Ring the command doorbell */ - xhci_doorbell ( &xhci->command ); - - /* Wait for the command to complete */ - for ( i = 0 ; i < XHCI_COMMAND_MAX_WAIT_MS ; i++ ) { - - /* Poll event ring */ - xhci_event_poll ( xhci ); - - /* Check for completion */ - if ( ! xhci->pending ) { - if ( complete->code != XHCI_CMPLT_SUCCESS ) { - rc = -ECODE ( complete->code ); - DBGC ( xhci, "XHCI %s command failed (code " - "%d): %s\n", xhci->name, complete->code, - strerror ( rc ) ); - DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) ); - return rc; - } - return 0; - } - - /* Delay */ - mdelay ( 1 ); - } - - /* Timeout */ - DBGC ( xhci, "XHCI %s timed out waiting for completion\n", xhci->name ); - rc = -ETIMEDOUT; - - /* Abort command */ - xhci_abort ( xhci ); - - err_enqueue: - xhci->pending = NULL; - return rc; -} - -/** - * Issue NOP and wait for completion - * - * @v xhci xHCI device - * @ret rc Return status code - */ -static inline int xhci_nop ( struct xhci_device *xhci ) { - union xhci_trb trb; - struct xhci_trb_common *nop = &trb.common; - int rc; - - /* Construct command */ - memset ( nop, 0, sizeof ( *nop ) ); - nop->flags = XHCI_TRB_IOC; - nop->type = XHCI_TRB_NOP_CMD; - - /* Issue command and wait for completion */ - if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Enable slot - * - * @v xhci xHCI device - * @v type Slot type - * @ret slot Device slot ID, or negative error - */ -static inline int xhci_enable_slot ( struct xhci_device *xhci, - unsigned int type ) { - union xhci_trb trb; - struct xhci_trb_enable_slot *enable = &trb.enable; - struct xhci_trb_complete *enabled = &trb.complete; - unsigned int slot; - int rc; - - /* Construct command */ - memset ( enable, 0, sizeof ( *enable ) ); - enable->slot = type; - enable->type = XHCI_TRB_ENABLE_SLOT; - - /* Issue command and wait for completion */ - if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) { - DBGC ( xhci, "XHCI %s could not enable new slot: %s\n", - xhci->name, strerror ( rc ) ); - return rc; - } - - /* Extract slot number */ - slot = enabled->slot; - - DBGC2 ( xhci, "XHCI %s slot %d enabled\n", xhci->name, slot ); - return slot; -} - -/** - * Disable slot - * - * @v xhci xHCI device - * @v slot Device slot - * @ret rc Return status code - */ -static inline int xhci_disable_slot ( struct xhci_device *xhci, - unsigned int slot ) { - union xhci_trb trb; - struct xhci_trb_disable_slot *disable = &trb.disable; - int rc; - - /* Construct command */ - memset ( disable, 0, sizeof ( *disable ) ); - disable->type = XHCI_TRB_DISABLE_SLOT; - disable->slot = slot; - - /* Issue command and wait for completion */ - if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) { - DBGC ( xhci, "XHCI %s could not disable slot %d: %s\n", - xhci->name, slot, strerror ( rc ) ); - return rc; - } - - DBGC2 ( xhci, "XHCI %s slot %d disabled\n", xhci->name, slot ); - return 0; -} - -/** - * Issue context-based command and wait for completion - * - * @v xhci xHCI device - * @v slot Device slot - * @v endpoint Endpoint - * @v type TRB type - * @v populate Input context populater - * @ret rc Return status code - */ -static int xhci_context ( struct xhci_device *xhci, struct xhci_slot *slot, - struct xhci_endpoint *endpoint, unsigned int type, - void ( * populate ) ( struct xhci_device *xhci, - struct xhci_slot *slot, - struct xhci_endpoint *endpoint, - void *input ) ) { - union xhci_trb trb; - struct xhci_trb_context *context = &trb.context; - size_t len; - void *input; - int rc; - - /* Allocate an input context */ - len = xhci_input_context_offset ( xhci, XHCI_CTX_END ); - input = malloc_dma ( len, xhci_align ( len ) ); - if ( ! input ) { - rc = -ENOMEM; - goto err_alloc; - } - memset ( input, 0, len ); - - /* Populate input context */ - populate ( xhci, slot, endpoint, input ); - - /* Construct command */ - memset ( context, 0, sizeof ( *context ) ); - context->type = type; - context->input = cpu_to_le64 ( virt_to_phys ( input ) ); - context->slot = slot->id; - - /* Issue command and wait for completion */ - if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) - goto err_command; - - err_command: - free_dma ( input, len ); - err_alloc: - return rc; -} - -/** - * Populate address device input context - * - * @v xhci xHCI device - * @v slot Device slot - * @v endpoint Endpoint - * @v input Input context - */ -static void xhci_address_device_input ( struct xhci_device *xhci, - struct xhci_slot *slot, - struct xhci_endpoint *endpoint, - void *input ) { - struct xhci_control_context *control_ctx; - struct xhci_slot_context *slot_ctx; - struct xhci_endpoint_context *ep_ctx; - - /* Sanity checks */ - assert ( endpoint->ctx == XHCI_CTX_EP0 ); - - /* Populate control context */ - control_ctx = input; - control_ctx->add = cpu_to_le32 ( ( 1 << XHCI_CTX_SLOT ) | - ( 1 << XHCI_CTX_EP0 ) ); - - /* Populate slot context */ - slot_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_SLOT )); - slot_ctx->info = cpu_to_le32 ( XHCI_SLOT_INFO ( 1, 0, slot->psiv, - slot->route ) ); - slot_ctx->port = slot->port; - slot_ctx->tt_id = slot->tt_id; - slot_ctx->tt_port = slot->tt_port; - - /* Populate control endpoint context */ - ep_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_EP0 ) ); - ep_ctx->type = XHCI_EP_TYPE_CONTROL; - ep_ctx->burst = endpoint->ep->burst; - ep_ctx->mtu = cpu_to_le16 ( endpoint->ep->mtu ); - ep_ctx->dequeue = cpu_to_le64 ( virt_to_phys ( endpoint->ring.trb ) | - XHCI_EP_DCS ); - ep_ctx->trb_len = cpu_to_le16 ( XHCI_EP0_TRB_LEN ); -} - -/** - * Address device - * - * @v xhci xHCI device - * @v slot Device slot - * @ret rc Return status code - */ -static inline int xhci_address_device ( struct xhci_device *xhci, - struct xhci_slot *slot ) { - struct usb_device *usb = slot->usb; - struct xhci_slot_context *slot_ctx; - int rc; - - /* Assign device address */ - if ( ( rc = xhci_context ( xhci, slot, slot->endpoint[XHCI_CTX_EP0], - XHCI_TRB_ADDRESS_DEVICE, - xhci_address_device_input ) ) != 0 ) - return rc; - - /* Get assigned address */ - slot_ctx = ( slot->context + - xhci_device_context_offset ( xhci, XHCI_CTX_SLOT ) ); - usb->address = slot_ctx->address; - DBGC2 ( xhci, "XHCI %s assigned address %d to %s\n", - xhci->name, usb->address, usb->name ); - - return 0; -} - -/** - * Populate configure endpoint input context - * - * @v xhci xHCI device - * @v slot Device slot - * @v endpoint Endpoint - * @v input Input context - */ -static void xhci_configure_endpoint_input ( struct xhci_device *xhci, - struct xhci_slot *slot, - struct xhci_endpoint *endpoint, - void *input ) { - struct xhci_control_context *control_ctx; - struct xhci_slot_context *slot_ctx; - struct xhci_endpoint_context *ep_ctx; - - /* Populate control context */ - control_ctx = input; - control_ctx->add = cpu_to_le32 ( ( 1 << XHCI_CTX_SLOT ) | - ( 1 << endpoint->ctx ) ); - - /* Populate slot context */ - slot_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_SLOT )); - slot_ctx->info = cpu_to_le32 ( XHCI_SLOT_INFO ( ( XHCI_CTX_END - 1 ), - ( slot->ports ? 1 : 0 ), - slot->psiv, 0 ) ); - slot_ctx->ports = slot->ports; - - /* Populate endpoint context */ - ep_ctx = ( input + xhci_input_context_offset ( xhci, endpoint->ctx ) ); - ep_ctx->interval = endpoint->interval; - ep_ctx->type = endpoint->type; - ep_ctx->burst = endpoint->ep->burst; - ep_ctx->mtu = cpu_to_le16 ( endpoint->ep->mtu ); - ep_ctx->dequeue = cpu_to_le64 ( virt_to_phys ( endpoint->ring.trb ) | - XHCI_EP_DCS ); - ep_ctx->trb_len = cpu_to_le16 ( endpoint->ep->mtu ); /* best guess */ -} - -/** - * Configure endpoint - * - * @v xhci xHCI device - * @v slot Device slot - * @v endpoint Endpoint - * @ret rc Return status code - */ -static inline int xhci_configure_endpoint ( struct xhci_device *xhci, - struct xhci_slot *slot, - struct xhci_endpoint *endpoint ) { - int rc; - - /* Configure endpoint */ - if ( ( rc = xhci_context ( xhci, slot, endpoint, - XHCI_TRB_CONFIGURE_ENDPOINT, - xhci_configure_endpoint_input ) ) != 0 ) - return rc; - - DBGC2 ( xhci, "XHCI %s slot %d ctx %d configured\n", - xhci->name, slot->id, endpoint->ctx ); - return 0; -} - -/** - * Populate deconfigure endpoint input context - * - * @v xhci xHCI device - * @v slot Device slot - * @v endpoint Endpoint - * @v input Input context - */ -static void -xhci_deconfigure_endpoint_input ( struct xhci_device *xhci __unused, - struct xhci_slot *slot __unused, - struct xhci_endpoint *endpoint, - void *input ) { - struct xhci_control_context *control_ctx; - struct xhci_slot_context *slot_ctx; - - /* Populate control context */ - control_ctx = input; - control_ctx->add = cpu_to_le32 ( 1 << XHCI_CTX_SLOT ); - control_ctx->drop = cpu_to_le32 ( 1 << endpoint->ctx ); - - /* Populate slot context */ - slot_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_SLOT )); - slot_ctx->info = cpu_to_le32 ( XHCI_SLOT_INFO ( ( XHCI_CTX_END - 1 ), - 0, 0, 0 ) ); -} - -/** - * Deconfigure endpoint - * - * @v xhci xHCI device - * @v slot Device slot - * @v endpoint Endpoint - * @ret rc Return status code - */ -static inline int xhci_deconfigure_endpoint ( struct xhci_device *xhci, - struct xhci_slot *slot, - struct xhci_endpoint *endpoint ) { - int rc; - - /* Deconfigure endpoint */ - if ( ( rc = xhci_context ( xhci, slot, endpoint, - XHCI_TRB_CONFIGURE_ENDPOINT, - xhci_deconfigure_endpoint_input ) ) != 0 ) - return rc; - - DBGC2 ( xhci, "XHCI %s slot %d ctx %d deconfigured\n", - xhci->name, slot->id, endpoint->ctx ); - return 0; -} - -/** - * Populate evaluate context input context - * - * @v xhci xHCI device - * @v slot Device slot - * @v endpoint Endpoint - * @v input Input context - */ -static void xhci_evaluate_context_input ( struct xhci_device *xhci, - struct xhci_slot *slot __unused, - struct xhci_endpoint *endpoint, - void *input ) { - struct xhci_control_context *control_ctx; - struct xhci_slot_context *slot_ctx; - struct xhci_endpoint_context *ep_ctx; - - /* Populate control context */ - control_ctx = input; - control_ctx->add = cpu_to_le32 ( ( 1 << XHCI_CTX_SLOT ) | - ( 1 << endpoint->ctx ) ); - - /* Populate slot context */ - slot_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_SLOT )); - slot_ctx->info = cpu_to_le32 ( XHCI_SLOT_INFO ( ( XHCI_CTX_END - 1 ), - 0, 0, 0 ) ); - - /* Populate endpoint context */ - ep_ctx = ( input + xhci_input_context_offset ( xhci, endpoint->ctx ) ); - ep_ctx->mtu = cpu_to_le16 ( endpoint->ep->mtu ); -} - -/** - * Evaluate context - * - * @v xhci xHCI device - * @v slot Device slot - * @v endpoint Endpoint - * @ret rc Return status code - */ -static inline int xhci_evaluate_context ( struct xhci_device *xhci, - struct xhci_slot *slot, - struct xhci_endpoint *endpoint ) { - int rc; - - /* Configure endpoint */ - if ( ( rc = xhci_context ( xhci, slot, endpoint, - XHCI_TRB_EVALUATE_CONTEXT, - xhci_evaluate_context_input ) ) != 0 ) - return rc; - - DBGC2 ( xhci, "XHCI %s slot %d ctx %d (re-)evaluated\n", - xhci->name, slot->id, endpoint->ctx ); - return 0; -} - -/** - * Reset endpoint - * - * @v xhci xHCI device - * @v slot Device slot - * @v endpoint Endpoint - * @ret rc Return status code - */ -static inline int xhci_reset_endpoint ( struct xhci_device *xhci, - struct xhci_slot *slot, - struct xhci_endpoint *endpoint ) { - union xhci_trb trb; - struct xhci_trb_reset_endpoint *reset = &trb.reset; - int rc; - - /* Construct command */ - memset ( reset, 0, sizeof ( *reset ) ); - reset->slot = slot->id; - reset->endpoint = endpoint->ctx; - reset->type = XHCI_TRB_RESET_ENDPOINT; - - /* Issue command and wait for completion */ - if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) { - DBGC ( xhci, "XHCI %s slot %d ctx %d could not reset endpoint " - "in state %d: %s\n", xhci->name, slot->id, endpoint->ctx, - endpoint->context->state, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Stop endpoint - * - * @v xhci xHCI device - * @v slot Device slot - * @v endpoint Endpoint - * @ret rc Return status code - */ -static inline int xhci_stop_endpoint ( struct xhci_device *xhci, - struct xhci_slot *slot, - struct xhci_endpoint *endpoint ) { - union xhci_trb trb; - struct xhci_trb_stop_endpoint *stop = &trb.stop; - int rc; - - /* Construct command */ - memset ( stop, 0, sizeof ( *stop ) ); - stop->slot = slot->id; - stop->endpoint = endpoint->ctx; - stop->type = XHCI_TRB_STOP_ENDPOINT; - - /* Issue command and wait for completion */ - if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) { - DBGC ( xhci, "XHCI %s slot %d ctx %d could not stop endpoint " - "in state %d: %s\n", xhci->name, slot->id, endpoint->ctx, - endpoint->context->state, strerror ( rc ) ); - return rc; - } - - return 0; -} - -/** - * Set transfer ring dequeue pointer - * - * @v xhci xHCI device - * @v slot Device slot - * @v endpoint Endpoint - * @ret rc Return status code - */ -static inline int -xhci_set_tr_dequeue_pointer ( struct xhci_device *xhci, - struct xhci_slot *slot, - struct xhci_endpoint *endpoint ) { - union xhci_trb trb; - struct xhci_trb_set_tr_dequeue_pointer *dequeue = &trb.dequeue; - struct xhci_trb_ring *ring = &endpoint->ring; - unsigned int cons; - unsigned int mask; - unsigned int index; - unsigned int dcs; - int rc; - - /* Construct command */ - memset ( dequeue, 0, sizeof ( *dequeue ) ); - cons = ring->cons; - mask = ring->mask; - dcs = ( ( ~( cons >> ring->shift ) ) & XHCI_EP_DCS ); - index = ( cons & mask ); - dequeue->dequeue = - cpu_to_le64 ( virt_to_phys ( &ring->trb[index] ) | dcs ); - dequeue->slot = slot->id; - dequeue->endpoint = endpoint->ctx; - dequeue->type = XHCI_TRB_SET_TR_DEQUEUE_POINTER; - - /* Issue command and wait for completion */ - if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) { - DBGC ( xhci, "XHCI %s slot %d ctx %d could not set TR dequeue " - "pointer in state %d: %s\n", xhci->name, slot->id, - endpoint->ctx, endpoint->context->state, strerror ( rc)); - return rc; - } - - return 0; -} - -/****************************************************************************** - * - * Endpoint operations - * - ****************************************************************************** - */ - -/** - * Open endpoint - * - * @v ep USB endpoint - * @ret rc Return status code - */ -static int xhci_endpoint_open ( struct usb_endpoint *ep ) { - struct usb_device *usb = ep->usb; - struct xhci_slot *slot = usb_get_hostdata ( usb ); - struct xhci_device *xhci = slot->xhci; - struct xhci_endpoint *endpoint; - unsigned int ctx; - unsigned int type; - unsigned int interval; - int rc; - - /* Calculate context index */ - ctx = XHCI_CTX ( ep->address ); - assert ( slot->endpoint[ctx] == NULL ); - - /* Calculate endpoint type */ - type = XHCI_EP_TYPE ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); - if ( type == XHCI_EP_TYPE ( USB_ENDPOINT_ATTR_CONTROL ) ) - type = XHCI_EP_TYPE_CONTROL; - if ( ep->address & USB_DIR_IN ) - type |= XHCI_EP_TYPE_IN; - - /* Calculate interval */ - if ( type & XHCI_EP_TYPE_PERIODIC ) { - interval = ( fls ( ep->interval ) - 1 ); - } else { - interval = ep->interval; - } - - /* Allocate and initialise structure */ - endpoint = zalloc ( sizeof ( *endpoint ) ); - if ( ! endpoint ) { - rc = -ENOMEM; - goto err_alloc; - } - usb_endpoint_set_hostdata ( ep, endpoint ); - slot->endpoint[ctx] = endpoint; - endpoint->xhci = xhci; - endpoint->slot = slot; - endpoint->ep = ep; - endpoint->ctx = ctx; - endpoint->type = type; - endpoint->interval = interval; - endpoint->context = ( ( ( void * ) slot->context ) + - xhci_device_context_offset ( xhci, ctx ) ); - - /* Allocate transfer ring */ - if ( ( rc = xhci_ring_alloc ( xhci, &endpoint->ring, - XHCI_TRANSFER_TRBS_LOG2, - slot->id, ctx, 0 ) ) != 0 ) - goto err_ring_alloc; - - /* Configure endpoint, if applicable */ - if ( ( ctx != XHCI_CTX_EP0 ) && - ( ( rc = xhci_configure_endpoint ( xhci, slot, endpoint ) ) != 0 )) - goto err_configure_endpoint; - - DBGC2 ( xhci, "XHCI %s slot %d ctx %d ring [%08lx,%08lx)\n", - xhci->name, slot->id, ctx, virt_to_phys ( endpoint->ring.trb ), - ( virt_to_phys ( endpoint->ring.trb ) + endpoint->ring.len ) ); - return 0; - - xhci_deconfigure_endpoint ( xhci, slot, endpoint ); - err_configure_endpoint: - xhci_ring_free ( &endpoint->ring ); - err_ring_alloc: - slot->endpoint[ctx] = NULL; - free ( endpoint ); - err_alloc: - return rc; -} - -/** - * Close endpoint - * - * @v ep USB endpoint - */ -static void xhci_endpoint_close ( struct usb_endpoint *ep ) { - struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); - struct xhci_slot *slot = endpoint->slot; - struct xhci_device *xhci = slot->xhci; - struct io_buffer *iobuf; - unsigned int ctx = endpoint->ctx; - - /* Deconfigure endpoint, if applicable */ - if ( ctx != XHCI_CTX_EP0 ) - xhci_deconfigure_endpoint ( xhci, slot, endpoint ); - - /* Cancel any incomplete transfers */ - while ( xhci_ring_fill ( &endpoint->ring ) ) { - iobuf = xhci_dequeue_multi ( &endpoint->ring ); - usb_complete_err ( ep, iobuf, -ECANCELED ); - } - - /* Free endpoint */ - xhci_ring_free ( &endpoint->ring ); - slot->endpoint[ctx] = NULL; - free ( endpoint ); -} - -/** - * Reset endpoint - * - * @v ep USB endpoint - * @ret rc Return status code - */ -static int xhci_endpoint_reset ( struct usb_endpoint *ep ) { - struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); - struct xhci_slot *slot = endpoint->slot; - struct xhci_device *xhci = slot->xhci; - int rc; - - /* Reset endpoint context */ - if ( ( rc = xhci_reset_endpoint ( xhci, slot, endpoint ) ) != 0 ) - return rc; - - /* Set transfer ring dequeue pointer */ - if ( ( rc = xhci_set_tr_dequeue_pointer ( xhci, slot, endpoint ) ) != 0) - return rc; - - /* Ring doorbell to resume processing */ - xhci_doorbell ( &endpoint->ring ); - - DBGC ( xhci, "XHCI %s slot %d ctx %d reset\n", - xhci->name, slot->id, endpoint->ctx ); - return 0; -} - -/** - * Update MTU - * - * @v ep USB endpoint - * @ret rc Return status code - */ -static int xhci_endpoint_mtu ( struct usb_endpoint *ep ) { - struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); - struct xhci_slot *slot = endpoint->slot; - struct xhci_device *xhci = slot->xhci; - int rc; - - /* Evalulate context */ - if ( ( rc = xhci_evaluate_context ( xhci, slot, endpoint ) ) != 0 ) - return rc; - - return 0; -} - -/** - * Enqueue message transfer - * - * @v ep USB endpoint - * @v iobuf I/O buffer - * @ret rc Return status code - */ -static int xhci_endpoint_message ( struct usb_endpoint *ep, - struct io_buffer *iobuf ) { - struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); - struct usb_setup_packet *packet; - unsigned int input; - size_t len; - union xhci_trb trbs[ 1 /* setup */ + 1 /* possible data */ + - 1 /* status */ ]; - union xhci_trb *trb = trbs; - struct xhci_trb_setup *setup; - struct xhci_trb_data *data; - struct xhci_trb_status *status; - int rc; - - /* Profile message transfers */ - profile_start ( &xhci_message_profiler ); - - /* Construct setup stage TRB */ - memset ( trbs, 0, sizeof ( trbs ) ); - assert ( iob_len ( iobuf ) >= sizeof ( *packet ) ); - packet = iobuf->data; - iob_pull ( iobuf, sizeof ( *packet ) ); - setup = &(trb++)->setup; - memcpy ( &setup->packet, packet, sizeof ( setup->packet ) ); - setup->len = cpu_to_le32 ( sizeof ( *packet ) ); - setup->flags = XHCI_TRB_IDT; - setup->type = XHCI_TRB_SETUP; - len = iob_len ( iobuf ); - input = ( packet->request & cpu_to_le16 ( USB_DIR_IN ) ); - if ( len ) - setup->direction = ( input ? XHCI_SETUP_IN : XHCI_SETUP_OUT ); - - /* Construct data stage TRB, if applicable */ - if ( len ) { - data = &(trb++)->data; - data->data = cpu_to_le64 ( virt_to_phys ( iobuf->data ) ); - data->len = cpu_to_le32 ( len ); - data->type = XHCI_TRB_DATA; - data->direction = ( input ? XHCI_DATA_IN : XHCI_DATA_OUT ); - } - - /* Construct status stage TRB */ - status = &(trb++)->status; - status->flags = XHCI_TRB_IOC; - status->type = XHCI_TRB_STATUS; - status->direction = - ( ( len && input ) ? XHCI_STATUS_OUT : XHCI_STATUS_IN ); - - /* Enqueue TRBs */ - if ( ( rc = xhci_enqueue_multi ( &endpoint->ring, iobuf, trbs, - ( trb - trbs ) ) ) != 0 ) - return rc; - - /* Ring the doorbell */ - xhci_doorbell ( &endpoint->ring ); - - profile_stop ( &xhci_message_profiler ); - return 0; -} - -/** - * Enqueue stream transfer - * - * @v ep USB endpoint - * @v iobuf I/O buffer - * @v terminate Terminate using a short packet - * @ret rc Return status code - */ -static int xhci_endpoint_stream ( struct usb_endpoint *ep, - struct io_buffer *iobuf, int terminate ) { - struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); - union xhci_trb trbs[ 1 /* Normal */ + 1 /* Possible zero-length */ ]; - union xhci_trb *trb = trbs; - struct xhci_trb_normal *normal; - size_t len = iob_len ( iobuf ); - int rc; - - /* Profile stream transfers */ - profile_start ( &xhci_stream_profiler ); - - /* Construct normal TRBs */ - memset ( &trbs, 0, sizeof ( trbs ) ); - normal = &(trb++)->normal; - normal->data = cpu_to_le64 ( virt_to_phys ( iobuf->data ) ); - normal->len = cpu_to_le32 ( len ); - normal->type = XHCI_TRB_NORMAL; - if ( terminate && ( ( len & ( ep->mtu - 1 ) ) == 0 ) ) { - normal->flags = XHCI_TRB_CH; - normal = &(trb++)->normal; - normal->type = XHCI_TRB_NORMAL; - } - normal->flags = XHCI_TRB_IOC; - - /* Enqueue TRBs */ - if ( ( rc = xhci_enqueue_multi ( &endpoint->ring, iobuf, trbs, - ( trb - trbs ) ) ) != 0 ) - return rc; - - /* Ring the doorbell */ - xhci_doorbell ( &endpoint->ring ); - - profile_stop ( &xhci_stream_profiler ); - return 0; -} - -/****************************************************************************** - * - * Device operations - * - ****************************************************************************** - */ - -/** - * Open device - * - * @v usb USB device - * @ret rc Return status code - */ -static int xhci_device_open ( struct usb_device *usb ) { - struct xhci_device *xhci = usb_bus_get_hostdata ( usb->port->hub->bus ); - struct usb_port *tt = usb_transaction_translator ( usb ); - struct xhci_slot *slot; - struct xhci_slot *tt_slot; - size_t len; - int type; - int id; - int rc; - - /* Determine applicable slot type */ - type = xhci_port_slot_type ( xhci, usb->port->address ); - if ( type < 0 ) { - rc = type; - DBGC ( xhci, "XHCI %s-%d has no slot type\n", - xhci->name, usb->port->address ); - goto err_type; - } - - /* Allocate a device slot number */ - id = xhci_enable_slot ( xhci, type ); - if ( id < 0 ) { - rc = id; - goto err_enable_slot; - } - assert ( ( id > 0 ) && ( ( unsigned int ) id <= xhci->slots ) ); - assert ( xhci->slot[id] == NULL ); - - /* Allocate and initialise structure */ - slot = zalloc ( sizeof ( *slot ) ); - if ( ! slot ) { - rc = -ENOMEM; - goto err_alloc; - } - usb_set_hostdata ( usb, slot ); - xhci->slot[id] = slot; - slot->xhci = xhci; - slot->usb = usb; - slot->id = id; - if ( tt ) { - tt_slot = usb_get_hostdata ( tt->hub->usb ); - slot->tt_id = tt_slot->id; - slot->tt_port = tt->address; - } - - /* Allocate a device context */ - len = xhci_device_context_offset ( xhci, XHCI_CTX_END ); - slot->context = malloc_dma ( len, xhci_align ( len ) ); - if ( ! slot->context ) { - rc = -ENOMEM; - goto err_alloc_context; - } - memset ( slot->context, 0, len ); - - /* Set device context base address */ - assert ( xhci->dcbaa[id] == 0 ); - xhci->dcbaa[id] = cpu_to_le64 ( virt_to_phys ( slot->context ) ); - - DBGC2 ( xhci, "XHCI %s slot %d device context [%08lx,%08lx) for %s\n", - xhci->name, slot->id, virt_to_phys ( slot->context ), - ( virt_to_phys ( slot->context ) + len ), usb->name ); - return 0; - - xhci->dcbaa[id] = 0; - free_dma ( slot->context, len ); - err_alloc_context: - xhci->slot[id] = NULL; - free ( slot ); - err_alloc: - xhci_disable_slot ( xhci, id ); - err_enable_slot: - err_type: - return rc; -} - -/** - * Close device - * - * @v usb USB device - */ -static void xhci_device_close ( struct usb_device *usb ) { - struct xhci_slot *slot = usb_get_hostdata ( usb ); - struct xhci_device *xhci = slot->xhci; - size_t len = xhci_device_context_offset ( xhci, XHCI_CTX_END ); - unsigned int id = slot->id; - int rc; - - /* Disable slot */ - if ( ( rc = xhci_disable_slot ( xhci, id ) ) != 0 ) { - /* Slot is still enabled. Leak the slot context, - * since the controller may still write to this - * memory, and leave the DCBAA entry intact. - * - * If the controller later reports that this same slot - * has been re-enabled, then some assertions will be - * triggered. - */ - DBGC ( xhci, "XHCI %s slot %d leaking context memory\n", - xhci->name, slot->id ); - slot->context = NULL; - } - - /* Free slot */ - if ( slot->context ) { - free_dma ( slot->context, len ); - xhci->dcbaa[id] = 0; - } - xhci->slot[id] = NULL; - free ( slot ); -} - -/** - * Assign device address - * - * @v usb USB device - * @ret rc Return status code - */ -static int xhci_device_address ( struct usb_device *usb ) { - struct xhci_slot *slot = usb_get_hostdata ( usb ); - struct xhci_device *xhci = slot->xhci; - struct usb_port *port = usb->port; - struct usb_port *root_port; - int psiv; - int rc; - - /* Calculate route string */ - slot->route = usb_route_string ( usb ); - - /* Calculate root hub port number */ - root_port = usb_root_hub_port ( usb ); - slot->port = root_port->address; - - /* Calculate protocol speed ID */ - psiv = xhci_port_psiv ( xhci, slot->port, port->speed ); - if ( psiv < 0 ) { - rc = psiv; - return rc; - } - slot->psiv = psiv; - - /* Address device */ - if ( ( rc = xhci_address_device ( xhci, slot ) ) != 0 ) - return rc; - - return 0; -} - -/****************************************************************************** - * - * Bus operations - * - ****************************************************************************** - */ - -/** - * Open USB bus - * - * @v bus USB bus - * @ret rc Return status code - */ -static int xhci_bus_open ( struct usb_bus *bus ) { - struct xhci_device *xhci = usb_bus_get_hostdata ( bus ); - int rc; - - /* Allocate device slot array */ - xhci->slot = zalloc ( ( xhci->slots + 1 ) * sizeof ( xhci->slot[0] ) ); - if ( ! xhci->slot ) { - rc = -ENOMEM; - goto err_slot_alloc; - } - - /* Allocate device context base address array */ - if ( ( rc = xhci_dcbaa_alloc ( xhci ) ) != 0 ) - goto err_dcbaa_alloc; - - /* Allocate scratchpad buffers */ - if ( ( rc = xhci_scratchpad_alloc ( xhci ) ) != 0 ) - goto err_scratchpad_alloc; - - /* Allocate command ring */ - if ( ( rc = xhci_command_alloc ( xhci ) ) != 0 ) - goto err_command_alloc; - - /* Allocate event ring */ - if ( ( rc = xhci_event_alloc ( xhci ) ) != 0 ) - goto err_event_alloc; - - /* Start controller */ - xhci_run ( xhci ); - - return 0; - - xhci_stop ( xhci ); - xhci_event_free ( xhci ); - err_event_alloc: - xhci_command_free ( xhci ); - err_command_alloc: - xhci_scratchpad_free ( xhci ); - err_scratchpad_alloc: - xhci_dcbaa_free ( xhci ); - err_dcbaa_alloc: - free ( xhci->slot ); - err_slot_alloc: - return rc; -} - -/** - * Close USB bus - * - * @v bus USB bus - */ -static void xhci_bus_close ( struct usb_bus *bus ) { - struct xhci_device *xhci = usb_bus_get_hostdata ( bus ); - unsigned int i; - - /* Sanity checks */ - assert ( xhci->slot != NULL ); - for ( i = 0 ; i <= xhci->slots ; i++ ) - assert ( xhci->slot[i] == NULL ); - - xhci_stop ( xhci ); - xhci_event_free ( xhci ); - xhci_command_free ( xhci ); - xhci_scratchpad_free ( xhci ); - xhci_dcbaa_free ( xhci ); - free ( xhci->slot ); -} - -/** - * Poll USB bus - * - * @v bus USB bus - */ -static void xhci_bus_poll ( struct usb_bus *bus ) { - struct xhci_device *xhci = usb_bus_get_hostdata ( bus ); - - /* Poll event ring */ - xhci_event_poll ( xhci ); -} - -/****************************************************************************** - * - * Hub operations - * - ****************************************************************************** - */ - -/** - * Open hub - * - * @v hub USB hub - * @ret rc Return status code - */ -static int xhci_hub_open ( struct usb_hub *hub ) { - struct xhci_slot *slot; - - /* Do nothing if this is the root hub */ - if ( ! hub->usb ) - return 0; - - /* Get device slot */ - slot = usb_get_hostdata ( hub->usb ); - - /* Update device slot hub parameters. We don't inform the - * hardware of this information until the hub's interrupt - * endpoint is opened, since the only mechanism for so doing - * provided by the xHCI specification is a Configure Endpoint - * command, and we can't issue that command until we have a - * non-EP0 endpoint to configure. - */ - slot->ports = hub->ports; - - return 0; -} - -/** - * Close hub - * - * @v hub USB hub - */ -static void xhci_hub_close ( struct usb_hub *hub __unused ) { - - /* Nothing to do */ -} - -/****************************************************************************** - * - * Root hub operations - * - ****************************************************************************** - */ - -/** - * Open root hub - * - * @v hub USB hub - * @ret rc Return status code - */ -static int xhci_root_open ( struct usb_hub *hub ) { - struct usb_bus *bus = hub->bus; - struct xhci_device *xhci = usb_bus_get_hostdata ( bus ); - struct usb_port *port; - uint32_t portsc; - unsigned int i; - - /* Enable power to all ports */ - for ( i = 1 ; i <= xhci->ports ; i++ ) { - portsc = readl ( xhci->op + XHCI_OP_PORTSC ( i ) ); - portsc &= XHCI_PORTSC_PRESERVE; - portsc |= XHCI_PORTSC_PP; - writel ( portsc, xhci->op + XHCI_OP_PORTSC ( i ) ); - } - - /* xHCI spec requires us to potentially wait 20ms after - * enabling power to a port. - */ - mdelay ( XHCI_PORT_POWER_DELAY_MS ); - - /* USB3 ports may power up as Disabled */ - for ( i = 1 ; i <= xhci->ports ; i++ ) { - portsc = readl ( xhci->op + XHCI_OP_PORTSC ( i ) ); - port = usb_port ( hub, i ); - if ( ( port->protocol >= USB_PROTO_3_0 ) && - ( ( portsc & XHCI_PORTSC_PLS_MASK ) == - XHCI_PORTSC_PLS_DISABLED ) ) { - /* Force link state to RxDetect */ - portsc &= XHCI_PORTSC_PRESERVE; - portsc |= ( XHCI_PORTSC_PLS_RXDETECT | XHCI_PORTSC_LWS); - writel ( portsc, xhci->op + XHCI_OP_PORTSC ( i ) ); - } - } - - /* Some xHCI cards seem to require an additional delay after - * setting the link state to RxDetect. - */ - mdelay ( XHCI_LINK_STATE_DELAY_MS ); - - /* Record hub driver private data */ - usb_hub_set_drvdata ( hub, xhci ); - - return 0; -} - -/** - * Close root hub - * - * @v hub USB hub - */ -static void xhci_root_close ( struct usb_hub *hub ) { - - /* Clear hub driver private data */ - usb_hub_set_drvdata ( hub, NULL ); -} - -/** - * Enable port - * - * @v hub USB hub - * @v port USB port - * @ret rc Return status code - */ -static int xhci_root_enable ( struct usb_hub *hub, struct usb_port *port ) { - struct xhci_device *xhci = usb_hub_get_drvdata ( hub ); - uint32_t portsc; - unsigned int i; - - /* Reset port */ - portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port->address ) ); - portsc &= XHCI_PORTSC_PRESERVE; - portsc |= XHCI_PORTSC_PR; - writel ( portsc, xhci->op + XHCI_OP_PORTSC ( port->address ) ); - - /* Wait for port to become enabled */ - for ( i = 0 ; i < XHCI_PORT_RESET_MAX_WAIT_MS ; i++ ) { - - /* Check port status */ - portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port->address ) ); - if ( portsc & XHCI_PORTSC_PED ) - return 0; - - /* Delay */ - mdelay ( 1 ); - } - - DBGC ( xhci, "XHCI %s-%d timed out waiting for port to enable\n", - xhci->name, port->address ); - return -ETIMEDOUT; -} - -/** - * Disable port - * - * @v hub USB hub - * @v port USB port - * @ret rc Return status code - */ -static int xhci_root_disable ( struct usb_hub *hub, struct usb_port *port ) { - struct xhci_device *xhci = usb_hub_get_drvdata ( hub ); - uint32_t portsc; - - /* Disable port */ - portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port->address ) ); - portsc &= XHCI_PORTSC_PRESERVE; - portsc |= XHCI_PORTSC_PED; - writel ( portsc, xhci->op + XHCI_OP_PORTSC ( port->address ) ); - - return 0; -} - -/** - * Update root hub port speed - * - * @v hub USB hub - * @v port USB port - * @ret rc Return status code - */ -static int xhci_root_speed ( struct usb_hub *hub, struct usb_port *port ) { - struct xhci_device *xhci = usb_hub_get_drvdata ( hub ); - uint32_t portsc; - unsigned int psiv; - int ccs; - int ped; - int csc; - int speed; - int rc; - - /* Read port status */ - portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port->address ) ); - DBGC2 ( xhci, "XHCI %s-%d status is %08x\n", - xhci->name, port->address, portsc ); - ccs = ( portsc & XHCI_PORTSC_CCS ); - ped = ( portsc & XHCI_PORTSC_PED ); - csc = ( portsc & XHCI_PORTSC_CSC ); - psiv = XHCI_PORTSC_PSIV ( portsc ); - - /* Record disconnections and clear changes */ - port->disconnected |= csc; - portsc &= ( XHCI_PORTSC_PRESERVE | XHCI_PORTSC_CHANGE ); - writel ( portsc, xhci->op + XHCI_OP_PORTSC ( port->address ) ); - - /* Port speed is not valid unless port is connected */ - if ( ! ccs ) { - port->speed = USB_SPEED_NONE; - return 0; - } - - /* For USB2 ports, the PSIV field is not valid until the port - * completes reset and becomes enabled. - */ - if ( ( port->protocol < USB_PROTO_3_0 ) && ! ped ) { - port->speed = USB_SPEED_FULL; - return 0; - } - - /* Get port speed and map to generic USB speed */ - speed = xhci_port_speed ( xhci, port->address, psiv ); - if ( speed < 0 ) { - rc = speed; - return rc; - } - - port->speed = speed; - return 0; -} - -/** - * Clear transaction translator buffer - * - * @v hub USB hub - * @v port USB port - * @v ep USB endpoint - * @ret rc Return status code - */ -static int xhci_root_clear_tt ( struct usb_hub *hub, struct usb_port *port, - struct usb_endpoint *ep ) { - struct xhci_device *xhci = usb_hub_get_drvdata ( hub ); - - /* Should never be called; this is a root hub */ - DBGC ( xhci, "XHCI %s-%d nonsensical CLEAR_TT for %s %s\n", xhci->name, - port->address, ep->usb->name, usb_endpoint_name ( ep ) ); - - return -ENOTSUP; -} - -/****************************************************************************** - * - * PCI interface - * - ****************************************************************************** - */ - -/** USB host controller operations */ -static struct usb_host_operations xhci_operations = { - .endpoint = { - .open = xhci_endpoint_open, - .close = xhci_endpoint_close, - .reset = xhci_endpoint_reset, - .mtu = xhci_endpoint_mtu, - .message = xhci_endpoint_message, - .stream = xhci_endpoint_stream, - }, - .device = { - .open = xhci_device_open, - .close = xhci_device_close, - .address = xhci_device_address, - }, - .bus = { - .open = xhci_bus_open, - .close = xhci_bus_close, - .poll = xhci_bus_poll, - }, - .hub = { - .open = xhci_hub_open, - .close = xhci_hub_close, - }, - .root = { - .open = xhci_root_open, - .close = xhci_root_close, - .enable = xhci_root_enable, - .disable = xhci_root_disable, - .speed = xhci_root_speed, - .clear_tt = xhci_root_clear_tt, - }, -}; - -/** - * Fix Intel PCH-specific quirks - * - * @v xhci xHCI device - * @v pci PCI device - */ -static void xhci_pch_fix ( struct xhci_device *xhci, struct pci_device *pci ) { - struct xhci_pch *pch = &xhci->pch; - uint32_t xusb2pr; - uint32_t xusb2prm; - uint32_t usb3pssen; - uint32_t usb3prm; - - /* Enable SuperSpeed capability. Do this before rerouting - * USB2 ports, so that USB3 devices connect at SuperSpeed. - */ - pci_read_config_dword ( pci, XHCI_PCH_USB3PSSEN, &usb3pssen ); - pci_read_config_dword ( pci, XHCI_PCH_USB3PRM, &usb3prm ); - if ( usb3prm & ~usb3pssen ) { - DBGC ( xhci, "XHCI %s enabling SuperSpeed on ports %08x\n", - xhci->name, ( usb3prm & ~usb3pssen ) ); - } - pch->usb3pssen = usb3pssen; - usb3pssen |= usb3prm; - pci_write_config_dword ( pci, XHCI_PCH_USB3PSSEN, usb3pssen ); - - /* Route USB2 ports from EHCI to xHCI */ - pci_read_config_dword ( pci, XHCI_PCH_XUSB2PR, &xusb2pr ); - pci_read_config_dword ( pci, XHCI_PCH_XUSB2PRM, &xusb2prm ); - if ( xusb2prm & ~xusb2pr ) { - DBGC ( xhci, "XHCI %s routing ports %08x from EHCI to xHCI\n", - xhci->name, ( xusb2prm & ~xusb2pr ) ); - } - pch->xusb2pr = xusb2pr; - xusb2pr |= xusb2prm; - pci_write_config_dword ( pci, XHCI_PCH_XUSB2PR, xusb2pr ); -} - -/** - * Undo Intel PCH-specific quirk fixes - * - * @v xhci xHCI device - * @v pci PCI device - */ -static void xhci_pch_undo ( struct xhci_device *xhci, struct pci_device *pci ) { - struct xhci_pch *pch = &xhci->pch; - - /* Restore USB2 port routing to original state */ - pci_write_config_dword ( pci, XHCI_PCH_XUSB2PR, pch->xusb2pr ); - - /* Restore SuperSpeed capability to original state */ - pci_write_config_dword ( pci, XHCI_PCH_USB3PSSEN, pch->usb3pssen ); -} - -/** - * Probe PCI device - * - * @v pci PCI device - * @ret rc Return status code - */ -static int xhci_probe ( struct pci_device *pci ) { - struct xhci_device *xhci; - struct usb_port *port; - unsigned long bar_start; - size_t bar_size; - unsigned int i; - int rc; - - /* Allocate and initialise structure */ - xhci = zalloc ( sizeof ( *xhci ) ); - if ( ! xhci ) { - rc = -ENOMEM; - goto err_alloc; - } - xhci->name = pci->dev.name; - xhci->quirks = pci->id->driver_data; - - /* Fix up PCI device */ - adjust_pci_device ( pci ); - - /* Map registers */ - bar_start = pci_bar_start ( pci, XHCI_BAR ); - bar_size = pci_bar_size ( pci, XHCI_BAR ); - xhci->regs = ioremap ( bar_start, bar_size ); - if ( ! xhci->regs ) { - rc = -ENODEV; - goto err_ioremap; - } - - /* Initialise xHCI device */ - xhci_init ( xhci, xhci->regs ); - - /* Initialise USB legacy support and claim ownership */ - xhci_legacy_init ( xhci ); - xhci_legacy_claim ( xhci ); - - /* Fix Intel PCH-specific quirks, if applicable */ - if ( xhci->quirks & XHCI_PCH ) - xhci_pch_fix ( xhci, pci ); - - /* Reset device */ - if ( ( rc = xhci_reset ( xhci ) ) != 0 ) - goto err_reset; - - /* Allocate USB bus */ - xhci->bus = alloc_usb_bus ( &pci->dev, xhci->ports, XHCI_MTU, - &xhci_operations ); - if ( ! xhci->bus ) { - rc = -ENOMEM; - goto err_alloc_bus; - } - usb_bus_set_hostdata ( xhci->bus, xhci ); - usb_hub_set_drvdata ( xhci->bus->hub, xhci ); - - /* Set port protocols */ - for ( i = 1 ; i <= xhci->ports ; i++ ) { - port = usb_port ( xhci->bus->hub, i ); - port->protocol = xhci_port_protocol ( xhci, i ); - } - - /* Register USB bus */ - if ( ( rc = register_usb_bus ( xhci->bus ) ) != 0 ) - goto err_register; - - pci_set_drvdata ( pci, xhci ); - return 0; - - unregister_usb_bus ( xhci->bus ); - err_register: - free_usb_bus ( xhci->bus ); - err_alloc_bus: - xhci_reset ( xhci ); - err_reset: - if ( xhci->quirks & XHCI_PCH ) - xhci_pch_undo ( xhci, pci ); - xhci_legacy_release ( xhci ); - iounmap ( xhci->regs ); - err_ioremap: - free ( xhci ); - err_alloc: - return rc; -} - -/** - * Remove PCI device - * - * @v pci PCI device - */ -static void xhci_remove ( struct pci_device *pci ) { - struct xhci_device *xhci = pci_get_drvdata ( pci ); - struct usb_bus *bus = xhci->bus; - - unregister_usb_bus ( bus ); - free_usb_bus ( bus ); - xhci_reset ( xhci ); - if ( xhci->quirks & XHCI_PCH ) - xhci_pch_undo ( xhci, pci ); - xhci_legacy_release ( xhci ); - iounmap ( xhci->regs ); - free ( xhci ); -} - -/** XHCI PCI device IDs */ -static struct pci_device_id xhci_ids[] = { - PCI_ROM ( 0x8086, 0x9d2f, "xhci-skylake", "xHCI (Skylake)", ( XHCI_PCH | XHCI_BAD_PSIV ) ), - PCI_ROM ( 0x8086, 0xffff, "xhci-pch", "xHCI (Intel PCH)", XHCI_PCH ), - PCI_ROM ( 0xffff, 0xffff, "xhci", "xHCI", 0 ), -}; - -/** XHCI PCI driver */ -struct pci_driver xhci_driver __pci_driver = { - .ids = xhci_ids, - .id_count = ( sizeof ( xhci_ids ) / sizeof ( xhci_ids[0] ) ), - .class = PCI_CLASS_ID ( PCI_CLASS_SERIAL, PCI_CLASS_SERIAL_USB, - PCI_CLASS_SERIAL_USB_XHCI ), - .probe = xhci_probe, - .remove = xhci_remove, -}; - -/** - * Prepare for exit - * - * @v booting System is shutting down for OS boot - */ -static void xhci_shutdown ( int booting ) { - /* If we are shutting down to boot an OS, then prevent the - * release of ownership back to BIOS. - */ - xhci_legacy_prevent_release = booting; -} - -/** Startup/shutdown function */ -struct startup_fn xhci_startup __startup_fn ( STARTUP_LATE ) = { - .shutdown = xhci_shutdown, -}; diff --git a/qemu/roms/ipxe/src/drivers/usb/xhci.h b/qemu/roms/ipxe/src/drivers/usb/xhci.h deleted file mode 100644 index 83bf71e7e..000000000 --- a/qemu/roms/ipxe/src/drivers/usb/xhci.h +++ /dev/null @@ -1,1150 +0,0 @@ -#ifndef _IPXE_XHCI_H -#define _IPXE_XHCI_H - -/** @file - * - * USB eXtensible Host Controller Interface (xHCI) driver - * - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <assert.h> -#include <ipxe/pci.h> -#include <ipxe/uaccess.h> -#include <ipxe/usb.h> - -/** Minimum alignment required for data structures - * - * With the exception of the scratchpad buffer pages (which are - * page-aligned), data structures used by xHCI generally require from - * 16 to 64 byte alignment and must not cross an (xHCI) page boundary. - * We simplify this requirement by aligning each structure on its own - * size, with a minimum of a 64 byte alignment. - */ -#define XHCI_MIN_ALIGN 64 - -/** Maximum transfer size */ -#define XHCI_MTU 65536 - -/** xHCI PCI BAR */ -#define XHCI_BAR PCI_BASE_ADDRESS_0 - -/** Capability register length */ -#define XHCI_CAP_CAPLENGTH 0x00 - -/** Host controller interface version number */ -#define XHCI_CAP_HCIVERSION 0x02 - -/** Structural parameters 1 */ -#define XHCI_CAP_HCSPARAMS1 0x04 - -/** Number of device slots */ -#define XHCI_HCSPARAMS1_SLOTS(params) ( ( (params) >> 0 ) & 0xff ) - -/** Number of interrupters */ -#define XHCI_HCSPARAMS1_INTRS(params) ( ( (params) >> 8 ) & 0x3ff ) - -/** Number of ports */ -#define XHCI_HCSPARAMS1_PORTS(params) ( ( (params) >> 24 ) & 0xff ) - -/** Structural parameters 2 */ -#define XHCI_CAP_HCSPARAMS2 0x08 - -/** Number of page-sized scratchpad buffers */ -#define XHCI_HCSPARAMS2_SCRATCHPADS(params) \ - ( ( ( (params) >> 16 ) & 0x3e0 ) | ( ( (params) >> 27 ) & 0x1f ) ) - -/** Capability parameters */ -#define XHCI_CAP_HCCPARAMS1 0x10 - -/** 64-bit addressing capability */ -#define XHCI_HCCPARAMS1_ADDR64(params) ( ( (params) >> 0 ) & 0x1 ) - -/** Context size shift */ -#define XHCI_HCCPARAMS1_CSZ_SHIFT(params) ( 5 + ( ( (params) >> 2 ) & 0x1 ) ) - -/** xHCI extended capabilities pointer */ -#define XHCI_HCCPARAMS1_XECP(params) ( ( ( (params) >> 16 ) & 0xffff ) << 2 ) - -/** Doorbell offset */ -#define XHCI_CAP_DBOFF 0x14 - -/** Runtime register space offset */ -#define XHCI_CAP_RTSOFF 0x18 - -/** xHCI extended capability ID */ -#define XHCI_XECP_ID(xecp) ( ( (xecp) >> 0 ) & 0xff ) - -/** Next xHCI extended capability pointer */ -#define XHCI_XECP_NEXT(xecp) ( ( ( (xecp) >> 8 ) & 0xff ) << 2 ) - -/** USB legacy support extended capability */ -#define XHCI_XECP_ID_LEGACY 1 - -/** USB legacy support BIOS owned semaphore */ -#define XHCI_USBLEGSUP_BIOS 0x02 - -/** USB legacy support BIOS ownership flag */ -#define XHCI_USBLEGSUP_BIOS_OWNED 0x01 - -/** USB legacy support OS owned semaphore */ -#define XHCI_USBLEGSUP_OS 0x03 - -/** USB legacy support OS ownership flag */ -#define XHCI_USBLEGSUP_OS_OWNED 0x01 - -/** USB legacy support control/status */ -#define XHCI_USBLEGSUP_CTLSTS 0x04 - -/** Supported protocol extended capability */ -#define XHCI_XECP_ID_SUPPORTED 2 - -/** Supported protocol revision */ -#define XHCI_SUPPORTED_REVISION 0x00 - -/** Supported protocol minor revision */ -#define XHCI_SUPPORTED_REVISION_VER(revision) ( ( (revision) >> 16 ) & 0xffff ) - -/** Supported protocol name */ -#define XHCI_SUPPORTED_NAME 0x04 - -/** Supported protocol ports */ -#define XHCI_SUPPORTED_PORTS 0x08 - -/** Supported protocol port offset */ -#define XHCI_SUPPORTED_PORTS_OFFSET(ports) ( ( (ports) >> 0 ) & 0xff ) - -/** Supported protocol port count */ -#define XHCI_SUPPORTED_PORTS_COUNT(ports) ( ( (ports) >> 8 ) & 0xff ) - -/** Supported protocol PSI count */ -#define XHCI_SUPPORTED_PORTS_PSIC(ports) ( ( (ports) >> 28 ) & 0x0f ) - -/** Supported protocol slot */ -#define XHCI_SUPPORTED_SLOT 0x0c - -/** Supported protocol slot type */ -#define XHCI_SUPPORTED_SLOT_TYPE(slot) ( ( (slot) >> 0 ) & 0x1f ) - -/** Supported protocol PSI */ -#define XHCI_SUPPORTED_PSI(index) ( 0x10 + ( (index) * 4 ) ) - -/** Supported protocol PSI value */ -#define XHCI_SUPPORTED_PSI_VALUE(psi) ( ( (psi) >> 0 ) & 0x0f ) - -/** Supported protocol PSI mantissa */ -#define XHCI_SUPPORTED_PSI_MANTISSA(psi) ( ( (psi) >> 16 ) & 0xffff ) - -/** Supported protocol PSI exponent */ -#define XHCI_SUPPORTED_PSI_EXPONENT(psi) ( ( (psi) >> 4 ) & 0x03 ) - -/** Default PSI values */ -enum xhci_default_psi_value { - /** Full speed (12Mbps) */ - XHCI_SPEED_FULL = 1, - /** Low speed (1.5Mbps) */ - XHCI_SPEED_LOW = 2, - /** High speed (480Mbps) */ - XHCI_SPEED_HIGH = 3, - /** Super speed */ - XHCI_SPEED_SUPER = 4, -}; - -/** USB command register */ -#define XHCI_OP_USBCMD 0x00 - -/** Run/stop */ -#define XHCI_USBCMD_RUN 0x00000001UL - -/** Host controller reset */ -#define XHCI_USBCMD_HCRST 0x00000002UL - -/** USB status register */ -#define XHCI_OP_USBSTS 0x04 - -/** Host controller halted */ -#define XHCI_USBSTS_HCH 0x00000001UL - -/** Page size register */ -#define XHCI_OP_PAGESIZE 0x08 - -/** Page size */ -#define XHCI_PAGESIZE(pagesize) ( (pagesize) << 12 ) - -/** Device notifcation control register */ -#define XHCI_OP_DNCTRL 0x14 - -/** Command ring control register */ -#define XHCI_OP_CRCR 0x18 - -/** Command ring cycle state */ -#define XHCI_CRCR_RCS 0x00000001UL - -/** Command abort */ -#define XHCI_CRCR_CA 0x00000004UL - -/** Command ring running */ -#define XHCI_CRCR_CRR 0x00000008UL - -/** Device context base address array pointer */ -#define XHCI_OP_DCBAAP 0x30 - -/** Configure register */ -#define XHCI_OP_CONFIG 0x38 - -/** Maximum device slots enabled */ -#define XHCI_CONFIG_MAX_SLOTS_EN(slots) ( (slots) << 0 ) - -/** Maximum device slots enabled mask */ -#define XHCI_CONFIG_MAX_SLOTS_EN_MASK \ - XHCI_CONFIG_MAX_SLOTS_EN ( 0xff ) - -/** Port status and control register */ -#define XHCI_OP_PORTSC(port) ( 0x400 - 0x10 + ( (port) << 4 ) ) - -/** Current connect status */ -#define XHCI_PORTSC_CCS 0x00000001UL - -/** Port enabled */ -#define XHCI_PORTSC_PED 0x00000002UL - -/** Port reset */ -#define XHCI_PORTSC_PR 0x00000010UL - -/** Port link state */ -#define XHCI_PORTSC_PLS(pls) ( (pls) << 5 ) - -/** Disabled port link state */ -#define XHCI_PORTSC_PLS_DISABLED XHCI_PORTSC_PLS ( 4 ) - -/** RxDetect port link state */ -#define XHCI_PORTSC_PLS_RXDETECT XHCI_PORTSC_PLS ( 5 ) - -/** Port link state mask */ -#define XHCI_PORTSC_PLS_MASK XHCI_PORTSC_PLS ( 0xf ) - -/** Port power */ -#define XHCI_PORTSC_PP 0x00000200UL - -/** Time to delay after enabling power to a port */ -#define XHCI_PORT_POWER_DELAY_MS 20 - -/** Port speed ID value */ -#define XHCI_PORTSC_PSIV(portsc) ( ( (portsc) >> 10 ) & 0xf ) - -/** Port indicator control */ -#define XHCI_PORTSC_PIC(indicators) ( (indicators) << 14 ) - -/** Port indicator control mask */ -#define XHCI_PORTSC_PIC_MASK XHCI_PORTSC_PIC ( 3 ) - -/** Port link state write strobe */ -#define XHCI_PORTSC_LWS 0x00010000UL - -/** Time to delay after writing the port link state */ -#define XHCI_LINK_STATE_DELAY_MS 20 - -/** Connect status change */ -#define XHCI_PORTSC_CSC 0x00020000UL - -/** Port enabled/disabled change */ -#define XHCI_PORTSC_PEC 0x00040000UL - -/** Warm port reset change */ -#define XHCI_PORTSC_WRC 0x00080000UL - -/** Over-current change */ -#define XHCI_PORTSC_OCC 0x00100000UL - -/** Port reset change */ -#define XHCI_PORTSC_PRC 0x00200000UL - -/** Port link state change */ -#define XHCI_PORTSC_PLC 0x00400000UL - -/** Port config error change */ -#define XHCI_PORTSC_CEC 0x00800000UL - -/** Port status change mask */ -#define XHCI_PORTSC_CHANGE \ - ( XHCI_PORTSC_CSC | XHCI_PORTSC_PEC | XHCI_PORTSC_WRC | \ - XHCI_PORTSC_OCC | XHCI_PORTSC_PRC | XHCI_PORTSC_PLC | \ - XHCI_PORTSC_CEC ) - -/** Port status and control bits which should be preserved - * - * The port status and control register is a horrendous mix of - * differing semantics. Some bits are written to only when a separate - * write strobe bit is set. Some bits should be preserved when - * modifying other bits. Some bits will be cleared if written back as - * a one. Most excitingly, the "port enabled" bit has the semantics - * that 1=enabled, 0=disabled, yet writing a 1 will disable the port. - */ -#define XHCI_PORTSC_PRESERVE ( XHCI_PORTSC_PP | XHCI_PORTSC_PIC_MASK ) - -/** Port power management status and control register */ -#define XHCI_OP_PORTPMSC(port) ( 0x404 - 0x10 + ( (port) << 4 ) ) - -/** Port link info register */ -#define XHCI_OP_PORTLI(port) ( 0x408 - 0x10 + ( (port) << 4 ) ) - -/** Port hardware link power management control register */ -#define XHCI_OP_PORTHLPMC(port) ( 0x40c - 0x10 + ( (port) << 4 ) ) - -/** Event ring segment table size register */ -#define XHCI_RUN_ERSTSZ(intr) ( 0x28 + ( (intr) << 5 ) ) - -/** Event ring segment table base address register */ -#define XHCI_RUN_ERSTBA(intr) ( 0x30 + ( (intr) << 5 ) ) - -/** Event ring dequeue pointer register */ -#define XHCI_RUN_ERDP(intr) ( 0x38 + ( (intr) << 5 ) ) - -/** A transfer request block template */ -struct xhci_trb_template { - /** Parameter */ - uint64_t parameter; - /** Status */ - uint32_t status; - /** Control */ - uint32_t control; -}; - -/** A transfer request block */ -struct xhci_trb_common { - /** Reserved */ - uint64_t reserved_a; - /** Reserved */ - uint32_t reserved_b; - /** Flags */ - uint8_t flags; - /** Type */ - uint8_t type; - /** Reserved */ - uint16_t reserved_c; -} __attribute__ (( packed )); - -/** Transfer request block cycle bit flag */ -#define XHCI_TRB_C 0x01 - -/** Transfer request block toggle cycle bit flag */ -#define XHCI_TRB_TC 0x02 - -/** Transfer request block chain flag */ -#define XHCI_TRB_CH 0x10 - -/** Transfer request block interrupt on completion flag */ -#define XHCI_TRB_IOC 0x20 - -/** Transfer request block immediate data flag */ -#define XHCI_TRB_IDT 0x40 - -/** Transfer request block type */ -#define XHCI_TRB_TYPE(type) ( (type) << 2 ) - -/** Transfer request block type mask */ -#define XHCI_TRB_TYPE_MASK XHCI_TRB_TYPE ( 0x3f ) - -/** A normal transfer request block */ -struct xhci_trb_normal { - /** Data buffer */ - uint64_t data; - /** Length */ - uint32_t len; - /** Flags */ - uint8_t flags; - /** Type */ - uint8_t type; - /** Reserved */ - uint16_t reserved; -} __attribute__ (( packed )); - -/** A normal transfer request block */ -#define XHCI_TRB_NORMAL XHCI_TRB_TYPE ( 1 ) - -/** Construct TD size field */ -#define XHCI_TD_SIZE(remaining) \ - ( ( ( (remaining) <= 0xf ) ? remaining : 0xf ) << 17 ) - -/** A setup stage transfer request block */ -struct xhci_trb_setup { - /** Setup packet */ - struct usb_setup_packet packet; - /** Length */ - uint32_t len; - /** Flags */ - uint8_t flags; - /** Type */ - uint8_t type; - /** Transfer direction */ - uint8_t direction; - /** Reserved */ - uint8_t reserved; -} __attribute__ (( packed )); - -/** A setup stage transfer request block */ -#define XHCI_TRB_SETUP XHCI_TRB_TYPE ( 2 ) - -/** Setup stage input data direction */ -#define XHCI_SETUP_IN 3 - -/** Setup stage output data direction */ -#define XHCI_SETUP_OUT 2 - -/** A data stage transfer request block */ -struct xhci_trb_data { - /** Data buffer */ - uint64_t data; - /** Length */ - uint32_t len; - /** Flags */ - uint8_t flags; - /** Type */ - uint8_t type; - /** Transfer direction */ - uint8_t direction; - /** Reserved */ - uint8_t reserved; -} __attribute__ (( packed )); - -/** A data stage transfer request block */ -#define XHCI_TRB_DATA XHCI_TRB_TYPE ( 3 ) - -/** Input data direction */ -#define XHCI_DATA_IN 0x01 - -/** Output data direction */ -#define XHCI_DATA_OUT 0x00 - -/** A status stage transfer request block */ -struct xhci_trb_status { - /** Reserved */ - uint64_t reserved_a; - /** Reserved */ - uint32_t reserved_b; - /** Flags */ - uint8_t flags; - /** Type */ - uint8_t type; - /** Direction */ - uint8_t direction; - /** Reserved */ - uint8_t reserved_c; -} __attribute__ (( packed )); - -/** A status stage transfer request block */ -#define XHCI_TRB_STATUS XHCI_TRB_TYPE ( 4 ) - -/** Input status direction */ -#define XHCI_STATUS_IN 0x01 - -/** Output status direction */ -#define XHCI_STATUS_OUT 0x00 - -/** A link transfer request block */ -struct xhci_trb_link { - /** Next ring segment */ - uint64_t next; - /** Reserved */ - uint32_t reserved_a; - /** Flags */ - uint8_t flags; - /** Type */ - uint8_t type; - /** Reserved */ - uint16_t reserved_c; -} __attribute__ (( packed )); - -/** A link transfer request block */ -#define XHCI_TRB_LINK XHCI_TRB_TYPE ( 6 ) - -/** A no-op transfer request block */ -#define XHCI_TRB_NOP XHCI_TRB_TYPE ( 8 ) - -/** An enable slot transfer request block */ -struct xhci_trb_enable_slot { - /** Reserved */ - uint64_t reserved_a; - /** Reserved */ - uint32_t reserved_b; - /** Flags */ - uint8_t flags; - /** Type */ - uint8_t type; - /** Slot type */ - uint8_t slot; - /** Reserved */ - uint8_t reserved_c; -} __attribute__ (( packed )); - -/** An enable slot transfer request block */ -#define XHCI_TRB_ENABLE_SLOT XHCI_TRB_TYPE ( 9 ) - -/** A disable slot transfer request block */ -struct xhci_trb_disable_slot { - /** Reserved */ - uint64_t reserved_a; - /** Reserved */ - uint32_t reserved_b; - /** Flags */ - uint8_t flags; - /** Type */ - uint8_t type; - /** Reserved */ - uint8_t reserved_c; - /** Slot ID */ - uint8_t slot; -} __attribute__ (( packed )); - -/** A disable slot transfer request block */ -#define XHCI_TRB_DISABLE_SLOT XHCI_TRB_TYPE ( 10 ) - -/** A context transfer request block */ -struct xhci_trb_context { - /** Input context */ - uint64_t input; - /** Reserved */ - uint32_t reserved_a; - /** Flags */ - uint8_t flags; - /** Type */ - uint8_t type; - /** Reserved */ - uint8_t reserved_b; - /** Slot ID */ - uint8_t slot; -} __attribute__ (( packed )); - -/** An address device transfer request block */ -#define XHCI_TRB_ADDRESS_DEVICE XHCI_TRB_TYPE ( 11 ) - -/** A configure endpoint transfer request block */ -#define XHCI_TRB_CONFIGURE_ENDPOINT XHCI_TRB_TYPE ( 12 ) - -/** An evaluate context transfer request block */ -#define XHCI_TRB_EVALUATE_CONTEXT XHCI_TRB_TYPE ( 13 ) - -/** A reset endpoint transfer request block */ -struct xhci_trb_reset_endpoint { - /** Reserved */ - uint64_t reserved_a; - /** Reserved */ - uint32_t reserved_b; - /** Flags */ - uint8_t flags; - /** Type */ - uint8_t type; - /** Endpoint ID */ - uint8_t endpoint; - /** Slot ID */ - uint8_t slot; -} __attribute__ (( packed )); - -/** A reset endpoint transfer request block */ -#define XHCI_TRB_RESET_ENDPOINT XHCI_TRB_TYPE ( 14 ) - -/** A stop endpoint transfer request block */ -struct xhci_trb_stop_endpoint { - /** Reserved */ - uint64_t reserved_a; - /** Reserved */ - uint32_t reserved_b; - /** Flags */ - uint8_t flags; - /** Type */ - uint8_t type; - /** Endpoint ID */ - uint8_t endpoint; - /** Slot ID */ - uint8_t slot; -} __attribute__ (( packed )); - -/** A stop endpoint transfer request block */ -#define XHCI_TRB_STOP_ENDPOINT XHCI_TRB_TYPE ( 15 ) - -/** A set transfer ring dequeue pointer transfer request block */ -struct xhci_trb_set_tr_dequeue_pointer { - /** Dequeue pointer */ - uint64_t dequeue; - /** Reserved */ - uint32_t reserved; - /** Flags */ - uint8_t flags; - /** Type */ - uint8_t type; - /** Endpoint ID */ - uint8_t endpoint; - /** Slot ID */ - uint8_t slot; -} __attribute__ (( packed )); - -/** A set transfer ring dequeue pointer transfer request block */ -#define XHCI_TRB_SET_TR_DEQUEUE_POINTER XHCI_TRB_TYPE ( 16 ) - -/** A no-op command transfer request block */ -#define XHCI_TRB_NOP_CMD XHCI_TRB_TYPE ( 23 ) - -/** A transfer event transfer request block */ -struct xhci_trb_transfer { - /** Transfer TRB pointer */ - uint64_t transfer; - /** Residual transfer length */ - uint16_t residual; - /** Reserved */ - uint8_t reserved; - /** Completion code */ - uint8_t code; - /** Flags */ - uint8_t flags; - /** Type */ - uint8_t type; - /** Endpoint ID */ - uint8_t endpoint; - /** Slot ID */ - uint8_t slot; -} __attribute__ (( packed )); - -/** A transfer event transfer request block */ -#define XHCI_TRB_TRANSFER XHCI_TRB_TYPE ( 32 ) - -/** A command completion event transfer request block */ -struct xhci_trb_complete { - /** Command TRB pointer */ - uint64_t command; - /** Parameter */ - uint8_t parameter[3]; - /** Completion code */ - uint8_t code; - /** Flags */ - uint8_t flags; - /** Type */ - uint8_t type; - /** Virtual function ID */ - uint8_t vf; - /** Slot ID */ - uint8_t slot; -} __attribute__ (( packed )); - -/** A command completion event transfer request block */ -#define XHCI_TRB_COMPLETE XHCI_TRB_TYPE ( 33 ) - -/** xHCI completion codes */ -enum xhci_completion_code { - /** Success */ - XHCI_CMPLT_SUCCESS = 1, - /** Short packet */ - XHCI_CMPLT_SHORT = 13, - /** Command ring stopped */ - XHCI_CMPLT_CMD_STOPPED = 24, -}; - -/** A port status change transfer request block */ -struct xhci_trb_port_status { - /** Reserved */ - uint8_t reserved_a[3]; - /** Port ID */ - uint8_t port; - /** Reserved */ - uint8_t reserved_b[7]; - /** Completion code */ - uint8_t code; - /** Flags */ - uint8_t flags; - /** Type */ - uint8_t type; - /** Reserved */ - uint16_t reserved_c; -} __attribute__ (( packed )); - -/** A port status change transfer request block */ -#define XHCI_TRB_PORT_STATUS XHCI_TRB_TYPE ( 34 ) - -/** A port status change transfer request block */ -struct xhci_trb_host_controller { - /** Reserved */ - uint64_t reserved_a; - /** Reserved */ - uint8_t reserved_b[3]; - /** Completion code */ - uint8_t code; - /** Flags */ - uint8_t flags; - /** Type */ - uint8_t type; - /** Reserved */ - uint16_t reserved_c; -} __attribute__ (( packed )); - -/** A port status change transfer request block */ -#define XHCI_TRB_HOST_CONTROLLER XHCI_TRB_TYPE ( 37 ) - -/** A transfer request block */ -union xhci_trb { - /** Template */ - struct xhci_trb_template template; - /** Common fields */ - struct xhci_trb_common common; - /** Normal TRB */ - struct xhci_trb_normal normal; - /** Setup stage TRB */ - struct xhci_trb_setup setup; - /** Data stage TRB */ - struct xhci_trb_data data; - /** Status stage TRB */ - struct xhci_trb_status status; - /** Link TRB */ - struct xhci_trb_link link; - /** Enable slot TRB */ - struct xhci_trb_enable_slot enable; - /** Disable slot TRB */ - struct xhci_trb_disable_slot disable; - /** Input context TRB */ - struct xhci_trb_context context; - /** Reset endpoint TRB */ - struct xhci_trb_reset_endpoint reset; - /** Stop endpoint TRB */ - struct xhci_trb_stop_endpoint stop; - /** Set transfer ring dequeue pointer TRB */ - struct xhci_trb_set_tr_dequeue_pointer dequeue; - /** Transfer event */ - struct xhci_trb_transfer transfer; - /** Command completion event */ - struct xhci_trb_complete complete; - /** Port status changed event */ - struct xhci_trb_port_status port; - /** Host controller event */ - struct xhci_trb_host_controller host; -} __attribute__ (( packed )); - -/** An input control context */ -struct xhci_control_context { - /** Drop context flags */ - uint32_t drop; - /** Add context flags */ - uint32_t add; - /** Reserved */ - uint32_t reserved_a[5]; - /** Configuration value */ - uint8_t config; - /** Interface number */ - uint8_t intf; - /** Alternate setting */ - uint8_t alt; - /** Reserved */ - uint8_t reserved_b; -} __attribute__ (( packed )); - -/** A slot context */ -struct xhci_slot_context { - /** Device info */ - uint32_t info; - /** Maximum exit latency */ - uint16_t latency; - /** Root hub port number */ - uint8_t port; - /** Number of downstream ports */ - uint8_t ports; - /** TT hub slot ID */ - uint8_t tt_id; - /** TT port number */ - uint8_t tt_port; - /** Interrupter target */ - uint16_t intr; - /** USB address */ - uint8_t address; - /** Reserved */ - uint16_t reserved_a; - /** Slot state */ - uint8_t state; - /** Reserved */ - uint32_t reserved_b[4]; -} __attribute__ (( packed )); - -/** Construct slot context device info */ -#define XHCI_SLOT_INFO( entries, hub, speed, route ) \ - ( ( (entries) << 27 ) | ( (hub) << 26 ) | ( (speed) << 20 ) | (route) ) - -/** An endpoint context */ -struct xhci_endpoint_context { - /** Endpoint state */ - uint8_t state; - /** Stream configuration */ - uint8_t stream; - /** Polling interval */ - uint8_t interval; - /** Max ESIT payload high */ - uint8_t esit_high; - /** Endpoint type */ - uint8_t type; - /** Maximum burst size */ - uint8_t burst; - /** Maximum packet size */ - uint16_t mtu; - /** Transfer ring dequeue pointer */ - uint64_t dequeue; - /** Average TRB length */ - uint16_t trb_len; - /** Max ESIT payload low */ - uint16_t esit_low; - /** Reserved */ - uint32_t reserved[3]; -} __attribute__ (( packed )); - -/** Endpoint states */ -enum xhci_endpoint_state { - /** Endpoint is disabled */ - XHCI_ENDPOINT_DISABLED = 0, - /** Endpoint is running */ - XHCI_ENDPOINT_RUNNING = 1, - /** Endpoint is halted due to a USB Halt condition */ - XHCI_ENDPOINT_HALTED = 2, - /** Endpoint is stopped */ - XHCI_ENDPOINT_STOPPED = 3, - /** Endpoint is halted due to a TRB error */ - XHCI_ENDPOINT_ERROR = 4, -}; - -/** Endpoint state mask */ -#define XHCI_ENDPOINT_STATE_MASK 0x07 - -/** Endpoint type */ -#define XHCI_EP_TYPE(type) ( (type) << 3 ) - -/** Control endpoint type */ -#define XHCI_EP_TYPE_CONTROL XHCI_EP_TYPE ( 4 ) - -/** Input endpoint type */ -#define XHCI_EP_TYPE_IN XHCI_EP_TYPE ( 4 ) - -/** Periodic endpoint type */ -#define XHCI_EP_TYPE_PERIODIC XHCI_EP_TYPE ( 1 ) - -/** Endpoint dequeue cycle state */ -#define XHCI_EP_DCS 0x00000001UL - -/** Control endpoint average TRB length */ -#define XHCI_EP0_TRB_LEN 8 - -/** An event ring segment */ -struct xhci_event_ring_segment { - /** Base address */ - uint64_t base; - /** Number of TRBs */ - uint32_t count; - /** Reserved */ - uint32_t reserved; -} __attribute__ (( packed )); - -/** A transfer request block command/transfer ring */ -struct xhci_trb_ring { - /** Producer counter */ - unsigned int prod; - /** Consumer counter */ - unsigned int cons; - /** Ring size (log2) */ - unsigned int shift; - /** Ring counter mask */ - unsigned int mask; - - /** I/O buffers */ - struct io_buffer **iobuf; - - /** Transfer request blocks */ - union xhci_trb *trb; - /** Length of transfer request blocks */ - size_t len; - /** Link TRB (if applicable) */ - struct xhci_trb_link *link; - - /** Doorbell register */ - void *db; - /** Doorbell register value */ - uint32_t dbval; -}; - -/** An event ring */ -struct xhci_event_ring { - /** Consumer counter */ - unsigned int cons; - /** Event ring segment table */ - struct xhci_event_ring_segment *segment; - /** Transfer request blocks */ - union xhci_trb *trb; -}; - -/** - * Calculate doorbell register value - * - * @v target Doorbell target - * @v stream Doorbell stream ID - * @ret dbval Doorbell register value - */ -#define XHCI_DBVAL( target, stream ) ( (target) | ( (stream) << 16 ) ) - -/** - * Calculate space used in TRB ring - * - * @v ring TRB ring - * @ret fill Number of entries used - */ -static inline __attribute__ (( always_inline )) unsigned int -xhci_ring_fill ( struct xhci_trb_ring *ring ) { - - return ( ring->prod - ring->cons ); -} - -/** - * Calculate space remaining in TRB ring - * - * @v ring TRB ring - * @ret remaining Number of entries remaining - * - * xHCI does not allow us to completely fill a ring; there must be at - * least one free entry (excluding the Link TRB). - */ -static inline __attribute__ (( always_inline )) unsigned int -xhci_ring_remaining ( struct xhci_trb_ring *ring ) { - unsigned int fill = xhci_ring_fill ( ring ); - - /* We choose to utilise rings with ( 2^n + 1 ) entries, with - * the final entry being a Link TRB. The maximum fill level - * is therefore - * - * ( ( 2^n + 1 ) - 1 (Link TRB) - 1 (one slot always empty) - * == ( 2^n - 1 ) - * - * which is therefore equal to the ring mask. - */ - assert ( fill <= ring->mask ); - return ( ring->mask - fill ); -} - -/** - * Calculate physical address of most recently consumed TRB - * - * @v ring TRB ring - * @ret trb TRB physical address - */ -static inline __attribute__ (( always_inline )) physaddr_t -xhci_ring_consumed ( struct xhci_trb_ring *ring ) { - unsigned int index = ( ( ring->cons - 1 ) & ring->mask ); - - return virt_to_phys ( &ring->trb[index] ); -} - -/** Slot context index */ -#define XHCI_CTX_SLOT 0 - -/** Calculate context index from USB endpoint address */ -#define XHCI_CTX(address) \ - ( (address) ? ( ( ( (address) & 0x0f ) << 1 ) | \ - ( ( (address) & 0x80 ) >> 7 ) ) : 1 ) - -/** Endpoint zero context index */ -#define XHCI_CTX_EP0 XHCI_CTX ( 0x00 ) - -/** End of contexts */ -#define XHCI_CTX_END 32 - -/** Device context index */ -#define XHCI_DCI(ctx) ( (ctx) + 0 ) - -/** Input context index */ -#define XHCI_ICI(ctx) ( (ctx) + 1 ) - -/** Number of TRBs (excluding Link TRB) in the command ring - * - * This is a policy decision. - */ -#define XHCI_CMD_TRBS_LOG2 2 - -/** Number of TRBs in the event ring - * - * This is a policy decision. - */ -#define XHCI_EVENT_TRBS_LOG2 6 - -/** Number of TRBs in a transfer ring - * - * This is a policy decision. - */ -#define XHCI_TRANSFER_TRBS_LOG2 6 - -/** Maximum time to wait for BIOS to release ownership - * - * This is a policy decision. - */ -#define XHCI_USBLEGSUP_MAX_WAIT_MS 100 - -/** Maximum time to wait for host controller to stop - * - * This is a policy decision. - */ -#define XHCI_STOP_MAX_WAIT_MS 100 - -/** Maximum time to wait for reset to complete - * - * This is a policy decision. - */ -#define XHCI_RESET_MAX_WAIT_MS 500 - -/** Maximum time to wait for a command to complete - * - * The "address device" command involves waiting for a response to a - * USB control transaction, and so we must wait for up to the 5000ms - * that USB allows for devices to respond to control transactions. - */ -#define XHCI_COMMAND_MAX_WAIT_MS USB_CONTROL_MAX_WAIT_MS - -/** Time to delay after aborting a command - * - * This is a policy decision - */ -#define XHCI_COMMAND_ABORT_DELAY_MS 500 - -/** Maximum time to wait for a port reset to complete - * - * This is a policy decision. - */ -#define XHCI_PORT_RESET_MAX_WAIT_MS 500 - -/** Intel PCH quirk */ -struct xhci_pch { - /** USB2 port routing register original value */ - uint32_t xusb2pr; - /** USB3 port SuperSpeed enable register original value */ - uint32_t usb3pssen; -}; - -/** Intel PCH quirk flag */ -#define XHCI_PCH 0x0001 - -/** Intel PCH USB2 port routing register */ -#define XHCI_PCH_XUSB2PR 0xd0 - -/** Intel PCH USB2 port routing mask register */ -#define XHCI_PCH_XUSB2PRM 0xd4 - -/** Intel PCH SuperSpeed enable register */ -#define XHCI_PCH_USB3PSSEN 0xd8 - -/** Intel PCH USB3 port routing mask register */ -#define XHCI_PCH_USB3PRM 0xdc - -/** Invalid protocol speed ID values quirk */ -#define XHCI_BAD_PSIV 0x0002 - -/** An xHCI device */ -struct xhci_device { - /** Registers */ - void *regs; - /** Name */ - const char *name; - /** Quirks */ - unsigned int quirks; - - /** Capability registers */ - void *cap; - /** Operational registers */ - void *op; - /** Runtime registers */ - void *run; - /** Doorbell registers */ - void *db; - - /** Number of device slots */ - unsigned int slots; - /** Number of interrupters */ - unsigned int intrs; - /** Number of ports */ - unsigned int ports; - - /** Number of page-sized scratchpad buffers */ - unsigned int scratchpads; - - /** 64-bit addressing capability */ - int addr64; - /** Context size shift */ - unsigned int csz_shift; - /** xHCI extended capabilities offset */ - unsigned int xecp; - - /** Page size */ - size_t pagesize; - - /** USB legacy support capability (if present and enabled) */ - unsigned int legacy; - - /** Device context base address array */ - uint64_t *dcbaa; - - /** Scratchpad buffer area */ - userptr_t scratchpad; - /** Scratchpad buffer array */ - uint64_t *scratchpad_array; - - /** Command ring */ - struct xhci_trb_ring command; - /** Event ring */ - struct xhci_event_ring event; - /** Current command (if any) */ - union xhci_trb *pending; - - /** Device slots, indexed by slot ID */ - struct xhci_slot **slot; - - /** USB bus */ - struct usb_bus *bus; - - /** Intel PCH quirk */ - struct xhci_pch pch; -}; - -/** An xHCI device slot */ -struct xhci_slot { - /** xHCI device */ - struct xhci_device *xhci; - /** USB device */ - struct usb_device *usb; - /** Slot ID */ - unsigned int id; - /** Slot context */ - struct xhci_slot_context *context; - /** Route string */ - unsigned int route; - /** Root hub port number */ - unsigned int port; - /** Protocol speed ID */ - unsigned int psiv; - /** Number of ports (if this device is a hub) */ - unsigned int ports; - /** Transaction translator slot ID */ - unsigned int tt_id; - /** Transaction translator port */ - unsigned int tt_port; - /** Endpoints, indexed by context ID */ - struct xhci_endpoint *endpoint[XHCI_CTX_END]; -}; - -/** An xHCI endpoint */ -struct xhci_endpoint { - /** xHCI device */ - struct xhci_device *xhci; - /** xHCI slot */ - struct xhci_slot *slot; - /** USB endpoint */ - struct usb_endpoint *ep; - /** Context index */ - unsigned int ctx; - /** Endpoint type */ - unsigned int type; - /** Endpoint interval */ - unsigned int interval; - /** Endpoint context */ - struct xhci_endpoint_context *context; - /** Transfer ring */ - struct xhci_trb_ring ring; -}; - -#endif /* _IPXE_XHCI_H */ |