VxWorks驱动开发原理 USB驱动 3

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的初始化。
图6.12 函数usbdInitialize对变量semPoolQueue的初始化

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数据结构中保存。