iPXE
Functions | Variables
uhci.c File Reference

USB Universal Host Controller Interface (UHCI) driver. More...

#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"

Go to the source code of this file.

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static int uhci_reachable (void *addr, size_t len)
 Check that address is reachable.
static void uhci_run (struct uhci_device *uhci)
 Start UHCI device.
static int uhci_stop (struct uhci_device *uhci)
 Stop UHCI device.
static int uhci_reset (struct uhci_device *uhci)
 Reset UHCI device.
static int uhci_ring_alloc (struct uhci_ring *ring)
 Allocate transfer ring.
static void uhci_ring_free (struct uhci_ring *ring)
 Free transfer ring.
static int uhci_enqueue (struct uhci_ring *ring, struct io_buffer *iobuf, unsigned int count)
 Enqueue new transfer.
static void uhci_describe (struct uhci_ring *ring, void *data, size_t len, uint8_t pid)
 Describe transfer.
static struct io_bufferuhci_dequeue (struct uhci_ring *ring)
 Dequeue transfer.
static void uhci_restart (struct uhci_ring *ring, uint32_t toggle)
 Restart ring.
static uint32_t uhci_link_qh (struct uhci_queue_head *queue)
 Get link value for a queue head.
static void uhci_async_schedule (struct uhci_device *uhci)
 (Re)build asynchronous schedule
static void uhci_async_add (struct uhci_endpoint *endpoint)
 Add endpoint to asynchronous schedule.
static void uhci_async_del (struct uhci_endpoint *endpoint)
 Remove endpoint from asynchronous schedule.
static void uhci_periodic_schedule (struct uhci_device *uhci)
 (Re)build periodic schedule
static void uhci_periodic_add (struct uhci_endpoint *endpoint)
 Add endpoint to periodic schedule.
static void uhci_periodic_del (struct uhci_endpoint *endpoint)
 Remove endpoint from periodic schedule.
static void uhci_schedule_add (struct uhci_endpoint *endpoint)
 Add endpoint to appropriate schedule.
static void uhci_schedule_del (struct uhci_endpoint *endpoint)
 Remove endpoint from appropriate schedule.
static int uhci_endpoint_open (struct usb_endpoint *ep)
 Open endpoint.
static void uhci_endpoint_close (struct usb_endpoint *ep)
 Close endpoint.
static int uhci_endpoint_reset (struct usb_endpoint *ep)
 Reset endpoint.
static int uhci_endpoint_mtu (struct usb_endpoint *ep)
 Update MTU.
static int uhci_endpoint_message (struct usb_endpoint *ep, struct io_buffer *iobuf)
 Enqueue message transfer.
static int uhci_endpoint_stream (struct usb_endpoint *ep, struct io_buffer *iobuf, int zlp)
 Enqueue stream transfer.
static int uhci_is_message (struct uhci_transfer *xfer)
 Check if transfer is a message transfer.
static void uhci_endpoint_poll (struct uhci_endpoint *endpoint)
 Poll for completions.
static int uhci_device_open (struct usb_device *usb)
 Open device.
static void uhci_device_close (struct usb_device *usb)
 Close device.
static int uhci_device_address (struct usb_device *usb)
 Assign device address.
static int uhci_hub_open (struct usb_hub *hub __unused)
 Open hub.
static void uhci_hub_close (struct usb_hub *hub __unused)
 Close hub.
static int uhci_root_open (struct usb_hub *hub)
 Open root hub.
static void uhci_root_close (struct usb_hub *hub)
 Close root hub.
static int uhci_root_enable (struct usb_hub *hub, struct usb_port *port)
 Enable port.
static int uhci_root_disable (struct usb_hub *hub, struct usb_port *port)
 Disable port.
static int uhci_root_speed (struct usb_hub *hub, struct usb_port *port)
 Update root hub port speed.
static int uhci_root_clear_tt (struct usb_hub *hub, struct usb_port *port, struct usb_endpoint *ep)
 Clear transaction translator buffer.
static void uhci_root_poll (struct usb_hub *hub, struct usb_port *port)
 Poll for port status changes.
