HDF(Hardware Driver Foundation)驅動框架是HarmonyOS硬體生態開放的基礎,為開發者提供了驅動載入、驅動服務管理和驅動訊息機制等驅動能力,讓開發者能精準且高效地開發驅動程式。
本期,我們將為大家帶來HDF驅動框架中USB DDK的解析與指導。
一、USB DDK介紹
USB(Universal Serial Bus)通用序列匯流排,用於規範電腦與外部裝置的連線和通訊,包含了主機端(Host)和裝置端(Device)。其中,主機端負責USB匯流排中的資料傳輸及埠管理,裝置端則可以連線各種外設,所以USB驅動開發又分為主機端驅動開發和裝置端驅動開發。
由於基於核心態開發的USB驅動功能擴充套件性較差,目前開發者通常選擇Libusb庫進行USB驅動開發。該庫是一種跨平臺的使用者態開源USB通訊庫,可以滿足開發者基於使用者態開發功能驅動的需求。但是,由於Libusb庫是完全按照USB協議來封裝介面的,所以需要開發者對USB協議要有較深的瞭解才能很好的使用,對開發者的要求相對較高,讓很多比較初級的開發者望而卻步。為了讓更多的開發者都能進行基於使用者態的USB驅動開發,HDF引入了USB DDK開發套件。
USB DDK(USB DriverDevelop Kit)是HDF驅動框架為開發者提供的USB驅動程式開發套件,包括USB Host DDK及USB Device DDK兩部分,支援基於使用者態開發USB裝置驅動的同時,還提供了豐富的USB驅動開發能力,讓廣大開發者能精準且高效的開發USB驅動程式。
下面,我們將一一道來。
1)USB Host DDK
USB Host DDK給開發者提供了主機端USB驅動開發能力,按照功能分類三大類,分別是DDK初始化類、interface物件操作類及request物件操作類。併為開發者提供了普通模式和專家模式兩種開發模式。普通模式下,開發者可通過USBDDK API直接完成相關USB資料讀寫操作,不需要過多關注底層傳輸細節。
專家模式下,開發者通過USB RAW API直接訪問OS平臺USB通道的介面,自定義實現更加複雜的功能。目的是給驅動層留有更靈活,更強大的擴充套件方案,同時也能夠相容現有驅動,便於移植。USBHost DDK架構如圖1所示:
圖1 USB Host DDK架構
(1)USB Interface Pool負責USBInterface管理。提供USB Interface申請和回收,USB Interface記錄裝置埠資訊以及資源。USB Interface Pool按照USB Port對USB Interface進行分類管理。同時,此模組還提供了USB DDK API,方便開發者USB資料讀寫操作。
(2)USB Protocol Layer提供USB協議封裝,根據USB協議對裝置IO/控制命令的“翻譯/解析”,同時負責裝置描述符的管理,根據USB Device上報的列舉資訊,匹配對應的描述符,並構建對應的USB Interface,並加入到USB Interface Pool中管理。
(3)Device IO Manager負責USBIO請求管理,提供了同步IO和非同步IO管理機制,對於非同步IO,IO Manager負責將該請求記錄下來,然後通過Raw API Library提供的介面依次處理待傳送的IO請求;當收到USB控制器應答的處理結果後,IO接收執行緒負責解析並上報處理結果給上層呼叫者。
(4)Raw API Library抽象了底層OS能力,定義了統一的OS能力介面,對外提供了USB RAW API,讓開發者自定義實現更加複雜的驅動功能。
(5)OS Adapter用於封裝與平臺(Linux和LiteOS)相關的操作,根據不同平臺配置編譯對應平臺的封裝介面。在Linux平臺上,訪問USBFS的操作,全部都封裝在這個模組中;而在LiteOS平臺上,基於FreeBSD USB框架的裝置訪問操作,對應的也都全部封裝在這個模組中。
(6)PNP Notify用於動態監測USB狀態變化,當有新裝置新增/移除時,變化裝置資訊。同時將所有USB裝置資訊都通過KHDF上報給UHDF側的PNPNotify Manager模組來完成載入/解除安裝第三方功能驅動。
2)USB Device DDK
USB Device DDK給開發者提供了裝置端USB驅動開發能力。例如,USB埠動態註冊和去註冊能力,開發者可以基於能力實現USB埠的動態新增和組合;動態例項化能力,支援根據動態下發裝置、配置、介面及端點描述符建立裝置例項及傳輸通道;使用者態的資料傳送及接收能力,支援使用者態下傳送及接收資料;複合裝置能力,支援一個物理裝置上多個邏輯裝置,實現多個邏輯裝置間隔離,並支援不同邏輯裝置同時被不同的應用程式訪問。
USB Device DDK架構如圖2所示:
圖2 USB Device DDK架構
(1)SDK IF負責將USB裝置按照裝置、介面、管道進行邏輯劃分,對配置管理、裝置管理、IO管理進行封裝。此模組還向開發者提供了裝置建立、獲取介面、接收Event事件、收發資料等裝置測驅動開發的能力介面。
(2)Configuration Manager負責解析HCS檔案描述的USB描述符資訊,得到的USB描述符資訊用於裝置建立,同時模組還提供了自定義屬性的讀取、建立、刪除、修改等操作。
(3)Device Manager負責根據配置模組解析的USB描述符,並根據USB描述符建立裝置。同時模組還負責獲取裝置、刪除裝置、獲取裝置狀態,獲取裝置上面介面資訊。
(4)IO Manager負責資料的讀寫,包括Events事件、資料讀寫完成事件的接受,支援同步和非同步模式資料讀寫。
(5)Adapter IF主要是對複合裝置配置驅動及通用功能驅動裝置節點操作進行封裝,為上層提供統一的裝置管理介面。
(6)Adapter該模組由複合裝置配置驅動及通用功能驅動提供。
二、USB DDK開發指導
相信大家已對USB DDK已經有了一定的認識。下面,我們來看看如何使用USB DDK來開發USB Host和USB Device驅動程式吧。
1)USB Host的開發
USB Host(主機端驅動)主要完成協議封裝、裝置管理、驅動安裝與解除安裝等。通過上文的介紹,開發者可通過USB DDK API和USB RAW API來實現主機端驅動。
1. USB DDK API的使用
USB DDK API主要實現主機端USB資料讀寫操作,如圖3所示,是USB DDK API提供的部分介面。
圖3 USB DDK API部分介面
使用步驟如下:
(1) 配置驅動匹配表,完成主機端驅動總體資訊的配置,具體如下:
struct UsbPnpMatchIdTable {
//驅動模組名,該欄位的值必須和驅動入口結構的moduleName一致
const char *moduleName;
//驅動對外發布服務的名稱,必須唯一
const char *serviceName;
//驅動私有資料匹配關鍵字
const char *deviceMatchAttr;
//從該欄位開始(包含該欄位)之後資料長度,以byte為單位
uint8_t length;
//USB驅動匹配規則
uint16_t matchFlag;
//廠商編號
uint16_t vendorId;
//產品編號
uint16_t productId;
//裝置出廠編號,低16位
uint16_t bcdDeviceLow;
//裝置出廠編號,高16位
uint16_t bcdDeviceHigh;
//USB分配的裝置類程式碼
uint8_t deviceClass;
//USB分配的子類程式碼
uint8_t deviceSubClass;
//USB分配的裝置協議程式碼
uint8_t deviceProtocol;
//介面型別,根據實際需要可填寫多個
uint8_t interfaceClass[USB_PNP_INFO_MAX_INTERFACES];
//介面子型別,根據實際需要可填寫多個
uint8_t interfaceSubClass[USB_PNP_INFO_MAX_INTERFACES];
//介面所遵循的協議,根據實際需要可填寫多個
uint8_t interfaceProtocol[USB_PNP_INFO_MAX_INTERFACES];
//介面的編號,根據實際需要可填寫多個
uint8_t interfaceNumber[USB_PNP_INFO_MAX_INTERFACES];
};
其中matchFlag表示驅動匹配規則,每個bit表示一種匹配方式,其取值如下:
enum {
USB_PNP_NOTIFY_MATCH_VENDOR = 0x0001,
USB_PNP_NOTIFY_MATCH_PRODUCT = 0x0002,
USB_PNP_NOTIFY_MATCH_DEV_LOW = 0x0004,
USB_PNP_NOTIFY_MATCH_DEV_HIGH = 0x0008,
USB_PNP_NOTIFY_MATCH_DEV_CLASS = 0x0010,
USB_PNP_NOTIFY_MATCH_DEV_SUBCLASS = 0x0020,
USB_PNP_NOTIFY_MATCH_DEV_PROTOCOL = 0x0040,
USB_PNP_NOTIFY_MATCH_INT_CLASS = 0x0080,
USB_PNP_NOTIFY_MATCH_INT_SUBCLASS = 0x0100,
USB_PNP_NOTIFY_MATCH_INT_PROTOCOL = 0x0200,
USB_PNP_NOTIFY_MATCH_INT_NUMBER = 0x0400,
};
(2) USB主機端驅動開發工具包初始化,使用如下介面:
int32_t UsbInitHostSdk(struct UsbSession **session)
(3) 待步驟2初始化完後獲取UsbInterface物件,使用如下介面:
const struct UsbInterface *UsbClaimInterface(const struct UsbSession *session, uint8_t busNum, uint8_t usbAddr, uint8_t interfaceIndex);
(4) 開啟步驟3獲取到的UsbInterface介面物件,獲取對應介面的UsbInterfaceHandle物件,使用如下介面:
UsbInterfaceHandle *UsbOpenInterface(const struct UsbInterface *interfaceObj);
(5) 根據步驟4獲取到的UsbInterfaceHandle物件,獲取指定索引為pinpeIndex的pipeInfo資訊,使用如下介面:
int32_t UsbGetPipeInfo(const UsbInterfaceHandle *interfaceHandle, uint8_t settingIndex, uint8_t pipeId, struct UsbPipeInfo *pipeInfo);
(6) 為步驟4獲取到的UsbInterfaceHandle預先分配待傳送的IO Request物件,使用如下介面:
struct UsbRequest *UsbAllocRequest(const UsbInterfaceHandle *interfaceHandle, int isoPackets, int length);
(7) 根據輸入引數params填充步驟6預先分配的IO Request,使用如下介面:
int32_t UsbFillRequest(const struct UsbRequest *request, const UsbInterfaceHandle *interfaceHandle, const struct UsbRequestParams *params);
(8) 提交IO Request物件,可以選擇同步或非同步兩種模式,使用如下介面:
int32_t UsbSubmitRequestSync(const struct UsbRequest *request);//傳送同步IO請求
int32_t UsbSubmitRequestAsync(const struct UsbRequest *request);//傳送非同步IO請求
2. USB RAW API 的使用
USB RAW API主要實現USB更加複雜的功能,如獲取描述符資訊、獲取裝置指標、復位裝置、提交傳輸請求等,如圖4所示,是USB RAW API提供的部分介面。
圖4 USB RAW API
使用步驟如下:
(1) 同USB DDK API的步驟1一樣,需先進行驅動匹配表配置。
(2) 初始化Host RAW,使用如下介面:
int32_t UsbRawInit(struct UsbSession **session);
(3) 待步驟2完成後開啟USB裝置,使用如下介面:
UsbRawHandle *UsbRawOpenDevice(const struct UsbSession *session, uint8_t busNum, uint8_t usbAddr);
(4) 待步驟3完成後獲取描述符,通過描述符獲取介面、端點資訊,使用如下介面:
int32_t UsbRawGetConfigDescriptor(const UsbRawDevice *rawDev, uint8_t configIndex, struct UsbRawConfigDescriptor **config);
(5) 分配Request,並根據不同的傳輸型別使用相應的介面對Request進行填充:
int32_t UsbRawFillBulkRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawFillRequestData *fillData);// 填充用於批量傳輸的請求
int32_t UsbRawFillControlSetup(const unsigned char *setup, const struct UsbControlRequestData *requestData);
int32_t UsbRawFillControlRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawFillRequestData *fillData);// 填充用於控制傳輸的請求
int32_t UsbRawFillInterruptRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawFillRequestData *fillData);// 填充用於中斷傳輸的請求
int32_t UsbRawFillIsoRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawFillRequestData *fillData);// 填充用於同步傳輸的請求
(6) 提交IO Request物件,可以選擇同步或非同步兩種模式,分別使用如下介面:
int32_t UsbRawSendControlRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbControlRequestData *requestData);//傳送同步USB控制傳輸請求
int32_t UsbRawSendBulkRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRequestData *requestData);//傳送同步USB批量傳輸請求
int32_t UsbRawSendInterruptRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRequestData *requestData);//傳送同步執行USB中斷傳輸請求
int32_t UsbRawSubmitRequest(const struct UsbRawRequest *request);//提交非同步IO請求
感興趣的小夥伴可點選下方連結檢視完整的USB Host開發程式碼:
https://gitee.com/openharmony/drivers_peripheral/tree/master/usb/serial/src
2)USB Device的開發
USB Device(裝置端驅動)主要實現裝置管理、配置管理、IO管理、資料通訊等。USB Deivce DDK給開發者提供了裝置建立、獲取介面、接收Event事件、收發資料等驅動能力介面,如圖5所示:
圖5 USB Device DDK開放的API
下面,我們將根據USB Deivce DDK提供的驅動能力介面來開發裝置端驅動。
1. 構造描述符
首先,需構造描述符來說明裝置的總體資訊。開發者可以通過裝置功能程式碼及裝置私有資料HCS兩種途徑進行配置,下面將分別介紹。
(1) 在裝置功能程式碼中配置描述符,配置程式碼如下:
static struct UsbFnFunction g_acmFunction = {//功能描述符 .enable = true, .funcName = "f_generic.a", .strings = g_acmStrings, .fsDescriptors = g_acmFsFunction, .hsDescriptors = g_acmHsFunction, .ssDescriptors = g_acmSsFunction, .sspDescriptors = NULL, }; struct UsbFnFunction *g_functions[] = { #ifdef CDC_ECM &g_ecmFunction, #endif #ifdef CDC_ACM &g_acmFunction, #endif NULL }; static struct UsbFnConfiguration g_masterConfig = {//配置描述符 .configurationValue = 1, .iConfiguration = USB_FUNC_CONFIG_IDX, .attributes = USB_CFG_BUS_POWERED, .maxPower = POWER, .functions = g_functions, }; static struct UsbFnConfiguration *g_configs[] = { &g_masterConfig, NULL, }; static struct UsbDeviceDescriptor g_cdcMasterDeviceDesc = {//裝置描述符 .bLength = sizeof(g_cdcMasterDeviceDesc), .bDescriptorType = USB_DDK_DT_DEVICE, .bcdUSB = CpuToLe16(BCD_USB), .bDeviceClass = 0, .bDeviceSubClass = 0, .bDeviceProtocol = 0, .bMaxPacketSize0 = USB_MAX_PACKET_SIZE, .idVendor = CpuToLe16(DEVICE_VENDOR_ID), .idProduct = CpuToLe16(DEVICE_PRODUCT_ID), .bcdDevice = CpuToLe16(DEVICE_VERSION), .iManufacturer = USB_FUNC_MANUFACTURER_IDX, .iProduct = USB_FUNC_PRODUCT_IDX, .iSerialNumber = USB_FUNC_SERIAL_IDX, .bNumConfigurations = 1, }; static struct UsbFnDeviceDesc g_masterFuncDevice = {//描述符入口 .deviceDesc = &g_cdcMasterDeviceDesc, .deviceStrings = g_devStrings, .configs = g_configs, };
(2) 在裝置私有資料HCS中配置,配置程式碼如下:
root {
module = "master";
master_config {
match_attr = "usbfn_master_driver";//該欄位與device中deviceMatchAttr
保持一致,否則無法找到的這個節點的資訊。
use_hcs = 1; //使用者可以用該值決定是否使用hcs配置資訊
udc_name = "100e0000.hidwc3_0"; //UDC的名字
usb_dev_desc = "UsbDeviceDescriptor";//裝置描述符的節點UsbDeviceDescriptor
usb_dev_string = "UsbDeviceStrings"; //裝置字串的節點為UsbDeviceStrings
usb_configuration = "UsbConfigs"; //配置描述符的節點為UsbConfigs
...
}
}
裝置描述符的節點為UsbDeviceDescriptor,配置如下:
UsbDeviceDescriptor {
bLength = 18;
bDescriptorType = 0x01;
bcdUSB = 0x0200;
bDeviceClass = 0;
bDeviceSubClass = 0;
bDeviceProtocol = 0;
bMaxPacketSize0 = 0x40;
idVendor = 0x0525;
idProduct = 0xA4A7;
bcdDevice = 0x0100;
manufacturer = 0;
product = 1;
serialnumber = 2;
numConfigurations = 1;
}
2. 建立裝置
描述符構造完成後,使用UsbFnDeviceCreate函式建立一個USB裝置,並傳入UDC控制器名和UsbFnDescriptorData結構體。實現程式碼如下:
if (useHcs == 0) {//使用程式碼編寫的描述符 descData.type = USBFN_DESC_DATA_TYPE_DESC; descData.descriptor = &g_acmFuncDevice; } else { //使用hcs編寫的描述符 descData.type = USBFN_DESC_DATA_TYPE_PROP; descData.property = acm->device->property; } //建立裝置 fnDev = (struct UsbFnDevice *) UsbFnCreateDevice(acm->udcName, &descData);
3.獲取介面
裝置建立後,使用UsbFnDeviceGetInterface函式獲取UsbInterface介面物件,並通過UsbFnGetInterfacePipeInfo函式獲取USB管道資訊,實現程式碼如下:
//獲取介面 fnIface = (struct UsbFnInterface *)UsbFnGetInterface(fnDev, i); //獲取Pipe資訊 UsbFnGetInterfacePipeInfo(fnIface, i, &pipeInfo); //獲取Handle handle = UsbFnOpenInterface(fnIface); //獲取控制(EP0)Request req = UsbFnAllocCtrlRequest(acm->ctrlIface.handle, sizeof(struct UsbCdcLineCoding) + sizeof(struct UsbCdcLineCoding)); //獲取Request req = UsbFnAllocCtrlRequest(acm->ctrlIface.handle, sizeof(struct UsbCdcLineCoding) + sizeof(struct UsbCdcLineCoding));
4. 接收Event事件
通過UsbFnStartRecvInterfaceEvent函式接收Event事件,並通過UsbFnEventCallback回撥函式對Event事件做出響應,實現程式碼如下:
//開始接收Event事件
ret = UsbFnStartRecvInterfaceEvent(acm->ctrlIface.fn, 0xff, UsbAcmEventCallback, acm);
//Event處理回撥函式
static void UsbAcmEventCallback(struct UsbFnEvent *event)
{
struct UsbAcmDevice *acm = NULL;
if (event == NULL || event->context == NULL) {
HDF_LOGE("%s: event is null", __func__);
return;
}
acm = (struct UsbAcmDevice *)event->context;
switch (event->type) {
case USBFN_STATE_BIND:
HDF_LOGI("%s: receive bind event", __func__);
break;
case USBFN_STATE_UNBIND:
HDF_LOGI("%s: receive unbind event", __func__);
break;
case USBFN_STATE_ENABLE:
HDF_LOGI("%s: receive enable event", __func__);
AcmEnable(acm);
break;
case USBFN_STATE_DISABLE:
HDF_LOGI("%s: receive disable event", __func__);
AcmDisable(acm);
acm->enableEvtCnt = 0;
break;
case USBFN_STATE_SETUP:
HDF_LOGI("%s: receive setup event", __func__);
if (event->setup != NULL) {
AcmSetup(acm, event->setup);
}
break;
case USBFN_STATE_SUSPEND:
HDF_LOGI("%s: receive suspend event", __func__);
AcmSuspend(acm);
break;
case USBFN_STATE_RESUME:
HDF_LOGI("%s: receive resume event", __func__);
AcmResume(acm);
break;
default:
break;
}
}
5. 收發資料
可以選擇同步非同步傳送模式,實現程式碼如下:
notify = (struct UsbCdcNotification *)req->buf;
...
if (memcpy_s((void *)(notify + 1), length, data, length) != EOK) {
return HDF_FAILURE;
}
ret = UsbFnSubmitRequestAsync(req);//非同步傳送
感興趣的小夥伴可點選下方連結檢視完整的裝置測開發程式碼。
完整裝置測開發程式碼:https://gitee.com/openharmony/drivers_peripheral/tree/master/usb/gadget/function/
以上就是本期全部內容,通過本文的介紹相信你已經對USB DDK有了深刻的認識,期待廣大的開發者加入我們,一起豐富基於USB DDK的第三方驅動。
掃碼新增開發者小助手微信
獲取更多HarmonyOS開發資源和開發者活動資訊