VxWorks驱动开发原理 USB驱动 4

6.2.3 函数库usbdCoreLib

函数库usbdCoreLib是USBD层的具体实现。当USBD首次初始化时,它创建了一个内部client,该内部client主要被USBD用于控制每个USB hub/device的控制pipe传输,这是因为当一个USB设备刚刚插上时,系统并不知道该设备是一个什么类型的设备,也不知道该设备需要什么类型的pipe,而只能通过一个默认的pipe进行通信,而该内部client则是正好起到了利用默认pipe和新发现USB设备通信的作用,且该client自创建后一直保持active状态直至USBD被关闭(shutdown)。

对于包括内部client在内的所有client,USBD会为每个client分配一个独立的任务clientThread,该任务继承了内部client任务的优先级。该任务通常是unactive的(因等待消息而休眠),仅仅在一个client的回调函数被执行(发送消息)的时候才被激活(如图6.13)。因此当一个client被其唯一的一个回调函数被激活时,其他的USBD任务不受影响。

图6.13 USBD层client的活动状态

对每个通过usbdHcdAttach()函数attach到USBD的HC来说,USBD同样创建了一个唯一的总线监控任务busThread。该任务负责配置和监控一个给定USB总线上所有HUB的状态。系统不断向每个HUB发送一个状态查询IRP,当发现该HUB上一个端口出现了设备插拔状态改变时,总线监控线程就负责更新内部数据结构,配置HUB,并触发动态插拔回调函数(如图6.14)。

图6.14 busThread的活动状态

1. LOCAL BOOL validateClient
    (
    USBD_CLIENT_HANDLE clientHandle,

    pUSBD_CLIENT *ppClient
    )

调用usbHandleValidate (clientHandle, USBD_CLIENT_SIG, (pVOID *) ppClient)函数获取client的地址并返回到参数*ppClient中。在给出client的地址前需要检查client handle是否有效:

  • l usbHandleLib是否已经进行过初始化;
  • l 检查handles[INDEX(handle)].handle是否等于handle;
  • l 检查handles[INDEX(handle)].handleSig是否等于handleSignature;
  • l 如果有效则需要将该client指针返回到ppClient中。

2. LOCAL BOOL validateNode
    (
    USBD_NODE_ID nodeId,

    pUSBD_NODE *ppNode
    )

调用usbHandleValidate (nodeId, USBD_NODE_SIG, (pVOID *) ppNode) 函数获取一个node的地址并返回到参数*ppNode中,在返回地址之前它需要检查nodeID(即handle)是否有效。

除了调用函数usbHandleValidate检查nodeID是否有效之外,函数validateNode还要检查元素(*ppNode)->nodeDeletePending看该node是否正在被deleted,如果正在被deleted,则仍然是无效的。


3. LOCAL BOOL validatePipe
    (
    USBD_PIPE_HANDLE pipeHandle,

    pUSBD_PIPE *ppPipe
    )

调用usbHandleValidate (pipeHandle, USBD_PIPE_SIG, (pVOID *) ppPipe) 函数获取一个pipe的地址并返回到参数*ppPipe中,在返回地址之前它需要检查pipeHandle是否有效。

除了调用函数usbHandleValidate函数检查pipeHandle是否有效之外,函数validateNode还要检查元素(*ppPipe)->pipeDeletePending是否正在被deleted,如果正在被deleted,则依然是无效的。

此外由于pipe是依赖于node而存在的,如果node正在被删除,那么pipe也没有存在的意义了,因此也是无效的。


4. LOCAL int validateUrb
    (
    pVOID pUrb,

    UINT16 expectedLen,

    pUSBD_CLIENT *ppClient
    )

检查URB结构变量的有效性。主要包括如下内容:

  • l 首先检查usbdLib.c是否初始化;
  • l 检查pUrb中指定的urbLength是否和参数expectedLen一致;
  • l 如果ppClient地址有效,则调用validateClient (pHeader->handle, ppClient)返回pHeader->handle指定client的地址。

5. LOCAL int setUrbResult
    (
    pURB_HEADER pUrb,

    int result
    )

设置URB的结果并执行回调函数(*pUrb->callback)()。


6. LOCAL VOID doShutdown (void)

关闭usbdCoreLib。主要过程如下:

  • l 调用usbdClientUnregister函数unregister clientList链上所有的client;
  • l 调用usbdHcdDetach函数detach hcdList链上所有的USBD_HCD结构;
  • l 释放structMutex;
  • l 调用函数usbHandleShutdown ()关闭函数库usbHandle.c;
  • l 调用函数ossShutdown ()关闭OSS函数库.

7. LOCAL int fncInitialize
    (
    pURB_HEADER pUrb
    )

初始化usbdCoreLib函数库。usbdCoreLib文件中的initCount确保fncInitialize只会有效运行一次。

