接上篇文章 介绍完了Host,再来看下Peripheral驱动栈,下图为Peripheral驱动栈的结构图:
风河USB Peripheral驱动栈中,位于底层的是目标控制器TC,它是Peripheral栈中用于连接USB的硬件部分。对于每种类型的TC,都会有对应的TCD,风河提供了Freescale Dual Role、NetChip NET2280、PDIUSBD12和PhilipsIsp1582四种TCD,它们的功能主要包括:
1、实现任何硬件相关的功能;
2、实现寄存器访问,USB Peripheral栈的其它层都不允许实现寄存器访问;
3、为与栈中上层通讯提供entry point。
在这之上就又是HAL(Hardware Adaptation Layer),该层为驱动栈中的更上层提供了硬件独立的访问方式,使得整个驱动栈更容易移植到新的TC硬件上。target layer的功能与之类似,也是一个抽象的中介物。在运行时,目标应用程序会命令目标层Attach一个TCD,之后目标层就负责TCD与目标应用程序间的请求及回应,它可以同时处理多个TCD与应用程序的通讯。所以,这部分的重点就分布在目标层和TCD上。
先看目标层,下图显示了目标层是如何串联应用层和HAL的,并描述了目标层的内部组成:
要通过该层实现通讯,初始化代码和应用程序需要经过以下几步:
1、初始化目标层:和USB Host栈类似,这里有一个初始化代码usbTargInitialize,主要功能也是初始化OS库、创建句柄和互斥访问量,同样的嵌套式调用,所以需要至少调用一次。
2、实现必须的callback函数:目标层有一个callback表,列举了应用程序中所有的功能入口函数。一但TCD与之attach成功,目标层会通过这些入口执行异步回调。所以在第3步开始前,应用程序会根据表中的入口函数将对应的功能函数指针与之对应。回调表的原型定义在usbTargLib.h中,定义如下:
typedef struct usbTargCallbackTable /* USB_TARG_CALLBACK_TABLE */ { /* device management callbacks */ USB_TARG_MANAGEMENT_FUNC mngmtFunc; /* management callback */ /* Control pipe callbacks */ USB_TARG_FEATURE_CLEAR_FUNC featureClear; /* feature clear */ USB_TARG_FEATURE_SET_FUNC featureSet; /* feature set */ USB_TARG_CONFIGURATION_GET_FUNC configurationGet; /*configuration get*/ USB_TARG_CONFIGURATION_SET_FUNC configurationSet; /*configuration set*/ USB_TARG_DESCRIPTOR_GET_FUNC descriptorGet; /* descriptor get */ USB_TARG_DESCRIPTOR_SET_FUNC descriptorSet; /* descriptor set */ USB_TARG_INTERFACE_GET_FUNC interfaceGet; /* interface get */ USB_TARG_INTERFACE_SET_FUNC interfaceSet; /* interface set */ USB_TARG_STATUS_GET_FUNC statusGet; /* status get */ USB_TARG_ADDRESS_SET_FUNC addressSet; /* address set */ USB_TARG_SYNCH_FRAME_GET_FUNC synchFrameGet; /* frame get */ USB_TARG_VENDOR_SPECIFIC_FUNC vendorSpecific; /* vendor specific */ } USB_TARG_CALLBACK_TABLE, *pUSB_TARG_CALLBACK_TABLE;
具体各函数及其参数的介绍见《Wind River USB Programmer’s Guide》第5.5节。
3、Attach一个TCD:在目标程序能从host收发指令之前,初始化代码还必须将自己和TCD attach起来,使用函数usbTargTcdAttach,该函数原型如下:
usbTargTcdAttach (USB_TCD_EXEC_FUNC tcdExecFunc, pVOID tcdParam,
pUSB_TARG_CALLBACK_TABLE pCallbacks,
pVOID callbackParam,
pUSB_TARG_CHANNEL pTargChannel);
需要传入的参数包括TCD的Single Entry Point指针,TCD-defined属性值,目标应用程序callback表的指针以及callback函数的参数。当目标控制器TC成功的attach到TCD后,TCD会返回自己的句柄,保存在USB_TARG_CHANNEL中,应用程序可以通过该句柄与TCD进行接下来的通讯。在HAL层,函数会调用usbHalTcdAttach真正与TCD连接上,该函数主要源码如下:
/* 初始化数据结构TRB (Target Request Block) - Start */ trbHeaderInit((pTRB_HEADER)&trbAttach, NULL, TCD_FNC_ATTACH, sizeof(TRB_ATTACH)); trbAttach.tcdParam = tcdParam; trbAttach.usbHalIsr = (USB_HAL_ISR_CALLBACK)usbHalIsr; trbAttach.usbHalIsrParam = pUsbHal; trbAttach.pHalDeviceInfo = &pUsbHal->halDeviceInfo; trbAttach.pDeviceInfo = pDeviceInfo; /*End */ /* Call the single entry point for the TCD */ status = (*tcdExecFunc)(&trbAttach); /* Check if the function is executed successfully */ if (status != OK) { /* WindView Instrumentation */ USB_HAL_LOG_EVENT(….); USBHAL_ERR (….); /* Call the function to free the HAL TCD resources */ usbHalFreeTCDResources(pUsbHal); return ERROR; } /* 将TCD句柄存在HAL 结构体中 */ pUsbHal->pTCDHandle = trbAttach.header.handle; /* 为指针分配内存Allocate memory for the array of pointers */ pUsbHal->pPipeInfo = (pUSBHAL_PIPE_INFO *) OSS_CALLOC(sizeof(pUSBHAL_PIPE_INFO) * pUsbHal->halDeviceInfo.uNumberEndpoints); /* Check if memory allocation is successful */ if (pUsbHal->pPipeInfo == NULL) { /*处理内存分配失败的释放TCD代码,同上面的status != OK,略去*/ …… } /* 下面向TCD中存数据*/ /*Store the single entry point in the TCD data structure */ pUsbHal->tcdExecFunc = tcdExecFunc; /* Store the management callback in the TCD data structure */ pUsbHal->mngmtCallback = mngmtCallback; /* Store the management callback parameter in the TCD data structure */ pUsbHal->mngmtCallbackParam = mngmtCallbackParam; * Store the HAL TCD pointer in the pNexus data structure */ pNexus->handle = pUsbHal;
4、使能该TCD:在attach成功后,应用程序会调用usbTargEnable使能TCD,和第三步的attach类似,此时TCD也会使能底层的目标控制器,HAL会通过TCD_FNC_ENABLE执行TCD的single entry point(usbHalTcdEnable)。
5、创建管道pipes:上层应用程序会调用函数usbTargPipeCreate创建管道,管道的信息存储在结构体TARG_PIPE中,定义如下:
typedef struct targPipe /* TARG_PIPE */ { USB_TARG_PIPE pipeHandle; /* pipe handle information */ pVOID pHalPipeHandle; /* HAL specific pipe handle */ pTARG_TCD pTargTcd; /* pointer to targ_tcd data structure*/ } TARG_PIPE, *pTARG_PIPE;
pipeHandle只是管道的标示符,用于应用程序执行USB传输到终端;pHalPipeHandle是HAL信息的指针,用于在目标层中的内部记录,只要管道创建成功,指向结构体USB_HAL_PIPE_INFO的指针就会被存在该句柄中;
6、传输数据:数据的传输有两种形式,通用的和控制的。前者使用目标层函数usbTargTransfer实现USB外围驱动栈与主机间的数据传输;而如果应用程序要通过默认控制管道与主机通信,即为后者,需调用usbTargControlResponseSend、usbTargControlStatusSend和usbTargControlPayloadRcv三个函数。usbTargTransfer函数通过pipeHandle初始化一个管道上的传输,需要传输的数据用结构体USB_ERP描述,结构体定义如下:
typedef struct usb_erp { LINK targLink; /* 应用程序用它存储通过将USB_ERP转换为LINK后的ERP列*/ pVOID targPtr; /*用于控制型传输,指定TARG_TCD的指针*/ LINK tcdLink; /*HAL用于保存传递给TCD的ERP列*/ pVOID tcdPtr; /*当HAL需发送一个零长度的包时需要,指向TCD */ pVOID userPtr; /* Ptr field for use by client */ UINT16 erpLen; /* ERP structure的长度*/ int result; /* ERP 完成结果: S_usbTcdLib_xxxx */ ERP_CALLBACK targCallback; /* usbTargLib completion callback routine */ ERP_CALLBACK userCallback; /* client's completion callback routine */ pVOID pPipeHandle; /* Pipe handle */ UINT16 transferType; /* Type of ERP: control, bulk, etc. */ UINT16 dataToggle; /* ERP should start with DATA0/DATA1. */ UINT16 bfrCount; /* Indicates count of buffers in BfrList */ UINT16 endpointId; /* device endpoint */ /* Added for complaince with the old stack */ USB_BFR_LIST bfrList [1]; } USB_ERP, *pUSB_ERP;
至于后者,目标应用程序用usbTargControlResponseSend发送控制管道回应主机用usbTargControlResponseSend发出的请求,应用程序在处理主机发送的各种Control-IN请求时都会利用之前发送的数据。例如,一个GET_DESCRIPTOR请求,应用程序会调用API发送回应数据来响应主机的请求。usbTargControlStatusSend用于当控制传输没有数据段时向主机发送状态;usbTargControlPayloadRcv用于注册一个回调函数接收主机发来的控制管道回应,接收到回应后,回调函数就会被调用,pBfr指向控制数据。