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

时间:2021-05-22 03:59:58

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驱动结构图示:
Windows CE USB Function Driver驱动简析(1)-驱动架构及UfnPdd函数(基于WinCE5.0 SMDK2410 BSP的UFBFN驱动)
这里我们实现的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.

2.UfnPdd_Init
这个会被MDD层的Init函数调用,首先初始化一个UFN_PDD_INTERFACE_INFO 接口的数组,里面定义了很多函数指针,这些PDD函数都会被MDD层调用.这些函数后面一一分析.
该结构原型如下:

接下来就是读取注册表获取相关配置信息,存到一个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释放之前分配的资源.

ResetDevice:
再来看看ResetDevice的具体内容,对UDC内部寄存器的操作有禁止UDC中断,禁止端点中断(EP0-EP4),清楚USB中断标志,清楚端点中断标志,然后调用ResetEndpoint对每个端点进行复位.

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中断.

FreeCtrlrContext:
FreeCtrlrContext首先释放g_pUDCBase映射的IO空间,然后调用CloseBusAccessHandle关闭总线驱动句柄,然后禁止中断唤醒,释放逻辑中断号.

下面就一个个来看UfnPdd_Init中声明的UFN_PDD_INTERFACE_INFO结构中的函数.
3.UfnPdd_Deinit
UfnPdd_Deinit用来进行资源的释放,检查完参数后就调用FreeCtrlrContext释放资源,如UDC寄存器地址空间的释放,SYSINTR和IRQ的释放等.

4.UfnPdd_Start
UfnPdd_Start用来启动USB Device设备.
一开始先创建中断事件hevInterrupt,并与SYSINTR号关联中断初始化.然后调用InterruptDone完成中断.创建对应的IST线程.如有错误,则跳转到EXIT禁止中断,释放事件句柄.

5.UfnPdd_Stop
UfnPdd_Stop用来停止USB Device设备.
主要工作为关闭中断线程,设置中断线程和事件句柄为NULL,调用ResetDevice复位设备,并标记运行状态fRunning为false.

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端点挂起和端点中断状态.

UfnPdd_ClearEndpointStall函数下面介绍,先来看看ClearEndpointInterrupt
ClearEndpointInterrupt用来清除EP_INT_REG寄存器对应端点的中断.
7.UfnPdd_DeinitEndpoint
UfnPdd_DeinitEndpoint通过调用ResetEndpoint和ClearEndpointInterrupt进行端点复位,屏蔽中断,清除端点中断状态.

8.UfnPdd_ClearEndpointStall
UfnPdd_ClearEndpointStall用来清除端点的stall停止状态.
首先依然是参数检查,然后获取端点信息保存到EP_STATUS结构peps中,如果是端点0,清除EP0_CSR的send和sent stall状态.如果是其他端点,根据传输方向设置IN_CSR1_REG或OUT_CSR1_REG清除send和sent stall状态.

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.

10.UfnPdd_IsConfigurationSupportable
这个函数没有做具体工作.

11.UfnPdd_IsEndpointSupportable
UfnPdd_IsEndpointSupportable用来查询指定端点是否支持.
首先判断是否是端点0,如果是端点0则检查其属性是否为CONTROL,并判断其做大包字节数,如果小于8,则设置成8.如果是其他端点,则需保证其属性不为CONTROL端点.然后根据不同的传输类型设置其包大小的范围.异步类型CPU UDC不支持,批量或中断传输硬件支持8,16,32,64字节的包, 如果要求设置的包大小超过范围,则设置支持的最大的值.

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

13.UfnPdd_SetAddress
UfnPdd_SetAddress用来设置USB设备地址.通过写ADDRESS寄存器0-6位为地址,第7为写1通知地址更新.

14.UfnPdd_InitiateRemoteWakeup
UfnPdd_InitiateRemoteWakeup用来初始化远程唤醒.通过写PWR_REG置位MCU_RESUME.

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.

关于SetPowerState函数
首先根据新的电源状态进行调整,如果是D1,D2,D4将调整为D0.然后如果新的电源状态小于原来的,请求总线驱动设置新的电源状态.
然后根据新的电源状态进行不同处理:
D0:禁止唤醒,重启IST线程.
D3:允许唤醒
D4:禁止唤醒

如果新的电源状态大于原来的,请求总线驱动设置新的电源状态.
最后设置pContext->cpsCurrent为新的电源状态.

接下来来看看驱动的IST.