static int uhci_bus_open (struct usb_bus *bus)
 Open USB bus.
static void uhci_bus_close (struct usb_bus *bus)
 Close USB bus.
static void uhci_bus_poll (struct usb_bus *bus)
 Poll USB bus.
__weak unsigned int ehci_companion (struct pci_device *pci __unused)
 Locate EHCI companion controller (when no EHCI support is present)
static int uhci_probe (struct pci_device *pci)
 Probe PCI device.
static void uhci_remove (struct pci_device *pci)
 Remove PCI device.

Variables

static struct usb_host_operations uhci_operations
 USB host controller operations.
static struct pci_device_id uhci_ids []
 UHCI PCI device IDs.
struct pci_driver uhci_driver __pci_driver
 UHCI PCI driver.

Detailed Description

USB Universal Host Controller Interface (UHCI) driver.

Definition in file uhci.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static int uhci_reachable ( void *  addr,
size_t  len 
) [inline, static]

Check that address is reachable.

Parameters:
addrAddress
lenLength
Return values:
rcReturn status code

Definition at line 57 of file uhci.c.

References ENOTSUP, phys, and virt_to_phys().

Referenced by uhci_bus_open(), uhci_enqueue(), and uhci_ring_alloc().

                                          {
        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;
}
static void uhci_run ( struct uhci_device uhci) [static]

Start UHCI device.

Parameters:
uhciUHCI device

Definition at line 83 of file uhci.c.

References inw(), outw(), uhci_device::regs, UHCI_USBCMD, UHCI_USBCMD_MAX64, and UHCI_USBCMD_RUN.

Referenced by uhci_bus_open().

                                                  {
        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 );
}
static int uhci_stop ( struct uhci_device uhci) [static]

Stop UHCI device.

Parameters:
uhciUHCI device
Return values:
rcReturn status code

Definition at line 98 of file uhci.c.

References DBGC, ETIMEDOUT, inw(), mdelay(), uhci_device::name, outw(), uhci_device::regs, UHCI_STOP_MAX_WAIT_MS, UHCI_USBCMD, UHCI_USBCMD_RUN, UHCI_USBSTS, and UHCI_USBSTS_HCHALTED.

Referenced by uhci_bus_close(), uhci_bus_open(), and uhci_reset().

                                                  {
        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;
}
static int uhci_reset ( struct uhci_device uhci) [static]

Reset UHCI device.

Parameters:
uhciUHCI device
Return values:
rcReturn status code

Definition at line 130 of file uhci.c.

References DBGC, ETIMEDOUT, inw(), mdelay(), uhci_device::name, outw(), rc, uhci_device::regs, UHCI_RESET_MAX_WAIT_MS, uhci_stop(), UHCI_USBCMD, and UHCI_USBCMD_HCRESET.

Referenced by uhci_probe(), and uhci_remove().

                                                   {
        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;
}
static int uhci_ring_alloc ( struct uhci_ring ring) [static]

Allocate transfer ring.

Parameters:
ringTransfer ring
Return values:
rcReturn status code

Definition at line 175 of file uhci.c.

References cpu_to_le32, uhci_queue_head::current, ENOMEM, free_dma(), uhci_ring::head, malloc_dma(), memset(), rc, UHCI_ALIGN, UHCI_LINK_TERMINATE, and uhci_reachable().

Referenced by uhci_endpoint_open().

                                                      {
        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;
}
static void uhci_ring_free ( struct uhci_ring ring) [static]

Free transfer ring.

Parameters:
ringTransfer ring

Definition at line 207 of file uhci.c.

References assert, free_dma(), uhci_ring::head, NULL, UHCI_RING_COUNT, uhci_ring_fill(), and uhci_ring::xfer.

Referenced by uhci_endpoint_close(), and uhci_endpoint_open().

                                                      {
        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 ) );
}
static int uhci_enqueue ( struct uhci_ring ring,
struct io_buffer iobuf,
unsigned int  count 
) [static]

Enqueue new transfer.