下面是初始化过程:

  • l 调用ossInitialize ()初始化OSS库;
  • l 调用usbHandleInitialize初始化usbHandle函数库;
  • l 创建互斥量structMutex;
  • l 初始化hcdList及clientList链;
  • l 初始化internalClient并调用usbdClientRegister (INTERNAL_CLIENT_NAME, &internalClient) register一个内部client。注意usbdClientRegister是函数库usbdLib中的函数,这样的调用方式不利用不同函数库的隔离和层次划分,不建议采用。

8. LOCAL int fncShutdown
    (
    pURB_HEADER pUrb
    )

调用doShutndown函数。


9. LOCAL VOID clientThread
    (
    pVOID param
    )

对每个注册的client USBD都会为其启动一个独立的线程函数clientThread,client向USB设备发送一个IRP,当该IRP执行完毕之后,会将该IRP的最终执行状态作为一个信号量发送给clientThread,该线程主要负责检查该信号量并进行相应的处理。

每个client都使用一个独立的clientThread线程能够确保每个client的行为不会影响到其他其他client。

下面我们看看该函数都做了那些工作

  • l 调用函数usbQueueGet (pClient->callbackQueue, &msg, OSS_BLOCK) 读取队列中的消息;
  • l 如果是CALLBACK_FNC_IRP_COMPLETE消息,则调用(*pIrp->userCallback) (pIrp)函数
  • l 如果是CALLBACK_FNC_NOTIFY_ATTACH消息,则调用(*pNotification->callback) ()函数
  • l 如果是CALLBACK_FNC_MNGMT_EVENT消息,则调用(*pClient->mngmtCallback)函数
  • l 如果是CALLBACK_FNC_TERMINATE消息则退出循环并释放pClient->callbackExit;否则重新开始循环。

注意:该thread并不需要获取信号量structMutex。pClient->callbackExit已经确保在client被取消前clientThread函数已经终止。


10. LOCAL int doTransferAbort
    (
    pUSBD_PIPE pPipe,

    pUSB_IRP pIrp
    )

如果client发出一个IRP之后,可以调用函数doTranferAbort取消该IRP。它主要完成以下任务:

  • l 设置pPipe->irpBeingDeleted = pIrp,pPipe->irpDeleted = FALSE
  • l 调用usbHcdIrpCancel (&pPipe->pNode->pBus->pHcd->nexus, pIrp)
  • l 等待pPipe->irpDeleted变为TRUE,为TRUE则表明函数执行完成。

当调用client先调用usbdTransfer→fncTransfer进行传输IRP时,就指定了回调函数transferIrpCallback。当后来调用函数doTransferAbort时,可能该IRP已经被传送到HCD层的执行队列,因此如果想要终止该IRP的执行就必须立刻向HCD层发送一个取消命令将尚未执行或者尚未执行完毕的IRP截住取消,当然HCD层有可能来得及取消,也有可能来不及取消。如果来不及取消则返回S_usbdLib_CANNOT_CANCEL;如果来得及取消,则会通过函数doTransferAbort→usbHcdIrpCancel→usbHcdOhciExec→fncIrpCancel→cancelIrp→setIrpResult→pIrp->usbdCallback→将变量pPipe->irpDeleted变为TRUE。


11. LOCAL VOID destroyNotify
    (
    pUSBD_NOTIFY_REQ pNotify
    )

释放一个。图6.15描述了USBD_NOTIFY_REQ结构链。可见每个USBD_CLIENT结构都有一个USBD_NOTIFY_REQ结构链,它记录了该设备登记的需要被通知的情况。

图6.15 USBD_NOTIFY_REQ结构链

12. LOCAL VOID destroyPipe
    (
    pUSBD_PIPE pPipe
    )

释放一个 USBD_PIPE结构及其资源。主要执行流程如下:

  • l 设置pPipe->pipeDeletePending = TRUE;
  • l 首先清除pPipe->irps链上所有的IRP;
  • l 释放该pipe所在总线的带宽pBus->nanoseconds -= pPipe->nanoseconds;
  • l 调用usbHcdPipeDestroy (&pBus->pHcd->nexus, pPipe->hcdHandle)释放HCD层pipe结构HCD_PIPE。注意USBD_PIPE和HCD_PIPE结构的不同;
  • l 调用usbListUnlink (&pPipe->clientPipeLink)将pipe从其所属client的USBD_CLIENT结构中将该pipe移除;
  • l 调用usbListUnlink (&pPipe->nodePipeLink)将pipe从其所属的node中移除;
  • l 调用usbHandleDestroy (pPipe->handle),释放该pipe的handle结构;
  • l OSS_FREE (pPipe)。

图6.16描述了USBD_PIPE与其他数据结构的相互关系。从图中可以看出一个USBD_CLIENT结构变量可以有多个pipe,而一个USBD_NODE也可以有多个pipe,二者的pipe,因此一个pipe既属于一个client,也属于一个node。

图6.16 USBD结构与其他数据结构之间的关系

13. LOCAL VOID destroyClient
    (
    pUSBD_CLIENT pClient
    )

释放一个USBD_CLIENT结构及其资源。

  • l 调用usbListUnlink (&pClient->clientLink)将该client从clientList链中删除;
  • l 循环调用destroyNotify (pNotify)删除pClient->notifyReqs链上所有的USBD_NOTIFY_REQ结构;
  • l 循环调用destroyPipe (pPipe)删除pClient->pipes链上所有USBD_PIPE结构;
  • l 如果该client为其所在USB总线上的master client,那么当释放掉client时该总线上再没有master client(可能在多个HC总线上);
  • l 终止并destory该client的clientThread县城,然后destory该clientThread;
  • l 调用函数usbQueueDestroy ()删除client结构中的pClient->callbackQueue;
  • l 调用函数usbHandleDestroy (pClient->handle)释放其handle;
  • l OSS_FREE (pClient)。

图6.17描述了USBD_CLIENT与其他结构之间的的复杂关系。

图6.17 USBD_CLIENT与其他结构的相互关系

14. LOCAL int fncClientReg
    (
    pURB_CLIENT_REG pUrb
    )

注册一个client。构建图6.18的结构。

图6.18 函数fncClientReg构建的结构图

15. LOCAL int fncClientUnreg
    (
    pURB_CLIENT_UNREG pUrb
    )

调用函数destroyClient (pClient)。


16. LOCAL int fncMngmtCallbackSet
    (
    pURB_MNGMT_CALLBACK_SET pUrb
    )

为client设置管理回调函数。


17. LOCAL int fncVersionGet
    (
    pURB_VERSION_GET pUrb
    )

返回USBD的版本号USBD_VERSION(在函数库usbdCoreLib中有宏定义)。


18. LOCAL VOID releaseAddress
    (
    pUSBD_NODE pNode
    )

在node的上级总线结构的pBus->adrsVec[]元素中将该node对应的地址有效位删除。

结构USBD_BUS中有一个数组adrsVec,它是一个有127个元素,每个元素为一个字节。每一位对应一个地址,表明该地址的分配情况,如果地址已经分配,则该位为1,否则为0。实际上最大的设备号为127,因此adrsVec只要16个字节就够了。


19. LOCAL BOOL assignAddress
    (
    pUSBD_NODE pNode
    )

在设备上层总线结构USBD_BUS中的pBus->adrsVec []按照从小到大的顺序查找一个空闲的地址,赋值给pNode->busAddress,并通过函数usbdAddressSet通过默认的client分配给指定的node。


20. LOCAL VOID notifyIfMatch
    (
    pUSBD_NODE pNode,

    pUSBD_NODE_CLASS pClassType,

    pUSBD_CLIENT pClient,

    pUSBD_NOTIFY_REQ pNotify,

    UINT16 attachCode
    )

简单地说,就是client交给函数库usbdCoreLib一张名片,上面写着自己的姓名(deviceClass、deviceSubClass以及deviceProtocol)告诉:“这是我的名片,如果有我的信息(此类设备的插拔),就送到这个地方(pClient->callbackQueue)”。而函数notifyIfMatch所做的事情就是在系统收到一条信息之后和这张名片比较,如果匹配上了就向client发送一个消息。

注意:NOTIFICATION结构在这个函数中创建,在clientThread中free。


21. LOCAL VOID notifyClients
    (
    pUSBD_NODE pNode,

    UINT16 attachCode
    )

usbdCoreLib库发现了一个node的插拔后,根据该node的类型(在链表中,可能会有多个classType),检查每个clientList链上的每个client,针对该client注册的USBD_NOTIFY_REQ(可能有多个req,这些req保存在链表pClient->notifyReqs中),调用函数notifyIfMatch进行检查,如果classType匹配,向该client的消息队列中发送消息。参见图6.19。

图6.19 notifyClients函数设计的数据结构

22. LOCAL BOOL createNodeClass
    (
    pUSBD_NODE pNode,

    UINT16 configuration,

    UINT16 interface,

    UINT16 deviceClass,

    UINT16 deviceSubClass,

    UINT16 deviceProtocol
    )

一个设备可能有多个配置(configuration),而一个配置又可能有多个interface,对于每一个interface都要创建一个USBD_NODE_CLASS结构,并链接到参数pNode指定的pNode->classTypes链当中。如图6.20。

图6.20 结构USBD_NODE和USBE_NODE_CLASS的关系图

而notifyClients函数的作用就是发现一个设备插拔,根据该设备上注册的所有USBD_NODE_CLASS结构,挨个通知相应的client。


23. LOCAL BOOL interrogateDeviceClass
    (
    pUSBD_NODE pNode,

    UINT16 deviceClass,

    UINT16 deviceSubClass,

    UINT16 deviceProtocol
    )

一个设备可能有多个配置(configuration);而每个配置还可以有多个interface;每个interface则对应一个USBD_NODE_CLASS结构变量。该函数的作用就是寻找设备的所有配置中的每个接口信息并调用函数createNodeClass为其创建一个USBD_NODE_CLASS结构,并链接到USND_NODE.classTypes链中。最终形成图6.20所示的结构。

注意:如果参数deviceClass、deviceSubClass、deviceProtocol为有效着,则无需读取USB设备的信息直接调用函数createNodeClass向pNode的classTypes链中增加一个USBD_NODE_CLASS结构变量。


24. LOCAL VOID destroyNode
    (
    pUSBD_NODE pNode
    )

当一个USB设备终止时调用该函数。Destory过程主要分为以下几个步骤(参考图6.21):

  • l pNode->nodeDeletePending置删除信号;
  • l 循环调用destroyPipe (pPipe)删除pNode->pipes链;
  • l 调用所有notifyClients (pNode, USBD_DYNA_REMOVE)通知clientList上感兴趣的client该节点即将removed;
  • l 循环调用usbListUnlink (&pClassType->classLink)及OSS_FREE (pClassType)删除pNode->classTypes链;
  • l releaseAddress (pNode)释放地址;
  • l OSS_SEM_DESTROY (pNode->controlSem)释放pNode->controlSem信号;
  • l usbHandleDestroy (pNode->nodeHandle);
  • l OSS_FREE (pNode->pHubStatus);
  • l OSS_FREE (pNode)。
图6.21 USBD结构与其他数据结构之间的关系

25. LOCAL VOID destroyAllNodes
    (
    pUSBD_NODE pNode
    )

如果该设备是HUB设备,要先遍历其各个port,删除各port下层所有节点,最后再删除节点pNode。

注意:这里有个递归调用。


26. LOCAL VOID updateHubPort
    (
    pUSBD_NODE pNode,

    UINT16 port
    )

更新USB hub指定端口port的状态,判断该HUB端口上是否有设备插拔操作。

实现过程:

  • l 调用函数usbdStatusGet ()获取hub端口port的状态;
  • l 如果该端口设备被拔掉,则要调用destroyAllNodes (pNode->pPorts [port].pNode)清除该节点机下层节点,并令pNode->pPorts [port].pNode = NULL
  • l 如果新的USB设备插入,则需要给设备加电并进行设备复位;
  • l 调用usbdFeatureClear 函数清除该端口的连接状态变化情况;
  • l 如果是新增加的设备,需要根据当前pStatus->status来计算设备的nodeSpeed属性,并调用函数createNode为pNode->pPorts [port].pNode创建一个新的节点;
  • l 分别调用usbdFeatureClear函数清除该端口的ENABLE状态、SUSPEND状态、CURRENT状态、RESET状态(参考USB规范2.0 的11.24.2节)。

注意:图6.22描述了USBD_NODE结构是如何反映整个USBD总线的拓扑结构的。

图6.22 USBD_NODE结构链反映USB总线的拓扑结构

27. LOCAL VOID hubIrpCallback
    (
    pVOID p
    )

函数initHubIrp会发送一个查询hub状态的IRP,当hub状态查询IRP完成的时候回调此函数,主要用于向busThread发送消息BUS_FNC_UPDATE_HUB,busThread收到此消息后将检查hub的端口的状态如果发现某个端口发生了变化,则调用函数updateHubPort (pNode, port)检查该端口的详细信息并作相应处理,处理完毕后重新调用函数initHubIrp周而复始。

图6.23描述了这个不断循环查询hub状态的过程。

图6.23 hub状态循环更新的过程

28. LOCAL BOOL initHubIrp
    (
    pUSBD_NODE pNode
    )

向hub发送一个IRP,用于检查HUB的状态。

当发现一个新的hub时,就调用函数initHubIrp,发送状态检查IRP,其回调函数为hubIrpCallback,当该IRP正常执行后会调用回调函数hubIrpCallback向busThread发送一个信号,而负责侦听信号的busThread函数发现该信号后调用函数checkHubStatus对hub的状态进行更新,参见图6.23。


29. LOCAL BOOL initHubNode
    (
    pUSBD_NODE pNode
    )

初始化一个HUB节点。实现过程:

  • l 读取HUB的各种信息,包括供电信息、端口数等信息。
  • l 调用函数usbdConfigurationSet设置pNode的configuration;
  • l 调用函数usbdPipeCreate为该node创建一个中断pipe;
  • l 为pNode->pPorts分配空间,pPorts是一个数组,里面存放了各个端口对应的USBD_NODE结构变量的地址;
  • l 对每个端口,调用usbdFeatureSet函数为其加电;
  • l 调用函数initHubIrp (pNode)发送一个IRP侦听其状态;

30. LOCAL pUSBD_NODE createNode 
    (
    pUSBD_BUS pBus,

    USBD_NODE_ID rootId,

    USBD_NODE_ID parentHubId,

    UINT16 parentHubPort,

    UINT16 nodeSpeed,

    UINT16 topologyDepth
    )

创建并初始化一个USBD_NODE结构。如果该node是个hub ,还要初始化hub。

