?? usbhcdsl811hslib.c
字號:
/* usbHcdSl811hsLib.c - Defines entry point for ScanLogic SL811Hs HCD */
/* Copyright 2000 Wind River Systems, Inc. */
/*
Modification history
--------------------
01a,16nov01,dgj Initial version, based on version 01n of usbHcdUhciLib.c
*/
/*
DESCRIPTION
This is the HCD (host controller driver) for the ScanLogic SL811HS USB controller.
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"
#include "usbSl811hs.h"
#include "config.h"
#include "sysLib.h"
#include "intLib.h"
#include "stdio.h"
#include "logLib.h"
/* defines */
#undef LOCAL
#define LOCAL
/* #define SL811HS_DEBUG */
/* #define SL811HS_LOG */
#ifdef SL811HS_DEBUG
char sl811hDebug = 1;
/*#define DBG_PRINTF(p1,p2,p3,p4) if (sl811hDebug) printf(p1,p2,p3,p4)*/
#define DBG_PRINTF(X) if (sl811hDebug) printf X;
#else
#define DBG_PRINTF(X)
#endif /* SL811HS_DEBUG */
#ifdef SL811HS_LOG
char sl811hLog = 1;
#define DBG_LOG(f,a1,a2,a3,a4,a5,a6) if (sl811hLog) logMsg(f,a1,a2,a3,a4,a5,a6)
#else
#define DBG_LOG(f,a1,a2,a3,a4,a5,a6)
#endif /* SL811HS_LOG */
#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) 120000L) /* ns, est. */
#define UHC_HUB_LS_SETUP ((UINT32) 500L) /* 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)
#if 0
#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)
#else
#define DMA_FLUSH(pBfr, bytes) CACHE_DMA_FLUSH (pBfr, bytes);EIEIO_SYNC
#define DMA_INVALIDATE(pBfr, bytes) CACHE_DMA_INVALIDATE (pBfr, bytes);EIEIO_SYNC
#define USER_FLUSH(pBfr, bytes)
#define USER_INVALIDATE(pBfr,bytes)
#endif
/*
* 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 */
#ifdef INCLUDE_USB_PCI
#define UHC_END_OF_LIST TO_LITTLEL (UHCI_LINK_TERMINATE)
#else
#define UHC_END_OF_LIST UHCI_LINK_TERMINATE
#endif
/*
* Default macro definitions for BSP interface.
* These macros can be redefined in a wrapper file, to generate
* a new module with an optimized interface.
*/
#ifndef UHC_INT_CONNECT
#define UHC_INT_CONNECT(pHost,routine,arg,pResult) \
{ \
*pResult = intConnect ((VOIDFUNCPTR *)INUM_TO_IVEC (pHost->sl811CfgHdr.intVec), \
routine, (int)arg); \
}
#endif /*UHC_INT_CONNECT*/
#ifndef UHC_INT_DISCONNECT
LOCAL VOID uhcDummyISR (void) { };
#define UHC_INT_DISCONNECT(pHost,routine,arg,pResult) \
{ \
*pResult = intConnect ((VOIDFUNCPTR *)INUM_TO_IVEC (pHost->sl811CfgHdr.intVec), \
uhcDummyISR, (int)arg); \
}
#endif /*UHC_INT_DISCONNECT*/
#ifndef UHC_INT_ENABLE
#define UHC_INT_ENABLE(pHost,pResult) \
{ \
*pResult = intEnable (pHost->sl811CfgHdr.intLvl); \
}
#endif /* UHC_INT_ENABLE*/
/* Macro to disable the appropriate interrupt level */
#ifndef UHC_INT_DISABLE
#define UHC_INT_DISABLE(pHost,pResult) \
{ \
*pResult = intDisable (pHost->sl811CfgHdr.intLvl); \
}
#endif
/* SL811HS I/O access macros.
*
* NOTE: These macros assume that the calling function defines pHost.
*/
#define SL811_REG_WRITE(a,d) (sysOutByte(pHost->ioBase, a), \
sysOutByte(pHost->ioBaseData, d))
#define SL811_REG_READ(a) (sysOutByte(pHost->ioBase, a), \
sysInByte(pHost->ioBaseData))
#define SL811_BUF_WRITE(a,d,l) {\
sysOutByte(pHost->ioBase, a);\
while (l--) sysOutByte(pHost->ioBaseData, *d++);\
}
#define SL811_BUF_READ(a,d,l) {\
sysOutByte(pHost->ioBase, a);\
while (l--) *d++ = sysInByte(pHost->ioBaseData);\
}
#ifdef INCLUDE_USB_PCI
#define UHC_WORD_IN(p) USB_PCI_WORD_IN (pHost->ioBase + (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 + (p), (w))
/*#define UHC_DWORD_OUT(p,d) USB_PCI_DWORD_OUT (pHost->ioBase + (p), (d))*/
#define UHC_SET_BITS(p,bits) UHC_WORD_OUT (p, UHC_WORD_IN (p) | (bits))
#define UHC_CLR_BITS(p,bits) UHC_WORD_OUT (p, UHC_WORD_IN (p) & ~(bits))
#endif
/* 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) FALSE
/*(((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 "SL811HS Root Hub"
/* PCI pointer macros */
#ifdef INCLUDE_USB_PCI
#define UHC_IF_INIT()
#define TO_PCIPTR(p) TO_LITTLEL (USB_MEM_TO_PCI (p))
#define FROM_PCIPTR(d) USB_PCI_TO_MEM (d)
#define QH_TO_PCIPTR(p) (TO_PCIPTR (p) | TO_LITTLEL (UHCI_LINK_QH))
#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)))
#define UHC_FROM_LITTLEL(d) FROM_LITTLEL(d)
#else
#define UHC_IF_INIT() sl811DcrInit()
#define TO_PCIPTR(p) ((UINT32) (p))
#define FROM_PCIPTR(d) (d)
#define QH_TO_PCIPTR(p) (TO_PCIPTR (p) | (UHCI_LINK_QH))
#define QH_FROM_PCIPTR(d) \
((pQH_WRAPPER) (FROM_PCIPTR ((d) & UHCI_LINK_PTR_MASK)))
#define TD_FROM_PCIPTR(d) \
((pTD_WRAPPER) (FROM_PCIPTR ((d) & UHCI_LINK_PTR_MASK)))
#define UHC_FROM_LITTLEL(d) (d)
#endif
/* 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.
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -