接上篇文章 介紹完了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_FUNCmngmtFunc; /* management callback *//* Control pipe callbacks */USB_TARG_FEATURE_CLEAR_FUNCfeatureClear;/* feature clear */USB_TARG_FEATURE_SET_FUNCfeatureSet;/* feature set */USB_TARG_CONFIGURATION_GET_FUNCconfigurationGet; /*configuration get*/USB_TARG_CONFIGURATION_SET_FUNCconfigurationSet; /*configuration set*/USB_TARG_DESCRIPTOR_GET_FUNCdescriptorGet;/* descriptor get */USB_TARG_DESCRIPTOR_SET_FUNCdescriptorSet;/* descriptor set */USB_TARG_INTERFACE_GET_FUNCinterfaceGet;/* interface get */USB_TARG_INTERFACE_SET_FUNCinterfaceSet;/* interface set */USB_TARG_STATUS_GET_FUNCstatusGet;/* status get */USB_TARG_ADDRESS_SET_FUNCaddressSet;/* address set */USB_TARG_SYNCH_FRAME_GET_FUNCsynchFrameGet;/* frame get */USB_TARG_VENDOR_SPECIFIC_FUNCvendorSpecific;/* 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_PIPEpipeHandle;/* pipe handle information */ pVOIDpHalPipeHandle;/* HAL specific pipe handle */ pTARG_TCDpTargTcd; /* 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指向控制資料。