关于harmonyos:USB-DDK助你轻松实现USB驱动开发

65次阅读

共计 11211 个字符,预计需要花费 29 分钟才能阅读完成。

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 申请
  1. 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…
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; 
 }
  1. 创立设施
    描述符结构实现后,应用 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));
  2. 接管 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;
    }
}
  1. 收发数据
    能够抉择同步异步发送模式,实现代码如下:
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… 查看残缺的设施测开发代码。

以上就是本期全部内容,通过本文的介绍置信你曾经对 USB DDK 有了粗浅的意识,期待宽广的开发者退出咱们,一起丰盛基于 USB DDK 的第三方驱动。

正文完
 0