6.2.2 函数库usbdLib
USBD层是一个抽象层,如同USB规范里所示,USBD层将USB设备抽象为一个node,从而将系统与USB设备的交互变成了client和USB node的交互。USBD层的实现可以两个子层:接口子层和实现子层。接口子层为上层提供了一系列的通用的接口,主要由函数库usbdLib来完成;而实现子层则是实现了通用的接口函数的功能,主要由函数库usbdCoreLib来实现。
其实在函数库usbdCoreLib层已经实现了一个通用的接口函数urbExecBlock,只是这个接口函数过于复杂,不利于上层的调用,因此usbdLib通过调用函数urbExecBlock实现了一系列通用的接口,从而避开了应用层与usbd层隔离开来。
这个函数库比较简单,大概描述一下。
1. LOCAL VOID urbInit
(
pURB_HEADER pUrb,
USBD_CLIENT_HANDLE clientHandle,
UINT16 function,
URB_CALLBACK callback,
pVOID userPtr,
UINT16 totalLen
)
这个函数是利用参数来初始化一个URB结构。URB结构的将被用于通用接口函数的参数。
urbInit函数的参数有:
- clientHandle:指明一个client;
- function:需要完成的功能;
- callback:URB结束时调用;
- userPtr:用户指定的指针;
- totalLen:URB结构的长度,通常是sizeof(USBD_URB)
2. LOCAL VOID urbCallback
(
pVOID pUrb
)
该函数被urbExecBlock()函数指定为URB结构的回调函数去调用函数usbdCoreEntry,函数usbdCoreEntry在结束前调用该函数,用于同步。
3. LOCAL STATUS urbExecBlock
(
pURB_HEADER pUrb
)
这个函数也就是usbdLib函数库的核心。它负责初始化参数pUrb并以该参数调用函数usbdCoreEntry进行底层处理。
该函数有几个需要注意的地方:
- l 信号量队列semPoolQueue是一个FIFO队列,只有从FIFO队列中取到msg才可能调用usbdCoreEntry函数,调用完毕后将信号量再重新存入FIFO中,这也就保证了系统中同时执行函数usbdCoreEntry的任务数不能超过MAX_SYNCH_SEM个(参见函数usbdInitialize)。
- l 队列中的每个元素是一个信号量,这个信号量的作用是什么呢?但从代码上理解仅仅是为了确保URB执行完成,由于函数urbExecBlock直接调用函数usbdCoreEntry因此而这必然在同一个任务中,不存在调用之后函数还没有执行完毕的情况。个人感觉这段功能并无必要。
- l 代码中我们看到,如果usbdCoreEntry (pUrb) != OK这时候不能确定函数usbdCoreEntry 是否调用函数OSS_SEM_GIVE ((SEM_HANDLE) ((pURB_HEADER) pUrb)->userPtr),因此需要调用函数OSS_SEM_TAKE ((SEM_HANDLE) msg.lParam, OSS_DONT_BLOCK)以确保在归还该信号量之前要把它恢复为原来使用前的状态(注意最后一个参数OSS_DONT_BLOCK和usbdCoreEntry成功执行时调用的参数OSS_BLOCK不同)。
4. STATUS usbdInitialize (void)
初始化函数库usbdLib。
初始化过程包括:
- l 创建队列semPoolQueue(如图);
- l 调用urbInit (&urb.header, NULL, USBD_FNC_INITIALIZE, NULL, NULL,sizeof (urb))及urbExecBlock (&urb.header)完成usbdCoreLib的初始化。
5. STATUS usbdShutdown (void)
usbdInitialize函数的逆过程。先关闭usbdCoreLib层,然后再释放初始化函数分配的信号量队列空间,最后是关闭OSS函数库。
注意:在释放信号量队列semPoolQueue的时候,首先要取得一个信号量,才能删除该信号量;删除所有的信号量后才能删除semPoolQueue指向的USB_QUEUE结构。而代码usbQueueGet (semPoolQueue, &msg, SYNCH_SEM_TIMEOUT) == OK这个条件容易造成内存的泄露。
6. STATUS usbdClientRegister
(
pCHAR pClientName,
pUSBD_CLIENT_HANDLE pClientHandle
)
调用urbExecBlock函数(参数USBD_FNC_CLIENT_REG)根据参数提供的pClientName生成一个USBD_CLIENT结构,并返回该结构的handle。
7. STATUS usbdClientUnregister
(
USBD_CLIENT_HANDLE clientHandle
)
调用urbExecBlock(参数USBD_FNC_CLIENT_UNREG,)删除一个clientHandle指定的USBD_CLIENT结构。
8. STATUS usbdMngmtCallbackSet
(
USBD_CLIENT_HANDLE clientHandle,
USBD_MNGMT_CALLBACK mngmtCallback
pVOID mngmtCallbackParam
)
为一个client设定一个management回调函数。
Management回调函数为USBD提供了一种手段来通知USB上的异步管理事件。比如如果USB处于SUSPEND状态时,有一个USB设备发出了RESUME信号,那么这个事件将会通过management 回调函数通知client。
9. STATUS usbdBusStateSet
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId,
UINT16 busState
)
该函数允许一个client设置bus的状态(SUSPEND/RESUME),该总线由参数node指定(pNode->pBus)。
当设定一条bus为SUSPEND状态,必须清楚USBD不会自动将该bus恢复为RESUME状态,要想恢复为RESUME状态,还必须再次调用该函数(使用RESUME参数)。这对于一个USB “remote wakeup”特性来说是很重要的,该特性允许一个远端设备驱动总线上的RESUME信号。Client正是通过management callback监控到该信号,并调用usbdBusStateSet函数来完成的。
同样,这个函数还是调用了urbExecBlock(实际上是usbdCoreLib)来完成的。
注意:client必须小心使用该函数,因为它会影响到一个bus的所有USB设备,从而影响到和这些设备通信的所有clients。
10. STATUS usbdBusCountGet
(
USBD_CLIENT_HANDLE clientHandle,
pUINT16 pBusCount
)
该函数是为了获取链接到系统中的所有USB host 控制器的数目。根据USB规范,每个host controller都有自己的root hub。
系统中的HC的数目不是固定的,它可以通过usbdHcdAttach()函数和usbdHcdDetach()函数来修改。
11. STATUS usbdRootNodeIdGet
(
USBD_CLIENT_HANDLE clientHandle,
UINT16 busIndex,
pUSBD_NODE_ID pRootId
)
该函数是为了找到一个HC的root hub 对应的nodeID。busIndex指的是hcdList链上USBD_HCD结构的下标。
12. STATUS usbdHubPortCountGet
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID hubId,
pUINT16 pPortCount
)
通过urbExecBlock接口调用usbdCoreLib库的fncHubPortCountGet函数。该函数可以得到一个hub设备的port数目。
13. STATUS usbdNodeIdGet
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID hubId,
UINT16 portIndex,
pUINT16 pNodeType,
pUSBD_NODE_ID pNodeId
)
获取一个hub的指定端口连接的设备的nodeId。
14. STATUS usbdNodeInfoGet
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId,
pUSBD_NODE_INFO pNodeInfo,
UINT16 infoLen
)
获取一个node的信息。该信息保存在USBD_NODE结构的nodeInfo元素当中。
15. STATUS usbdDynamicAttachRegister
(
USBD_CLIENT_HANDLE clientHandle,
UINT16 deviceClass,
UINT16 deviceSubClass,
UINT16 deviceProtocol
USBD_ATTACH_CALLBACK attachCallback
)
该函数由client来调用执行,目的是告诉USBD:当USBD发现一个deviceClass/deviceSubClass/deviceProtocol的设备插拔时,通知该client。
16. STATUS usbdDynamicAttachUnRegister
(
USBD_CLIENT_HANDLE clientHandle,
UINT16 deviceClass,
UINT16 deviceSubClass,
UINT16 deviceProtocol,
USBD_ATTACH_CALLBACK attachCallback
)
通知USBD当有deviceClass/deviceSubClass/deviceProtocol类型的设备插拔的时候不再通知该client。
17. STATUS usbdFeatureClear
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId,
UINT16 requestType,
UINT16 feature,
UINT16 index
)
清除USB feature。
18. STATUS usbdFeatureSet
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId,
UINT16 requestType,
UINT16 feature,
UINT16 index
)
设定USB feature。
19. STATUS usbdConfigurationGet
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId,
pUINT16 pConfiguration
)
获取USB设备的配置信息,配置信息保存在pConfiguration地址当中。
20. STATUS usbdConfigurationSet
(
USBD_CLIENT_HANDLE clientHandle, /* Client handle */
USBD_NODE_ID nodeId, /* Node Id of device/hub */
UINT16 configuration, /* New configuration to be set */
UINT16 maxPower /* max power this config will draw */
)
设定一个USB设备的配置信息。
21. STATUS usbdDescriptorGet
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId,
UINT8 requestType,
UINT8 descriptorType,
UINT8 descriptorIndex,
UINT16 languageId,
UINT16 bfrLen,
pUINT8 pBfr,
pUINT16 pActLen
)
获取一个USB设备(nodeId)的描述符信息。
22. STATUS usbdDescriptorSet
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId,
UINT8 requestType,
UINT8 descriptorType,
UINT8 descriptorIndex,
UINT16 languageId,
UINT16 bfrLen,
pUINT8 pBfr
)
设置USB设备的描述符信息。
23. STATUS usbdInterfaceGet
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId,
UINT16 interfaceIndex,
pUINT16 pAlternateSetting
)
获取一个USB设备的interface信息。
24. STATUS usbdInterfaceSet
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId,
UINT16 interfaceIndex,
UINT16 alternateSetting
)
设置一个USB设备的interface信息。
25. STATUS usbdStatusGet
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId,
UINT16 requestType,
UINT16 index,
UINT16 bfrLen,
pUINT8 pBfr,
pUINT16 pActLen
)
询问一个USB设备的状态。
26. STATUS usbdAddressGet
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId,
pUINT16 pDeviceAddress
)
获取一个设备的地址。
27. STATUS usbdAddressSet
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId,
UINT16 deviceAddress
)
设置一个USB设备的地址。
28. STATUS usbdVendorSpecific
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId,
UINT8 requestType,
UINT8 request,
UINT16 value,
UINT16 index,
UINT16 length,
pUINT8 pBfr,
pUINT16 pActLen
)
该函数允许设备发出设备特殊的USB请求。
特定的USB设备会有一些特殊的USB请求,这些请求不能用标准的USB功能来完成,因此就需要为client提供一个函数向该USB设备的控制pipe直接发送请求。
29. STATUS usbdPipeCreate
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId,
UINT16 endpoint,
UINT16 configuration,
UINT16 interface,
UINT16 transferType,
UINT16 direction,
UINT16 maxPayload,
UINT32 bandwidth,
UINT16 serviceInterval,
pUSBD_PIPE_HANDLE pPipeHandle
)
创建一个USBD_PIPE。
- nodeId:指明了设备
- endpoint:指明了设备的endpoint,而pipe正是在client和一个设备的endpoint之间建立的。而configuration和interface正指定了设备的配置和接口。
- transferType:传输类型:control和BULK
- direction:指明pipe传输的方向:IN/OUT/INOUT。一个pipe的方向特性是不会改变的。INOUT仅仅用于control Pipe。
- maxPayload:该endpoint支持的最大payload。这个数值通常在设备的配置描述符中都标明了,因此这个参数是USBD首先从USB设备读取了配置描述符之后才直接将这个数值作为参数传递。
- bandwidth:对control和bulk pipe来说,bandwidth为0;对interrupt pipe来说该数值为每frame要传输的字节数;对isochronous pipe,bandwidth指的是每秒钟传输的字节数。
- serviceInterval:仅适用于中断传输pipe,标明该pipe的最大潜伏期(latency,单位毫秒)。如果一个设备的serviceInterval数值为20,说明该设备没20毫秒需要服务一次。
- pPipeHandle:创建的PIPE的handle。
30. STATUS usbdPipeDestroy
(
USBD_CLIENT_HANDLE clientHandle,
USBD_PIPE_HANDLE pipeHandle
)
Destroys 一个USB传输pipe。
31. STATUS usbdTransfer
(
USBD_CLIENT_HANDLE clientHandle, /* Client handle */
USBD_PIPE_HANDLE pipeHandle, /* Pipe handle */
pUSB_IRP pIrp /* ptr to I/O request packet */
)
client使用该函数来启动一个指定pipe上的传输。传输是由一个IRP结构或一个I/O request packet来描述的,该IRP结构或者I/O request packet必须在调用usbdTransfer()函数前被分配并初始化。
32. STATUS usbdTransferAbort
(
USBD_CLIENT_HANDLE clientHandle,
USBD_PIPE_HANDLE pipeHandle,
pUSB_IRP pIrp
)
终止一个USB传输。
33. STATUS usbdSynchFrameGet
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId,
UINT16 endpoint,
pUINT16 pFrameNo
)
获取一个设备的isochronous同步帧。当client和一个USB设备之间利用isochronous pipe进行数据传输的时候,USB设备会记录本次传输开始的帧号,如果出现了错误,client需要调用此函数恢复帧号,以方便重新传输。这个函数通过一个control request实现。参见USB规范9.4.11。
34. STATUS usbdCurrentFrameGet
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId,
pUINT32 pFrameNo,
pUINT32 pFrameWindow
)
获取一个USB设备(nodeId)所在的USB总线上的当前帧的帧号。
如果参数pFrameWindow有效,USBD也会返回指定host controller的最大帧调度窗口。帧调度窗口是由USB host controller跟踪的,多数USB host controller维护了一个10bit或者11bit的内部帧计数器。当我们进行isochronous传输时,一个client有时需要明确此次传输是从那个帧号开始的。对给定的USB host controller来说,起始帧号应该是介于当前帧号和frameWindow 帧之间的一个数值。
注意:USBD能够同时管理多个USB host controller,每个host controller都是独立的。因此指定正确的nodeId是很重要的。nodeId确定了所在的总线,也确定了host controller。
35. STATUS usbdSofMasterTake
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId
)
在isochronous传输中,有时候client需要调整usb frame的时间长度,这时候只有当client成为master client的时候才能够进行调整,一个usb总线只能有一个master client,别的client只有当当前的master client释放master权利的时候才能够成功申请成为新的masterclient。
36. STATUS usbdSofMasterRelease
(
USBD_CLIENT_HANDLE clientHandle, /* Client handle */
USBD_NODE_ID nodeId /* Node Id of node on desired USB */
)
一个client放弃自己对USB总线的master地位,之后其他client就可以申请成为一个新的master。
37. STATUS usbdSofIntervalGet
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId,
pUINT16 pSofInterval
)
获取总线的SOF间隔,也就是帧的长度。这个数值保存在nodeId所在总线的USBD_BUS结构的sofInterval元素当中。
38. STATUS usbdSofIntervalSet
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId,
UINT16 sofInterval
)
设定总线的SOF间隔,也就是帧的长度。这个数值保存在nodeId所在总线的USBD_BUS结构的sofInterval元素当中。
39. STATUS usbdVersionGet
(
pUINT16 pVersion,
pCHAR pMfg
)
返回USBD的版本,及相关的说明字符串。和USBD有关,宏定义。
40. STATUS usbdHcdAttach
(
HCD_EXEC_FUNC hcdExecFunc,
pVOID param,
pGENERIC_HANDLE pAttachToken
)
多数系统在系统初始化的时候调用此函数,以便register一个或者多个HCD。
usbdHcdAttach函数是usbdLib函数库中为数不多的不需要首先调用usbdClientRegister()函数就可以使用的一个函数,因此也就不需要向该函数传输一个USBD_CLIENT_HANDLE参数。
41. STATUS usbdHcdDetach
(
GENERIC_HANDLE attachToken
)
从USBD中detach一个HCD。
42. STATUS usbdStatisticsGet
(
USBD_CLIENT_HANDLE clientHandle,
USBD_NODE_ID nodeId,
pUSBD_STATS pStatistics,
UINT16 statLen
)
获取指定USBD_BUS上的统计数据。在pBus->stats数据结构中保存。