实现过程:

  • l 创建USBD_NODE结构;
  • l 为pNode->pHubStatus分配空间,大小是MAX_HUB_STATUS_LEN,数值为((USB_MAX_DEVICES + 1 + 7) / 8),是当询问一个hub的状态的时候能够返回最大数据长度,该状态字的每一位代表一个端口,而且再加上1bit是该hub自己使用的;
  • l 调用usbHandleCreate为该node分配handle;
  • l 调用OSS_SEM_CREATE 为pNode->controlSem创建信号量,该信号量的最大值1,初值1;
  • l 根据函数参数填写如下参数:该设备所在的总线pBus指针、该设备的传输速度信息nodeSpeed、该设备的上级hub的ID  parentHubId、该设备占用上级hub的端口号parentHubPort、该设备所属的跟设备ID  rootId、该设备的拓扑深度topologyDepth;
  • l 调用usbdPipeCreate创建一个control pipe赋值给pNode->controlPipe;
  • l 调用函数usbdDescriptorGet(参数USB_DESCR_DEVICE)获取设备描述符,通过检查设备描述符的最大控制数据包长度作为该pipe能够传输的最大控制数据包长度;
  • l 调用assignAddress (pNode)为该设备分配地址;
  • l 调用函数usbHcdPipeModify通知HCD,设备的地址和maxPacketSize发生了变化;
  • l 如果pNode->devDescr.deviceClass表明是hub则要调initHubNode用初始化hub;
  • l 调用interrogateDeviceClass创建USBD_NODE_CLASS结构与USBD_NODE结构相关联;
  • l 调用notifyClients通知感兴趣的client。

图6.24描述了USBD_NODE的相关数据结构。

图6.24 createNode函数创建的数据结构

注意:在创建pipe的时候,并没有为其设备分配地址,只有在pipe创建之后进行分配并通知HCD。


31. LOCAL VOID checkHubStatus
    (
    USBD_NODE_ID nodeId
    )

当函数busThread检测到BUS_FNC_UPDATE_HUB消息时,需要效用函数checkHubStatus检查每个端口的有没有设备插拔,如发现有设备插拔则进行相应处理,最后调用函数initHubIrp函数重新提交IRP开始下一次循环(参考图6.23)。


32. LOCAL VOID busThread
    (
    pVOID param
    )

每个attached到USBD的HC都会有一个独立的thread,主要负责监控该bus的状态,比如设备的attached和removed。每一个bus有一个单独的busThread,这样可以确保一条bus的行为不会影响到其他的bus。

busThread的作用在于不停的监控总线上的busQueue消息队列,如果队列中有数据就取出数据进行状态,如果是BUS_FNC_UPDATE_HUB消息,则调用函数checkHubStatus函数进行详细的状态检查;如果是BUS_FNC_TERMINATE事件则退出循环返回(参考图6.23)。


33. LOCAL VOID hcdMngmtCallback
    (
    pVOID mngmtCallbackParam,

    HCD_CLIENT_HANDLE handle,

    UINT16 busNo,

    UINT16 mngmtCode
    )

当HCD探测到management事件的时候调用。

该函数主要是对clientList队列的每个client调用usbQueuePut函数向client发出一个CALLBACK_FNC_MNGMT_EVENT信号。那么clientThread的作用就是用于监听这些新信号,一旦监听到CALLBACK_FNC_MNGMT_EVENT信号则执行函数(*pClient->mngmtCallback)()。这是USB设备主动向计算机系统发送信号的一种方法。


34. LOCAL int initHcdBus
    (
    pUSBD_HCD pHcd,

    UINT16 busNo
    )

该函数主要在系统初始化的时候伴随着HC的初始化调用,主要是初始化HC控制的USB总线,一个HC可以控制多条USB总线。在调用函数initHcdBus之前,结构USBD_HCD及其内部元素pBuses [busNo]对应的总线结构USBD_BUS都已经创建完毕。

函数initHcdBus的主要工作就是初始化结构USBD_HCD下的一个元素pBuses [busNo],即USBD_BUS。初始化步骤如下:

  • l 创建信号量OSS_SEM_CREATE (1, 0, &pBus->busExit);
  • l 创建队列usbQueueCreate (BUS_Q_DEPTH, &pBus->busQueue);
  • l 创建任务busThread;
  • l 创建root node。

初始化过程可以参照图6.25所示。

图6.25 函数initHcdBus处理的相关结构

35. LOCAL VOID destroyHcd
    (
    pUSBD_HCD pHcd
    )

