USB DDK介绍与开发指导

描述

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驱动程序。下面,我们将一一道来。  

USB Host DDK

USB Host DDK给开发者提供了主机端USB驱动开发能力,按照功能分类三大类,分别是DDK初始化类、interface对象操作类及request对象操作类。并为开发者提供了普通模式和专家模式两种开发模式。普通模式下,开发者可通过USBDDK API直接完成相关USB数据读写操作,不需要过多关注底层传输细节。专家模式下,开发者通过USB RAW API直接访问OS平台USB通道的接口,自定义实现更加复杂的功能。目的是给驱动层留有更灵活,更强大的扩展方案,同时也能够兼容现有驱动,便于移植。USBHost DDK架构如图1所示:

 

DDK

图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模块来完成加载/卸载第三方功能驱动。  

USB Device DDK

USB Device DDK给开发者提供了设备端USB驱动开发能力。例如,USB端口动态注册和去注册能力,开发者可以基于能力实现USB端口的动态添加和组合;动态实例化能力,支持根据动态下发设备、配置、接口及端点描述符创建设备实例及传输通道;用户态的数据发送及接收能力,支持用户态下发送及接收数据;复合设备能力,支持一个物理设备上多个逻辑设备,实现多个逻辑设备间隔离,并支持不同逻辑设备同时被不同的应用进程访问。USB Device DDK架构如图2所示:

 

DDK
图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驱动程序吧。
 

USB Host的开发

USB Host(主机端驱动)主要完成协议封装、设备管理、驱动安装与卸载等。通过上文的介绍,开发者可通过USB DDK API和USB RAW API来实现主机端驱动。

1. USB DDK API的使用 

 USB DDK API主要实现主机端USB数据读写操作,如图3所示,是USB DDK API提供的部分接口。         

                         
         

DDK

 

 图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提供的部分接口。

DDK

图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

USB Device的开发

USB Device(设备端驱动)主要实现设备管理、配置管理、IO管理、数据通信等。USB Deivce DDK给开发者提供了设备创建、获取接口、接收Event事件、收发数据等驱动能力接口,如图5所示:

DDK

图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,#endifNULL};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);//获取Handlehandle = UsbFnOpenInterface(fnIface);//获取控制(EP0)Requestreq = UsbFnAllocCtrlRequest(acm->ctrlIface.handle,            sizeof(struct UsbCdcLineCoding) + sizeof(struct UsbCdcLineCoding));//获取Requestreq = 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);//异步发送
 (左右滑动,查看更多)

以上就是本期全部内容,通过本文的介绍相信你已经对USB DDK有了深刻的认识,期待广大的开发者加入我们,一起丰富基于USB DDK的第三方驱动。

原文标题:USB DDK助你轻松实现USB驱动开发

文章出处:【微信公众号:HarmonyOS开发者】欢迎添加关注!文章转载请注明出处。
审核编辑:汤梓红


打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分