Windows CE USB Function Driver驱动简析
——驱动架构及UfnPdd函数(基于WinCE5.0 SMDK2410 BSP的UFBFN驱动)
本文转自:http://blog.youkuaiyun.com/shevsten/archive/2010/07/15/5736889.aspx
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.
+ expand sourceview plaincopy to clipboardprint?
// 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;
}
// 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层调用.这些函数后面一一分析.
该结构原型如下:
+ expand sourceview plaincopy to clipboardprint?
// 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;
// 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释放之前分配的资源.
+ expand sourceview plaincopy to clipboardprint?
// 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;
}
// 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对每个端点进行复位.
+ expand sourceview plaincopy to clipboardprint?
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();
}
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中断.
+ expand sourceview plaincopy to clipboardprint?
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();
}
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关闭总线驱动句柄,然后禁止中断唤醒,释放逻辑中断号.
+ expand sourceview plaincopy to clipboardprint?
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);
}
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的释放等.
+ expand sourceview plaincopy to clipboardprint?
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;
}
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禁止中断,释放事件句柄.
+ expand sourceview plaincopy to clipboardprint?
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;
}
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.
+ expand sourceview plaincopy to clipboardprint?
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;
}
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端点挂起和端点中断状态.
+ expand sourceview plaincopy to clipboardprint?
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;
}
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寄存器对应端点的中断.+ expand sourceview plaincopy to clipboardprint?
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
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进行端点复位,屏蔽中断,清除端点中断状态.
+ expand sourceview plaincopy to clipboardprint?
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;
}
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状态.
+ expand sourceview plaincopy to clipboardprint?
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;
}
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.
+ expand sourceview plaincopy to clipboardprint?
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;
}
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
这个函数没有做具体工作.
+ expand sourceview plaincopy to clipboardprint?
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;
}
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字节的包, 如果要求设置的包大小超过范围,则设置支持的最大的值.
+ expand sourceview plaincopy to clipboardprint?
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;
}
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
+ expand sourceview plaincopy to clipboardprint?
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;
}
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通知地址更新.
+ expand sourceview plaincopy to clipboardprint?
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;
}
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.
+ expand sourceview plaincopy to clipboardprint?
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;
}
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.
+ expand sourceview plaincopy to clipboardprint?
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;
}
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为新的电源状态.
+ expand sourceview plaincopy to clipboardprint?
// 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;
}
// 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.
本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/shevsten/archive/2010/07/15/5736889.aspx
上一篇我们简单分析了UFN驱动中的Ufn_pdd函数,现在我们来看看IST主线程ISTMain函数及相关处理函数.
1.ISTMain
ISTMain在UfnPdd_Start被创建:
pContext->hIST = CreateThread(NULL, 0, ISTMain, pContext, 0, NULL);
ISTMain在参数检查后,设置优先级后,然后进入while循环,循环条件为fExitIST为FALSE,即不退出IST.
循环内禁止所有端点中断,然后使能USB reset中断,使能端点0中断.然后等待中断事件hevInterrupt发生.发生后调用HandleUSBEvent进行中断处理.当退出线程fExitIST或重启线程fRestartIST为TRUE时,在禁止中断和清除中断状态后退出线程.并通知MDD层DETACH事件.
+ expand sourceview plaincopy to clipboardprint?
static
DWORD
WINAPI
ISTMain(
LPVOID lpParameter
)
{
SETFNAME();
FUNCTION_ENTER_MSG();
PCTRLR_PDD_CONTEXT pContext = (PCTRLR_PDD_CONTEXT) lpParameter;
ValidateContext(pContext);
CeSetThreadPriority(pContext->hIST, pContext->dwISTPriority);
while (!pContext->fExitIST) {
pContext->fRestartIST = FALSE;
// Enable Suspend Mode in the Power Register
// SetClearReg(pContext, PWR_REG_OFFSET, SUSPEND_MODE_ENABLE_CTRL, SET);
// Disable All Endpoint interrupts
WriteReg(pContext, EP_INT_EN_REG_OFFSET, 0); // Disable All
// Enable Device interrupts
WriteReg(pContext, USB_INT_EN_REG_OFFSET, (USB_RESET_INTR));// | USB_SUSPEND_INTR));
// Enable Endpoint interrupt 0
EnableEndpointInterrupt(pContext, 0);
while (TRUE) {
DWORD dwWait = WaitForSingleObject(pContext->hevInterrupt, INFINITE);
if (pContext->fExitIST || pContext->fRestartIST) {
break;
}
if (dwWait == WAIT_OBJECT_0) {
HandleUSBEvent(pContext);
InterruptDone(pContext->dwSysIntr);
}
else {
RETAILMSG(1, (_T("%s WaitForMultipleObjects failed. Exiting IST./r/n"),
pszFname));
break;
}
}
// 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);
// Send detach
pContext->pfnNotify(pContext->pvMddContext,
UFN_MSG_BUS_EVENTS, UFN_DETACH);
pContext->fSpeedReported = FALSE;
pContext->attachedState = UFN_DETACH;
}
FUNCTION_LEAVE_MSG();
return 0;
}
static
DWORD
WINAPI
ISTMain(
LPVOID lpParameter
)
{
SETFNAME();
FUNCTION_ENTER_MSG();
PCTRLR_PDD_CONTEXT pContext = (PCTRLR_PDD_CONTEXT) lpParameter;
ValidateContext(pContext);
CeSetThreadPriority(pContext->hIST, pContext->dwISTPriority);
while (!pContext->fExitIST) {
pContext->fRestartIST = FALSE;
// Enable Suspend Mode in the Power Register
// SetClearReg(pContext, PWR_REG_OFFSET, SUSPEND_MODE_ENABLE_CTRL, SET);
// Disable All Endpoint interrupts
WriteReg(pContext, EP_INT_EN_REG_OFFSET, 0); // Disable All
// Enable Device interrupts
WriteReg(pContext, USB_INT_EN_REG_OFFSET, (USB_RESET_INTR));// | USB_SUSPEND_INTR));
// Enable Endpoint interrupt 0
EnableEndpointInterrupt(pContext, 0);
while (TRUE) {
DWORD dwWait = WaitForSingleObject(pContext->hevInterrupt, INFINITE);
if (pContext->fExitIST || pContext->fRestartIST) {
break;
}
if (dwWait == WAIT_OBJECT_0) {
HandleUSBEvent(pContext);
InterruptDone(pContext->dwSysIntr);
}
else {
RETAILMSG(1, (_T("%s WaitForMultipleObjects failed. Exiting IST./r/n"),
pszFname));
break;
}
}
// 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);
// Send detach
pContext->pfnNotify(pContext->pvMddContext,
UFN_MSG_BUS_EVENTS, UFN_DETACH);
pContext->fSpeedReported = FALSE;
pContext->attachedState = UFN_DETACH;
}
FUNCTION_LEAVE_MSG();
return 0;
}
2.HandleUSBEvent
当UDC中断发生时就会调用HandleUSBEvent来处理.
首先读取INT_REG和USB_INT_REG寄存器分别获取EP中断状态和USB中断状态(RESET or RESUME or SUSPEND)
BYTE bEpIrqStat = ReadReg(pContext, EP_INT_REG_OFFSET);
BYTE bUSBBusIrqStat = ReadReg(pContext, USB_INT_REG_OFFSET);
然后根据具体的中断状态调用相应的处理函数,如USBbus中断调用HandleUSBBusIrq
if (bUSBBusIrqStat) {
RETAILMSG(1, (_T("%s USB_INT_REG = 0x%02x/r/n"),
pszFname, bUSBBusIrqStat));
HandleUSBBusIrq(pContext, bUSBBusIrqStat);
}
EP中断根据是EP0中断还是EP1-4中断分别调用HandleEndpoint0Event和HandleEndpointEvent.
if (bEpIrqStat & EP0_INT_INTR) {
HandleEndpoint0Event(pContext);
}
for (DWORD dwEndpoint = 1; dwEndpoint < ENDPOINT_COUNT; ++dwEndpoint) {
BYTE bEpBit = EpToIrqStatBit(dwEndpoint);
if (bEpIrqStat & bEpBit) {
HandleEndpointEvent(pContext, dwEndpoint, bEpIrqStat);
}
}
然后继续读取INT_REG和USB_INT_REG寄存器获取中断状态,如果还有中断则继续调用上面的流程进行处理.
完整代码如下:
+ expand sourceview plaincopy to clipboardprint?
static
VOID
HandleUSBEvent(
PCTRLR_PDD_CONTEXT pContext
)
{
SETFNAME();
FUNCTION_ENTER_MSG();
ValidateContext(pContext);
BYTE bEpIrqStat = ReadReg(pContext, EP_INT_REG_OFFSET);
BYTE bUSBBusIrqStat = ReadReg(pContext, USB_INT_REG_OFFSET);
while (bEpIrqStat || bUSBBusIrqStat) {
if (bUSBBusIrqStat) {
RETAILMSG(1, (_T("%s USB_INT_REG = 0x%02x/r/n"),
pszFname, bUSBBusIrqStat));
HandleUSBBusIrq(pContext, bUSBBusIrqStat);
}
if (bEpIrqStat) {
RETAILMSG(1, (_T("%s EP_INT_REG = 0x%02x/r/n"),
pszFname, bEpIrqStat));
if (bEpIrqStat & EP0_INT_INTR) {
HandleEndpoint0Event(pContext);
}
// Process All Other (besides EP0) Endpoints
for (DWORD dwEndpoint = 1; dwEndpoint < ENDPOINT_COUNT; ++dwEndpoint) {
// Check the Interrupt Mask
// Check the Interrupt Status
BYTE bEpBit = EpToIrqStatBit(dwEndpoint);
if (bEpIrqStat & bEpBit) {
HandleEndpointEvent(pContext, dwEndpoint, bEpIrqStat);
}
}
}
bEpIrqStat = ReadReg(pContext, EP_INT_REG_OFFSET);
bUSBBusIrqStat = ReadReg(pContext, USB_INT_REG_OFFSET);
}
FUNCTION_LEAVE_MSG();
}
static
VOID
HandleUSBEvent(
PCTRLR_PDD_CONTEXT pContext
)
{
SETFNAME();
FUNCTION_ENTER_MSG();
ValidateContext(pContext);
BYTE bEpIrqStat = ReadReg(pContext, EP_INT_REG_OFFSET);
BYTE bUSBBusIrqStat = ReadReg(pContext, USB_INT_REG_OFFSET);
while (bEpIrqStat || bUSBBusIrqStat) {
if (bUSBBusIrqStat) {
RETAILMSG(1, (_T("%s USB_INT_REG = 0x%02x/r/n"),
pszFname, bUSBBusIrqStat));
HandleUSBBusIrq(pContext, bUSBBusIrqStat);
}
if (bEpIrqStat) {
RETAILMSG(1, (_T("%s EP_INT_REG = 0x%02x/r/n"),
pszFname, bEpIrqStat));
if (bEpIrqStat & EP0_INT_INTR) {
HandleEndpoint0Event(pContext);
}
// Process All Other (besides EP0) Endpoints
for (DWORD dwEndpoint = 1; dwEndpoint < ENDPOINT_COUNT; ++dwEndpoint) {
// Check the Interrupt Mask
// Check the Interrupt Status
BYTE bEpBit = EpToIrqStatBit(dwEndpoint);
if (bEpIrqStat & bEpBit) {
HandleEndpointEvent(pContext, dwEndpoint, bEpIrqStat);
}
}
}
bEpIrqStat = ReadReg(pContext, EP_INT_REG_OFFSET);
bUSBBusIrqStat = ReadReg(pContext, USB_INT_REG_OFFSET);
}
FUNCTION_LEAVE_MSG();
}
3.HandleUSBBusIrq
HandleUSBBusIrq用来处理USBbus中段(RESET or RESUME or SUSPEND).实际上只处理了RESET中断.
如果是RESET中断,清除中断标志,设置fSpeedReported为false;
如果是UFN_DETACH状态,则通知MDD层UFN_ATTACH事件.
接着设置EP0状态为EP0_STATE_IDLE,并通知MDD层UFN_RESET事件.
+ expand sourceview plaincopy to clipboardprint?
static
VOID
HandleUSBBusIrq(
PCTRLR_PDD_CONTEXT pContext,
BYTE bUSBBusIrqStat
)
{
SETFNAME();
FUNCTION_ENTER_MSG();
if (bUSBBusIrqStat & USB_RESET_INTR) {
WriteReg(pContext, USB_INT_REG_OFFSET, USB_RESET_INTR);
RETAILMSG(1, (_T("%s Reset/r/n"), pszFname));
pContext->fSpeedReported = FALSE;
if (pContext->attachedState == UFN_DETACH){
pContext->pfnNotify(pContext->pvMddContext, UFN_MSG_BUS_EVENTS, UFN_ATTACH);
pContext->attachedState = UFN_ATTACH;
}
pContext->Ep0State = EP0_STATE_IDLE;
pContext->pfnNotify(pContext->pvMddContext, UFN_MSG_BUS_EVENTS, UFN_RESET);
}
FUNCTION_LEAVE_MSG();
}
static
VOID
HandleUSBBusIrq(
PCTRLR_PDD_CONTEXT pContext,
BYTE bUSBBusIrqStat
)
{
SETFNAME();
FUNCTION_ENTER_MSG();
if (bUSBBusIrqStat & USB_RESET_INTR) {
WriteReg(pContext, USB_INT_REG_OFFSET, USB_RESET_INTR);
RETAILMSG(1, (_T("%s Reset/r/n"), pszFname));
pContext->fSpeedReported = FALSE;
if (pContext->attachedState == UFN_DETACH){
pContext->pfnNotify(pContext->pvMddContext, UFN_MSG_BUS_EVENTS, UFN_ATTACH);
pContext->attachedState = UFN_ATTACH;
}
pContext->Ep0State = EP0_STATE_IDLE;
pContext->pfnNotify(pContext->pvMddContext, UFN_MSG_BUS_EVENTS, UFN_RESET);
}
FUNCTION_LEAVE_MSG();
}
4.HandleEndpoint0Event
当EP0中断发生时,被会调用HandleEndpoint0Event.
首先进行参数检查,获取EP0状态结构PEP_STATUS保存到peps.然后清除EP0中断状态.然后设置INDEX_REG为EP0读取EP0_CSR.
当中断状态为SETUP_END配置结束时,写EP0_CSR寄存器清除SETUP_END,将要写的值先保存在bEp0CsrToWrite中,最后一起写入.同时设置EP0状态为IDLE.
接着获取EP0状态结构的PSTransfer,如果存在,调用CompleteTransfer通知MDD UFN_MSG_TRANSFER_COMPLETE事件.
if (bEP0IrqStatus & SETUP_END) {
bEp0CsrToWrite |= SERVICED_SETUP_END;
pContext->Ep0State = EP0_STATE_IDLE;
PSTransfer pTransfer = peps->pTransfer;
// Make MDD think everything is ok.
if(pTransfer) {
pContext->sendDataEnd = FALSE;
CompleteTransfer(pContext, peps, UFN_NO_ERROR);
}
}
如果是SENT_STALL状态,即协议冲突发生时,设置Ep0State为EP0_STATE_IDLE,交由下面的代码继续处理.
if (bEP0IrqStatus & EP0_SENT_STALL) {
pContext->Ep0State = EP0_STATE_IDLE;
}
接着读取OUT_FIFO_CNT1寄存器,该寄存器保存了写出数据的字节数低位.
DWORD cbFifo = ReadIndexedReg(pContext, 0, OUT_FIFO_CNT1_REG_OFFSET);
当Ep0State状态为EP0_STATE_IDLE时,判断bEP0IrqStatus是否是EP0_OUT_PACKET_RDY(写数据包准备好),如果未通知MDD速度则发出FULL SPEED的通知.
然后准备发送配置包,读取要发送包的字节数,获得USB Device Request指针:
const DWORD cbOutFifo = ReadIndexedReg(pContext, 0,
OUT_FIFO_CNT1_REG_OFFSET);
PBYTE pbUdr = (PBYTE) &pContext->udr;
读取FIFO数据(EP0_FIFO_REG)到pbUdr
volatile ULONG *pulFifoReg = _GetDataRegister(0);
DWORD cbBytesRemaining = cbOutFifo;
while (cbBytesRemaining--) {
*pbUdr++ = (BYTE) *pulFifoReg;
}
如果数据大小不匹配,设置bEp0CsrToWrite,清除相应状态标志.
if (cbOutFifo != sizeof(pContext->udr)) {
// Ideally this should not hapen. This is a recovery mechanism if
// we get out of sync somehow.
bEp0CsrToWrite |= (EP0_SEND_STALL | SERVICED_OUT_PKT_RDY |
DATA_END);
}
否则对读到的数据进行解析,如果数据长度大于0,获得传输方向:
if (pContext->udr.bmRequestType & USB_ENDPOINT_DIRECTION_MASK) {
// Start the SW OUT State Machine
pContext->Ep0State = EP0_STATE_IN_DATA_PHASE;
}
else {
// Start the SW OUT State Machine
pContext->Ep0State = EP0_STATE_OUT_DATA_PHASE;
}
如果数据长度为0,设置sendDataEnd为TRUE表示数据传输完成.
接下来根据上面获得是发出数据还是接收数据状态,调用不同的处理流程.
如果是接收数据(EP0_STATE_OUT_DATA_PHASE),并且OUT_PACKET_READY数据准备好,调用HandleRx:
bEp0CsrToWrite |= HandleRx(pContext, peps, &fCompleted, &dwStatus);
否则设置状态为EP0_STATE_IDLE.
如果是发送数据(EP0_STATE_IN_DATA_PHASE),当OUT_PACKET_READY和cbFifo为0(控制传输结束)或者IN_PACKET_READY,调用HandleTx:
if (bHandleTx) {
bEp0CsrToWrite |= HandleTx(pContext, peps, 0);
}
最后然后清除中断状态,调用CompleteTransfer置pTransfer为NULL,并如果通知MDD UFN_MSG_TRANSFER_COMPLETE事件.
如果向UDR发送数据(fSendUdr为TRUE)通知MDD UFN_MSG_SETUP_PACKET事件.
+ expand sourceview plaincopy to clipboardprint?
static
VOID
HandleEndpoint0Event(
PCTRLR_PDD_CONTEXT pContext
)
{
SETFNAME();
FUNCTION_ENTER_MSG();
ValidateContext(pContext);
DEBUGCHK(pContext->fRunning);
PEP_STATUS peps = GetEpStatus(pContext, 0);
LOCK_ENDPOINT(peps);
ClearEndpointInterrupt(pContext, 0);
BYTE bEP0IrqStatus = ReadIndexedReg(pContext, 0, EP0_CSR_REG_OFFSET);
RETAILMSG(1, (_T("%s EP0_CSR_REG = 0x%02x. Data phase = %u/r/n"),
pszFname, bEP0IrqStatus, pContext->Ep0State));
// Write 0 to SEND_STALL and SENT_STALL to clear them, so we need to
// leave them unchanged by default.
BYTE bEp0CsrToWrite = 0;
if (bEP0IrqStatus & SETUP_END) {
bEp0CsrToWrite |= SERVICED_SETUP_END;
pContext->Ep0State = EP0_STATE_IDLE;
PSTransfer pTransfer = peps->pTransfer;
// Make MDD think everything is ok.
if(pTransfer) {
pContext->sendDataEnd = FALSE;
CompleteTransfer(pContext, peps, UFN_NO_ERROR);
}
RETAILMSG(1, (_T("%s Setup End, %x/r/n"),
pszFname, bEP0IrqStatus));
}
// Set By USB if protocol violation detected
if (bEP0IrqStatus & EP0_SENT_STALL) {
// Must Clear both Send and Sent Stall
pContext->Ep0State = EP0_STATE_IDLE;
}
BOOL fSendUdr = FALSE;
DWORD cbFifo = ReadIndexedReg(pContext, 0, OUT_FIFO_CNT1_REG_OFFSET);
BOOL fCompleted = FALSE;
DWORD dwStatus;
if (pContext->Ep0State == EP0_STATE_IDLE) {
if (bEP0IrqStatus & EP0_OUT_PACKET_RDY) {
if (pContext->fSpeedReported == FALSE) {
// After Every Reset Notify MDD of Speed setting.
// This device can only support FULL Speed.
pContext->pfnNotify(pContext->pvMddContext, UFN_MSG_BUS_SPEED,
BS_FULL_SPEED);
pContext->fSpeedReported = TRUE;
}
// New setup packet
const DWORD cbOutFifo = ReadIndexedReg(pContext, 0,
OUT_FIFO_CNT1_REG_OFFSET);
PBYTE pbUdr = (PBYTE) &pContext->udr;
volatile ULONG *pulFifoReg = _GetDataRegister(0);
DWORD cbBytesRemaining = cbOutFifo;
while (cbBytesRemaining--) {
*pbUdr++ = (BYTE) *pulFifoReg;
}
if (cbOutFifo != sizeof(pContext->udr)) {
RETAILMSG(1, (_T("%s Setup packet was only %x bytes!/r/n"),
pszFname, cbOutFifo));
// Ideally this should not hapen. This is a recovery mechanism if
// we get out of sync somehow.
bEp0CsrToWrite |= (EP0_SEND_STALL | SERVICED_OUT_PKT_RDY |
DATA_END);
}
else {
// Parse the Setup Command this is necessary to Configure the
// SW State Machine and to set bits to enable the HW to
// ACK/NAK correctly.
// Determine if this is a NO Data Packet
if (pContext->udr.wLength > 0) {
// Determine transfer Direction
if (pContext->udr.bmRequestType & USB_ENDPOINT_DIRECTION_MASK) {
// Start the SW OUT State Machine
pContext->Ep0State = EP0_STATE_IN_DATA_PHASE;
}
else {
// Start the SW OUT State Machine
pContext->Ep0State = EP0_STATE_OUT_DATA_PHASE;
}
pContext->sendDataEnd = FALSE;
}
else { // udr.wLength == 0
// ClientDriver will issue a SendControlStatusHandshake to
// complete the transaction.
pContext->sendDataEnd = TRUE;
// Nothing left to do... stay in IDLE.
DEBUGCHK(pContext->Ep0State == EP0_STATE_IDLE);
}
fSendUdr = TRUE;
}
}
}
else if (pContext->Ep0State == EP0_STATE_OUT_DATA_PHASE) {
if (bEP0IrqStatus & OUT_PACKET_READY) {
bEp0CsrToWrite |= SERVICED_OUT_PKT_RDY;
// Check For out packet read && receive fifo not empty -> out token event
if (cbFifo) {
RETAILMSG(1, (_T("%s out token packet on endpoint 0 /r/n"),
pszFname));
bEp0CsrToWrite |= HandleRx(pContext, peps, &fCompleted, &dwStatus);
}
// status stage of control transfer; zero-length packet received
else {
RETAILMSG(1, (_T("%s status stage of control transfer on endpoint 0/r/n"),
pszFname));
pContext->Ep0State = EP0_STATE_IDLE;
}
}
}
else {
RETAILMSG(1, (_T("%s Data Phase In Token/r/n"), pszFname));
DEBUGCHK(pContext->Ep0State == EP0_STATE_IN_DATA_PHASE);
BOOL bHandleTx = FALSE;
// IN Data Phase and IPR is cleared
// Check For status stage - End control transfer; zero-length packet received
if ( (bEP0IrqStatus & OUT_PACKET_READY) && (cbFifo == 0) ) {
bHandleTx = TRUE;
RETAILMSG(1, (_T("%s In - end xfer/r/n"),
pszFname));
}
else if ((bEP0IrqStatus & IN_PACKET_READY) == 0) {
bHandleTx = TRUE;
}
if (bHandleTx) {
bEp0CsrToWrite |= HandleTx(pContext, peps, 0);
}
}
// Clear any interrupts
RETAILMSG(1, (_T("%s Writing 0x%02x to EP0_CSR/r/n"), pszFname, bEp0CsrToWrite));
WriteIndexedReg(pContext, 0, EP0_CSR_REG_OFFSET, bEp0CsrToWrite);
if (fCompleted) {
CompleteTransfer(pContext, peps, dwStatus);
}
if (fSendUdr) {
pContext->pfnNotify(pContext->pvMddContext, UFN_MSG_SETUP_PACKET, (DWORD) &pContext->udr);
}
FUNCTION_LEAVE_MSG();
UNLOCK_ENDPOINT(peps);
}
static
VOID
HandleEndpoint0Event(
PCTRLR_PDD_CONTEXT pContext
)
{
SETFNAME();
FUNCTION_ENTER_MSG();
ValidateContext(pContext);
DEBUGCHK(pContext->fRunning);
PEP_STATUS peps = GetEpStatus(pContext, 0);
LOCK_ENDPOINT(peps);
ClearEndpointInterrupt(pContext, 0);
BYTE bEP0IrqStatus = ReadIndexedReg(pContext, 0, EP0_CSR_REG_OFFSET);
RETAILMSG(1, (_T("%s EP0_CSR_REG = 0x%02x. Data phase = %u/r/n"),
pszFname, bEP0IrqStatus, pContext->Ep0State));
// Write 0 to SEND_STALL and SENT_STALL to clear them, so we need to
// leave them unchanged by default.
BYTE bEp0CsrToWrite = 0;
if (bEP0IrqStatus & SETUP_END) {
bEp0CsrToWrite |= SERVICED_SETUP_END;
pContext->Ep0State = EP0_STATE_IDLE;
PSTransfer pTransfer = peps->pTransfer;
// Make MDD think everything is ok.
if(pTransfer) {
pContext->sendDataEnd = FALSE;
CompleteTransfer(pContext, peps, UFN_NO_ERROR);
}
RETAILMSG(1, (_T("%s Setup End, %x/r/n"),
pszFname, bEP0IrqStatus));
}
// Set By USB if protocol violation detected
if (bEP0IrqStatus & EP0_SENT_STALL) {
// Must Clear both Send and Sent Stall
pContext->Ep0State = EP0_STATE_IDLE;
}
BOOL fSendUdr = FALSE;
DWORD cbFifo = ReadIndexedReg(pContext, 0, OUT_FIFO_CNT1_REG_OFFSET);
BOOL fCompleted = FALSE;
DWORD dwStatus;
if (pContext->Ep0State == EP0_STATE_IDLE) {
if (bEP0IrqStatus & EP0_OUT_PACKET_RDY) {
if (pContext->fSpeedReported == FALSE) {
// After Every Reset Notify MDD of Speed setting.
// This device can only support FULL Speed.
pContext->pfnNotify(pContext->pvMddContext, UFN_MSG_BUS_SPEED,
BS_FULL_SPEED);
pContext->fSpeedReported = TRUE;
}
// New setup packet
const DWORD cbOutFifo = ReadIndexedReg(pContext, 0,
OUT_FIFO_CNT1_REG_OFFSET);
PBYTE pbUdr = (PBYTE) &pContext->udr;
volatile ULONG *pulFifoReg = _GetDataRegister(0);
DWORD cbBytesRemaining = cbOutFifo;
while (cbBytesRemaining--) {
*pbUdr++ = (BYTE) *pulFifoReg;
}
if (cbOutFifo != sizeof(pContext->udr)) {
RETAILMSG(1, (_T("%s Setup packet was only %x bytes!/r/n"),
pszFname, cbOutFifo));
// Ideally this should not hapen. This is a recovery mechanism if
// we get out of sync somehow.
bEp0CsrToWrite |= (EP0_SEND_STALL | SERVICED_OUT_PKT_RDY |
DATA_END);
}
else {
// Parse the Setup Command this is necessary to Configure the
// SW State Machine and to set bits to enable the HW to
// ACK/NAK correctly.
// Determine if this is a NO Data Packet
if (pContext->udr.wLength > 0) {
// Determine transfer Direction
if (pContext->udr.bmRequestType & USB_ENDPOINT_DIRECTION_MASK) {
// Start the SW OUT State Machine
pContext->Ep0State = EP0_STATE_IN_DATA_PHASE;
}
else {
// Start the SW OUT State Machine
pContext->Ep0State = EP0_STATE_OUT_DATA_PHASE;
}
pContext->sendDataEnd = FALSE;
}
else { // udr.wLength == 0
// ClientDriver will issue a SendControlStatusHandshake to
// complete the transaction.
pContext->sendDataEnd = TRUE;
// Nothing left to do... stay in IDLE.
DEBUGCHK(pContext->Ep0State == EP0_STATE_IDLE);
}
fSendUdr = TRUE;
}
}
}
else if (pContext->Ep0State == EP0_STATE_OUT_DATA_PHASE) {
if (bEP0IrqStatus & OUT_PACKET_READY) {
bEp0CsrToWrite |= SERVICED_OUT_PKT_RDY;
// Check For out packet read && receive fifo not empty -> out token event
if (cbFifo) {
RETAILMSG(1, (_T("%s out token packet on endpoint 0 /r/n"),
pszFname));
bEp0CsrToWrite |= HandleRx(pContext, peps, &fCompleted, &dwStatus);
}
// status stage of control transfer; zero-length packet received
else {
RETAILMSG(1, (_T("%s status stage of control transfer on endpoint 0/r/n"),
pszFname));
pContext->Ep0State = EP0_STATE_IDLE;
}
}
}
else {
RETAILMSG(1, (_T("%s Data Phase In Token/r/n"), pszFname));
DEBUGCHK(pContext->Ep0State == EP0_STATE_IN_DATA_PHASE);
BOOL bHandleTx = FALSE;
// IN Data Phase and IPR is cleared
// Check For status stage - End control transfer; zero-length packet received
if ( (bEP0IrqStatus & OUT_PACKET_READY) && (cbFifo == 0) ) {
bHandleTx = TRUE;
RETAILMSG(1, (_T("%s In - end xfer/r/n"),
pszFname));
}
else if ((bEP0IrqStatus & IN_PACKET_READY) == 0) {
bHandleTx = TRUE;
}
if (bHandleTx) {
bEp0CsrToWrite |= HandleTx(pContext, peps, 0);
}
}
// Clear any interrupts
RETAILMSG(1, (_T("%s Writing 0x%02x to EP0_CSR/r/n"), pszFname, bEp0CsrToWrite));
WriteIndexedReg(pContext, 0, EP0_CSR_REG_OFFSET, bEp0CsrToWrite);
if (fCompleted) {
CompleteTransfer(pContext, peps, dwStatus);
}
if (fSendUdr) {
pContext->pfnNotify(pContext->pvMddContext, UFN_MSG_SETUP_PACKET, (DWORD) &pContext->udr);
}
FUNCTION_LEAVE_MSG();
UNLOCK_ENDPOINT(peps);
}
5.HandleRx
HandleRx用来读取端点数据.
首先获取端点号dwEndpoint,传输结构pTransfer,如果为TRUE,则验证传输方向,然后获得端点FIFO数据地址:
volatile ULONG *pulFifoReg = _GetDataRegister(dwEndpoint);
然后获取buffer地址pbBuffer,剩余buffer大小,读取数据的大小cbFifo,实际读取数据大小cbRead,接着将pulFifoReg地址的FIFO数据读取到pbBuffer中.
PBYTE pbBuffer = (PBYTE)pTransfer->pvBuffer + pTransfer->cbTransferred;
DWORD cbBuffer = pTransfer->cbBuffer - pTransfer->cbTransferred;
DWORD cbFifo = ReadIndexedReg(pContext, dwEndpoint, OUT_FIFO_CNT1_REG_OFFSET);
DEBUGCHK(cbFifo <= peps->dwPacketSizeAssigned);
// Read from the FIFO
const DWORD cbRead = min(cbFifo, cbBuffer);
DWORD cbToRead = cbRead;
while (cbToRead--) {
*pbBuffer++ = (BYTE) *pulFifoReg;
}
同时设置已传输数据大小:
pTransfer->cbTransferred += cbRead;
如果是short packet或者buffer已满,则结束传输清除Out Packet Ready,如果是端点0设置DATA_END的返回值.
+ expand sourceview plaincopy to clipboardprint?
static
BYTE
HandleRx(
PCTRLR_PDD_CONTEXT pContext,
PEP_STATUS peps,
PBOOL pfCompleted,
PDWORD pdwStatus
)
{
BOOL fCompleted = FALSE;
DWORD dwStatus = ERROR_GEN_FAILURE;
DWORD dwEndpoint = peps->dwEndpointNumber;
BYTE bRet = 0;
SETFNAME();
FUNCTION_ENTER_MSG();
PSTransfer pTransfer = peps->pTransfer;
pTransfer = peps->pTransfer;
if (pTransfer) {
DEBUGCHK(pTransfer->dwFlags == USB_OUT_TRANSFER);
DEBUGCHK(pTransfer->dwUsbError == UFN_NOT_COMPLETE_ERROR);
ValidateTransferDirection(pContext, peps, pTransfer);
DEBUGCHK(peps->fInitialized);
DWORD dwCurrentPermissions = GetCurrentPermissions();
SetProcPermissions(pTransfer->dwCallerPermissions);
__try {
volatile ULONG *pulFifoReg = _GetDataRegister(dwEndpoint);
DEBUGCHK(pulFifoReg != NULL);
PBYTE pbBuffer = (PBYTE)pTransfer->pvBuffer + pTransfer->cbTransferred;
DWORD cbBuffer = pTransfer->cbBuffer - pTransfer->cbTransferred;
DWORD cbFifo = ReadIndexedReg(pContext, dwEndpoint, OUT_FIFO_CNT1_REG_OFFSET);
DEBUGCHK(cbFifo <= peps->dwPacketSizeAssigned);
// Read from the FIFO
const DWORD cbRead = min(cbFifo, cbBuffer);
DWORD cbToRead = cbRead;
while (cbToRead--) {
*pbBuffer++ = (BYTE) *pulFifoReg;
}
DEBUGCHK(cbRead <= pTransfer->cbBuffer - pTransfer->cbTransferred);
pTransfer->cbTransferred += cbRead;
if ( (cbRead < peps->dwPacketSizeAssigned) ||
(pTransfer->cbTransferred == pTransfer->cbBuffer) ) {
// Short packet or filled buffer. Complete transfer.
fCompleted = TRUE;
dwStatus = UFN_NO_ERROR;
}
if (dwEndpoint == 0) {
bRet |= SERVICED_OUT_PKT_RDY;
if (fCompleted) {
bRet |= DATA_END;
pContext->Ep0State = EP0_STATE_IDLE;
}
}
else {
// Clear Out Packet Ready - Allow next packet to come in.
DEBUGCHK( (bRet & OUT_PACKET_READY) == 0);
}
}
__except(EXCEPTION_EXECUTE_HANDLER) {
RETAILMSG(1, (_T("%s Exception!/r/n"), pszFname));
fCompleted = TRUE;
dwStatus = UFN_CLIENT_BUFFER_ERROR;
}
SetProcPermissions(dwCurrentPermissions);
RETAILMSG(1, (_T("%s Rx Ep%x BufferSize=%u,Xfrd=%u /r/n"),
pszFname, dwEndpoint, pTransfer->cbBuffer, pTransfer->cbTransferred));
if (fCompleted) {
RETAILMSG(1, (_T("%s RxDone Ep%x BufferSize=%u, Xfrd=%u/r/n"),
pszFname, dwEndpoint,pTransfer->cbBuffer, pTransfer->cbTransferred));
}
}
*pfCompleted = fCompleted;
*pdwStatus = dwStatus;
FUNCTION_LEAVE_MSG();
return bRet;
}
static
BYTE
HandleRx(
PCTRLR_PDD_CONTEXT pContext,
PEP_STATUS peps,
PBOOL pfCompleted,
PDWORD pdwStatus
)
{
BOOL fCompleted = FALSE;
DWORD dwStatus = ERROR_GEN_FAILURE;
DWORD dwEndpoint = peps->dwEndpointNumber;
BYTE bRet = 0;
SETFNAME();
FUNCTION_ENTER_MSG();
PSTransfer pTransfer = peps->pTransfer;
pTransfer = peps->pTransfer;
if (pTransfer) {
DEBUGCHK(pTransfer->dwFlags == USB_OUT_TRANSFER);
DEBUGCHK(pTransfer->dwUsbError == UFN_NOT_COMPLETE_ERROR);
ValidateTransferDirection(pContext, peps, pTransfer);
DEBUGCHK(peps->fInitialized);
DWORD dwCurrentPermissions = GetCurrentPermissions();
SetProcPermissions(pTransfer->dwCallerPermissions);
__try {
volatile ULONG *pulFifoReg = _GetDataRegister(dwEndpoint);
DEBUGCHK(pulFifoReg != NULL);
PBYTE pbBuffer = (PBYTE)pTransfer->pvBuffer + pTransfer->cbTransferred;
DWORD cbBuffer = pTransfer->cbBuffer - pTransfer->cbTransferred;
DWORD cbFifo = ReadIndexedReg(pContext, dwEndpoint, OUT_FIFO_CNT1_REG_OFFSET);
DEBUGCHK(cbFifo <= peps->dwPacketSizeAssigned);
// Read from the FIFO
const DWORD cbRead = min(cbFifo, cbBuffer);
DWORD cbToRead = cbRead;
while (cbToRead--) {
*pbBuffer++ = (BYTE) *pulFifoReg;
}
DEBUGCHK(cbRead <= pTransfer->cbBuffer - pTransfer->cbTransferred);
pTransfer->cbTransferred += cbRead;
if ( (cbRead < peps->dwPacketSizeAssigned) ||
(pTransfer->cbTransferred == pTransfer->cbBuffer) ) {
// Short packet or filled buffer. Complete transfer.
fCompleted = TRUE;
dwStatus = UFN_NO_ERROR;
}
if (dwEndpoint == 0) {
bRet |= SERVICED_OUT_PKT_RDY;
if (fCompleted) {
bRet |= DATA_END;
pContext->Ep0State = EP0_STATE_IDLE;
}
}
else {
// Clear Out Packet Ready - Allow next packet to come in.
DEBUGCHK( (bRet & OUT_PACKET_READY) == 0);
}
}
__except(EXCEPTION_EXECUTE_HANDLER) {
RETAILMSG(1, (_T("%s Exception!/r/n"), pszFname));
fCompleted = TRUE;
dwStatus = UFN_CLIENT_BUFFER_ERROR;
}
SetProcPermissions(dwCurrentPermissions);
RETAILMSG(1, (_T("%s Rx Ep%x BufferSize=%u,Xfrd=%u /r/n"),
pszFname, dwEndpoint, pTransfer->cbBuffer, pTransfer->cbTransferred));
if (fCompleted) {
RETAILMSG(1, (_T("%s RxDone Ep%x BufferSize=%u, Xfrd=%u/r/n"),
pszFname, dwEndpoint,pTransfer->cbBuffer, pTransfer->cbTransferred));
}
}
*pfCompleted = fCompleted;
*pdwStatus = dwStatus;
FUNCTION_LEAVE_MSG();
return bRet;
}
6.HandleTx
HandleTx用来写数据到端点.
首先获取端点号dwEndpoint,传输结构pTransfer,如果为TRUE,则验证传输方向.
然后获取buffer地址pbBuffer,剩余buffer大小,数据FIFO地址pulFifoReg,写数据大小cbToWrite,读取IN_CSR1_REG寄存器
PBYTE pbBuffer = (PBYTE) pTransfer->pvBuffer + pTransfer->cbTransferred;
DWORD cbBuffer = pTransfer->cbBuffer - pTransfer->cbTransferred;
volatile ULONG *pulFifoReg = _GetDataRegister(dwEndpoint);
DWORD cbWritten = 0;
// Min of input byte count and supported size
DWORD cbToWrite = min(cbBuffer, peps->dwPacketSizeAssigned);
BYTE bRegStatus = ReadIndexedReg(pContext, dwEndpoint, IN_CSR1_REG_OFFSET);
如果是端点0.将pbBuffer的数据写入FIFO寄存器:
if (dwEndpoint == 0) {
for (cbWritten = 0; cbWritten < cbToWrite; cbWritten++) {
*pulFifoReg = *pbBuffer++;
}
如果Buffer已满或者数据长度为0,则标志数据传输完成.设置EP状态为IDLE以及DATA_END的返回值.
如果数据大于0,或者buffer已满,设置EP0_IN_PACKET_RDY
如果数据长度为0,设置EP0_IN_PACKET_RDY,并置pTransfer->pvPddData为0.
如果是其他端点,使能端点中断:
EnableEndpointInterrupt(pContext, dwEndpoint);
写数据到FIFO:
for (cbWritten = 0; cbWritten < cbToWrite; cbWritten++) {
*pulFifoReg = (ULONG) *pbBuffer++;
}
如果Buffer已满或者数据长度为0,则标志传输完成.否则设置IN_PACKET_READY,并更新已发送数据大小.
数据传输完成后(fCompleted为TRUE),禁止端点中断,通知MDD传输完成:
DisableEndpointInterrupt(pContext, peps->dwEndpointNumber);
CompleteTransfer(pContext, peps, dwStatus);
+ expand sourceview plaincopy to clipboardprint?
static
BYTE
HandleTx(
PCTRLR_PDD_CONTEXT pContext,
PEP_STATUS peps,
BOOL fEnableInterrupts
)
{
SETFNAME();
DEBUGCHK(pContext);
PREFAST_DEBUGCHK(peps);
// This routine can be entered from both ISTMain and MDD/Client threads so
// need critical section.
FUNCTION_ENTER_MSG();
BYTE bRet = 0;
BOOL fCompleted = FALSE;
PSTransfer pTransfer = peps->pTransfer;
DWORD dwStatus = ERROR_GEN_FAILURE;
DEBUGCHK(peps->fInitialized);
DWORD dwEndpoint = peps->dwEndpointNumber;
pTransfer = peps->pTransfer;
if (pTransfer) {
ValidateTransferDirection(pContext, peps, pTransfer);
DEBUGCHK(pTransfer->dwFlags == USB_IN_TRANSFER);
DEBUGCHK(pTransfer->dwUsbError == UFN_NOT_COMPLETE_ERROR);
DWORD dwCurrentPermissions = GetCurrentPermissions();
SetProcPermissions(pTransfer->dwCallerPermissions);
// Transfer is ready
__try {
PBYTE pbBuffer = (PBYTE) pTransfer->pvBuffer + pTransfer->cbTransferred;
DWORD cbBuffer = pTransfer->cbBuffer - pTransfer->cbTransferred;
volatile ULONG *pulFifoReg = _GetDataRegister(dwEndpoint);
DWORD cbWritten = 0;
// Min of input byte count and supported size
DWORD cbToWrite = min(cbBuffer, peps->dwPacketSizeAssigned);
BYTE bRegStatus = ReadIndexedReg(pContext, dwEndpoint,
IN_CSR1_REG_OFFSET);
RETAILMSG(1, (_T("%s Tx on EP %u, Bytes = %u/r/n"),
pszFname, dwEndpoint, cbToWrite));
if (dwEndpoint == 0) {
for (cbWritten = 0; cbWritten < cbToWrite; cbWritten++) {
*pulFifoReg = *pbBuffer++;
}
/* We can complete on a packet which is full. We need to wait till
* next time and generate a zero length packet, so only complete
* if we're at the end and it is not the max packet size.
*/
pTransfer->cbTransferred += cbWritten;
if (pTransfer->cbTransferred == pTransfer->cbBuffer && pTransfer->pvPddData == 0) {
dwStatus = UFN_NO_ERROR;
fCompleted = TRUE;
pContext->Ep0State = EP0_STATE_IDLE;
bRet |= DATA_END;
}
/* Update the register - Set the IPR bit
and possibly data end*/
if( (cbWritten > 0) || (pTransfer->cbBuffer == 0) ) {
bRet |= EP0_IN_PACKET_RDY;
}
// Also set IPR if a 0 byte packet needs to go out.
else if(pTransfer->pvPddData) {
bRet |= EP0_IN_PACKET_RDY;
pTransfer->pvPddData = 0;
}
}
else {
// Enable Interrupts before writing to the FIFO. This insures
// That any interrupts generated because of the write will be
// "latched"
if (fEnableInterrupts) {
DEBUGCHK(dwEndpoint != 0);
EnableEndpointInterrupt(pContext, dwEndpoint);
}
// Write to the FIFO directly to send the bytes.
for (cbWritten = 0; cbWritten < cbToWrite; cbWritten++) {
*pulFifoReg = (ULONG) *pbBuffer++;
}
// By Placing the check for packet complete here, before
// cbTransferred is updated, there is a 1 interrupt cycle delay
// That is complete is not declared until the data has actually
// been ACKd (TPC set) by the host
if ( (pTransfer->cbTransferred == pTransfer->cbBuffer) ||
(cbWritten == 0) ){
fCompleted = TRUE;
dwStatus = UFN_NO_ERROR;
}
else {
/* Set In Packet Ready , this will Tells the HW the FIFO is
ready to be transitted to be sent
*/
bRet |= IN_PACKET_READY;
}
// Update the Transfered Count
pTransfer->cbTransferred += cbWritten;
}
}
__except(EXCEPTION_EXECUTE_HANDLER) {
RETAILMSG(1, (_T("%s Exception!/r/n"), pszFname));
fCompleted = TRUE;
dwStatus = UFN_CLIENT_BUFFER_ERROR;
}
SetProcPermissions(dwCurrentPermissions);
}
else {
// It is possible for an interrupt to come in while still in this
// function for first pass of transfer. If this happens it is possible
// to complete the transfer and have that interrupt be unnecessary
// so... just ignore it.
goto EXIT;
}
if (fCompleted) {
// Disable transfer interrupts until another transfer is issued.
if (peps->dwEndpointNumber != 0) {
DisableEndpointInterrupt(pContext, peps->dwEndpointNumber);
}
RETAILMSG(1, (_T("%s Tx Done Ep%x Status %u/r/n"), pszFname,
dwEndpoint, dwStatus));
CompleteTransfer(pContext, peps, dwStatus);
}
else {
RETAILMSG(1, (_T("%s Tx EP%x BufferSize=%u, Xfrd=%u/r/n"),
pszFname, dwEndpoint, pTransfer->cbBuffer, pTransfer->cbTransferred));
}
EXIT:
FUNCTION_LEAVE_MSG();
return bRet;
}
static
BYTE
HandleTx(
PCTRLR_PDD_CONTEXT pContext,
PEP_STATUS peps,
BOOL fEnableInterrupts
)
{
SETFNAME();
DEBUGCHK(pContext);
PREFAST_DEBUGCHK(peps);
// This routine can be entered from both ISTMain and MDD/Client threads so
// need critical section.
FUNCTION_ENTER_MSG();
BYTE bRet = 0;
BOOL fCompleted = FALSE;
PSTransfer pTransfer = peps->pTransfer;
DWORD dwStatus = ERROR_GEN_FAILURE;
DEBUGCHK(peps->fInitialized);
DWORD dwEndpoint = peps->dwEndpointNumber;
pTransfer = peps->pTransfer;
if (pTransfer) {
ValidateTransferDirection(pContext, peps, pTransfer);
DEBUGCHK(pTransfer->dwFlags == USB_IN_TRANSFER);
DEBUGCHK(pTransfer->dwUsbError == UFN_NOT_COMPLETE_ERROR);
DWORD dwCurrentPermissions = GetCurrentPermissions();
SetProcPermissions(pTransfer->dwCallerPermissions);
// Transfer is ready
__try {
PBYTE pbBuffer = (PBYTE) pTransfer->pvBuffer + pTransfer->cbTransferred;
DWORD cbBuffer = pTransfer->cbBuffer - pTransfer->cbTransferred;
volatile ULONG *pulFifoReg = _GetDataRegister(dwEndpoint);
DWORD cbWritten = 0;
// Min of input byte count and supported size
DWORD cbToWrite = min(cbBuffer, peps->dwPacketSizeAssigned);
BYTE bRegStatus = ReadIndexedReg(pContext, dwEndpoint,
IN_CSR1_REG_OFFSET);
RETAILMSG(1, (_T("%s Tx on EP %u, Bytes = %u/r/n"),
pszFname, dwEndpoint, cbToWrite));
if (dwEndpoint == 0) {
for (cbWritten = 0; cbWritten < cbToWrite; cbWritten++) {
*pulFifoReg = *pbBuffer++;
}
/* We can complete on a packet which is full. We need to wait till
* next time and generate a zero length packet, so only complete
* if we're at the end and it is not the max packet size.
*/
pTransfer->cbTransferred += cbWritten;
if (pTransfer->cbTransferred == pTransfer->cbBuffer && pTransfer->pvPddData == 0) {
dwStatus = UFN_NO_ERROR;
fCompleted = TRUE;
pContext->Ep0State = EP0_STATE_IDLE;
bRet |= DATA_END;
}
/* Update the register - Set the IPR bit
and possibly data end*/
if( (cbWritten > 0) || (pTransfer->cbBuffer == 0) ) {
bRet |= EP0_IN_PACKET_RDY;
}
// Also set IPR if a 0 byte packet needs to go out.
else if(pTransfer->pvPddData) {
bRet |= EP0_IN_PACKET_RDY;
pTransfer->pvPddData = 0;
}
}
else {
// Enable Interrupts before writing to the FIFO. This insures
// That any interrupts generated because of the write will be
// "latched"
if (fEnableInterrupts) {
DEBUGCHK(dwEndpoint != 0);
EnableEndpointInterrupt(pContext, dwEndpoint);
}
// Write to the FIFO directly to send the bytes.
for (cbWritten = 0; cbWritten < cbToWrite; cbWritten++) {
*pulFifoReg = (ULONG) *pbBuffer++;
}
// By Placing the check for packet complete here, before
// cbTransferred is updated, there is a 1 interrupt cycle delay
// That is complete is not declared until the data has actually
// been ACKd (TPC set) by the host
if ( (pTransfer->cbTransferred == pTransfer->cbBuffer) ||
(cbWritten == 0) ){
fCompleted = TRUE;
dwStatus = UFN_NO_ERROR;
}
else {
/* Set In Packet Ready , this will Tells the HW the FIFO is
ready to be transitted to be sent
*/
bRet |= IN_PACKET_READY;
}
// Update the Transfered Count
pTransfer->cbTransferred += cbWritten;
}
}
__except(EXCEPTION_EXECUTE_HANDLER) {
RETAILMSG(1, (_T("%s Exception!/r/n"), pszFname));
fCompleted = TRUE;
dwStatus = UFN_CLIENT_BUFFER_ERROR;
}
SetProcPermissions(dwCurrentPermissions);
}
else {
// It is possible for an interrupt to come in while still in this
// function for first pass of transfer. If this happens it is possible
// to complete the transfer and have that interrupt be unnecessary
// so... just ignore it.
goto EXIT;
}
if (fCompleted) {
// Disable transfer interrupts until another transfer is issued.
if (peps->dwEndpointNumber != 0) {
DisableEndpointInterrupt(pContext, peps->dwEndpointNumber);
}
RETAILMSG(1, (_T("%s Tx Done Ep%x Status %u/r/n"), pszFname,
dwEndpoint, dwStatus));
CompleteTransfer(pContext, peps, dwStatus);
}
else {
RETAILMSG(1, (_T("%s Tx EP%x BufferSize=%u, Xfrd=%u/r/n"),
pszFname, dwEndpoint, pTransfer->cbBuffer, pTransfer->cbTransferred));
}
EXIT:
FUNCTION_LEAVE_MSG();
return bRet;
}
SMDK2410的USB Device Driver的PDD层驱动就简单解析到这里,如果错误,欢迎指正.,更多代码请参考sc2410pdd.cpp.要深入了解USB驱动还需要更多的阅读和实践.
本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/shevsten/archive/2010/08/10/5801723.aspx