解除一个USBD_HCD结构的绑定,可以参考图6.25及图6.26的结构。

  • l 调用usbListUnlink (&pHcd->hcdLink)将HCD结构从hcdList中删除;
  • l 对该HCD管理下的每条bus,调用destroyAllNodes (pBus->pRoot)清除连接该总线的所有node;清除总线线程;清除该USBD_BUS结构中的busQueue队列;清除pBus->busExit信号量(注意这里有一个bug清除信号量pBus->busExit函数的参数用错了,正确的参数应该是pBus->busExit而不是pBus->busQueue);
  • l 调用usbHcdDetach (&pHcd->nexus) detach该HCD;
  • l 调用usbHandleDestroy (pHcd->attachToken)释放USBD_HCD对应的handle资源(结构HDL_DESCR);
  • l 释放HCD结构OSS_FREE (pHcd)。

36. LOCAL int fncHcdAttach
    (
    pURB_HCD_ATTACH pUrb
    )

向USBD中添加一个USBD。相关的数据结构如图6.26所示。执行步骤如下:

  • l OSS_CALLOC (sizeof (*pHcd))创建一个USBD_HCD结构;
  • l 调用usbHcdAttach函数,发送一个attach请求,如果成功则返回该HCD管理的bus数目pHcd->busCount;
  • l 分配pHcd->busCount个USBD_BUS结构;
  • l usbHandleCreate (USBD_HCD_SIG, pHcd, &pHcd->attachToken)为USBD_HCD分配handle;
  • l 调用initHcdBus初始化每条bus;
  • l usbListLink (&hcdList, pHcd, &pHcd->hcdLink, LINK_TAIL)将该usbd_hcd结构加入到hcdList链表。
图6.26 函数fncHcdAttach处理的相关结构

37. LOCAL int fncHcdDetach
    (
    pURB_HCD_DETACH pUrb
    )

调用destroyHcd (pHcd)函数解除一个USBD_HCD结构的绑定。


38. LOCAL int fncStatisticsGet
    (
    pURB_STATISTICS_GET pUrb
    )

读取参数(pUrb->nodeId)指定的node,返回pNode->pBus->stats中的数值。


39. LOCAL int fncBusCountGet
    (
    pURB_BUS_COUNT_GET pUrb
    )

遍历hcdList链中的USBD_HCD结构,计算各个hcd管理的bus数目之和。


40. LOCAL int fncRootIdGet
    (
    pURB_ROOT_ID_GET pUrb
    )

返回USB的root ID即root USBD_NODE结构的handle。

pUrb->busIndex这个参数是从hcdList链的第一个USBD_HCD结构管理的第一条bus开始计数(计数为0)的总线下表。因此只需要找到相应的USBD_HCD结构及其中的总线下标,即可找到该总线结构USBD_BUS结构中的pRoot元素,即跟节点的USBD_NODE结构,并以此找到其handle。参考图6.26。


41. LOCAL int fncHubPortCountGet
    (
    pURB_HUB_PORT_COUNT_GET pUrb
    )

返回一个hub上的端口数目。保存在pNode->numPorts中。


42. LOCAL int fncNodeIdGet
    (
    pURB_NODE_ID_GET pUrb
    )

获取一个hub指定端口的node的ID,就是pNode->nodeHandle。

注意参数pUrb指定的是一个hub的node,函数要查询的是该hub上某个指定端口连接的device的node。如果该node存在,就返回nodeType和nodeHandle。参见图6.22。


43. LOCAL VOID scanClassTypes
    (
    pUSBD_NODE pNode,

    pUSBD_CLIENT pClient,

    pUSBD_NOTIFY_REQ pNotify
    )

通常在发现一个新的USB设备插入时,需要识别该USB设备的类型,并和每个client所注册的USBD_NOTIFY_REQ结构变量pNotify进行比对,如果匹配就调用USBD_NOTIFY_REQ结构中的回调函数通知该client。当然如果发现一个新插入的USB设备是一个HUB那么该HUB的端口上还可能有USB设备或者HUB构成级联,因此需要递归调用scanClassTypes函数循环检查底层各个设备的类别,以通知相应的client。

注意该函数的循环顺序,是对node中classTypes链上的每个USB_NODE_CLASS结构,和参数pNotify指定的USBD_NOTIFY_REQ结构进行比对,如果匹配就调用相应的函数通知参数指定的client(即给client发送消息),参考图6.21。


44. LOCAL int fncDynaAttachReg
    (
    pURB_DYNA_ATTACH_REG_UNREG pUrb
    )

当增加了一个client的时候,要对这个client进行注册,也就是为该client添加它所感兴趣的USBD_NOTIFY_REQ(保存到pUrb指定的pClient结构的pClient->notifyReqs链中),然后调用函数scanClassTypes检查USBD的各个HCD管理的设备中有没有符合该USBD_NOTIFY_REQ的设备,如果有则通知该client。

注意:检查所有设备是因为在该client注册之前这些设备可能都已经存在了。


45. LOCAL int fncDynaAttachUnreg
    (
    pURB_DYNA_ATTACH_REG_UNREG pUrb
    )

在pUrb指定的pClient->notifyReqs链中查找,调用destroyNotify (pNotify)将符合条件的USBD_NOTIFY_REQ结构变量删除。


46. LOCAL VOID controlIrpCallback
    (
    pVOID p
    )

