usbhcduhcilib.c

来自「cpc-1631的BSP包for VxWorks操作系统」· C语言 代码 · 共 2,337 行 · 第 1/5 页

C
2,337
字号
/* usbHcdUhciLib.c - Defines entry point for UHCI HCD */

/* Copyright 2000-2001 Wind River Systems, Inc. */

/*
Modification history
--------------------
01m,22oct01,wef  bug in fncIrpSubmit() - was incorrectly flushing buffer list
                 instead of the actual list of buffers
01l,11oct01,wef  fix SPR's 70492, 69922 and 70716
01o,18sep01,wef  merge from wrs.tor2_0.usb1_1-f for veloce
01n,23jul01,wef  Fixed SPR #68202 and SPR #68209
01m,05jan01,wef  Fixed alignment problem w/ code.  General clean up
01l,12apr00,wef  fixed a comment that was messing up the man page generation 
01k,20mar00,rcb  Flush all cache buffers during assignTds() to avoid 
		 cache-line boundary problems with some CPU architectures
		 (e.g., MIPS).
01j,26jan00,rcb  Change references to "bytesPerFrame" to "bandwidth" in
		 pipe creation logic.
		 Modify isoch. TD creation to calculate each TD length in 
		 order to maintain average bandwidth.
01i,29nov99,rcb  Remove obsolete function HCD_FNC_BUS_RESET.
		 Correct code in rootFeature() to set port suspend status to
		 FALSE when issuing resume to a port.
01h,23nov99,rcb  Replace bandwidth alloc/release functions with pipe
		 create/destroy functions...generalizes approach for use
		 with OHCI HCD.
01g,11nov99,rcb  In destroyHost() cancel outstanding IRPs before disabling
		 host controller...prevents deadlock in hcSync() called from
		 cancelIrp().
01f,26sep99,rcb  Add code to synchronize with host controller after list
		 changes.  Add use of VOLATILE for QH/TD structures.
		 Add code to modify driver behavior based on specific
		 vendor/device combinations.
01e,24sep99,rcb  Remove extraneous end-of-loop test in unscheduleBulkIrp().
01d,21sep99,rcb  Change memory allocation strategy to use cacheDmaMalloc()
		.enables code to use CACHE_DMA_xxxx functions instead of
		 CACHE_USER_xxxx functions, yielding improved performance.
01c,07sep99,rcb  Add support for management callbacks and for set-bus-state.
01b,02sep99,rcb  Fix bug in delinking last bulk QH from work list.
01a,09jun99,rcb  First.
*/