Parameters:
ringTransfer ring
iobufI/O buffer
countNumber of descriptors
Return values:
rcReturn status code

Definition at line 227 of file uhci.c.

References assert, uhci_transfer::cons, count, cpu_to_le32, uhci_queue_head::current, io_buffer::data, uhci_transfer::desc, end, uhci_ring::end, ENOBUFS, ENOMEM, uhci_transfer_descriptor::flags, uhci_ring::flags, free, free_dma(), uhci_ring::head, index, iob_len(), uhci_transfer::iobuf, uhci_transfer::len, len, link, uhci_transfer_descriptor::link, malloc(), malloc_dma(), memset(), NULL, uhci_transfer::prod, uhci_ring::prod, rc, UHCI_ALIGN, UHCI_FL_IOC, UHCI_LINK_DEPTH_FIRST, UHCI_LINK_TERMINATE, uhci_reachable(), UHCI_RING_COUNT, uhci_ring_fill(), uhci_ring_remaining(), virt_to_phys(), wmb, and uhci_ring::xfer.

Referenced by uhci_endpoint_message(), and uhci_endpoint_stream().

                                               {
        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;
}
static void uhci_describe ( struct uhci_ring ring,
void *  data,
size_t  len,
uint8_t  pid 
) [static]

Describe transfer.

Parameters:
ringTransfer ring
dataData
lenLength of data
pidPacket ID

Definition at line 319 of file uhci.c.

References control, uhci_transfer_descriptor::control, uhci_ring::control, cpu_to_le32, uhci_transfer_descriptor::data, uhci_transfer::desc, uhci_ring::end, uhci_transfer_descriptor::flags, len, uhci_ring::mtu, uhci_transfer::prod, uhci_transfer_descriptor::status, UHCI_CONTROL_LEN, UHCI_CONTROL_PID, UHCI_CONTROL_TOGGLE, UHCI_FL_SPD, UHCI_STATUS_ACTIVE, USB_PID_IN, virt_to_phys(), and wmb.

Referenced by uhci_endpoint_message(), and uhci_endpoint_stream().

                                                      {
        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 );
}
static struct io_buffer* uhci_dequeue ( struct uhci_ring ring) [static, read]

Dequeue transfer.

Parameters:
ringTransfer ring
Return values:
iobufI/O buffer

Definition at line 360 of file uhci.c.

References assert, uhci_ring::cons, uhci_transfer::desc, free, free_dma(), index, uhci_transfer::iobuf, len, NULL, uhci_transfer::prod, UHCI_RING_COUNT, uhci_ring_fill(), and uhci_ring::xfer.

Referenced by uhci_endpoint_close(), and uhci_endpoint_poll().

                                                                  {
        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;
}
static void uhci_restart ( struct uhci_ring ring,
uint32_t  toggle 
) [static]

Restart ring.

Parameters:
ringTransfer ring
toggleExpected data toggle for next descriptor

Definition at line 394 of file uhci.c.

References assert, uhci_transfer::cons, uhci_ring::cons, uhci_transfer_descriptor::control, uhci_ring::control, cpu_to_le32, uhci_queue_head::current, uhci_transfer::desc, first, uhci_ring::head, le32_to_cpu, link, NULL, uhci_transfer::prod, uhci_ring::prod, UHCI_CONTROL_TOGGLE, UHCI_LINK_TERMINATE, UHCI_RING_COUNT, uhci_ring_fill(), virt_to_phys(), wmb, and uhci_ring::xfer.

Referenced by uhci_endpoint_poll(), and uhci_endpoint_reset().

                                                                     {
        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 );
}
static uint32_t uhci_link_qh ( struct uhci_queue_head queue) [inline, static]

Get link value for a queue head.

Parameters:
queueQueue head
Return values:
linkLink value

Definition at line 458 of file uhci.c.

References UHCI_LINK_TYPE_QH, and virt_to_phys().

Referenced by uhci_async_schedule(), and uhci_periodic_schedule().

                                                                      {

        return ( virt_to_phys ( queue ) | UHCI_LINK_TYPE_QH );
}
static void uhci_async_schedule ( struct uhci_device uhci) [static]