Control IRP的回调函数,在control IRP完成的时候调用。主要完成如下任务:

  • l 保存实际传输的长度*pNode->pActLen = pIrp->bfrList [1].actLen;
  • l 执行回调函数(*pUrb->callback) (pUrb);
  • l 释放信号量OSS_SEM_GIVE (pNode->controlSem);

注意:信号量pNode->controlSem是为了确保下一个controlRequest调用前上一个control IRP的回调函数已经执行完毕。


47. LOCAL int controlRequest
    (
    pURB_HEADER pUrb,

    USBD_NODE_ID nodeId,

    UINT8 requestType,

    UINT8 request,

    UINT16 value,

    UINT16 index,

    UINT16 length,

    pVOID pBfr,

    pUINT16 pActLen
    )

根据参数形成一个control IRP并提交HCD层执行。主要完成如下任务:

  • l 首先调用获取信号量pNode->controlSem,这个信号量在函数controlIrpCallback中被释放,也就是说只有在上一个IRP被执行完毕之后才可能发送新的IRP;
  • l 根据参数形成一个初始化pNode->setup变量,这个变量将作为控制IRP要发送的第一个数据包;
  • l 初始化pNode->irp变量,IRP的第二个数据包在函数的参数中定义,第三个数据包则是一个ACK数据包,长度为0。也可能没有第二个数据包,只有setup包和ACK数据包;
  • l 调用函数usbdTransfer (internalClient, pNode->controlPipe, pIrp)提交。

48. LOCAL VOID resetDataToggle
    (
    USBD_NODE_ID nodeId,

    UINT16 configuration,

    UINT16 interface,

    UINT16 endpoint
    )

查找handle为nodeId的节点,遍历其pNode->pipes链中的Pipe如果其pPipe->configuration、pPipe->interface、pPipe->endpoint和参数configuration、interface、endpoint相匹配,则修改pPipe->dataToggle = USB_DATA0。

通常在系统检测到一个node的配置发生了改变的时候调用,这是因为配置发生改变的时候USB设备会自动复位,因此必须对对应的数据结构进行修改以确保和硬件的同步。根据代码分析,当下列函数被调用之后需要调用resetDataToggle函数:

  • l fncFeatureClear (参数为USB_FSEL_DEV_ENDPOINT_HALT时)
  • l fncConfigSet
  • l fncInterfaceSet。

49. LOCAL int fncFeatureClear
    (
    pURB_FEATURE_CLEAR_SET pUrb
    )

调用函数controlRequest清除一个USB设备的某些特性。

注意:如果清除的是USB_FSEL_DEV_ENDPOINT_HALT的特征位,说明设备开始工作,那么此时要调用函数resetDataToggle将该node所有pipe的dataToggle进行复位。


50. LOCAL int fncFeatureSet
    (
    pURB_FEATURE_CLEAR_SET pUrb
    )

调用函数controlRequest设置一个USB设备的某些特性。


51. LOCAL int fncConfigGet
    (
    pURB_CONFIG_GET_SET pUrb
    )

调用controlRequest函数获取一个USB设备的配置信息。


52. LOCAL int fncConfigSet
    (
    pURB_CONFIG_GET_SET pUrb
    )

调用controlRequest函数设置一个USB设备的配置信息。

注意:设置设备的配置信息后,设备的状态会自动复位,因此需要调用函数resetDataToggle将该node所有pipe的dataToggle状态进行复位。


53. LOCAL int fncDescriptorGet
    (
    pURB_DESCRIPTOR_GET_SET pUrb
    )

调用controlRequest函数获取一个USB设备的描述符信息。


54. LOCAL int fncDescriptorSet
    (
    pURB_DESCRIPTOR_SET_SET pUrb
    )

调用controlRequest函数设置一个USB设备的描述符信息。


55. LOCAL int fncInterfaceGet
    (
    pURB_INTERFACE_GET_SET pUrb
    )

调用controlRequest函数获取一个USB设备的接口信息。


56. LOCAL int fncInterfaceSet
    (
    pURB_INTERFACE_GET_SET pUrb
    )

调用controlRequest函数获取一个USB设备的接口信息。

注意:设置设备的配置信息后,设备的状态会自动复位,因此需要调用函数resetDataToggle将该node所有pipe的dataToggle状态进行复位。


57. LOCAL int fncStatusGet
    (
    pURB_STATUS_GET pUrb
    )

调用controlRequest函数获取一个USB设备/接口/endpooint的接口信息。


58. LOCAL int fncAddressGet
    (
    pURB_ADDRESS_GET_SET pUrb
    )

调用controlRequest函数获取一个USB设备的地址,并将该地址保存在pNode->busAddress中返回。


59. LOCAL int fncAddressSet
    (
    pURB_ADDRESS_GET_SET pUrb
    )

调用controlRequest函数设置一个USB设备的地址

注意:该函数只是通过调用controlRequest发送一个请求,并没有改变pNode->busAddress的数值。这是因为该函数是被函数assignAddress调用的,修改pNode->busAddress数值的操作是由assignAddress函数完成的。


60. LOCAL int fncNodeInfoGet
    (
    pURB_NODE_INFO_GET pUrb
    )

将pNode->nodeInfo复制到pUrb->pNodeInfo当中并返回。

注意没有USB硬件操作。


61. LOCAL int fncVendorSpecific
    (
    pURB_VENDOR_SPECIFIC pUrb
    )

调用controlRequest函数执行一个特殊的USB IRP。


62. LOCAL int fncPipeCreate
    (
    pURB_PIPE_CREATE pUrb
    )

根据参数pUrb创建一个USBD_PIPE结构。

PIPE是连接NODE.ENDPOINT 和CLIENT的一个管道。首先该函数调用usbHcdPipeCreate函数在HCD层建立了一个PIPE(hcdPipeHandle),然后在USBD层建立了一个USBD_PIPE结构,并分别调用usbListLink将之连接到pNode和pClient各自的pipes链当中。pPipe->nanoseconds则是HCD根据输入的传输类型和bandwideh、serviceInterval计算出的传输时间(单位纳秒)。


63. LOCAL int fncPipeDestroy
    (
    pURB_PIPE_DESTROY pUrb
    )

调用函数destroyPipe (pPipe)删除一个pipe


64. LOCAL VOID transferIrpCallback
    (
    pVOID p
    )

IRP传输回调函数。当一个IRP传输完成的时候调用。主要进行如下操作:

  • l 先从PIPE的IRP链中删除;
  • l 根据IRP的传输状态更新pPipe->pNode->pBus->stats状态。
  • l 计算此次IRP传输的packet数目,更新pPipe->dataToggle状态(只限于BULK和INTERRUPT类型的pipe;control和iso传输pipe都是以DATA0开始,因此不需要计算)。
  • l 根据不同的pIrp->userCallback类型执行不同的回调函数:hubIrpCallback、controlIrpCallback、usbQueuePut。

65. LOCAL int fncTransfer
    (
    pURB_TRANSFER pUrb
    )

构造一个IRP结构,并将其链接到PIPE中。最后调用HCD层的usbHcdIrpSubmit函数提交IRP。


66. LOCAL int fncTransferAbort
    (
    pURB_TRANSFER pUrb
    )

调用doTransferAbort (pPipe, pIrp)终止一个IRP传输。


67. LOCAL int fncSynchFrameGet
    (
    pURB_SYNCH_FRAME_GET pUrb
    )

调用函数controlRequest获取同步帧。


68. LOCAL int fncCurrentFrameGet
    (
    pURB_CURRENT_FRAME_GET pUrb
    )

调用usbHcdCurrentFrameGet来获取当前帧。


69. LOCAL int fncSofMasterTake
    (
    pURB_SOF_MASTER pUrb
    )

一个client通过调用该函数fncSofMasterTake获取总线的master权力。pNode->pBus->pSofMasterClient指向总线上拥有该SOF的master权力的client。只有在pNode->pBus->pSofMasterClient=0的时候才可以调用fncSofMasterTake函数设置新的pNode->pBus->pSofMasterClient。


70. LOCAL int fncSofMasterRelease
    (
    pURB_SOF_MASTER pUrb
    )

释放一个client对总线的控制权。注意,必须是参数指定的client和当前总线实际控制的client一致的时候才能够正常释放,否则返回错误。


71. LOCAL int fncSofIntervalGet
    (
    pURB_SOF_INTERVAL_GET_SET pUrb
    )

调用HCD层函数usbHcdSofIntervalGet获取总线的SOF间隔。


72. LOCAL int fncSofIntervalSet
    (
    pURB_SOF_INTERVAL_GET_SET pUrb
    )

调用HCD层函数usbHcdSofIntervalSet设置总线的SOF间隔(设置HCD层)。


73. LOCAL int fncBusStateSet
    (
    pURB_BUS_STATE_SET pUrb
    )

调用HCD层函数usbHcdSetBusState设置总线状态USBD_BUS_SUSPEND或者USBD_BUS_RESUME。


74. STATUS usbdCoreEntry
    (
    pURB_HEADER pUrb
    )

该库的唯一入口函数。主要调用名字为fnc开始的函数。

从函数库usbdLib和函数库usbdCoreLib可以看出,在USBD层,所有的相关的概念都被抽象为几类:NODE、PIPE、CLIENT以及IRP,其中一个USB设备抽象为一个NODE,对应的数据结构为USBD_NODE。一个client即对应一个任务,主要负责和一个或多个USB设备的交互,而client和NODE交互的通道即为PIPE,一个pipe可以为IN、OUT或者INOUT类型,其中INOUT类型主要对应于控制PIPE。

一个PIPE建立在一个client和USB设备的一个endpoint之间。一个USB设备可能有多个endpoint。

一个IRP则是隶属于一个PIPE。