/*
DESCRIPTION

This is the HCD (host controller driver) for UHCI.  This file implements
low-level functions required by the client (typically the USBD) to talk to
the underlying USB host controller hardware.  

The <param> to the HRB_ATTACH request should be a pointer to a 
PCI_CFG_HEADER which contains the PCI configuration header for the
universal host controller to be managed.  Each invocation of the HRB_ATTACH
function will return an HCD_CLIENT_HANDLE for which a single host controller
will be exposed.

NOTE: This HCD implementation assumes that the caller has already initialized
the osServices and handleFuncs libraries by calling ossInitialize() and
usbHandleInitialize(), respectively.  The USBD implementation guarantees that
these libraries have been initialized prior to invoking the HCD.

Regarding IRP callbacks...

There are two callback function pointers in each IRP, <usbdCallback> and
<userCallback>.  By convention, if a non-NULL <usbdCallback> is supplied,
then the HCD invokes only the <usbdCallback> upon IRP completion - and it is
the USBD's responsibility to invoke the <userCallback>.  If no <usbdCallback>
is provided, then the HCD invokes the <userCallback> directly.	Typically, 
all IRPs are delivered to the HCD through the USBD and a non-NULL <usbdCallback>
will in fact be provided.

Regarding UHCI frame lists...

We use the UHCI frame list in a way anticipated by, but not directly described
by the UHCI specfication.  As anticipated, we create an array of 1024 frame
list entries.  We also create an array of 1024 "interrupt anchor" QHs, one
"control anchor" QH and one "bulk anchor" QH.  Each frame list entry is 
initialized to point to a corresponding interrupt anchor.  Each interrupt anchor
points to the single control anchor, and the control anchor initially points to
the bulk anchor.  

When one or more interrupt transfers are pending, QHs for these transfers will
be inserted after the interrupt anchor QHs corresponding to the frames dictated 
by the interrupt scheduling interval.  The last pending interrupt QH in each
list points to the common control anchor QH.  While clients can request any
interrupt service interval they like, the algorithms here always choose an
interval which is the largest power of 2 less than or equal to the client's
desired interval.  For example, if a client requests an interval of 20msec, the
HCD will select a real interval of 16msec.  In each frame work list, the least
frequently scheduled QHs appear ahead of more frequently scheduled QHs.  Since
only a single QH is actually created for each interrupt transfer, the individual
frame lists actually "merge" at each interrupt QH.  Now, using the preceding
example of 16msec, suppose that there is a second interrupt QH with an interval
of 8 msec.  In half of the frame lists for which the 8msec interval transfer is
scheduled, the "interrupt anchor" QH will point to it directly.  In the other
half, the 16msec interval transfer will point to it.  

When control transfer QHs are scheduled, they are always placed in the list 
following the "control anchor" QH.  Similarly, bulk transfer QHs are always
placed after the "bulk anchor" QH.  When low speed control transfers are
pending, they are always inserted after the "control anchor" QH before any
high-speed control transfers.

The anchors themselves never describe work.  Instead, they are just
placeholders in the work lists to facilitate clean QH and TD list updates.
For example, when queuing a new control or bulk transfer, it is only necessary
to modify the work list after the single "control" or "bulk" anchor.  Similiarly,
when queuing interrupt transfers, it is only necessary to modify the work
list after the QH anchor in which the interrupt is to be scheduled.  Finally,
isochronous transfers can be added cleanly at the beginning of each frame's
work list; and the last isoch transfer TD in each frame always points to the
QH anchor corresponding to that frame.

In effect, this scheme decouples isoch, interrupt, control, and bulk transfers
in the TD/QH work lists.

Regarding bus time calculations...

The host controller driver is responsible for ensuring that certain kinds of
scheduled transfers never exceed the time available in a USB frame.  The HCD 
and the USBD work cooperatively to ensure this.  For its part, the USBD never 
allows isochronous and interrupt transfers to be scheduled which would exceed 
90% of the bus bandwidth.  However, the USBD will freely allow control and bulk 
pipes to be created, as these types of transfers take whatever bus time is left 
over after isochronous and interrupt transfers have been schedule - and the
HCD gives priority to control transfers.

The HCD keeps a running total of the worst case amount of bus time alloted to 
active isochronous and interrupt transfers.  As for control and bulk transfers,
the UHC theoretically allows us to schedule as many of them as we desire, and it
keeps track of how much time remains in each frame, executing only as many of
these transfers as will fit.  However, the UHC requires that the driver schedule
only as many low speed control transfers (as opposed to full speed control
transfers) as can actually fit within the frame.  Therefore, after taking into
account the time already allotted to isochronous and interrupt transfers, the
HCD only schedules as many low speed control transfers as can fit within the
current frame - and full speed control and bulk transfers follow. 
*/


/* includes */

#include "usb/usbPlatform.h"

#include "string.h"

#include "memLib.h"		/* memory sub-allocation functions */
#include "cacheLib.h"		/* cache functions */
#include "semLib.h"		/* semaphore functions */


#include "usb/ossLib.h"
#include "usb/usbHandleLib.h"
#include "usb/pciConstants.h"
#include "usb/usbPciLib.h"

#include "usb/usbLib.h"
#include "drv/usb/usbHcd.h"
#include "drv/usb/usbUhci.h"
#include "drv/usb/usbHcdUhciLib.h"


/* defines */

#define PENDING 	1

#define HCD_HOST_SIG	    ((UINT32) 0x00cd0000)
#define HCD_PIPE_SIG	    ((UINT32) 0x00cd0001)


#define MAX_INT_DEPTH	    8	/* max depth of pending interrupts */
#define INT_TIMEOUT	5000	/* wait 5 seconds for int routine to exit */
#define BIT_TIMEOUT	1000	/* max time to wait for a bit change */

#define UHC_ROOT_SRVC_INTERVAL	256 /* milliseconds */


/* UHC_HOST_DELAY and UHC_HUB_LS_SETUP are host-controller specific.
 * The following values are estimates for the UHCI controller.
 */

#define UHC_HOST_DELAY	    ((UINT32) 500L) /* 500 ns, est. */
#define UHC_HUB_LS_SETUP    ((UINT32) 500L) /* 500 ns, est. */


/*
 * MEMORY
 * 
 * To improve performance, a single block of (probably) "non-cached"
 * memory is allocated.  Then, all UHCI control structures are sub-allocated
 * from this block as needed.  The vxWorks CACHE_DMA_FLUSH/INVALIDATE macros
 * are used to ensure that this memory is flushed/invalidated at the correct
 * times (assuming that cacheable-memory *might* be allocated).
 */

#define DMA_MEMORY_SIZE 	0x10000 /* 64k */

#define DMA_MALLOC(bytes, alignment)	\
    memPartAlignedAlloc (pHost->memPartId, bytes, alignment)

#define DMA_FREE(pBfr)		memPartFree (pHost->memPartId, (char *) pBfr)

#define DMA_FLUSH(pBfr, bytes)	    CACHE_DMA_FLUSH (pBfr, bytes)
#define DMA_INVALIDATE(pBfr, bytes) CACHE_DMA_INVALIDATE (pBfr, bytes)

#define USER_FLUSH(pBfr, bytes)     CACHE_USER_FLUSH (pBfr, bytes)
#define USER_INVALIDATE(pBfr,bytes) CACHE_USER_INVALIDATE (pBfr, bytes)


/*
 * PLAN_AHEAD_TIME
 *
 * There is always going to be some latency betwen the time the UHC finishes
 * a portion of the frame list and before the HCD updates the frame list with
 * additional work.  It is not acceptable for certain transfers, like isoch.
 * transfers, to be interrupted by this latency.  Therefore, the UHCI HCD is
 * designed to plan ahead by the PLAN_AHEAD_TIME.  It is assumed that the
 * max interrupt latency will be *one half* of this time, and the HCD will
 * schedule itself accordingly.
 */

#define PLAN_AHEAD_TIME     125     /* plan UHCI TD list n msec in advance */
#define ISOCH_INT_INTERVAL  16	    /* generate isoch int every n msec */

#define MAX_IRP_TDS	32	/* overriding maximum number of TDs */
		    /* allowed for a single non-isoch IRP */

#define UHC_END_OF_LIST     TO_LITTLEL (UHCI_LINK_TERMINATE)


/* UHC I/O access macros.
 *
 * NOTE: These macros assume that the calling function defines pHost.
 */
#define PCI_IO_OFFSET		0xfe000000


#define UHC_BYTE_IN(p)	    uhciByteRead (pHost->ioBase + (p))
#define UHC_WORD_IN(p)	    uhciWordRead (/*pHost->ioBase*/0xa000 + (p))
#define UHC_DWORD_IN(p)     USB_PCI_DWORD_IN (pHost->ioBase + (p))

#define UHC_BYTE_OUT(p,b)   USB_PCI_BYTE_OUT (pHost->ioBase + (p), (b))
#define UHC_WORD_OUT(p,w)   USB_PCI_WORD_OUT (/*pHost->ioBase*/0xa000 + (p), (w))
#define UHC_DWORD_OUT(p,d)  USB_PCI_DWORD_OUT (pHost->ioBase + (p), (d))

/*#define usb_set_bit(p,bits)	UHC_WORD_OUT (p, UHC_WORD_IN (p) | (bits))
#define usb_clr_bit(p,bits)	UHC_WORD_OUT (p, UHC_WORD_IN (p) & ~(bits))*/
#define usb_set_bit(p,bits)	usbPciWordOut((pHost->ioBase+p),usbPciWordIn(pHost->ioBase+p)|bits)

#define usb_clr_bit(p,bits)	 usbPciWordOut((pHost->ioBase+p),usbPciWordIn(pHost->ioBase+p)&(~bits))

/* UHC run state flags to setUhcRunState() function */

#define UHC_RUN     1
#define UHC_STOP    0


/*
 * The HCD adds UHC_FRAME_SKIP to the current frame counter to determine
 * the first available frame for scheduling. This introduces a latency at
 * the beginning of each IRP, but also helps to ensure that the UHC won't
 * run ahead of the HCD while the HCD is scheduling a transaction.
 */

#define UHC_FRAME_SKIP		2


/* macro to produce frame list index from frame number */

#define FINDEX(f)	((f) & (UHCI_FRAME_LIST_ENTRIES - 1))


/* vendor/model identifiers */

#define PCI_VID_INTEL	    0x8086
#define PCI_VID_VIATECH     0x1106

#define PCI_DID_INTEL_PIIX3 0x7020
#define PCI_DID_INTEL_PIIX4 0x7112
#define PCI_DID_VIATECH_82C586	0x3038	    /* same for 83C572 */


/* UHC capabilities (vendor/model specific */

#define UHC_ATTR_BW_RECLAMATION 0x0001
#define UHC_ATTR_HC_SYNCH   0x0002


/* UHC_ATTR_DEFAULT is the combination of UHC attributes used for unrecognized
 * UHCI implementations.  It is generally the most conservative.
 */

#define UHC_ATTR_DEFAULT    UHC_ATTR_HC_SYNCH


/* Macros to interpret UHC capabilities. */

#define ENABLE_BANDWIDTH_RECLAMATION(pHost) \
    (((pHost->uhcAttributes & UHC_ATTR_BW_RECLAMATION) != 0) ? TRUE : FALSE)

#define ENABLE_HC_SYNCH(pHost)	\
    (((pHost->uhcAttributes & UHC_ATTR_HC_SYNCH) != 0) ? TRUE : FALSE)

#define HC_SYNCH(pHost) { if (ENABLE_HC_SYNCH (pHost)) hcSynch (pHost); }


/* defines for emulated USB descriptors */

#define USB_RELEASE	0x0110	/* USB level supported by this code */

#define UHC_MAX_PACKET_SIZE	8
    
#define UHC_CONFIG_VALUE	1

#define UHC_STATUS_ENDPOINT_ADRS    (1 | USB_ENDPOINT_IN)


/* interrupt bits */

#define UHC_INT_ENABLE_MASK (UHCI_INTR_SHORT | UHCI_INTR_COMPLETE | \
			     UHCI_INTR_RESUME | UHCI_INTR_TIME_CRC)

#define UHC_INT_PENDING_MASK	(UHCI_STS_PROCERR | UHCI_STS_HOSTERR | \
				 UHCI_STS_RESUME | UHCI_STS_USBERR | \
				 UHCI_STS_USBINT)


/* string identifiers */

#define UNICODE_ENGLISH     0x409

#define UHC_STR_MFG	1
#define UHC_STR_MFG_VAL     "Wind River Systems"

#define UHC_STR_PROD	    2
#define UHC_STR_PROD_VAL    "UHCI Root Hub"


/* PCI pointer macros */

#define TO_PCIPTR(p)	    TO_LITTLEL (USB_MEM_TO_PCI (p))
#define QH_TO_PCIPTR(p)     (TO_PCIPTR (p) | TO_LITTLEL (UHCI_LINK_QH))

#define FROM_PCIPTR(d)	    USB_PCI_TO_MEM (d)

#define QH_FROM_PCIPTR(d)   \
    ((pQH_WRAPPER) (FROM_PCIPTR (FROM_LITTLEL (d) & UHCI_LINK_PTR_MASK)))

#define TD_FROM_PCIPTR(d)   \
    ((pTD_WRAPPER) (FROM_PCIPTR (FROM_LITTLEL (d) & UHCI_LINK_PTR_MASK)))


/* typedefs */

/* UHC_TABLE
 *
 * Differences have been observed among UHCI implementations (including bugs
 * in some of those implementations) which favor a table-driven approach to
 * enabling certain features/operational modes.
 */

typedef struct uhc_table
    {
    UINT16 pciVendorId;     /* PCI vendor ID */
    UINT16 pciDeviceId;     /* vendor-assigned device ID */
    UINT16 uhcAttributes;   /* attributes */
    } UHC_TABLE, *pUHC_TABLE;


/*
 * TD_WRAPPER
 *
 * UHCI defines TD as 32 bytes long, with the last 16 bytes being reserved
 * for software use.  Each UHCI_TD must be aligned to a 16 byte boundary.
 */

typedef union td_wrapper
    {
    VOLATILE UHCI_TD td;	/* standard UHCI TD, 16 bytes */
    struct
	{
        VOLATILE UINT32 reserved [4]; /* 4 DWORDs used by TD */
        struct irp_workspace *pWork;/* pointer to IRP workspace */
        union td_wrapper *pNext;	/* next TD_WRAPPER used by the IRP */
        UINT32 nanoseconds;     /* calculated time for this TD to execute */
        UINT32 frameNo;	    /* used only for isoch. TDs */
	} sw;
    } TD_WRAPPER, *pTD_WRAPPER;

#define TD_WRAPPER_LEN	    32
#define TD_WRAPPER_ACTLEN   sizeof (TD_WRAPPER)


/*
 * QH_WRAPPER
 *
 * UHCI defines QH as 8 bytes long.  This wrapper adds fields at the end
 * for software use, padding the QH_WRAPPER to 16 bytes in the process.  
 * Each UHCI_QH must be aligned to a 16 byte boundary.
 */

typedef union qh_wrapper
    {
    VOLATILE UHCI_QH qh;	/* standard UHCI QH, 8 bytes */
        struct
        {
        VOLATILE UINT32 reserved [2]; /* 2 DWORDs used by QH */
        UINT32 reserved1;	    /* 1 DWORD pad */
        struct irp_workspace *pWork;/* workspace which owns QH */
        } sw;
    } QH_WRAPPER, *pQH_WRAPPER;

#define QH_WRAPPER_LEN	    16
#define QH_WRAPPER_ACTLEN   sizeof (QH_WRAPPER)

#define INT_ANCHOR_LIST_SIZE	(QH_WRAPPER_ACTLEN * UHCI_FRAME_LIST_ENTRIES)


/*
 * HCD_PIPE
 *
 * HCD_PIPE maintains all information about an active pipe.
 */

typedef struct hcd_pipe
    {
    HCD_PIPE_HANDLE pipeHandle;     /* handle assigned to pipe */

    LINK link;		    /* linked list of pipes */

    UINT16 busAddress;		/* bus address of USB device */
    UINT16 endpoint;		/* device endpoint */
    UINT16 transferType;	/* transfer type */
    UINT16 direction;		/* transfer/pipe direction */
    UINT16 speed;	    /* transfer speed */
    UINT16 maxPacketSize;	/* packet size */
    UINT32 bandwidth;		/* bandwidth required by pipe */
    UINT16 interval;		/* service interval */

    UINT32 time;	    /* bandwidth (time) allocated to pipe */

    } HCD_PIPE, *pHCD_PIPE;


/*
 * IRP_WORKSPACE
 *
 * Associates QHs and TDs with the IRPs they are currently servicing.
 *
 * Note: The pTdInUse and pTdFree lists use the TD_WRAPPER.sw.pNext field to 
 * maintain lists of TDs.  When TDs are scheduled, their UHCI_TD.linkPtr
 * fields are also used to link the TDs in the correct order.  The reader
 * must be aware that the UHCI_TD.linkPtr fields are formatted for direct
 * access by the UHCI PCI controller, and may not be dereferenced directly
 * by software.  Instead, the TD_FROM_PCIPTR() macro may be used to 
 * dereference the UHCI_TD.linkPtr field if necessary.	There is no such
 * restriction for the TD_WRAPPER.sw.pNext field.
 *
 * Note also that the linkPtr field uses the UHC convention that the end 
 * of a TD list is marked by a pointer with only the UHC_LINK_TERMINATE bit 
 * set (unlike the more conventional use of NULL to mark end of list).
 */

typedef struct irp_workspace
    {
    pHCD_PIPE pPipe;		/* pointer to pipe for this IRP */
    pUSB_IRP pIrp;	    /* pointer to parent IRP */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?