HDF(Hardware Driver Foundation)驅(qū)動框架是HarmonyOS硬件生態(tài)開放的基礎(chǔ),為開發(fā)者提供了驅(qū)動加載、驅(qū)動服務管理和驅(qū)動消息機制等驅(qū)動能力,讓開發(fā)者能精準且高效的開發(fā)驅(qū)動程序。本期,我們將為大家?guī)鞨DF驅(qū)動框架中USB DDK的解析與指導。
一、USB DDK介紹USB(Universal Serial Bus)通用串行總線,用于規(guī)范電腦與外部設備的連接和通訊,包含了主機端(Host)和設備端(Device)。其中,主機端負責USB總線中的數(shù)據(jù)傳輸及端口管理,設備端則可以連接各種外設,所以USB驅(qū)動開發(fā)又分為主機端驅(qū)動開發(fā)和設備端驅(qū)動開發(fā)。
由于基于內(nèi)核態(tài)開發(fā)的USB驅(qū)動功能擴展性較差,目前開發(fā)者通常選擇Libusb庫進行USB驅(qū)動開發(fā)。該庫是一種跨平臺的用戶態(tài)開源USB通信庫,可以滿足開發(fā)者基于用戶態(tài)開發(fā)功能驅(qū)動的需求。但是,由于Libusb庫是完全按照USB協(xié)議來封裝接口的,所以需要開發(fā)者對USB協(xié)議要有較深的了解才能很好的使用,對開發(fā)者的要求相對較高,讓很多比較初級的開發(fā)者望而卻步。為了讓更多的開發(fā)者都能進行基于用戶態(tài)的USB驅(qū)動開發(fā),HDF引入了USB DDK開發(fā)套件。
USB DDK(USB DriverDevelop Kit)是HDF驅(qū)動框架為開發(fā)者提供的USB驅(qū)動程序開發(fā)套件,包括USB Host DDK及USB Device DDK兩部分,支持基于用戶態(tài)開發(fā)USB設備驅(qū)動的同時,還提供了豐富的USB驅(qū)動開發(fā)能力,讓廣大開發(fā)者能精準且高效的開發(fā)USB驅(qū)動程序。下面,我們將一一道來。
1USB Host DDK
USB Host DDK給開發(fā)者提供了主機端USB驅(qū)動開發(fā)能力,按照功能分類三大類,分別是DDK初始化類、interface對象操作類及request對象操作類。并為開發(fā)者提供了普通模式和專家模式兩種開發(fā)模式。普通模式下,開發(fā)者可通過USBDDK API直接完成相關(guān)USB數(shù)據(jù)讀寫操作,不需要過多關(guān)注底層傳輸細節(jié)。專家模式下,開發(fā)者通過USB RAW API直接訪問OS平臺USB通道的接口,自定義實現(xiàn)更加復雜的功能。目的是給驅(qū)動層留有更靈活,更強大的擴展方案,同時也能夠兼容現(xiàn)有驅(qū)動,便于移植。USBHost DDK架構(gòu)如圖1所示:
圖1 USB Host DDK架構(gòu)
(1)USB Interface Pool負責USBInterface管理。提供USB Interface申請和回收,USB Interface記錄設備端口信息以及資源。USB Interface Pool按照USB Port對USB Interface進行分類管理。同時,此模塊還提供了USB DDK API,方便開發(fā)者USB數(shù)據(jù)讀寫操作。
(2)USB Protocol Layer提供USB協(xié)議封裝,根據(jù)USB協(xié)議對設備IO/控制命令的“翻譯/解析”,同時負責設備描述符的管理,根據(jù)USB Device上報的枚舉信息,匹配對應的描述符,并構(gòu)建對應的USB Interface,并加入到USB Interface Pool中管理。
(3)Device IO Manager負責USBIO請求管理,提供了同步IO和異步IO管理機制,對于異步IO,IO Manager負責將該請求記錄下來,然后通過Raw API Library提供的接口依次處理待發(fā)送的IO請求;當收到USB控制器應答的處理結(jié)果后,IO接收線程負責解析并上報處理結(jié)果給上層調(diào)用者。
(4)Raw API Library抽象了底層OS能力,定義了統(tǒng)一的OS能力接口,對外提供了USB RAW API,讓開發(fā)者自定義實現(xiàn)更加復雜的驅(qū)動功能。
(5)OS Adapter用于封裝與平臺(Linux和LiteOS)相關(guān)的操作,根據(jù)不同平臺配置編譯對應平臺的封裝接口。在Linux平臺上,訪問USBFS的操作,全部都封裝在這個模塊中;而在LiteOS平臺上,基于FreeBSD USB框架的設備訪問操作,對應的也都全部封裝在這個模塊中。
(6)PNP Notify用于動態(tài)監(jiān)測USB狀態(tài)變化,當有新設備添加/移除時,變化設備信息。同時將所有USB設備信息都通過KHDF上報給UHDF側(cè)的PNPNotify Manager模塊來完成加載/卸載第三方功能驅(qū)動。
2USB Device DDK
USB Device DDK給開發(fā)者提供了設備端USB驅(qū)動開發(fā)能力。例如,USB端口動態(tài)注冊和去注冊能力,開發(fā)者可以基于能力實現(xiàn)USB端口的動態(tài)添加和組合;動態(tài)實例化能力,支持根據(jù)動態(tài)下發(fā)設備、配置、接口及端點描述符創(chuàng)建設備實例及傳輸通道;用戶態(tài)的數(shù)據(jù)發(fā)送及接收能力,支持用戶態(tài)下發(fā)送及接收數(shù)據(jù);復合設備能力,支持一個物理設備上多個邏輯設備,實現(xiàn)多個邏輯設備間隔離,并支持不同邏輯設備同時被不同的應用進程訪問。USB Device DDK架構(gòu)如圖2所示:
圖2 USB Device DDK架構(gòu)
(1)SDK IF負責將USB設備按照設備、接口、管道進行邏輯劃分,對配置管理、設備管理、IO管理進行封裝。此模塊還向開發(fā)者提供了設備創(chuàng)建、獲取接口、接收Event事件、收發(fā)數(shù)據(jù)等設備測驅(qū)動開發(fā)的能力接口。
(2)Configuration Manager負責解析HCS文件描述的USB描述符信息,得到的USB描述符信息用于設備創(chuàng)建,同時模塊還提供了自定義屬性的讀取、創(chuàng)建、刪除、修改等操作。
(3)Device Manager負責根據(jù)配置模塊解析的USB描述符,并根據(jù)USB描述符創(chuàng)建設備。同時模塊還負責獲取設備、刪除設備、獲取設備狀態(tài),獲取設備上面接口信息。
(4)IO Manager負責數(shù)據(jù)的讀寫,包括Events事件、數(shù)據(jù)讀寫完成事件的接受,支持同步和異步模式數(shù)據(jù)讀寫。
(5)Adapter IF主要是對復合設備配置驅(qū)動及通用功能驅(qū)動設備節(jié)點操作進行封裝,為上層提供統(tǒng)一的設備管理接口。
(6)Adapter該模塊由復合設備配置驅(qū)動及通用功能驅(qū)動提供。
二、USB DDK開發(fā)指導
相信大家已對USB DDK已經(jīng)有了一定的認識。下面,我們來看看如何使用USB DDK來開發(fā)USB Host和USB Device驅(qū)動程序吧。
1USB Host的開發(fā)
USB Host(主機端驅(qū)動)主要完成協(xié)議封裝、設備管理、驅(qū)動安裝與卸載等。通過上文的介紹,開發(fā)者可通過USB DDK API和USB RAW API來實現(xiàn)主機端驅(qū)動。
1. USB DDK API的使用
圖3 USB DDK API部分接口
使用步驟如下:
(1) 配置驅(qū)動匹配表,完成主機端驅(qū)動總體信息的配置,具體如下:
(左右滑動,查看更多)struct UsbPnpMatchIdTable {
//驅(qū)動模塊名,該字段的值必須和驅(qū)動入口結(jié)構(gòu)的moduleName一致
const char *moduleName;
//驅(qū)動對外發(fā)布服務的名稱,必須唯一
const char *serviceName;
//驅(qū)動私有數(shù)據(jù)匹配關(guān)鍵字
const char *deviceMatchAttr;
//從該字段開始(包含該字段)之后數(shù)據(jù)長度,以byte為單位
uint8_t length;
//USB驅(qū)動匹配規(guī)則
uint16_t matchFlag;
//廠商編號
uint16_t vendorId;
//產(chǎn)品編號
uint16_t productId;
//設備出廠編號,低16位
uint16_t bcdDeviceLow;
//設備出廠編號,高16位
uint16_t bcdDeviceHigh;
//USB分配的設備類代碼
uint8_t deviceClass;
//USB分配的子類代碼
uint8_t deviceSubClass;
//USB分配的設備協(xié)議代碼
uint8_t deviceProtocol;
//接口類型,根據(jù)實際需要可填寫多個
uint8_t interfaceClass[USB_PNP_INFO_MAX_INTERFACES];
//接口子類型,根據(jù)實際需要可填寫多個
uint8_t interfaceSubClass[USB_PNP_INFO_MAX_INTERFACES];
//接口所遵循的協(xié)議,根據(jù)實際需要可填寫多個
uint8_t interfaceProtocol[USB_PNP_INFO_MAX_INTERFACES];
//接口的編號,根據(jù)實際需要可填寫多個
uint8_t interfaceNumber[USB_PNP_INFO_MAX_INTERFACES];
};
其中matchFlag表示驅(qū)動匹配規(guī)則,每個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主機端驅(qū)動開發(fā)工具包初始化,使用如下接口:
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) 根據(jù)步驟4獲取到的UsbInterfaceHandle對象,獲取指定索引為pinpeIndex的pipeInfo信息,使用如下接口:
int32_t UsbGetPipeInfo(const UsbInterfaceHandle *interfaceHandle, uint8_t settingIndex, uint8_t pipeId, struct UsbPipeInfo *pipeInfo);
(左右滑動,查看更多)(6) 為步驟4獲取到的UsbInterfaceHandle預先分配待發(fā)送的IO Request對象,使用如下接口:
struct UsbRequest *UsbAllocRequest(const UsbInterfaceHandle *interfaceHandle, int isoPackets, int length);
(左右滑動,查看更多)(7) 根據(jù)輸入參數(shù)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);//發(fā)送同步IO請求
int32_t UsbSubmitRequestAsync(const struct UsbRequest *request);//發(fā)送異步IO請求
2. USB RAW API 的使用
USB RAW API主要實現(xiàn)USB更加復雜的功能,如獲取描述符信息、獲取設備指針、復位設備、提交傳輸請求等,如圖4所示,是USB RAW API提供的部分接口。
圖4 USB RAW API
使用步驟如下:
(1) 同USB DDK API的步驟1一樣,需先進行驅(qū)動匹配表配置。
(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,并根據(jù)不同的傳輸類型使用相應的接口對Request進行填充:
(左右滑動,查看更多)int32_t UsbRawFillBulkRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawFillRequestData *fillData);// 填充用于批量傳輸?shù)恼埱?/span>
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);// 填充用于控制傳輸?shù)恼埱?/span>
int32_t UsbRawFillInterruptRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawFillRequestData *fillData);// 填充用于中斷傳輸?shù)恼埱?/span>
int32_t UsbRawFillIsoRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawFillRequestData *fillData);// 填充用于同步傳輸?shù)恼埱?/span>
(6) 提交IO Request對象,可以選擇同步或異步兩種模式,分別使用如下接口:
(左右滑動,查看更多)int32_t UsbRawSendControlRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbControlRequestData *requestData);//發(fā)送同步USB控制傳輸請求
int32_t UsbRawSendBulkRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRequestData *requestData);//發(fā)送同步USB批量傳輸請求
int32_t UsbRawSendInterruptRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRequestData *requestData);//發(fā)送同步執(zhí)行USB中斷傳輸請求
int32_t UsbRawSubmitRequest(const struct UsbRawRequest *request);//提交異步IO請求
感興趣的小伙伴可點擊下方鏈接查看完整的USB Host開發(fā)代碼:https://gitee.com/openharmony/drivers_peripheral/tree/master/usb/serial/src
2USB Device的開發(fā)
USB Device(設備端驅(qū)動)主要實現(xiàn)設備管理、配置管理、IO管理、數(shù)據(jù)通信等。USB Deivce DDK給開發(fā)者提供了設備創(chuàng)建、獲取接口、接收Event事件、收發(fā)數(shù)據(jù)等驅(qū)動能力接口,如圖5所示:
圖5 USB Device DDK開放的API
下面,我們將根據(jù)USB Deivce DDK提供的驅(qū)動能力接口來開發(fā)設備端驅(qū)動。
1. 構(gòu)造描述符
首先,需構(gòu)造描述符來說明設備的總體信息。開發(fā)者可以通過設備功能代碼及設備私有數(shù)據(jù)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[] = {
&g_ecmFunction,
&g_acmFunction,
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) 在設備私有數(shù)據(jù)HCS中配置,配置代碼如下:
(左右滑動,查看更多)root {
module = "master";
master_config {
match_attr = "usbfn_master_driver";//該字段與device中deviceMatchAttr
保持一致,否則無法找到的這個節(jié)點的信息。
use_hcs = 1; //用戶可以用該值決定是否使用hcs配置信息
udc_name = "100e0000.hidwc3_0"; //UDC的名字
usb_dev_desc = "UsbDeviceDescriptor";//設備描述符的節(jié)點UsbDeviceDescriptor
usb_dev_string = "UsbDeviceStrings"; //設備字符串的節(jié)點為UsbDeviceStrings
usb_configuration = "UsbConfigs"; //配置描述符的節(jié)點為UsbConfigs
...
}
}
設備描述符的節(jié)點為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. 創(chuàng)建設備
描述符構(gòu)造完成后,使用UsbFnDeviceCreate函數(shù)創(chuàng)建一個USB設備,并傳入UDC控制器名和UsbFnDescriptorData結(jié)構(gòu)體。實現(xiàn)代碼如下:
(左右滑動,查看更多)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;
}
//創(chuàng)建設備
fnDev = (struct UsbFnDevice *) UsbFnCreateDevice(acm->udcName, &descData);
3.獲取接口
設備創(chuàng)建后,使用UsbFnDeviceGetInterface函數(shù)獲取UsbInterface接口對象,并通過UsbFnGetInterfacePipeInfo函數(shù)獲取USB管道信息,實現(xiàn)代碼如下:
(左右滑動,查看更多)//獲取接口
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函數(shù)接收Event事件,并通過UsbFnEventCallback回調(diào)函數(shù)對Event事件做出響應,實現(xiàn)代碼如下:
(左右滑動,查看更多)//開始接收Event事件
ret = UsbFnStartRecvInterfaceEvent(acm->ctrlIface.fn, 0xff, UsbAcmEventCallback, acm);
//Event處理回調(diào)函數(shù)
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. 收發(fā)數(shù)據(jù)
可以選擇同步異步發(fā)送模式,實現(xiàn)代碼如下:
(左右滑動,查看更多)notify = (struct UsbCdcNotification *)req->buf;
...
if (memcpy_s((void *)(notify + 1), length, data, length) != EOK) {
return HDF_FAILURE;
}
ret = UsbFnSubmitRequestAsync(req);//異步發(fā)送
以上就是本期全部內(nèi)容,通過本文的介紹相信你已經(jīng)對USB DDK有了深刻的認識,期待廣大的開發(fā)者加入我們,一起豐富基于USB DDK的第三方驅(qū)動。
原文標題:USB DDK助你輕松實現(xiàn)USB驅(qū)動開發(fā)
文章出處:【微信公眾號:HarmonyOS開發(fā)者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
審核編輯:湯梓紅
-
usb
+關(guān)注
關(guān)注
60文章
7945瀏覽量
264657 -
驅(qū)動
+關(guān)注
關(guān)注
12文章
1840瀏覽量
85291 -
DDK
+關(guān)注
關(guān)注
0文章
5瀏覽量
8753
原文標題:USB DDK助你輕松實現(xiàn)USB驅(qū)動開發(fā)
文章出處:【微信號:HarmonyOS_Dev,微信公眾號:HarmonyOS開發(fā)者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論