(Re)build asynchronous schedule

Parameters:
uhciUHCI device

Definition at line 468 of file uhci.c.

References uhci_device::async, cpu_to_le32, end, uhci_ring::head, uhci_device::head, link, uhci_queue_head::link, list_for_each_entry_reverse, queue, uhci_endpoint::ring, uhci_link_qh(), UHCI_LINK_TERMINATE, and wmb.

Referenced by uhci_async_add(), uhci_async_del(), and uhci_bus_open().

                                                             {
        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();
}
static void uhci_async_add ( struct uhci_endpoint endpoint) [static]

Add endpoint to asynchronous schedule.

Parameters:
endpointEndpoint

Definition at line 497 of file uhci.c.

References uhci_device::async, list_add_tail, uhci_endpoint::schedule, uhci_endpoint::uhci, and uhci_async_schedule().

Referenced by uhci_schedule_add().

                                                              {
        struct uhci_device *uhci = endpoint->uhci;

        /* Add to end of schedule */
        list_add_tail ( &endpoint->schedule, &uhci->async );

        /* Rebuild schedule */
        uhci_async_schedule ( uhci );
}
static void uhci_async_del ( struct uhci_endpoint endpoint) [static]

Remove endpoint from asynchronous schedule.

Parameters:
endpointEndpoint

Definition at line 512 of file uhci.c.

References uhci_device::async, list_check_contains_entry, list_del, mdelay(), uhci_endpoint::schedule, uhci_endpoint::uhci, and uhci_async_schedule().

Referenced by uhci_schedule_del().

                                                              {
        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 );
}
static void uhci_periodic_schedule ( struct uhci_device uhci) [static]

(Re)build periodic schedule

Parameters:
uhciUHCI device

Definition at line 531 of file uhci.c.

References cpu_to_le32, DBGCP, end, uhci_endpoint::ep, ffs, uhci_device::frame, uhci_ring::head, uhci_device::head, usb_endpoint::interval, uhci_frame_list::link, link, uhci_queue_head::link, list_for_each_entry, list_for_each_entry_reverse, uhci_device::name, uhci_device::periodic, queue, uhci_endpoint::ring, UHCI_FRAMES, uhci_link_qh(), and wmb.

Referenced by uhci_bus_open(), uhci_periodic_add(), and uhci_periodic_del().

                                                                {
        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" );
}
static void uhci_periodic_add ( struct uhci_endpoint endpoint) [static]

Add endpoint to periodic schedule.

Parameters:
endpointEndpoint

Definition at line 599 of file uhci.c.

References uhci_endpoint::ep, usb_endpoint::interval, list_add_tail, list_for_each_entry, uhci_device::periodic, uhci_endpoint::schedule, uhci_endpoint::uhci, and uhci_periodic_schedule().

Referenced by uhci_schedule_add().

                                                                 {
        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 );
}
static void uhci_periodic_del ( struct uhci_endpoint endpoint) [static]

Remove endpoint from periodic schedule.

Parameters:
endpointEndpoint

Definition at line 620 of file uhci.c.

References list_check_contains_entry, list_del, mdelay(), uhci_device::periodic, uhci_endpoint::schedule, uhci_endpoint::uhci, and uhci_periodic_schedule().

Referenced by uhci_schedule_del().

                                                                 {
        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 );
}
static void uhci_schedule_add ( struct uhci_endpoint endpoint) [static]

Add endpoint to appropriate schedule.

Parameters:
endpointEndpoint

Definition at line 639 of file uhci.c.

References attr, usb_endpoint::attributes, uhci_endpoint::ep, uhci_async_add(), uhci_periodic_add(), USB_ENDPOINT_ATTR_INTERRUPT, and USB_ENDPOINT_ATTR_TYPE_MASK.

Referenced by uhci_endpoint_open().

                                                                 {
        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 );
        }
}
static void uhci_schedule_del ( struct uhci_endpoint endpoint) [static]

Remove endpoint from appropriate schedule.

Parameters:
endpointEndpoint

