s3c2410有2个USB Host接口,一个Device接口.首先介绍下USB的几个概念:
USB是主从结构的,PC是主端(Host),连接到PC上的设备就是从端(device或function).因此对应的驱动分别叫做USB
Host Driver及USB Function
Driver。我们的板子可以做host也可以做device,接U盘,鼠标到板子上,板子就是Host.板子连到PC作为Mass
Storage或者用Activesync连接就是function.
USB驱动结构图示:
这里我们实现的USB Function Driver就是板子和PC连接时板子端的USB驱动.USB Function
Driver包括Controller Driver和Client Driver.Controller
Driver介于硬件和Client Driver直之间的控制层,而Client Driver则是具体的应用,如Mass
Storage,RNDIS,Serial Class.
USB Function Driver包括MDD和PDD层,SMDK2410
BSP中的USBFN驱动实际上就是PDD部分.MDD部分在/WINCE500/PUBLIC/COMMON/OAK/DRIVERS/USBFN/CONTROLLER/MDD.在我们的PDD驱动中通过链接ufnmddbase.lib这个MDD库来生成驱动dll.
这里我们主要来看看USB Function Driver的PDD部分.实际上就一个文件,sc2410pdd.cpp.
USB Function
Driver也是一个标准的流接口驱动,流接口函数实现在/WINCE500/PUBLIC/COMMON/OAK/DRIVERS/USBFN/CONTROLLER/MDD/ufnmdd.cpp中.如UFN_***的函数,会调用在PDD中实现的函数.
1.UfnPdd_DllEntry
被MDD层的DllEntry调用,仅仅设置了g_pUDCBase为NULL.
// Called by MDD's DllEntry.
extern"C"
BOOL
UfnPdd_DllEntry(
HANDLE hDllHandle,
DWORD dwReason,
LPVOID lpReserved
)
{
SETFNAME();
switch(dwReason) {
caseDLL_PROCESS_ATTACH:
g_pUDCBase = NULL;
break;
}
returnTRUE;
}
// Called by MDD's DllEntry.
extern "C"
BOOL
UfnPdd_DllEntry(
HANDLE hDllHandle,
DWORD dwReason,
LPVOID lpReserved
)
{
SETFNAME();
switch (dwReason) {
case DLL_PROCESS_ATTACH:
g_pUDCBase = NULL;
break;
}
return TRUE;
}
2.UfnPdd_Init
这个会被MDD层的Init函数调用,首先初始化一个UFN_PDD_INTERFACE_INFO
接口的数组,里面定义了很多函数指针,这些PDD函数都会被MDD层调用.这些函数后面一一分析.
该结构原型如下:
// Structure that the PDD must fill out in UfnPdd_Init.
typedefstruct_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释放之前分配的资源.
// 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();
staticconstUFN_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));
gotoEXIT;
}
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));
gotoEXIT;
}
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 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));
gotoEXIT;
}
elseif(dwi.dwNumIoWindows != 1) {
DEBUGMSG(ZONE_ERROR, (_T("%s %d windows configured, expected 1/r/n"),
pszFname, dwi.dwNumIoWindows));
dwRet = ERROR_INVALID_DATA;
gotoEXIT;
}
elseif(dwi.ioWindows[0].dwLen
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;
gotoEXIT;
}
elseif(dwi.ioWindows[0].dwBase == 0){
DEBUGMSG(ZONE_INIT, (_T("%s no ioBase value specified/r/n"),pszFname));
dwRet = ERROR_INVALID_DATA;
gotoEXIT;
}
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));
gotoEXIT;
}
elseif( (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;
gotoEXIT;
}
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));
gotoEXIT;
}
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));
gotoEXIT;
}
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();
returndwRet;
}
// 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;