📄 usbhcduhcilib.c
字号:
/* 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 buffers01l,11oct01,wef fix SPR's 70492, 69922 and 7071601o,18sep01,wef merge from wrs.tor2_0.usb1_1-f for veloce01n,23jul01,wef Fixed SPR #68202 and SPR #6820901m,05jan01,wef Fixed alignment problem w/ code. General clean up01l,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.*//*DESCRIPTIONThis is the HCD (host controller driver) for UHCI. This file implementslow-level functions required by the client (typically the USBD) to talk tothe 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 theuniversal host controller to be managed. Each invocation of the HRB_ATTACHfunction will return an HCD_CLIENT_HANDLE for which a single host controllerwill be exposed.NOTE: This HCD implementation assumes that the caller has already initializedthe osServices and handleFuncs libraries by calling ossInitialize() andusbHandleInitialize(), respectively. The USBD implementation guarantees thatthese 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 isthe 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 describedby the UHCI specfication. As anticipated, we create an array of 1024 framelist 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 anchorpoints to the single control anchor, and the control anchor initially points tothe bulk anchor. When one or more interrupt transfers are pending, QHs for these transfers willbe inserted after the interrupt anchor QHs corresponding to the frames dictated by the interrupt scheduling interval. The last pending interrupt QH in eachlist points to the common control anchor QH. While clients can request anyinterrupt service interval they like, the algorithms here always choose aninterval which is the largest power of 2 less than or equal to the client'sdesired interval. For example, if a client requests an interval of 20msec, theHCD will select a real interval of 16msec. In each frame work list, the leastfrequently scheduled QHs appear ahead of more frequently scheduled QHs. Sinceonly a single QH is actually created for each interrupt transfer, the individualframe lists actually "merge" at each interrupt QH. Now, using the precedingexample of 16msec, suppose that there is a second interrupt QH with an intervalof 8 msec. In half of the frame lists for which the 8msec interval transfer isscheduled, the "interrupt anchor" QH will point to it directly. In the otherhalf, 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 alwaysplaced after the "bulk anchor" QH. When low speed control transfers arepending, they are always inserted after the "control anchor" QH before anyhigh-speed control transfers.The anchors themselves never describe work. Instead, they are justplaceholders 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 necessaryto modify the work list after the single "control" or "bulk" anchor. Similiarly,when queuing interrupt transfers, it is only necessary to modify the worklist 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'swork list; and the last isoch transfer TD in each frame always points to theQH anchor corresponding to that frame.In effect, this scheme decouples isoch, interrupt, control, and bulk transfersin the TD/QH work lists.Regarding bus time calculations...The host controller driver is responsible for ensuring that certain kinds ofscheduled 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 theHCD 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 itkeeps track of how much time remains in each frame, executing only as many ofthese transfers as will fit. However, the UHC requires that the driver scheduleonly as many low speed control transfers (as opposed to full speed controltransfers) as can actually fit within the frame. Therefore, after taking intoaccount the time already allotted to isochronous and interrupt transfers, theHCD only schedules as many low speed control transfers as can fit within thecurrent 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 UHC_BYTE_IN(p) uhciByteRead (pHost->ioBase + (p))#define UHC_WORD_IN(p) uhciWordRead (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))/* 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 */ UINT16 qhCount; /* count of QHs allocated for IRP */ pQH_WRAPPER pQh; /* pointer to QH for this transfer */ UINT16 tdCount; /* count of TDs allocated for IRP */ pTD_WRAPPER pTdPool; /* pointer to block of TDs allocated */ pTD_WRAPPER pTdFree; /* unused TDs */ pTD_WRAPPER pTdInUse; /* first TD in use */ pTD_WRAPPER pLastTdInUse; /* pointer to last TD */ UINT32 nanoseconds; /* bus time required for TDs in use */ UINT16 bfrNo; /* highest IRP bfrList[] serviced */ UINT32 bfrOffset; /* offset into bfrList[].pBfr */ BOOL zeroLenMapped; /* TRUE when zero len bfrList [] serviced */ UINT16 interval; /* how often interrupt xfr scheduled */ UINT32 isochTdsCreated; /* count of isoch TDs created, in total */ UINT32 frameCount; /* count of frames used for isoch pipe */ UINT32 bytesSoFar; /* bytes transferred so far for isoch pipe */ UINT16 isochNext; /* next isoch frame number to schedule */ UINT16 isochCount; /* count of isoch frames scheduled */ pTD_WRAPPER pNextIsochTd; /* next isoch TD to schedule */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -