6.2.4 函数库usrUsbHcdOhciInit
HCD层主要用于HC的管理控制,并为USBD层提供了统一的函数控制接口。
从硬件结构来数,HC是一种PCI设备,类似于PCI网卡。与PCI网卡相比,除了USB接口与网线的区别之外,还存在一个区别:PCI网卡的硬件接口都随着生产商的不同而不同,而HC设备的生产商生产的产品都必须严格遵照OHCI或者UHCI规范,因此从软件上来说只需要提供了OHCI和UHCI规范的软件接口,就可以为所有的HC提供驱动。本章将以OHCI为例对HCD层详细加以分析。
因此HCD主要包括以下几个部分:PCI配置部分、OHCI实现部分以及为USBD层提供的标准接口部分。
PCI接口的配置主要有函数库usrUsbHcdOhciInit来完成,函数库usrUsbHcdOhciInit中只有一个函数usrUsbHcdOhciAttach,这个函数库比较简单:首先在所有以经探测到的PCI设备中检查有没有OHCI类型的设备(OHCI_CLASS、OHCI_SUBCLASS、OHCI_PGMIF),一旦找到符合的设备就会调用usbPciConfigHeaderGet函数读取该设备的配置头以获取该设备的内存映射等信息,并调用函数usbdHcdAttach加载HC层的驱动。
6.2.5 函数库usbHcdLib
USBD通过HCD实现了对HC的控制,HCD主要为USBD提供了11个通用函数接口,从而使得USBD层不需要了解底层的细节,只需要直接调用这些函数就能完成对HC的控制。这11个函数接口分别为usbHcdAttach、usbHcdDetach、usbHcdSetBusState、usbHcdIrpSubmit、usbHcdIrpCancel、usbHcdCurrentFrameGet、usbHcdPipeCreate、usbHcdPipeDestroy、usbHcdPipeModify、usbHcdSofIntervalGet、usbHcdSofIntervalSet。这些函数从实现上来说都比较简单,它只是提供了简单的封装,而真正底层的实现则是由usbHcdOhciLib.c库来完成的。其层次结构如图6.27所示。
下面是hcdLib.c库中各个函数的简单分析。
1. LOCAL VOID hrbInit
(
pHRB_HEADER pHrb,
pHCD_NEXUS pNexus,
UINT16 function,
UINT16 totalLen
)
初始化一个HRB_HEADER结构变量,通过设置HRB_HEADER结构变量可以调用HCD_NEXUS.hcdExecFunc函数完成不同的功能。
2. STATUS usbHcdAttach
(
HCD_EXEC_FUNC hcdExecFunc,
pVOID param,
USB_HCD_MNGMT_CALLBACK callback,
pVOID callbackParam,
pHCD_NEXUS pNexus,
pUINT16 pBusCount
)
这个函数指定了一个USB总线的HCD函数入口hcdExecFunc,通过调该函数完成attach,同时也将该函数及hrb.header.handle作为参数初始化了变量HCD_NEXUS。
3. STATUS usbHcdDetach
(
pHCD_NEXUS pNexus
)
通过调用函数pNexus->hcdExecFunc完成了dettach,它的执行过程与usbHcdAttach相反。
4. STATUS usbHcdSetBusState
(
pHCD_NEXUS pNexus,
UINT16 busNo,
UINT16 busState
)
通过调用函数pNexus->hcdExecFunc来设定USB总线busNo的状态:USB_BUS_SUSPEND或者USB_BUS_RESUME。
5. STATUS usbHcdCurrentFrameGet
(
pHCD_NEXUS pNexus,
UINT16 busNo,
pUINT32 pFrameNo,
pUINT32 pFrameWindow
)
返回当前帧号和帧窗口。
6. STATUS usbHcdIrpSubmit
(
pHCD_NEXUS pNexus,
HCD_PIPE_HANDLE pipeHandle,
pUSB_IRP pIrp
)
该函数向HCD提交一个IRP。
7. STATUS usbHcdIrpCancel
(
pHCD_NEXUS pNexus,
pUSB_IRP pIrp
)
通知HCD取消一个IRP。当然,并不能保证一个IRP在提交给HCD后还能够在正常执行前取消。
8. STATUS usbHcdPipeCreate
(
pHCD_NEXUS pNexus,
UINT16 busNo,
UINT16 busAddress,
UINT16 endpoint,
UINT16 transferType,
UINT16 direction,
UINT16 speed,
UINT16 maxPacketSize,
UINT32 bandwidth,
UINT16 interval,
pUINT32 pTime,
pHCD_PIPE_HANDLE pPipeHandle
)
创建一个HCD层pipe,其结构为HCD_PIPE。如果是interrupt或者isochronous pipe,HCD需要计算传输给定的字节数的数据需要的时间(纳秒),计算公式参见usb1.1规范5.9.3。
如果有足够的带宽可用,则这段带宽将会被HCD保留给该pipe,并在usbHcdPipeDestory函数执行的时候释放。
对interrupt pipe来说,bandwidth指的是每帧数据要发送的字节数;对于isochronous pipe来说,bandwidth指的是每秒传输的字节数;而对control和bulk来说,该数值为0。
serviceInterval:仅适用于中断传输pipe,标明该pipe的最大潜伏期(latency,单位毫秒)。如果一个设备的serviceInterval数值为20,说明该设备每20毫秒需要服务一次。
最坏条件下packet的传输时间在pTime中被返回。
9. STATUS usbHcdPipeDestroy
(
pHCD_NEXUS pNexus,
HCD_PIPE_HANDLE pipeHandle
)
删除pipeHandle指定的HCD层的HCD_PIPE结构变量以及分配的资源。
10. STATUS usbHcdPipeModify
(
pHCD_NEXUS pNexus,
HCD_PIPE_HANDLE pipeHandle,
UINT16 busAddress,
UINT16 maxPacketSize
)
修改pipe属性。一个pipe的两个特性:device address和maxPacketSize,有可能在pipe被创建之后被修改。这主要发生在一个设备默认的控制pipe上,这是因为起初host并不知道一个设备的deviceAddress和maxPacketSize,它需要首先通过默认的deviceAddress和maxPacketSize通过默认控制pipe对设备进行讯问后才能得到设备真正的maxPacketSize并对设备分配地址。那么这时候USBD层、HCD层的deviceAddress和maxPacketSize都需要重新修改。
11. STATUS usbHcdSofIntervalGet
(
pHCD_NEXUS pNexus,
UINT16 busNo,
pUINT16 pSofInterval
)
获取SOF间隔。所谓帧间隔就是连续两个SOF信号之间的时间差。这里的帧间隔的描述单位为高速(high speed)总线上的发送1bit数据所用的时间。SOF典型的时间间隔为1ms,那么对高速设备来说数据率为12Mbps,1ms传输12kb,因此典型的SOF间隔的数值为12000。
12. STATUS usbHcdSofIntervalSet
(
pHCD_NEXUS pNexus,
UINT16 busNo,
UINT16 sofInterval
)
设置SOF间隔。