Windows CE USB Function Driver驱动简析(1)-驱动架构及UfnPdd函数(基于WinCE5.0 SMDK2410 BSP的UFBFN驱动)

s3c2410有2个USB Host接口,一个Device接口.首先介绍下USB的几个概念:
USB是主从结构的,PC是主端(Host),连接到PC上的设备就是从端(device或function).因此对应的驱动分别叫做USB Host Driver及USB Function Driver。我们的板子可以做host也可以做device,接U盘,鼠标到板子上,板子就是Host.板子连到PC作为Mass Storage或者用Activesync连接就是function.
USB驱动结构图示:

这里我们实现的USB Function Driver就是板子和PC连接时板子端的USB驱动.USB Function Driver包括Controller Driver和Client Driver.Controller Driver介于硬件和Client Driver直之间的控制层,而Client Driver则是具体的应用,如Mass Storage,RNDIS,Serial Class.
USB Function Driver包括MDD和PDD层,SMDK2410 BSP中的USBFN驱动实际上就是PDD部分.MDD部分在/WINCE500/PUBLIC/COMMON/OAK/DRIVERS/USBFN/CONTROLLER/MDD.在我们的PDD驱动中通过链接ufnmddbase.lib这个MDD库来生成驱动dll.
这里我们主要来看看USB Function Driver的PDD部分.实际上就一个文件,sc2410pdd.cpp.
USB Function Driver也是一个标准的流接口驱动,流接口函数实现在/WINCE500/PUBLIC/COMMON/OAK/DRIVERS/USBFN/CONTROLLER/MDD/ufnmdd.cpp中.如UFN_***的函数,会调用在PDD中实现的函数.
1.UfnPdd_DllEntry
被MDD层的DllEntry调用,仅仅设置了g_pUDCBase为NULL.
// Called by MDD's DllEntry. extern "C" BOOL UfnPdd_DllEntry( HANDLE hDllHandle, DWORD dwReason, LPVOID lpReserved ) { SETFNAME(); switch (dwReason) { case DLL_PROCESS_ATTACH: g_pUDCBase = NULL; break; } return TRUE; }
2.UfnPdd_Init
这个会被MDD层的Init函数调用,首先初始化一个UFN_PDD_INTERFACE_INFO 接口的数组,里面定义了很多函数指针,这些PDD函数都会被MDD层调用.这些函数后面一一分析.
该结构原型如下:
// Structure that the PDD must fill out in UfnPdd_Init. typedef struct _UFN_PDD_INTERFACE_INFO { DWORD dwVersion; DWORD dwCapabilities; DWORD dwEndpointCount; PVOID pvPddContext; PFN_UFN_PDD_DEINIT pfnDeinit; PFN_UFN_PDD_IS_CONFIGURATION_SUPPORTABLE pfnIsConfigurationSupportable; PFN_UFN_PDD_IS_ENDPOINT_SUPPORTABLE pfnIsEndpointSupportable; PFN_UFN_PDD_INIT_ENDPOINT pfnInitEndpoint; PFN_UFN_PDD_REGISTER_DEVICE pfnRegisterDevice; PFN_UFN_PDD_DEREGISTER_DEVICE pfnDeregisterDevice; PFN_UFN_PDD_START pfnStart; PFN_UFN_PDD_STOP pfnStop; PFN_UFN_PDD_ISSUE_TRANSFER pfnIssueTransfer; PFN_UFN_PDD_ABORT_TRANSFER pfnAbortTransfer; PFN_UFN_DEINIT_ENDPOINT pfnDeinitEndpoint; PFN_UFN_STALL_ENDPOINT pfnStallEndpoint; PFN_UFN_CLEAR_ENDPOINT_STALL pfnClearEndpointStall; PFN_UFN_SEND_CONTROL_STATUS_HANDSHAKE pfnSendControlStatusHandshake; PFN_UFN_SET_ADDRESS pfnSetAddress; PFN_UFN_IS_ENDPOINT_HALTED pfnIsEndpointHalted; PFN_UFN_INITIATE_REMOTE_WAKEUP pfnInitiateRemoteWakeup; PFN_UFN_POWER_DOWN pfnPowerDown; PFN_UFN_POWER_UP pfnPowerUp; PFN_UFN_IOCONTROL pfnIOControl; } UFN_PDD_INTERFACE_INFO, *PUFN_PDD_INTERFACE_INFO;
接下来就是读取注册表获取相关配置信息,存到一个PCTRLR_PDD_CONTEXT结构pContext中.一开始初始化一些符号,还有MDD Context等.然后设置每个ENDPOINT对应的端点状态结构EP_STATUS的端点号,这里最多5个端点,也就是有5个EP_STATUS的结构.
接下来定义了DDKISRINFO和DDKWINDOWINFO的对象,用来读取注册表信IoBase,IoLen,Irq,并向内核申请一个SYSINTR逻辑中断号.然后读取IST优先级("Priority256"=dword:64),接着创建一个访问总线句柄pContext->hBusAccess.
最后调用MapRegisterSet将IOBASE地址(USB device controller register地址)映射到虚拟地址空间.真正的g_pUDCBase是加上了0x140(UDC寄存器基址偏移)的偏移.
然后设置attachedState为UFN_DETACH.调用ResetDevice复位UDC寄存器.将sc_PddInterfaceInfo传递给输入参数.
如果中间出现任何错误将跳转至EXIT,RegCloseKey关闭注册表,FreeCtrlrContext释放之前分配的资源.
// Initialize the device. DWORD WINAPI UfnPdd_Init( LPCTSTR pszActiveKey, PVOID pvMddContext, PUFN_MDD_INTERFACE_INFO pMddInterfaceInfo, PUFN_PDD_INTERFACE_INFO pPddInterfaceInfo ) { SETFNAME(); FUNCTION_ENTER_MSG(); static const UFN_PDD_INTERFACE_INFO sc_PddInterfaceInfo = { UFN_PDD_INTERFACE_VERSION, UFN_PDD_CAPS_SUPPORTS_FULL_SPEED, ENDPOINT_COUNT, NULL, // This gets filled in later &UfnPdd_Deinit, &UfnPdd_IsConfigurationSupportable, &UfnPdd_IsEndpointSupportable, &UfnPdd_InitEndpoint, &UfnPdd_RegisterDevice, &UfnPdd_DeregisterDevice, &UfnPdd_Start, &UfnPdd_Stop, &UfnPdd_IssueTransfer, &UfnPdd_AbortTransfer, &UfnPdd_DeinitEndpoint, &UfnPdd_StallEndpoint, &UfnPdd_ClearEndpointStall, &UfnPdd_SendControlStatusHandshake, &UfnPdd_SetAddress, &UfnPdd_IsEndpointHalted, &UfnPdd_InitiateRemoteWakeup, &UfnPdd_PowerDown, &UfnPdd_PowerUp, &UfnPdd_IOControl, }; DWORD dwType; DWORD dwRet; HKEY hkDevice = NULL; PCTRLR_PDD_CONTEXT pContext = NULL; DEBUGCHK(pszActiveKey); DEBUGCHK(pMddInterfaceInfo); DEBUGCHK(pPddInterfaceInfo); hkDevice = OpenDeviceKey(pszActiveKey); if (!hkDevice) { dwRet = GetLastError(); DEBUGMSG(ZONE_ERROR, (_T("%s Could not open device key. Error: %d/r/n"), pszFname, dwRet)); goto EXIT; } pContext = (PCTRLR_PDD_CONTEXT) LocalAlloc(LPTR, sizeof(*pContext)); if (pContext == NULL) { dwRet = GetLastError(); PREFAST_DEBUGCHK(dwRet != ERROR_SUCCESS); DEBUGMSG(ZONE_ERROR, (_T("%s LocalAlloc failed. Error: %d/r/n"), pszFname, dwRet)); goto EXIT; } pContext->dwSig = SC2410_SIG; pContext->pvMddContext = pvMddContext; pContext->cpsCurrent = D4; pContext->dwIrq = IRQ_UNSPECIFIED; pContext->pfnNotify = pMddInterfaceInfo->pfnNotify; InitializeCriticalSection(&pContext->csIndexedRegisterAccess); for (DWORD dwEp = 0; dwEp < dim(pContext->rgEpStatus); ++dwEp) { pContext->rgEpStatus[dwEp].dwEndpointNumber = dwEp; } DWORD dwDataSize; DWORD dwPriority; DDKISRINFO dii; DDKWINDOWINFO dwi; // read window configuration from the registry dwi.cbSize = sizeof(dwi); dwRet = DDKReg_GetWindowInfo(hkDevice, &dwi); if(dwRet != ERROR_SUCCESS) { DEBUGMSG(ZONE_ERROR, (_T("%s DDKReg_GetWindowInfo() failed %d/r/n"), pszFname, dwRet)); goto EXIT; } else if (dwi.dwNumIoWindows != 1) { DEBUGMSG(ZONE_ERROR, (_T("%s %d windows configured, expected 1/r/n"), pszFname, dwi.dwNumIoWindows)); dwRet = ERROR_INVALID_DATA; goto EXIT; } else if (dwi.ioWindows[0].dwLen < REGISTER_SET_SIZE) { DEBUGMSG(ZONE_INIT, (_T("%s ioLen of 0x%x is less than required 0x%x/r/n"), pszFname, dwi.ioWindows[0].dwLen, REGISTER_SET_SIZE)); dwRet = ERROR_INVALID_DATA; goto EXIT; } else if (dwi.ioWindows[0].dwBase == 0){ DEBUGMSG(ZONE_INIT, (_T("%s no ioBase value specified/r/n"),pszFname)); dwRet = ERROR_INVALID_DATA; goto EXIT; } else { pContext->dwIOBase = dwi.ioWindows[0].dwBase; pContext->dwIOLen = dwi.ioWindows[0].dwLen; } // get ISR configuration information dii.cbSize = sizeof(dii); dwRet = DDKReg_GetIsrInfo(hkDevice, &dii); if (dwRet != ERROR_SUCCESS) { DEBUGMSG(ZONE_ERROR, (_T("%s DDKReg_GetIsrInfo() failed %d/r/n"), pszFname, dwRet)); goto EXIT; } else if( (dii.dwSysintr == SYSINTR_NOP) && (dii.dwIrq == IRQ_UNSPECIFIED) ) { DEBUGMSG(ZONE_ERROR, (_T("%s no IRQ or SYSINTR value specified/r/n"), pszFname)); dwRet = ERROR_INVALID_DATA; goto EXIT; } else { if (dii.dwSysintr == SYSINTR_NOP) { BOOL fSuccess = KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &dii.dwIrq, sizeof(DWORD), &dii.dwSysintr, sizeof(DWORD), NULL); if (!fSuccess) { DEBUGMSG(ZONE_ERROR, (_T("%s IOCTL_HAL_REQUEST_SYSINTR failed!/r/n"), pszFname)); goto EXIT; } pContext->dwIrq = dii.dwIrq; pContext->dwSysIntr = dii.dwSysintr; } else { pContext->dwSysIntr = dii.dwSysintr; } } // Read the IST priority dwDataSize = sizeof(dwPriority); dwRet = RegQueryValueEx(hkDevice, UDC_REG_PRIORITY_VAL, NULL, &dwType, (LPBYTE) &dwPriority, &dwDataSize); if (dwRet != ERROR_SUCCESS) { dwPriority = DEFAULT_PRIORITY; } pContext->hBusAccess = CreateBusAccessHandle(pszActiveKey); if (pContext->hBusAccess == NULL) { // This is not a failure. DEBUGMSG(ZONE_WARNING, (_T("%s Could not create bus access handle/r/n"), pszFname)); } DEBUGMSG(ZONE_INIT, (_T("%s Using IO Base %x/r/n"), pszFname, pContext->dwIOBase)); DEBUGMSG(ZONE_INIT, (_T("%s Using SysIntr %u/r/n"), pszFname, pContext->dwSysIntr)); DEBUGMSG(ZONE_INIT, (_T("%s Using IST priority %u/r/n"), pszFname, dwPriority)); pContext->dwISTPriority = dwPriority; // map register space to virtual memory dwRet = MapRegisterSet(pContext); if (dwRet != ERROR_SUCCESS) { DEBUGMSG(ZONE_ERROR, (_T("%s failed to map register space/r/n"), pszFname)); goto EXIT; } pContext->attachedState = UFN_DETACH; ResetDevice(pContext); ValidateContext(pContext); memcpy(pPddInterfaceInfo, &sc_PddInterfaceInfo, sizeof(sc_PddInterfaceInfo)); pPddInterfaceInfo->pvPddContext = pContext; EXIT: if (hkDevice) RegCloseKey(hkDevice); if (dwRet != ERROR_SUCCESS && pContext) { FreeCtrlrContext(pContext); } FUNCTION_LEAVE_MSG(); return dwRet; }
ResetDevice:
再来看看ResetDevice的具体内容,对UDC内部寄存器的操作有禁止UDC中断,禁止端点中断(EP0-EP4),清楚USB中断标志,清楚端点中断标志,然后调用ResetEndpoint对每个端点进行复位.
ResetDevice( PCTRLR_PDD_CONTEXT pContext ) { SETFNAME(); FUNCTION_ENTER_MSG(); DEBUGCHK(IS_VALID_SC2410_CONTEXT(pContext)); // Disable Device interrupts - write Zeros to Disable WriteReg(pContext, USB_INT_EN_REG_OFFSET, 0 ); // Disable endpoint interrupts - write Zeros to Disable WriteReg(pContext, EP_INT_EN_REG_OFFSET, 0); // Clear any outstanding device & endpoint interrupts // USB Device Interrupt Status - Write a '1' to Clear WriteReg(pContext, USB_INT_REG_OFFSET, (USB_RESET_INTR | USB_RESUME_INTR | USB_SUSPEND_INTR)); // End point Interrupt Status - Write a '1' to Clear WriteReg(pContext, EP_INT_REG_OFFSET, CLEAR_ALL_EP_INTRS); // Reset all endpoints for (DWORD dwEpIdx = 0; dwEpIdx < ENDPOINT_COUNT; ++dwEpIdx) { EP_STATUS *peps = GetEpStatus(pContext, dwEpIdx); ResetEndpoint(pContext, peps); } FUNCTION_LEAVE_MSG(); }
ResetEndpoint:
ResetEndpoint对具体某个端点进行复位.首先对端点0清除SETUP_END和OUT_PKT_RDY状态.接下来对具体端点进行操作.如果该端点已初始化,则读取IN_CSR2_REG和OUT_CSR2_REG寄存器的值并保存.接着设置IN_CSR2_REG,端点方向为IN,禁止的DMA中断.设置IN_CSR1_REG清除data toggle bit,包的PID(标识符段)包含DATA0.然后设置OUT方向,设置OUT_CSR1_REG FLUSH FIFO,清除data toggle bit,数据包顺序设为DATA0,设置OUT_CSR2_REG,禁止DMA中断.如果初始化状态为TRUE,则恢复之前保存的寄存器内容.最后清除所有EndPoint中断.
static VOID ResetEndpoint( PCTRLR_PDD_CONTEXT pContext, EP_STATUS *peps ) { SETFNAME(); FUNCTION_ENTER_MSG(); ValidateContext(pContext); PREFAST_DEBUGCHK(peps); // Since Reset can be called before/after an Endpoint has been configured, // it is best to clear all IN and OUT bits associated with endpoint. DWORD dwEndpoint = peps->dwEndpointNumber; if(dwEndpoint == 0 ) { // Clear all EP0 Status bits WriteIndexedReg(pContext, 0, IN_CSR1_REG_OFFSET, (SERVICED_OUT_PKT_RDY | SERVICED_SETUP_END)); } else if(dwEndpoint < ENDPOINT_COUNT) { // Clear the desired Endpoint - Clear both the In & Out Status bits BYTE bRegIn; BYTE bRegOut; if(peps->fInitialized){ PREFAST_DEBUGCHK(peps->fInitialized); // Give prefast a clue. // First Read the CSR2 Reg bit so that it can be restored bRegIn = ReadIndexedReg(pContext, dwEndpoint, IN_CSR2_REG_OFFSET); bRegOut = ReadIndexedReg(pContext, dwEndpoint, OUT_CSR2_REG_OFFSET); } // Clear the in register - Must set the Mode_in to IN WriteIndexedReg(pContext, dwEndpoint, IN_CSR2_REG_OFFSET,(SET_MODE_IN | IN_DMA_INT_DISABLE)); WriteIndexedReg(pContext, dwEndpoint, IN_CSR1_REG_OFFSET, ( IN_CLR_DATA_TOGGLE)); // Clear the Out register - Must set the Mode_in to OUT WriteIndexedReg(pContext, dwEndpoint, IN_CSR2_REG_OFFSET, (IN_DMA_INT_DISABLE)); // mode_in bit = OUT WriteIndexedReg(pContext, dwEndpoint, OUT_CSR1_REG_OFFSET, (FLUSH_OUT_FIFO | OUT_CLR_DATA_TOGGLE)); WriteIndexedReg(pContext, dwEndpoint, OUT_CSR2_REG_OFFSET, OUT_DMA_INT_DISABLE); if(peps->fInitialized) { // Set the Mode_In, ISO and other Modality type bits back to the way it was - this allows // ResetEndpoint to be called without having to re-init the endpoint. WriteIndexedReg(pContext, dwEndpoint, IN_CSR2_REG_OFFSET, bRegIn); WriteIndexedReg(pContext, dwEndpoint, OUT_CSR2_REG_OFFSET, bRegOut); } // Clear and disable interrupt WriteReg(pContext, EP_INT_REG_OFFSET, EpToIrqStatBit(peps->dwEndpointNumber)); DisableEndpointInterrupt(pContext, peps->dwEndpointNumber); } else { DEBUGCHK(FALSE); } FUNCTION_LEAVE_MSG(); }
FreeCtrlrContext:
FreeCtrlrContext首先释放g_pUDCBase映射的IO空间,然后调用CloseBusAccessHandle关闭总线驱动句柄,然后禁止中断唤醒,释放逻辑中断号.
static VOID FreeCtrlrContext( PCTRLR_PDD_CONTEXT pContext ) { PREFAST_DEBUGCHK(pContext); DEBUGCHK(!pContext->hevInterrupt); DEBUGCHK(!pContext->hIST); DEBUGCHK(!pContext->fRunning); pContext->dwSig = GARBAGE_DWORD; UnmapRegisterSet(pContext); if (pContext->hBusAccess) CloseBusAccessHandle(pContext->hBusAccess); if (pContext->dwSysIntr) { KernelIoControl(IOCTL_HAL_DISABLE_WAKE, &pContext->dwSysIntr, sizeof(pContext->dwSysIntr), NULL, 0, NULL); } if (pContext->dwIrq != IRQ_UNSPECIFIED) { KernelIoControl(IOCTL_HAL_RELEASE_SYSINTR, &pContext->dwIrq, sizeof(DWORD), NULL, 0, NULL); } DeleteCriticalSection(&pContext->csIndexedRegisterAccess); LocalFree(pContext); }
下面就一个个来看UfnPdd_Init中声明的UFN_PDD_INTERFACE_INFO结构中的函数.
3.UfnPdd_Deinit
UfnPdd_Deinit用来进行资源的释放,检查完参数后就调用FreeCtrlrContext释放资源,如UDC寄存器地址空间的释放,SYSINTR和IRQ的释放等.
DWORD WINAPI UfnPdd_Deinit( PVOID pvPddContext ) { SETFNAME(); FUNCTION_ENTER_MSG(); PCTRLR_PDD_CONTEXT pContext = (PCTRLR_PDD_CONTEXT) pvPddContext; ValidateContext(pContext); FUNCTION_ENTER_MSG(); FreeCtrlrContext(pContext); FUNCTION_LEAVE_MSG(); return ERROR_SUCCESS; }
4.UfnPdd_Start
UfnPdd_Start用来启动USB Device设备.
一开始先创建中断事件hevInterrupt,并与SYSINTR号关联中断初始化.然后调用InterruptDone完成中断.创建对应的IST线程.如有错误,则跳转到EXIT禁止中断,释放事件句柄.
DWORD WINAPI UfnPdd_Start( PVOID pvPddContext ) { //RETAILMSG(TRUE, (TEXT("++UfnPdd_Start for GEC2410 USBFN/r/n"))); SETFNAME(); FUNCTION_ENTER_MSG(); DWORD dwRet; PCTRLR_PDD_CONTEXT pContext = (PCTRLR_PDD_CONTEXT) pvPddContext; ValidateContext(pContext); FUNCTION_ENTER_MSG(); DEBUGCHK(!pContext->fRunning); BOOL fIntInitialized = FALSE; // Create the interrupt event pContext->hevInterrupt = CreateEvent(0, FALSE, FALSE, NULL); if (pContext->hevInterrupt == NULL) { dwRet = GetLastError(); RETAILMSG(1, (_T("%s Error creating interrupt event. Error = %d/r/n"), pszFname, dwRet)); goto EXIT; } fIntInitialized = InterruptInitialize(pContext->dwSysIntr, pContext->hevInterrupt, NULL, 0); if (fIntInitialized == FALSE) { dwRet = ERROR_GEN_FAILURE; RETAILMSG(1, (_T("%s interrupt initialization failed/r/n"), pszFname)); goto EXIT; } InterruptDone(pContext->dwSysIntr); pContext->fExitIST = FALSE; pContext->hIST = CreateThread(NULL, 0, ISTMain, pContext, 0, NULL); if (pContext->hIST == NULL) { RETAILMSG(1, (_T("%s IST creation failed/r/n"), pszFname)); dwRet = GetLastError(); goto EXIT; } pContext->fRunning = TRUE; dwRet = ERROR_SUCCESS; EXIT: if (pContext->fRunning == FALSE) { DEBUGCHK(dwRet != ERROR_SUCCESS); if (fIntInitialized) InterruptDisable(pContext->dwSysIntr); if (pContext->hevInterrupt) CloseHandle(pContext->hevInterrupt); pContext->hevInterrupt = NULL; } FUNCTION_LEAVE_MSG(); return dwRet; }
5.UfnPdd_Stop
UfnPdd_Stop用来停止USB Device设备.
主要工作为关闭中断线程,设置中断线程和事件句柄为NULL,调用ResetDevice复位设备,并标记运行状态fRunning为false.
DWORD WINAPI UfnPdd_Stop( PVOID pvPddContext ) { SETFNAME(); FUNCTION_ENTER_MSG(); PCTRLR_PDD_CONTEXT pContext = (PCTRLR_PDD_CONTEXT) pvPddContext; ValidateContext(pContext); DEBUGCHK(pContext->fRunning); // Stop the IST pContext->fExitIST = TRUE; InterruptDisable(pContext->dwSysIntr); SetEvent(pContext->hevInterrupt); WaitForSingleObject(pContext->hIST, INFINITE); CloseHandle(pContext->hevInterrupt); CloseHandle(pContext->hIST); pContext->hIST = NULL; pContext->hevInterrupt = NULL; ResetDevice(pContext); pContext->fRunning = FALSE; RETAILMSG(1, (_T("%s Device has been stopped/r/n"), pszFname)); FUNCTION_LEAVE_MSG(); return ERROR_SUCCESS; }
6.UfnPdd_InitEndpoint
UfnPdd_InitEndpoint进行初始化端点.
开始是参数检查,会调用UfnPdd_IsEndpointSupportable来查询端点是否支持(这个函数下面介绍),如果设置的参数超过硬件支持的数值,则进行修正.然后获取端点信息保存到EP_STATUS结构peps中.设置包最大字节数wMaxPacketSize(最大2047).如果是端点0,仅仅需要注册一个通知函数.同时将wMaxPacketSize赋值给EP_STATUS的成员dwPacketSizeAssigned.如果是其他端点,则首先调用ResetEndpoint复位端点,清除CSR2寄存器所有位,禁止DMA.然后设置端点方向(IN_CSR2_REG寄存器mode_in bit).
接着设置传输类型,根据输入参数pEndpointDesc来获得具体类型,如果是ISOCHRONOUS异步模式,则写IN_CSR2_REG的ISO比特,其他模式则设置该位为0,为Bulk模式.
最后将最大包字节数wMaxPacketSize写入MAX_PKT_SIZE_REG寄存器,调用UfnPdd_ClearEndpointStall和ClearEndpointInterrupt端点挂起和端点中断状态.
DWORD WINAPI UfnPdd_InitEndpoint( PVOID pvPddContext, DWORD dwEndpoint, UFN_BUS_SPEED Speed, PUSB_ENDPOINT_DESCRIPTOR pEndpointDesc, PVOID pvReserved, BYTE bConfigurationValue, BYTE bInterfaceNumber, BYTE bAlternateSetting ) { SETFNAME(); FUNCTION_ENTER_MSG(); DEBUGCHK(EP_VALID(dwEndpoint)); PREFAST_DEBUGCHK(pEndpointDesc); #ifdef DEBUG { USB_ENDPOINT_DESCRIPTOR EndpointDesc; memcpy(&EndpointDesc, pEndpointDesc, sizeof(EndpointDesc)); DEBUGCHK(UfnPdd_IsEndpointSupportable(pvPddContext, dwEndpoint, Speed, &EndpointDesc, bConfigurationValue, bInterfaceNumber, bAlternateSetting) == ERROR_SUCCESS); DEBUGCHK(memcmp(&EndpointDesc, pEndpointDesc, sizeof(EndpointDesc)) == 0); } #endif PCTRLR_PDD_CONTEXT pContext = (PCTRLR_PDD_CONTEXT) pvPddContext; ValidateContext(pContext); BYTE bEndpointAddress = 0; PEP_STATUS peps = GetEpStatus(pContext, dwEndpoint); DEBUGCHK(!peps->fInitialized); InitializeCriticalSection(&peps->cs); WORD wMaxPacketSize = pEndpointDesc->wMaxPacketSize & USB_ENDPOINT_MAX_PACKET_SIZE_MASK; DEBUGCHK(wMaxPacketSize); // If the target is endpoint 0, then only allow the function driver // to register a notification function. if (dwEndpoint == 0) { peps->dwPacketSizeAssigned = wMaxPacketSize; // Interrupts for endpoint 0 are enabled in ISTMain } else if (dwEndpoint < ENDPOINT_COUNT) { // Clear all Status bits and leave the Endpoint interrupt disabled // Clear Fifos, and all register bits ResetEndpoint(pContext,peps); // Clear all bits in CSR2 - Disable DMA for now... WriteIndexedReg(pContext, dwEndpoint, IN_CSR2_REG_OFFSET, 0); // Setup Direction (mode_in bit) bEndpointAddress = pEndpointDesc->bEndpointAddress; BOOL fModeOut = USB_ENDPOINT_DIRECTION_OUT(bEndpointAddress); if (fModeOut) { SetClearIndexedReg(pContext, dwEndpoint,IN_CSR2_REG_OFFSET, SET_MODE_IN, CLEAR); peps->dwDirectionAssigned = USB_OUT_TRANSFER; } else { SetClearIndexedReg(pContext, dwEndpoint, IN_CSR2_REG_OFFSET, SET_MODE_IN, SET); peps->dwDirectionAssigned = USB_IN_TRANSFER; } // Set Transfer Type BYTE bTransferType = pEndpointDesc->bmAttributes & USB_ENDPOINT_TYPE_MASK; DEBUGCHK(bTransferType != USB_ENDPOINT_TYPE_CONTROL); switch(bTransferType) { case USB_ENDPOINT_TYPE_ISOCHRONOUS: // Set the ISO bit SetClearIndexedReg(pContext, dwEndpoint,IN_CSR2_REG_OFFSET, SET_TYPE_ISO, SET); break; case USB_ENDPOINT_TYPE_BULK: case USB_ENDPOINT_TYPE_INTERRUPT: default: // Clear ISO bit - Set type to Bulk SetClearIndexedReg(pContext, dwEndpoint,IN_CSR2_REG_OFFSET, SET_TYPE_ISO, CLEAR); } peps->dwEndpointType = bTransferType; peps->dwPacketSizeAssigned = wMaxPacketSize; // Set the Max Packet Size Register BYTE maxPacketBits = (BYTE) (peps->dwPacketSizeAssigned >> 3); WriteIndexedReg(pContext, dwEndpoint, MAX_PKT_SIZE_REG_OFFSET, maxPacketBits); UfnPdd_ClearEndpointStall(pvPddContext,dwEndpoint); // Clear outstanding interrupts ClearEndpointInterrupt(pContext, dwEndpoint); } peps->fInitialized = TRUE; FUNCTION_LEAVE_MSG(); return ERROR_SUCCESS; }
UfnPdd_ClearEndpointStall函数下面介绍,先来看看ClearEndpointInterrupt
ClearEndpointInterrupt用来清除EP_INT_REG寄存器对应端点的中断.static VOID ClearEndpointInterrupt( PCTRLR_PDD_CONTEXT pContext, DWORD dwEndpoint ) { SETFNAME(); FUNCTION_ENTER_MSG(); // Clear the Endpoint Interrupt BYTE bIntBit = EpToIrqStatBit(dwEndpoint); WriteReg(pContext, EP_INT_REG_OFFSET, bIntBit); FUNCTION_LEAVE_MSG(); } // _ClearInterrupt
7.UfnPdd_DeinitEndpoint
UfnPdd_DeinitEndpoint通过调用ResetEndpoint和ClearEndpointInterrupt进行端点复位,屏蔽中断,清除端点中断状态.
DWORD WINAPI UfnPdd_DeinitEndpoint( PVOID pvPddContext, DWORD dwEndpoint ) { SETFNAME(); FUNCTION_ENTER_MSG(); DEBUGCHK(EP_VALID(dwEndpoint)); PCTRLR_PDD_CONTEXT pContext = (PCTRLR_PDD_CONTEXT) pvPddContext; ValidateContext(pContext); PEP_STATUS peps = GetEpStatus(pContext, dwEndpoint); LOCK_ENDPOINT(peps); DEBUGCHK(peps->fInitialized); DEBUGCHK(peps->pTransfer == NULL); // Reset and disable the endpoint // Mask endpoint interrupts ResetEndpoint(pContext, peps); // Clear endpoint interrupts ClearEndpointInterrupt(pContext, dwEndpoint); peps->fInitialized = FALSE; UNLOCK_ENDPOINT(peps); DeleteCriticalSection(&peps->cs); FUNCTION_LEAVE_MSG(); return ERROR_SUCCESS; }
8.UfnPdd_ClearEndpointStall
UfnPdd_ClearEndpointStall用来清除端点的stall停止状态.
首先依然是参数检查,然后获取端点信息保存到EP_STATUS结构peps中,如果是端点0,清除EP0_CSR的send和sent stall状态.如果是其他端点,根据传输方向设置IN_CSR1_REG或OUT_CSR1_REG清除send和sent stall状态.
DWORD WINAPI UfnPdd_ClearEndpointStall( PVOID pvPddContext, DWORD dwEndpoint ) { DWORD dwRet = ERROR_SUCCESS; SETFNAME(); FUNCTION_ENTER_MSG(); DEBUGCHK(EP_VALID(dwEndpoint)); PCTRLR_PDD_CONTEXT pContext = (PCTRLR_PDD_CONTEXT) pvPddContext; ValidateContext(pContext); PEP_STATUS peps = GetEpStatus(pContext, dwEndpoint); LOCK_ENDPOINT(peps); if (dwEndpoint == 0){ // Must Clear both Send and Sent Stall WriteIndexedReg(pContext, 0, EP0_CSR_REG_OFFSET, 0); } else if (peps->dwDirectionAssigned == USB_IN_TRANSFER){ SetClearIndexedReg(pContext, dwEndpoint, IN_CSR1_REG_OFFSET, (IN_SEND_STALL | IN_CLR_DATA_TOGGLE ), SET); // Must Clear both Send and Sent Stall SetClearIndexedReg(pContext, dwEndpoint, IN_CSR1_REG_OFFSET, (IN_SEND_STALL | IN_SENT_STALL), CLEAR); } else{ // Out Endpoint // Must Clear both Send and Sent Stall SetClearIndexedReg(pContext, dwEndpoint, OUT_CSR1_REG_OFFSET, ( OUT_SEND_STALL | OUT_CLR_DATA_TOGGLE ), SET); SetClearIndexedReg(pContext ,dwEndpoint, OUT_CSR1_REG_OFFSET, ( OUT_SEND_STALL | OUT_SENT_STALL), CLEAR); } UNLOCK_ENDPOINT(peps); FUNCTION_LEAVE_MSG(); return ERROR_SUCCESS; }
9.UfnPdd_StallEndpoint
UfnPdd_StallEndpoint用来停止端点.
首先依然是参数检查,然后获取端点信息保存到EP_STATUS结构peps中,如果是端点0,清除EP0_CSR的DATA_END,SERVICED_OUT_PKT_RDY状态,设置EP0_SEND_STALL.
如果是其他端点,根据传输方向,设置IN_CSR1_REG或OUT_CSR1_REG寄存器的IN_SEND_STALL或OUT_SEND_STALL,OUT_PACKET_READY.
DWORD WINAPI UfnPdd_StallEndpoint( PVOID pvPddContext, DWORD dwEndpoint ) { DWORD dwRet = ERROR_SUCCESS; SETFNAME(); FUNCTION_ENTER_MSG(); DEBUGCHK(EP_VALID(dwEndpoint)); PCTRLR_PDD_CONTEXT pContext = (PCTRLR_PDD_CONTEXT) pvPddContext; ValidateContext(pContext); PEP_STATUS peps = GetEpStatus(pContext, dwEndpoint); DEBUGCHK(peps->fInitialized); LOCK_ENDPOINT(peps); if (dwEndpoint == 0) { // Must Clear Out Packet Ready when sending Stall BYTE bEp0StallBits = (DATA_END | SERVICED_OUT_PKT_RDY | EP0_SEND_STALL); RETAILMSG(1, (_T("%s Writing 0x%02x to EP0_CSR_REG/r/n"), pszFname, bEp0StallBits)); WriteIndexedReg(pContext, 0, EP0_CSR_REG_OFFSET, bEp0StallBits); // Set Flag so that SendControlStatusHandshked does not // duplicate this HW Write. Manual says all bits need // to be set at the same time. pContext->sendDataEnd = FALSE; pContext->Ep0State = EP0_STATE_IDLE; } else if (peps->dwDirectionAssigned == USB_IN_TRANSFER) { SetClearIndexedReg(pContext, dwEndpoint, IN_CSR1_REG_OFFSET, (IN_SEND_STALL), SET); } else { // Out Endpoint // Must Clear Out Packet Ready when sending Stall SetClearIndexedReg(pContext, dwEndpoint, OUT_CSR1_REG_OFFSET, (OUT_SEND_STALL), SET); SetClearIndexedReg(pContext ,dwEndpoint, OUT_CSR1_REG_OFFSET, (OUT_PACKET_READY), CLEAR); } UNLOCK_ENDPOINT(peps); FUNCTION_LEAVE_MSG(); return ERROR_SUCCESS; }
10.UfnPdd_IsConfigurationSupportable
这个函数没有做具体工作.
DWORD WINAPI UfnPdd_IsConfigurationSupportable( PVOID pvPddContext, UFN_BUS_SPEED Speed, PUFN_CONFIGURATION pConfiguration ) { SETFNAME(); FUNCTION_ENTER_MSG(); DEBUGCHK(Speed == BS_FULL_SPEED); PCTRLR_PDD_CONTEXT pContext = (PCTRLR_PDD_CONTEXT) pvPddContext; ValidateContext(pContext); // This PDD does not have any special requirements that cannot be // handled through IsEndpointSupportable. DWORD dwRet = ERROR_SUCCESS; FUNCTION_LEAVE_MSG(); return dwRet; }
11.UfnPdd_IsEndpointSupportable
UfnPdd_IsEndpointSupportable用来查询指定端点是否支持.
首先判断是否是端点0,如果是端点0则检查其属性是否为CONTROL,并判断其做大包字节数,如果小于8,则设置成8.如果是其他端点,则需保证其属性不为CONTROL端点.然后根据不同的传输类型设置其包大小的范围.异步类型CPU UDC不支持,批量或中断传输硬件支持8,16,32,64字节的包, 如果要求设置的包大小超过范围,则设置支持的最大的值.
DWORD WINAPI UfnPdd_IsEndpointSupportable( PVOID pvPddContext, DWORD dwEndpoint, UFN_BUS_SPEED Speed, PUSB_ENDPOINT_DESCRIPTOR pEndpointDesc, BYTE bConfigurationValue, BYTE bInterfaceNumber, BYTE bAlternateSetting ) { SETFNAME(); FUNCTION_ENTER_MSG(); DEBUGCHK(EP_VALID(dwEndpoint)); DEBUGCHK(Speed == BS_FULL_SPEED); DWORD dwRet = ERROR_SUCCESS; PCTRLR_PDD_CONTEXT pContext = (PCTRLR_PDD_CONTEXT) pvPddContext; ValidateContext(pContext); PEP_STATUS peps = GetEpStatus(pContext, dwEndpoint); // Special case for endpoint 0 if (dwEndpoint == 0) { DEBUGCHK(pEndpointDesc->bmAttributes == USB_ENDPOINT_TYPE_CONTROL); // Endpoint 0 only supports 8 or 16 byte packet size if (pEndpointDesc->wMaxPacketSize < EP_0_PACKET_SIZE) { RETAILMSG(1, (_T("%s Endpoint 0 only supports %u byte packets/r/n"), pszFname, EP_0_PACKET_SIZE)); dwRet = ERROR_INVALID_PARAMETER; } else{ // Larger than EP 0 Max Packet Size - reduce to Max pEndpointDesc->wMaxPacketSize = EP_0_PACKET_SIZE; } } else if (dwEndpoint < ENDPOINT_COUNT) { BYTE bTransferType = pEndpointDesc->bmAttributes & USB_ENDPOINT_TYPE_MASK; DEBUGCHK(bTransferType != USB_ENDPOINT_TYPE_CONTROL); // Validate and adjust packet size WORD wPacketSize = (pEndpointDesc->wMaxPacketSize & USB_ENDPOINT_MAX_PACKET_SIZE_MASK); switch(bTransferType) { // Isoch not currently supported by Samsung HW case USB_ENDPOINT_TYPE_ISOCHRONOUS: RETAILMSG(1, (_T("%s Isochronous endpoints are not supported/r/n"), pszFname)); dwRet = ERROR_INVALID_PARAMETER; break; case USB_ENDPOINT_TYPE_BULK: case USB_ENDPOINT_TYPE_INTERRUPT: // HW Can only Support 8, 16, 32, 64 byte packets if((wPacketSize >= 8) && (wPacketSize < 16)){ wPacketSize = 8; } else if ((wPacketSize >= 16) && (wPacketSize < 64)){ // Note that 32 => Dual Packet mode - Do NOT allow wPacketSize = 16; } else if (wPacketSize >= 64 ){ wPacketSize = 64; } else{ // wPacketSize < 8 dwRet = ERROR_INVALID_PARAMETER; } break; default: dwRet = ERROR_INVALID_PARAMETER; break; } // If Requested Size is larger than what is supported ... change it. // Note only try and change it if no errors so far... meaning Ep is // Supportable. if ( (wPacketSize != (pEndpointDesc->wMaxPacketSize & USB_ENDPOINT_MAX_PACKET_SIZE_MASK)) && (dwRet == ERROR_SUCCESS) ) { pEndpointDesc->wMaxPacketSize &= ~USB_ENDPOINT_MAX_PACKET_SIZE_MASK; pEndpointDesc->wMaxPacketSize |= wPacketSize; } } FUNCTION_LEAVE_MSG(); return dwRet; }
12.UfnPdd_SendControlStatusHandshake
UfnPdd_SendControlStatusHandshake用来发送端点0的控制状态握手信息.
参数检查后,获取端点0结构信息,然后移除Out Packet Ready标志,读取EP0_CSR寄存器,设置DATA_END,SERVICED_OUT_PKT_RDY保留EP0_STALL_BITS位,然后写入EP0_CSR寄存器,标记sendDataEnd为FALSE
DWORD WINAPI UfnPdd_SendControlStatusHandshake( PVOID pvPddContext, DWORD dwEndpoint ) { SETFNAME(); FUNCTION_ENTER_MSG(); PCTRLR_PDD_CONTEXT pContext = (PCTRLR_PDD_CONTEXT) pvPddContext; ValidateContext(pContext); DEBUGCHK(dwEndpoint == 0); // This function is only valid for Endpoint 0 EP_STATUS *peps = GetEpStatus(pContext, 0); DEBUGCHK(peps->fInitialized); // Remove the Out Packet Ready Condition if(pContext->sendDataEnd) { LOCK_ENDPOINT(peps); RETAILMSG(1, (_T("%s Sending 0 packet /r/n"), pszFname)); BYTE bEP0IrqStatus = ReadIndexedReg(pContext, 0, EP0_CSR_REG_OFFSET); // Write 0 to SEND_STALL and SENT_STALL to clear them, so we need to // leave them unchanged by default. BYTE bEp0CsrToWrite = (bEP0IrqStatus & EP0_STALL_BITS); bEp0CsrToWrite |= (DATA_END | SERVICED_OUT_PKT_RDY); RETAILMSG(1, (_T("%s Status - 0x%02x, Writing 0x%02x/r/n"), pszFname, bEP0IrqStatus, bEp0CsrToWrite)); WriteIndexedReg(pContext, 0, EP0_CSR_REG_OFFSET, bEp0CsrToWrite); pContext->sendDataEnd = FALSE; UNLOCK_ENDPOINT(peps); } FUNCTION_LEAVE_MSG(); return ERROR_SUCCESS; }
13.UfnPdd_SetAddress
UfnPdd_SetAddress用来设置USB设备地址.通过写ADDRESS寄存器0-6位为地址,第7为写1通知地址更新.
DWORD WINAPI UfnPdd_SetAddress( PVOID pvPddContext, BYTE bAddress ) { SETFNAME(); FUNCTION_ENTER_MSG(); PCTRLR_PDD_CONTEXT pContext = (PCTRLR_PDD_CONTEXT) pvPddContext; ValidateContext(pContext); // Make sure that the Address Update bit is set (0x80) WriteReg(pContext, SET_ADDRESS_REG_OFFSET, (0x80 | bAddress)); FUNCTION_LEAVE_MSG(); return ERROR_SUCCESS; }
14.UfnPdd_InitiateRemoteWakeup
UfnPdd_InitiateRemoteWakeup用来初始化远程唤醒.通过写PWR_REG置位MCU_RESUME.
DWORD WINAPI UfnPdd_InitiateRemoteWakeup( PVOID pvPddContext ) { SETFNAME(); FUNCTION_ENTER_MSG(); PCTRLR_PDD_CONTEXT pContext = (PCTRLR_PDD_CONTEXT) pvPddContext; ValidateContext(pContext); SetClearReg(pContext, PWR_REG_OFFSET, MCU_RESUME, SET); FUNCTION_LEAVE_MSG(); return ERROR_SUCCESS; }
15.UfnPdd_RegisterDevice,UfnPdd_DeregisterDevice
这两个函数未实现任何实际工作.

16.UfnPdd_PowerDown,UfnPdd_PowerUp
这两个函数也未实现任何实际工作.

17.UfnPdd_IOControl
UfnPdd_IOControl实现了3个IOControl操作:IOCTL_UFN_GET_PDD_INFO,IOCTL_BUS_GET_POWER_STATE,IOCTL_BUS_SET_POWER_STATE.
其中IOCTL_UFN_GET_PDD_INFO未实现具体内容,仅仅检查参数后跳出.
IOCTL_BUS_GET_POWER_STATE:将pContext->cpsCurrent传递给输入参数pbIn的*pCePowerState->lpceDevicePowerState.
IOCTL_BUS_SET_POWER_STATE:调用SetPowerState将输入的lpceDevicePowerState赋值给pContext.
DWORD WINAPI UfnPdd_IOControl( PVOID pvPddContext, IOCTL_SOURCE source, DWORD dwCode, PBYTE pbIn, DWORD cbIn, PBYTE pbOut, DWORD cbOut, PDWORD pcbActualOut ) { SETFNAME(); FUNCTION_ENTER_MSG(); PCTRLR_PDD_CONTEXT pContext = (PCTRLR_PDD_CONTEXT) pvPddContext; ValidateContext(pContext); DWORD dwRet = ERROR_INVALID_PARAMETER; switch (dwCode) { case IOCTL_UFN_GET_PDD_INFO: if ( source != BUS_IOCTL || pbOut == NULL || cbOut != sizeof(UFN_PDD_INFO) ) { break; } // Not currently supported. break; case IOCTL_BUS_GET_POWER_STATE: if (source == MDD_IOCTL) { PREFAST_DEBUGCHK(pbIn); DEBUGCHK(cbIn == sizeof(CE_BUS_POWER_STATE)); PCE_BUS_POWER_STATE pCePowerState = (PCE_BUS_POWER_STATE) pbIn; PREFAST_DEBUGCHK(pCePowerState->lpceDevicePowerState); RETAILMSG(1, (_T("%s IOCTL_BUS_GET_POWER_STATE/r/n"), pszFname)); *pCePowerState->lpceDevicePowerState = pContext->cpsCurrent; dwRet = ERROR_SUCCESS; } break; case IOCTL_BUS_SET_POWER_STATE: if (source == MDD_IOCTL) { PREFAST_DEBUGCHK(pbIn); DEBUGCHK(cbIn == sizeof(CE_BUS_POWER_STATE)); PCE_BUS_POWER_STATE pCePowerState = (PCE_BUS_POWER_STATE) pbIn; PREFAST_DEBUGCHK(pCePowerState->lpceDevicePowerState); DEBUGCHK(VALID_DX(*pCePowerState->lpceDevicePowerState)); RETAILMSG(1, (_T("%s IOCTL_BUS_GET_POWER_STATE(D%u)/r/n"), pszFname, *pCePowerState->lpceDevicePowerState)); SetPowerState(pContext, *pCePowerState->lpceDevicePowerState); dwRet = ERROR_SUCCESS; } break; } FUNCTION_LEAVE_MSG(); return dwRet; }
关于SetPowerState函数
首先根据新的电源状态进行调整,如果是D1,D2,D4将调整为D0.然后如果新的电源状态小于原来的,请求总线驱动设置新的电源状态.
然后根据新的电源状态进行不同处理:
D0:禁止唤醒,重启IST线程.
D3:允许唤醒
D4:禁止唤醒

如果新的电源状态大于原来的,请求总线驱动设置新的电源状态.
最后设置pContext->cpsCurrent为新的电源状态.
// This does not do much because there is not any way to control // power on this controller. static CEDEVICE_POWER_STATE SetPowerState( PCTRLR_PDD_CONTEXT pContext, CEDEVICE_POWER_STATE cpsNew ) { SETFNAME(); PREFAST_DEBUGCHK(pContext); DEBUGCHK(VALID_DX(cpsNew)); ValidateContext(pContext); // Adjust cpsNew. if (cpsNew != pContext->cpsCurrent) { if (cpsNew == D1 || cpsNew == D2) { // D1 and D2 are not supported. cpsNew = D0; } else if (pContext->cpsCurrent == D4) { // D4 can only go to D0. cpsNew = D0; } } if (cpsNew != pContext->cpsCurrent) { RETAILMSG(1, (_T("%s Going from D%u to D%u/r/n"), pszFname, pContext->cpsCurrent, cpsNew)); if ( (cpsNew < pContext->cpsCurrent) && pContext->hBusAccess ) { SetDevicePowerState(pContext->hBusAccess, cpsNew, NULL); } switch (cpsNew) { case D0: KernelIoControl(IOCTL_HAL_DISABLE_WAKE, &pContext->dwSysIntr, sizeof(pContext->dwSysIntr), NULL, 0, NULL); if (pContext->fRunning) { // Cause the IST to restart. pContext->fRestartIST = TRUE; SetInterruptEvent(pContext->dwSysIntr); } break; case D3: KernelIoControl(IOCTL_HAL_ENABLE_WAKE, &pContext->dwSysIntr, sizeof(pContext->dwSysIntr), NULL, 0, NULL); break; case D4: KernelIoControl(IOCTL_HAL_DISABLE_WAKE, &pContext->dwSysIntr, sizeof(pContext->dwSysIntr), NULL, 0, NULL); break; } if ( (cpsNew > pContext->cpsCurrent) && pContext->hBusAccess ) { SetDevicePowerState(pContext->hBusAccess, cpsNew, NULL); } pContext->cpsCurrent = cpsNew; } return pContext->cpsCurrent; }
接下来来看看驱动的IST.

你可能感兴趣的:(Windows,CE,function,wince,windows,null,byte,interface)