Definition at line 655 of file uhci.c.

References attr, usb_endpoint::attributes, uhci_endpoint::ep, uhci_async_del(), uhci_periodic_del(), USB_ENDPOINT_ATTR_INTERRUPT, and USB_ENDPOINT_ATTR_TYPE_MASK.

Referenced by uhci_endpoint_close().

                                                                 {
        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 );
        }
}
static int uhci_endpoint_open ( struct usb_endpoint ep) [static]

Open endpoint.

Parameters:
epUSB endpoint
Return values:
rcReturn status code

Definition at line 679 of file uhci.c.

References usb_endpoint::address, usb_device::address, uhci_ring::control, uhci_device::endpoints, ENOMEM, uhci_endpoint::ep, uhci_ring::flags, free, uhci_endpoint::list, list_add_tail, uhci_ring::mtu, usb_endpoint::mtu, rc, uhci_endpoint::ring, usb_device::speed, uhci_endpoint::uhci, UHCI_CONTROL_DEVICE, UHCI_CONTROL_ENDPOINT, UHCI_FL_CERR_MAX, UHCI_FL_LS, uhci_ring_alloc(), uhci_ring_free(), uhci_schedule_add(), usb_endpoint::usb, usb_endpoint_set_hostdata(), usb_get_hostdata(), USB_SPEED_FULL, and zalloc().

                                                          {
        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->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;
}
static void uhci_endpoint_close ( struct usb_endpoint ep) [static]

Close endpoint.

Parameters:
epUSB endpoint

Definition at line 725 of file uhci.c.

References ECANCELED, free, uhci_endpoint::list, list_del, uhci_endpoint::ring, uhci_dequeue(), uhci_ring_fill(), uhci_ring_free(), uhci_schedule_del(), usb_complete_err(), and usb_endpoint_get_hostdata().

                                                            {
        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 );
}
static int uhci_endpoint_reset ( struct usb_endpoint ep) [static]

Reset endpoint.

Parameters:
epUSB endpoint
Return values:
rcReturn status code

Definition at line 755 of file uhci.c.

References uhci_endpoint::ring, uhci_restart(), and usb_endpoint_get_hostdata().

                                                           {
        struct uhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep );
        struct uhci_ring *ring = &endpoint->ring;

        /* Restart ring */
        uhci_restart ( ring, 0 );

        return 0;
}
static int uhci_endpoint_mtu ( struct usb_endpoint ep) [static]

Update MTU.

Parameters:
epUSB endpoint
Return values:
rcReturn status code

Definition at line 771 of file uhci.c.

References uhci_ring::mtu, usb_endpoint::mtu, uhci_endpoint::ring, and usb_endpoint_get_hostdata().

                                                         {
        struct uhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep );

        /* Update endpoint MTU */
        endpoint->ring.mtu = ep->mtu;

        return 0;
}
static int uhci_endpoint_message ( struct usb_endpoint ep,
struct io_buffer iobuf 
) [static]

Enqueue message transfer.

Parameters:
epUSB endpoint
iobufI/O buffer
Return values:
rcReturn status code

Definition at line 787 of file uhci.c.

References assert, uhci_ring::control, count, cpu_to_le16, io_buffer::data, uhci_ring::end, iob_len(), iob_pull, len, uhci_ring::mtu, NULL, uhci_transfer::prod, rc, usb_setup_packet::request, uhci_endpoint::ring, UHCI_CONTROL_TOGGLE, uhci_describe(), uhci_enqueue(), USB_DIR_IN, usb_endpoint_get_hostdata(), USB_PID_IN, USB_PID_OUT, and USB_PID_SETUP.

                                                             {
        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;
}
static int uhci_endpoint_stream ( struct usb_endpoint ep,
struct io_buffer iobuf,
int  zlp 
) [static]

Enqueue stream transfer.

Parameters:
epUSB endpoint
iobufI/O buffer
zlpAppend a zero-length packet
Return values:
rcReturn status code

Definition at line 841 of file uhci.c.

References usb_endpoint::address, assert, count, io_buffer::data, uhci_ring::end, iob_len(), len, uhci_ring::mtu, NULL, uhci_transfer::prod, rc, uhci_endpoint::ring, uhci_describe(), uhci_enqueue(), USB_DIR_IN, usb_endpoint_get_hostdata(), USB_PID_IN, and USB_PID_OUT.

                                                                     {
        struct uhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep );
        struct uhci_ring *ring = &endpoint->ring;
        unsigned int count;
        size_t len;
        int input;
        int rc;

        /* Calculate number of descriptors */
        len = iob_len ( iobuf );
        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;
}
static int uhci_is_message ( struct uhci_transfer xfer) [inline, static]

Check if transfer is a message transfer.

Parameters:
xferUHCI transfer
Return values:
is_messageTransfer is a message transfer

Definition at line 879 of file uhci.c.

References uhci_transfer_descriptor::control, cpu_to_le32, uhci_transfer::desc, UHCI_CONTROL_PID, UHCI_CONTROL_PID_MASK, and USB_PID_SETUP.

Referenced by uhci_endpoint_poll().

                                                                 {
        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 ) ) );
}
static void uhci_endpoint_poll ( struct uhci_endpoint endpoint) [static]

Poll for completions.

Parameters:
endpointEndpoint

Definition at line 891 of file uhci.c.

References uhci_transfer_descriptor::actual, assert, uhci_transfer::cons, uhci_ring::cons, control, uhci_transfer_descriptor::control, cpu_to_le32, uhci_queue_head::current, DBGC, uhci_transfer::desc, EIO, uhci_endpoint::ep, uhci_transfer_descriptor::flags, uhci_ring::head, index, iob_len(), iob_unput, le16_to_cpu, le32_to_cpu, uhci_transfer::len, len, link, usb_device::name, NULL, uhci_transfer::prod, uhci_endpoint::ring, rmb, uhci_transfer_descriptor::status, uhci_endpoint::uhci, UHCI_ACTUAL_LEN, UHCI_ALIGN, UHCI_CONTROL_TOGGLE, UHCI_DATA_PACKET, uhci_dequeue(), UHCI_FL_SPD, uhci_is_message(), UHCI_LINK_TERMINATE, uhci_restart(), UHCI_RING_COUNT, uhci_ring_fill(), UHCI_SHORT_PACKET, UHCI_STATUS_ACTIVE, UHCI_STATUS_STALLED, usb_endpoint::usb, usb_complete(), usb_complete_err(), usb_endpoint_name(), virt_to_phys(), wmb, and uhci_ring::xfer.

Referenced by uhci_bus_poll().

                                                                  {
        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 );
        }
}
static int uhci_device_open ( struct usb_device usb) [static]

Open device.

Parameters:
usbUSB device
Return values:
rcReturn status code

Definition at line 1021 of file uhci.c.

References usb_hub::bus, usb_port::hub, usb_device::port, usb_bus_get_hostdata(), and usb_set_hostdata().

                                                       {
        struct uhci_device *uhci = usb_bus_get_hostdata ( usb->port->hub->bus );

        usb_set_hostdata ( usb, uhci );
        return 0;
}
static void uhci_device_close ( struct usb_device usb) [static]

Close device.

Parameters:
usbUSB device

Definition at line 1033 of file uhci.c.

References usb_device::address, bus, uhci_device::bus, usb_free_address(), and usb_get_hostdata().

                                                         {
        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 );
}
static int uhci_device_address ( struct usb_device usb) [static]

Assign device address.

Parameters:
usbUSB device
Return values:
rcReturn status code

Definition at line 1048 of file uhci.c.

References address, usb_device::address, assert, bus, uhci_device::bus, uhci_ring::control, DBGC, usb_device::name, NULL, rc, uhci_endpoint::ring, strerror(), UHCI_CONTROL_DEVICE, usb_alloc_address(), usb_endpoint(), usb_endpoint_get_hostdata(), USB_EP0_ADDRESS, usb_free_address(), usb_get_hostdata(), and usb_set_address().

                                                          {
        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;
}
static int uhci_hub_open ( struct usb_hub *hub  __unused) [static]

Open hub.

Parameters:
hubUSB hub
Return values:
rcReturn status code

Definition at line 1098 of file uhci.c.

                                                          {

        /* Nothing to do */
        return 0;
}
static void uhci_hub_close ( struct usb_hub *hub  __unused) [static]

Close hub.

Parameters:
hubUSB hub

Definition at line 1109 of file uhci.c.

                                                            {

        /* Nothing to do */
}
static int uhci_root_open ( struct usb_hub hub) [static]

Open root hub.

Parameters:
hubUSB hub
Return values:
rcReturn status code

Definition at line 1127 of file uhci.c.

References bus, usb_hub::bus, usb_bus_get_hostdata(), and usb_hub_set_drvdata().

                                                  {
        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;
}
static void uhci_root_close ( struct usb_hub hub) [static]

Close root hub.

Parameters:
hubUSB hub

Definition at line 1142 of file uhci.c.

References NULL, and usb_hub_set_drvdata().

                                                    {

        /* Clear hub driver private data */
        usb_hub_set_drvdata ( hub, NULL );
}
static int uhci_root_enable ( struct usb_hub hub,
struct usb_port port 
) [static]

Enable port.

Parameters:
hubUSB hub
portUSB port
Return values:
rcReturn status code

Definition at line 1155 of file uhci.c.

References usb_port::address, DBGC, ETIMEDOUT, inw(), mdelay(), uhci_device::name, outw(), uhci_device::regs, UHCI_PORT_ENABLE_MAX_WAIT_MS, UHCI_PORTSC, UHCI_PORTSC_PED, UHCI_PORTSC_PR, usb_hub_get_drvdata(), USB_RESET_DELAY_MS, and USB_RESET_RECOVER_DELAY_MS.

                                                                           {
        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;
}
static int uhci_root_disable ( struct usb_hub hub,
struct usb_port port 
) [static]

Disable port.

Parameters:
hubUSB hub
portUSB port
Return values:
rcReturn status code

Definition at line 1198 of file uhci.c.

References usb_port::address, inw(), outw(), uhci_device::regs, UHCI_PORTSC, UHCI_PORTSC_PED, and usb_hub_get_drvdata().

                                                                            {
        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;
}
static int uhci_root_speed ( struct usb_hub hub,
struct usb_port port 
) [static]

Update root hub port speed.

Parameters:
hubUSB hub
portUSB port
Return values:
rcReturn status code

Definition at line 1217 of file uhci.c.

References usb_port::address, BUS_TYPE_PCI, uhci_device::companion, DBGC, usb_port::disconnected, find_usb_bus_by_location(), inw(), uhci_device::name, outw(), PCI_ARGS, PCI_FMT, pci_init(), uhci_device::regs, speed, usb_port::speed, UHCI_PORTSC, UHCI_PORTSC_CCS, UHCI_PORTSC_CSC, UHCI_PORTSC_LS, usb_hub_get_drvdata(), USB_SPEED_FULL, USB_SPEED_LOW, and USB_SPEED_NONE.

                                                                          {
        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;
}
static int uhci_root_clear_tt ( struct usb_hub hub,
struct usb_port port,
struct usb_endpoint ep 
) [static]

Clear transaction translator buffer.

Parameters:
hubUSB hub
portUSB port
epUSB endpoint
Return values:
rcReturn status code

Definition at line 1262 of file uhci.c.

References usb_port::address, DBGC, ENOTSUP, uhci_device::name, usb_device::name, usb_endpoint::usb, usb_endpoint_name(), and usb_hub_get_drvdata().

                                                          {
        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;
}
static void uhci_root_poll ( struct usb_hub hub,
struct usb_port port 
) [static]

Poll for port status changes.

Parameters:
hubUSB hub
portUSB port

Definition at line 1279 of file uhci.c.

References usb_port::address, usb_port::disconnected, inw(), outw(), uhci_device::regs, UHCI_PORTSC, UHCI_PORTSC_CHANGE, UHCI_PORTSC_CSC, usb_hub_get_drvdata(), and usb_port_changed().

Referenced by uhci_bus_poll().

                                                                          {
        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 );
}
static int uhci_bus_open ( struct usb_bus bus) [static]

Open USB bus.

Parameters:
busUSB bus
Return values:
rcReturn status code

Definition at line 1311 of file uhci.c.

References assert, uhci_device::async, cpu_to_le32, uhci_queue_head::current, ENOMEM, uhci_device::frame, free_dma(), uhci_device::head, list_empty, malloc_dma(), memset(), outl(), uhci_device::periodic, rc, uhci_device::regs, UHCI_ALIGN, uhci_async_schedule(), UHCI_FLBASEADD, UHCI_LINK_TERMINATE, uhci_periodic_schedule(), uhci_reachable(), uhci_run(), uhci_stop(), usb_bus_get_hostdata(), and virt_to_phys().

                                                 {
        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;
}
static void uhci_bus_close ( struct usb_bus bus) [static]

Close USB bus.

Parameters:
busUSB bus

Definition at line 1364 of file uhci.c.

References assert, uhci_device::async, uhci_device::frame, free_dma(), uhci_device::head, list_empty, uhci_device::periodic, uhci_stop(), and usb_bus_get_hostdata().

                                                   {
        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 ) );
}
static void uhci_bus_poll ( struct usb_bus bus) [static]

Poll USB bus.

Parameters:
busUSB bus

Definition at line 1386 of file uhci.c.

References uhci_device::endpoints, usb_bus::hub, uhci_endpoint::list, list_for_each_entry, uhci_endpoint_poll(), UHCI_PORTS, uhci_root_poll(), and usb_bus_get_hostdata().

                                                  {
        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 ) );
}
__weak unsigned int ehci_companion ( struct pci_device *pci  __unused)

Locate EHCI companion controller (when no EHCI support is present)

Parameters:
pciPCI device
Return values:
busdevfnEHCI companion controller bus:dev.fn (if any)

Definition at line 1461 of file uhci.c.

                                                                       {
        return 0;
}
static int uhci_probe ( struct pci_device pci) [static]

Probe PCI device.

Parameters:
pciPCI device
Return values:
rcReturn status code

Definition at line 1471 of file uhci.c.

References adjust_pci_device(), alloc_usb_bus(), uhci_device::async, uhci_device::bus, uhci_device::companion, pci_device::dev, ehci_companion(), uhci_device::endpoints, ENODEV, ENOMEM, free, free_usb_bus(), usb_bus::hub, INIT_LIST_HEAD, pci_device::ioaddr, device::name, uhci_device::name, pci_set_drvdata(), pci_write_config_word(), uhci_device::periodic, port, usb_port::protocol, rc, register_usb_bus(), uhci_device::regs, UHCI_MTU, UHCI_PORTS, uhci_reset(), UHCI_USBLEGSUP, UHCI_USBLEGSUP_DEFAULT, unregister_usb_bus(), usb_bus_set_hostdata(), usb_hub_set_drvdata(), usb_port(), USB_PROTO_2_0, and zalloc().

                                                 {
        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;
}
static void uhci_remove ( struct pci_device pci) [static]

Remove PCI device.

Parameters:
pciPCI device

Definition at line 1550 of file uhci.c.

References assert, uhci_device::async, bus, uhci_device::bus, free, free_usb_bus(), list_empty, pci_get_drvdata(), uhci_device::periodic, uhci_reset(), and unregister_usb_bus().

                                                   {
        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 );
}

Variable Documentation

USB host controller operations.

Definition at line 1422 of file uhci.c.

struct pci_device_id uhci_ids[] [static]
Initial value:
 {
        PCI_ROM ( 0xffff, 0xffff, "uhci", "UHCI", 0 ),
}

UHCI PCI device IDs.

Definition at line 1563 of file uhci.c.

struct pci_driver uhci_driver __pci_driver
Initial value:
 {
        .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,
}

UHCI PCI driver.

Definition at line 1568 of file uhci.c.