From 64b4d3ee53718af4300f95c730b3e738a02fedd3 Mon Sep 17 00:00:00 2001 From: biga2021 Date: Wed, 15 Sep 2021 17:28:06 +0800 Subject: [PATCH] add usb docs Signed-off-by: biga2021 Change-Id: I8d0f98365ec7b5b8d45a7092966ddd447f2eba67 --- zh-cn/device-dev/driver/Readme-CN.md | 2 +- .../driver/driver-peripherals-usb-des.md | 1572 +++++++++++++++++ ...0\346\250\241\345\236\213\345\233\276.png" | Bin 0 -> 38213 bytes ...0\346\250\241\345\236\213\345\233\276.png" | Bin 0 -> 45470 bytes 4 files changed, 1573 insertions(+), 1 deletion(-) create mode 100755 zh-cn/device-dev/driver/driver-peripherals-usb-des.md create mode 100755 "zh-cn/device-dev/driver/figure/USB-Device\351\251\261\345\212\250\346\250\241\345\236\213\345\233\276.png" create mode 100755 "zh-cn/device-dev/driver/figure/USB-Host\351\251\261\345\212\250\346\250\241\345\236\213\345\233\276.png" diff --git a/zh-cn/device-dev/driver/Readme-CN.md b/zh-cn/device-dev/driver/Readme-CN.md index 27caa89536..a5b29873e7 100755 --- a/zh-cn/device-dev/driver/Readme-CN.md +++ b/zh-cn/device-dev/driver/Readme-CN.md @@ -33,4 +33,4 @@ - [TOUCHSCREEN](driver-peripherals-touch-des.md) - [SENSOR](driver-peripherals-sensor-des.md) - [WLAN](driver-peripherals-external-des.md) - + - [USB](driver-peripherals-usb-des.md) diff --git a/zh-cn/device-dev/driver/driver-peripherals-usb-des.md b/zh-cn/device-dev/driver/driver-peripherals-usb-des.md new file mode 100755 index 0000000000..02882fcc10 --- /dev/null +++ b/zh-cn/device-dev/driver/driver-peripherals-usb-des.md @@ -0,0 +1,1572 @@ +# USB + +- [概述](#section175431838101617) + - [接口说明](#section17667171301711) + +- [开发指导](#section65745222184) + - [Host DDK API驱动开发步骤](#section865734181916) + - [Host RAW API驱动开发步骤](#section865734181916) + - [Device DDK API驱动开发步骤](#section865734181916) + +- [开发实例](#section263714411191) + - [Host DDK API驱动开发](#section18249155619195) + - [Host RAW API驱动开发](#section3571192072014) + - [Device DDK API驱动开发](#section6356758162015) + + +## 概述 + +USB Host部分,主要包括协议封装、设备管理、驱动安装与卸载等。 + +USB Device部分,支持USB功能设备的开发,提供USB设备相关功能,主要包括设备管理、配置管理、IO管理,实现USB功能设备创建、配置、数据通信等。 + +USB Host驱动模型如下图1所示: + +**图 1** USB Host驱动模型图 +![](figure/USB-Host驱动模型图.png "USB Host驱动模型图") + +**图 2** USB Device驱动模型图 +![](figure/USB-Device驱动模型图.png "USB Device驱动模型图") + +USB驱动模型对外开放的API接口能力如下: + +- Usb Host DDK提供给用户态可直接调用的驱动能力接口,按照功能分类三大类:DDK初始化类、对interface对象操作类、对request对象操作类,可以提供DDK初始化、interface绑定和释放,打开和关闭操作,request的申请和释放,同步和异步传输等。 +- Usb Deivce DDK提供设备管理、IO管理、配置管理,主要功能有:创建和删除设备、获取和打开接口、同步和异步传输等。 + +### 接口说明 + +USB驱动模型Host侧开放的API接口功能,参考表1。 + +**表 1** USB驱动模型Host侧开放的API接口功能介绍 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

头文件

+

接口名称

+

功能描述

+

usb_interface.h

+

+

+

int32_t UsbInitHostSdk(struct UsbSession **session);

+

USB主机端驱动开发工具包初始化

+

int32_t UsbExitHostSdk(const struct UsbSession *session);

+

USB主机端驱动开发工具包退出

+

const struct UsbInterface *UsbClaimInterface(const struct UsbSession *session, uint8_t busNum, uint8_t usbAddr, uint8_t interfaceIndex);

+

获取USB接口对象

+

int UsbReleaseInterface(const struct UsbInterface *interfaceObj);

+

释放USB接口对象

+

int UsbAddOrRemoveInterface(const struct UsbSession *session, uint8_t busNum, uint8_t usbAddr, uint8_t interfaceIndex, UsbInterfaceStatus status);

+

增加移除接口

+

UsbInterfaceHandle *UsbOpenInterface(const struct UsbInterface *interfaceObj);

+

打开USB对象接口

+

int32_t UsbCloseInterface(const UsbInterfaceHandle *interfaceHandle);

+

关闭USB接口对象

+

int32_t UsbSelectInterfaceSetting(const UsbInterfaceHandle *interfaceHandle, uint8_t settingIndex, struct UsbInterface **interfaceObj);

+

设置可选配置

+

int32_t UsbGetPipeInfo(const UsbInterfaceHandle *interfaceHandle, uint8_t settingIndex, uint8_t pipeId, struct UsbPipeInfo *pipeInfo);

+

获取指定可选设置的管道信息

+

int32_t UsbClearInterfaceHalt(const UsbInterfaceHandle *interfaceHandle, uint8_t pipeAddress);

+

清除指定索引的管道状态

+

struct UsbRequest *UsbAllocRequest(const UsbInterfaceHandle *interfaceHandle, int isoPackets, int length);

+

分配请求对象

+

int UsbFreeRequest(const struct UsbRequest *request);

+

释放请求对象

+

int UsbSubmitRequestAsync(const struct UsbRequest *request);

+

发送异步请求

+

int32_t UsbFillRequest(const struct UsbRequest *request, const UsbInterfaceHandle *interfaceHandle, const struct UsbRequestParams *params);

+

填充请求

+

sint UsbCancelRequest(const struct UsbRequest *request);

+

取消异步请求

+

int UsbSubmitRequestSync(const struct UsbRequest *request);

+

发送同步请求

+

usb_raw_api.h

+

+

+

+

+

+

int UsbRawInit(struct UsbSession **session);

+

USB驱动开发工具包专家模式初始化

+

int UsbRawExit(const struct UsbSession *session);

+

USB驱动开发工具包专家模式退出

+

UsbRawHandle *UsbRawOpenDevice(const struct UsbSession *session, uint8_t busNum, uint8_t usbAddr);

+

打开USB设备对象

+

int UsbRawCloseDevice(const UsbRawHandle *devHandle);

+

关闭USB设备对象

+

int UsbRawSendControlRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbControlRequestData *requestData);

+

执行同步控制传输

+

int UsbRawSendBulkRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRequestData *requestData);

+

执行同步批量传输

+

int UsbRawSendInterruptRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRequestData *requestData);

+

执行同步中断传输

+

int UsbRawGetConfigDescriptor(const UsbRawDevice *rawDev, uint8_t configIndex, struct UsbRawConfigDescriptor **config);

+

获取给定设备指定ID的设备配置描述符

+

void UsbRawFreeConfigDescriptor(const struct UsbRawConfigDescriptor *config);

+

释放配置描述符内存空间

+

int UsbRawGetConfiguration(const UsbRawHandle *devHandle, int *config);

+

获取当前激活配置

+

int UsbRawSetConfiguration(const UsbRawHandle *devHandle, int config);

+

设置当前激活配置

+

int UsbRawGetDescriptor(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawDescriptorParam *param, const unsigned char *data);

+

获取描述符信息

+

UsbRawDevice *UsbRawGetDevice(const UsbRawHandle *devHandle);

+

由设备句柄获取设备指针

+

int UsbRawGetDeviceDescriptor(const UsbRawDevice *rawDev, struct UsbDeviceDescriptor *desc);

+

获取给定设备的USB设备描述符

+

int UsbRawClaimInterface(const UsbRawHandle *devHandle, int interfaceNumber);

+

声明给定设备句柄上的接口

+

int UsbRawReleaseInterface(const UsbRawHandle *devHandle, int interfaceNumber);

+

释放之前声明的接口

+

int UsbRawResetDevice(const UsbRawHandle *devHandle);

+

复位设备

+

struct UsbRawRequest *UsbRawAllocRequest(const UsbRawHandle *devHandle, int isoPackets, int length);

+

分配一个带有指定数量的同步包描述符的传输请求

+

int UsbRawFreeRequest(const struct UsbRawRequest *request);

+

释放之前分配的传输请求

+

int UsbRawFillBulkRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawFillRequestData *fillData);

+

填充批量传输请求所需信息

+

int UsbRawFillControlSetup(const unsigned char *setup, const struct UsbControlRequestData *requestData);

+

填充控制传输设置包所需信息

+

int UsbRawFillControlRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawFillRequestData *fillData);

+

填充控制传输请求所需信息

+

int UsbRawFillInterruptRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawFillRequestData *fillData);

+

填充中断传输请求所需信息

+

int UsbRawFillIsoRequest(const struct UsbRawRequest *request, const UsbRawHandle *devHandle, const struct UsbRawFillRequestData *fillData);

+

填充同步传输(Isochronous Transfers)请求所需信息

+

int UsbRawSubmitRequest(const struct UsbRawRequest *request);

+

提交一个传输请求

+

int UsbRawCancelRequest(const struct UsbRawRequest *request);

+

取消一个传输请求

+

int UsbRawHandleRequests(const UsbRawHandle *devHandle);

+

传输请求事件完成处理

+
+ +USB驱动模型Device侧开放的API接口功能,参考表2。 + +**表 2** USB驱动模型Device侧开放的API接口功能介绍 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

头文件

+

接口名称

+

功能描述

+

usbfn_device.h

+

+

+

const struct UsbFnDevice *UsbFnCreateDevice(const char *udcName, const struct UsbFnDescriptorData *descriptor);

+

创建Usb设备

+

int UsbFnRemoveDevice(struct UsbFnDevice *fnDevice);

+

删除Usb设备

+

const struct UsbFnDevice *UsbFnGetDevice(const char *udcName);

+

获取Usb设备

+

usbfn_interface.h

+

+

+

+

+

+

int UsbFnStartRecvInterfaceEvent(struct UsbFnInterface *interface, uint32_t eventMask, UsbFnEventCallback callback, void *context);

+

开始接受Event事件

+

int UsbFnStopRecvInterfaceEvent(struct UsbFnInterface *interface);

+

停止接受Event事件

+

UsbFnInterfaceHandle UsbFnOpenInterface(struct UsbFnInterface *interface);

+

打开一个接口

+

int UsbFnCloseInterface(UsbFnInterfaceHandle handle);

+

关闭一个接口

+

int UsbFnGetInterfacePipeInfo(struct UsbFnInterface *interface, uint8_t pipeId, struct UsbFnPipeInfo *info);

+

获取管道信息

+

int UsbFnSetInterfaceProp(const struct UsbFnInterface *interface, const char *name, const char *value);

+

设置自定义属性

+

usbfn_request.h

+

+

+

+

+

+

+

+

struct UsbFnRequest *UsbFnAllocCtrlRequest(UsbFnInterfaceHandle handle, uint32_t len);

+

申请一个控制请求

+

struct UsbFnRequest *UsbFnAllocRequest(UsbFnInterfaceHandle handle, uint8_t pipe, uint32_t len);

+

申请一个数据请求

+

int UsbFnFreeRequest(struct UsbFnRequest *req);

+

释放一个请求

+

int UsbFnSubmitRequestAsync(struct UsbFnRequest *req);

+

发送异步请求

+

int UsbFnSubmitRequestSync(struct UsbFnRequest *req, uint32_t timeout);

+

发送同步请求

+

int UsbFnCancelRequest(struct UsbFnRequest *req);

+

取消请求

+
+ +## 开发指导 + +USB驱动是基于HDF框架、PLATFORM和OSAL基础接口进行开发,不区分操作系统和芯片平台,为不同USB器件提供统一的驱动模型。本篇开发指导以串口为例,分别介绍USB Host和USB Device驱动开发。 + +### 开发步骤 + +### Host DDK API驱动开发步骤 + +1. 驱动匹配表配置。 +2. 初始化Host DDK。 +3. 待步骤2初始化完后获取UsbInterface接口对象。 +4. 打开步骤3获取到的UsbInterface接口对象,获取对应接口的UsbInterfaceHandle对象。 +5. 根据步骤4获取到的UsbInterfaceHandle对象,获取指定索引为pinpeIndex的pipeInfo信息。 +6. 为步骤4获取到的UsbInterfaceHandle预先分配待发送的IO Request对象。 +7. 根据输入参数params填充步骤6预先分配的IO Request。 +8. 提交IO Request对象,可以选择同步或异步两种模式。 + + +### Host RAW API驱动开发步骤 + +1. 驱动匹配表配置。 +2. 初始化Host RAW,并打开USB设备,然后获取描述符,通过描述符获取接口、端点信息。 +3. 分配Request,并根据传输类型使用如下接口对Request进行填充。 +4. 提交IO Request对象,可以选择同步或异步两种模式。 + +### Device DDK API驱动开发步骤 + +1. 构造描述符。 +2. 创建设备,使用步骤1构造的描述符实例化一个USB设备。 +3. 根据创建的设备获取接口(UsbFnDeviceGetInterface),获取Pipe信息(UsbFnInterfaceGetPipeInfo),打开接口获取Handle(UsbFnInterfaceOpen),根据Handle和Pipe号获取Request(UsbFnRequestAlloc)。 +4. 接收Event事件(UsbFnInterfaceStartRecvEvent)如Enable、Setup等事件,回调函数(UsbFnEventCallback)中对Event事件做出响应。 +5. 收发数据,可以选择同步异步发送模式。 + +## 开发实例 + +本实例提供USB串口驱动开发示例,并简要对具体关键点进行开发说明。 + +### Host DDK API驱动开发 + +``` +root { + module = "usb_pnp_device"; + usb_pnp_config { + match_attr = "usb_pnp_match"; + usb_pnp_device_id = "UsbPnpDeviceId"; + UsbPnpDeviceId { + idTableList = [ + "host_acm_table" + ]; + host_acm_table { + //驱动模块名,该字段的值必须和驱动入口结构的moduleName一致 + moduleName = "usbhost_acm"; + //驱动对外发布服务的名称,必须唯一 + serviceName = "usbhost_acm_pnp_service"; + //驱动私有数据匹配关键字 + deviceMatchAttr = "usbhost_acm_pnp_matchAttr"; + //从该字段开始(包含该字段)之后数据长度,以byte为单位 + length = 21; + //USB驱动匹配规则vendorId+productId+interfaceSubClass+interfaceProtocol+interfaceNumber + matchFlag = 0x0303; + //厂商编号 + vendorId = 0x12D1; + //产品编号 + productId = 0x5000; + //设备出厂编号,低16位 + bcdDeviceLow = 0x0000; + //设备出厂编号,高16位 + bcdDeviceHigh = 0x0000; + //USB分配的设备类代码 + deviceClass = 0; + //USB分配的子类代码 + deviceSubClass = 0; + //USB分配的设备协议代码 + deviceProtocol = 0; + //接口类型,根据实际需要可填写多个 + interfaceClass = [0]; + //接口子类型,根据实际需要可填写多个 + interfaceSubClass = [2, 0]; + //接口所遵循的协议,根据实际需要可填写多个 + interfaceProtocol = [1, 2]; + //接口的编号,根据实际需要可填写多个 + interfaceNumber = [2, 3]; + } + } + } +} + +#include "usb_serial.h" +#include "hdf_base.h" +#include "hdf_log.h" +#include "osal_mem.h" +#include "osal_time.h" +#include "securec.h" +#include "usb_interface.h" +#include "hdf_usb_pnp_manage.h" + +#define HDF_LOG_TAG USB_HOST_ACM +#define STR_LEN 512 + +static struct UsbRequest *g_syncRequest = NULL; +static struct UsbRequest *g_ctrlCmdRequest = NULL; +static bool g_acmReleaseFlag = false; +static uint8_t *g_acmReadBuffer = NULL; +... +static int SerialCtrlMsg(struct AcmDevice *acm, uint8_t request, + uint16_t value, void *buf, uint16_t len) +{ + int ret; + uint16_t index = acm->intPipe->interfaceId; + struct UsbControlParams controlParams = {}; + struct UsbRequestParams parmas = {}; + if (acm == NULL || buf == NULL) { + HDF_LOGE("%{public}s:invalid param", __func__); + return HDF_ERR_IO; + } + if (acm->ctrlReq == NULL) { + acm->ctrlReq = UsbAllocRequest(acm->ctrDevHandle, 0, len); + if (acm->ctrlReq == NULL) { + HDF_LOGE("%{public}s: UsbAllocRequest faild", __func__); + return HDF_ERR_IO; + } + } + + controlParams.request = request; + controlParams.target = USB_REQUEST_TARGET_INTERFACE; + controlParams.reqType = USB_REQUEST_TYPE_CLASS; + controlParams.directon = USB_REQUEST_DIR_TO_DEVICE; + controlParams.value = value; + controlParams.index = index; + controlParams.data = buf; + controlParams.size = len; + + parmas.interfaceId = USB_CTRL_INTERFACE_ID; + parmas.pipeAddress = acm->ctrPipe->pipeAddress; + parmas.pipeId = acm->ctrPipe->pipeId; + parmas.requestType = USB_REQUEST_PARAMS_CTRL_TYPE; + parmas.timeout = USB_CTRL_SET_TIMEOUT; + parmas.ctrlReq = UsbControlSetUp(&controlParams); + ret = UsbFillRequest(acm->ctrlReq, acm->ctrDevHandle, &parmas); + if (HDF_SUCCESS != ret) { + HDF_LOGE("%{public}s: faile, ret=%{public}d ", __func__, ret); + return ret; + } + ret = UsbSubmitRequestSync(acm->ctrlReq); //发送同步IO Request + if (HDF_SUCCESS != ret) { + HDF_LOGE("UsbSubmitRequestSync faile, ret=%{public}d ", ret); + return ret; + } + if (!acm->ctrlReq->compInfo.status) { + HDF_LOGE("%{public}s status=%{public}d ", __func__, acm->ctrlReq->compInfo.status); + } + return HDF_SUCCESS; +} +... +static struct UsbInterface *GetUsbInterfaceById(const struct AcmDevice *acm, + uint8_t interfaceIndex) +{ + struct UsbInterface *tmpIf = NULL; + tmpIf = (struct UsbInterface *)UsbClaimInterface(acm->session, acm->busNum, \ + acm->devAddr, interfaceIndex); //获取UsbInterface接口对象 + return tmpIf; +} +... +static struct UsbPipeInfo *EnumePipe(const struct AcmDevice *acm, + uint8_t interfaceIndex, UsbPipeType pipeType, UsbPipeDirection pipeDirection) +{ + uint8_t i; + int ret; + struct UsbInterfaceInfo *info = NULL; + UsbInterfaceHandle *interfaceHandle = NULL; + if (pipeType == USB_PIPE_TYPE_CONTROL) + { + info = &acm->ctrIface->info; + interfaceHandle = acm->ctrDevHandle; + } + else + { + info = &acm->iface[interfaceIndex]->info; + interfaceHandle = InterfaceIdToHandle(acm, info->interfaceIndex); + } + + for (i = 0; i <= info->pipeNum; i++) { + struct UsbPipeInfo p; + ret = UsbGetPipeInfo(interfaceHandle, info->curAltSetting, i, &p);//获取指定索引为i的pipeInfo信息 + if (ret < 0) { + continue; + } + if ((p.pipeDirection == pipeDirection) && (p.pipeType == pipeType)) { + struct UsbPipeInfo *pi = OsalMemCalloc(sizeof(*pi)); + if (pi == NULL) { + HDF_LOGE("%{public}s: Alloc pipe failed", __func__); + return NULL; + } + p.interfaceId = info->interfaceIndex; + *pi = p; + return pi; + } + } + return NULL; +} + +static struct UsbPipeInfo *GetPipe(const struct AcmDevice *acm, + UsbPipeType pipeType, UsbPipeDirection pipeDirection) +{ + uint8_t i; + if (acm == NULL) { + HDF_LOGE("%{public}s: invalid parmas", __func__); + return NULL; + } + for (i = 0; i < acm->interfaceCnt; i++) { + struct UsbPipeInfo *p = NULL; + if (!acm->iface[i]) { + continue; + } + p = EnumePipe(acm, i, pipeType, pipeDirection); + if (p == NULL) { + continue; + } + return p; + } + return NULL; +} + +/* HdfDriverEntry implementations */ +static int32_t UsbSerialDriverBind(struct HdfDeviceObject *device) +{ + struct UsbPnpNotifyServiceInfo *info = NULL; + errno_t err; + struct AcmDevice *acm = NULL; + if (device == NULL) { + HDF_LOGE("%s: device is null", __func__); + return HDF_ERR_INVALID_OBJECT; + } + acm = (struct AcmDevice *)OsalMemCalloc(sizeof(*acm)); + if (acm == NULL) { + HDF_LOGE("%s: Alloc usb serial device failed", __func__); + return HDF_FAILURE; + } + if (OsalMutexInit(&acm->lock) != HDF_SUCCESS) { + HDF_LOGE("%s:%d OsalMutexInit fail", __func__, __LINE__); + goto error; + } + info = (struct UsbPnpNotifyServiceInfo *)device->priv; + if (info != NULL) { + HDF_LOGD("%s:%d busNum=%d,devAddr=%d,interfaceLength=%d", \ + __func__, __LINE__, info->busNum, info->devNum, info->interfaceLength); + acm->busNum = info->busNum; + acm->devAddr = info->devNum; + acm->interfaceCnt = info->interfaceLength; + err = memcpy_s((void *)(acm->interfaceIndex), USB_MAX_INTERFACES, + (const void*)info->interfaceNumber, info->interfaceLength); + if (err != EOK) { + HDF_LOGE("%s:%d memcpy_s faile err=%d", \ + __func__, __LINE__, err); + goto lock_error; + } + } else { + HDF_LOGE("%s:%d info is NULL!", __func__, __LINE__); + goto lock_error; + } + acm->device = device; + device->service = &(acm->service); + acm->device->service->Dispatch = UsbSerialDeviceDispatch; + HDF_LOGD("UsbSerialDriverBind=========================OK"); + return HDF_SUCCESS; + +lock_error: + if (OsalMutexDestroy(&acm->lock)) { + HDF_LOGE("%s:%d OsalMutexDestroy fail", __func__, __LINE__); + } +error: + OsalMemFree(acm); + acm = NULL; + return HDF_FAILURE; +} +... +static int AcmAllocReadRequests(struct AcmDevice *acm) +{ + int ret; + struct UsbRequestParams readParmas = {}; + for (int i = 0; i < ACM_NR; i++) { + acm->readReq[i] = UsbAllocRequest(InterfaceIdToHandle(acm, acm->dataInPipe->interfaceId), 0, acm->readSize); //分配待发送的readReq IO Request对象 + if (!acm->readReq[i]) { + HDF_LOGE("readReq request faild\n"); + goto error; + } + readParmas.userData = (void *)acm; + readParmas.pipeAddress = acm->dataInPipe->pipeAddress; + readParmas.pipeId = acm->dataInPipe->pipeId; + readParmas.interfaceId = acm->dataInPipe->interfaceId; + readParmas.callback = AcmReadBulk; + readParmas.requestType = USB_REQUEST_PARAMS_DATA_TYPE; + readParmas.timeout = USB_CTRL_SET_TIMEOUT; + readParmas.dataReq.numIsoPackets = 0; + readParmas.dataReq.directon = (acm->dataInPipe->pipeDirection >> USB_PIPE_DIR_OFFSET) & 0x1; + readParmas.dataReq.length = acm->readSize; + ret = UsbFillRequest(acm->readReq[i], InterfaceIdToHandle(acm, acm->dataInPipe->interfaceId), &readParmas); //填充待发送的readReq对象 + if (HDF_SUCCESS != ret) { + HDF_LOGE("%{public}s: UsbFillRequest faile, ret=%{public}d \n", __func__, ret); + goto error; + } + } + return HDF_SUCCESS; + +error: + AcmFreeReadRequests(acm); + return HDF_ERR_MALLOC_FAIL; +} + +static int AcmAllocNotifyRequest(struct AcmDevice *acm) +{ + int ret; + struct UsbRequestParams intParmas = {}; + acm->notifyReq = UsbAllocRequest(InterfaceIdToHandle(acm, acm->intPipe->interfaceId), 0, acm->intSize); //分配待发送的中断IO Request对象 + if (!acm->notifyReq) { + HDF_LOGE("notifyReq request fail\n"); + return HDF_ERR_MALLOC_FAIL; + } + intParmas.userData = (void *)acm; + intParmas.pipeAddress = acm->intPipe->pipeAddress; + intParmas.pipeId = acm->intPipe->pipeId; + intParmas.interfaceId = acm->intPipe->interfaceId; + intParmas.callback = AcmCtrlIrq; + intParmas.requestType = USB_REQUEST_PARAMS_DATA_TYPE; + intParmas.timeout = USB_CTRL_SET_TIMEOUT; + intParmas.dataReq.numIsoPackets = 0; + intParmas.dataReq.directon = (acm->intPipe->pipeDirection >> USB_PIPE_DIR_OFFSET) & DIRECTION_MASK; + intParmas.dataReq.length = acm->intSize; + ret = UsbFillRequest(acm->notifyReq, InterfaceIdToHandle(acm, acm->intPipe->interfaceId), &intParmas); //填充预先分配的中断IO Request + if (HDF_SUCCESS != ret) { + HDF_LOGE("%{public}s: UsbFillRequest faile, ret=%{public}d \n", __func__, ret); + goto error; + } + return HDF_SUCCESS; + +error: + AcmFreeNotifyReqeust(acm); + return ret; +} + +static void AcmReleaseInterfaces(struct AcmDevice *acm) +{ + for (int i = 0; i < acm->interfaceCnt; i++) { + if (acm->iface[i]) { + UsbReleaseInterface(acm->iface[i]); + acm->iface[i] = NULL; + } + } + if (acm->ctrIface) { + UsbReleaseInterface(acm->ctrIface); + acm->ctrIface = NULL; + } +} + +static int32_t AcmClaimInterfaces(struct AcmDevice *acm) +{ + for (int i = 0; i < acm->interfaceCnt; i++) { + acm->iface[i] = GetUsbInterfaceById((const struct AcmDevice *)acm, acm->interfaceIndex[i]); //获取UsbInterface接口对象 + if (acm->iface[i] == NULL) { + HDF_LOGE("%{public}s: interface%{public}d is null", __func__, acm->interfaceIndex[i]); + goto error; + } + } + + acm->ctrIface = GetUsbInterfaceById((const struct AcmDevice *)acm, USB_CTRL_INTERFACE_ID); //获取控制接口对应的UsbInterface接口对象 + if (acm->ctrIface == NULL) { + HDF_LOGE("%{public}s: GetUsbInterfaceById null", __func__); + goto error; + } + + return HDF_SUCCESS; + + error: + AcmReleaseInterfaces(acm); + return HDF_FAILURE; +} + +static void AcmCloseInterfaces(struct AcmDevice *acm) +{ + for (int i = 0; i < acm->interfaceCnt; i++) { + if (acm->devHandle[i]) { + UsbCloseInterface(acm->devHandle[i]); + acm->devHandle[i] = NULL; + } + } + if (acm->ctrDevHandle) { + UsbCloseInterface(acm->ctrDevHandle); + acm->ctrDevHandle = NULL; + } +} + +static int32_t AcmOpenInterfaces(struct AcmDevice *acm) +{ + for (int i = 0; i < acm->interfaceCnt; i++) { + if (acm->iface[i]) { + acm->devHandle[i] = UsbOpenInterface(acm->iface[i]); //打开获取到的UsbInterface接口对象 + if (acm->devHandle[i] == NULL) { + HDF_LOGE("%{public}s: UsbOpenInterface null", __func__); + goto error; + } + } + } + acm->ctrDevHandle = UsbOpenInterface(acm->ctrIface); + if (acm->ctrDevHandle == NULL) { + HDF_LOGE("%{public}s: ctrDevHandle UsbOpenInterface null", __func__); + goto error; + } + + return HDF_SUCCESS; + +error: + AcmCloseInterfaces(acm); + return HDF_FAILURE; +} + +static int32_t AcmGetPipes(struct AcmDevice *acm) +{ + acm->dataInPipe = GetPipe(acm, USB_PIPE_TYPE_BULK, USB_PIPE_DIRECTION_IN);//获取dataInPipe的pipeInfo信息 + if (acm->dataInPipe == NULL) { + HDF_LOGE("dataInPipe is NULL"); + goto error; + } + + acm->dataOutPipe = GetPipe(acm, USB_PIPE_TYPE_BULK, USB_PIPE_DIRECTION_OUT);//获取dataOutPipe的pipeInfo信息 + if (acm->dataOutPipe == NULL) { + HDF_LOGE("dataOutPipe is NULL"); + goto error; + } + + acm->ctrPipe = EnumePipe(acm, acm->ctrIface->info.interfaceIndex, USB_PIPE_TYPE_CONTROL, USB_PIPE_DIRECTION_OUT); //获取控制pipe的pipeInfo信息 + if (acm->ctrPipe == NULL) { + HDF_LOGE("ctrPipe is NULL"); + goto error; + } + + acm->intPipe = GetPipe(acm, USB_PIPE_TYPE_INTERRUPT, USB_PIPE_DIRECTION_IN);//获取中断pipe的pipeInfo信息 + if (acm->intPipe == NULL) { + HDF_LOGE("intPipe is NULL"); + goto error; + } + + acm->readSize = acm->dataInPipe->maxPacketSize; + acm->writeSize = acm->dataOutPipe->maxPacketSize; + acm->ctrlSize = acm->ctrPipe->maxPacketSize; + acm->intSize = acm->intPipe->maxPacketSize; + + return HDF_SUCCESS; + +error: + AcmFreePipes(acm); + return HDF_FAILURE; +} + +static void AcmFreeRequests(struct AcmDevice *acm) +{ + if (g_syncRequest != NULL) { + UsbFreeRequest(g_syncRequest); + g_syncRequest = NULL; + } + AcmFreeReadRequests(acm); + AcmFreeNotifyReqeust(acm); + AcmFreeWriteRequests(acm); + AcmWriteBufFree(acm); +} + +static int32_t AcmAllocRequests(struct AcmDevice *acm) +{ + int32_t ret; + + if (AcmWriteBufAlloc(acm) < 0) { + HDF_LOGE("%{public}s: AcmWriteBufAlloc failed", __func__); + return HDF_ERR_MALLOC_FAIL; + } + + for (int i = 0; i < ACM_NW; i++) { + struct AcmWb *snd = &(acm->wb[i]); + snd->request = UsbAllocRequest(InterfaceIdToHandle(acm, acm->dataOutPipe->interfaceId), 0, acm->writeSize); //分配待发送的IO Request对象 + snd->instance = acm; + if (snd->request == NULL) { + HDF_LOGE("%{public}s:%{public}d snd request fail", __func__, __LINE__); + goto error_alloc_write_req; + } + } + + ret = AcmAllocNotifyRequest(acm); //分配并填充中断IO Request对象 + if (ret != HDF_SUCCESS) { + HDF_LOGE("%{public}s:%{public}d AcmAllocNotifyRequest fail", __func__, __LINE__); + goto error_alloc_int_req; + } + + ret = AcmAllocReadRequests(acm); //分配并填充readReq IO Request对象 + if (ret) { + HDF_LOGE("%{public}s:%{public}d AcmAllocReadRequests fail", __func__, __LINE__); + goto error_alloc_read_req; + } + + return HDF_SUCCESS; + +error_alloc_read_req: + AcmFreeNotifyReqeust(acm); +error_alloc_int_req: + AcmFreeWriteRequests(acm); +error_alloc_write_req: + AcmWriteBufFree(acm); + return HDF_FAILURE; +} + +static int32_t AcmInit(struct AcmDevice *acm) +{ + int32_t ret; + struct UsbSession *session = NULL; + + if (acm->initFlag == true) { + HDF_LOGE("%{public}s:%{public}d: initFlag is true", __func__, __LINE__); + return HDF_SUCCESS; + } + + ret = UsbInitHostSdk(NULL); //初始化Host DDK + if (ret != HDF_SUCCESS) { + HDF_LOGE("%{public}s: UsbInitHostSdk faild", __func__); + return HDF_ERR_IO; + } + acm->session = session; + + ret = AcmClaimInterfaces(acm); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%{public}s: AcmClaimInterfaces faild", __func__); + goto error_claim_interfaces; + } + + ret = AcmOpenInterfaces(acm); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%{public}s: AcmOpenInterfaces faild", __func__); + goto error_open_interfaces; + } + + ret = AcmGetPipes(acm); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%{public}s: AcmGetPipes failed", __func__); + goto error_get_pipes; + } + + ret = AcmAllocRequests(acm); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%{public}s: AcmAllocRequests failed", __func__); + goto error_alloc_reqs; + } + + acm->lineCoding.dwDTERate = CpuToLe32(DATARATE); + acm->lineCoding.bCharFormat = CHARFORMAT; + acm->lineCoding.bParityType = USB_CDC_NO_PARITY; + acm->lineCoding.bDataBits = USB_CDC_1_STOP_BITS; + acm->initFlag = true; + + HDF_LOGD("%{public}s:%{public}d========OK", __func__, __LINE__); + return HDF_SUCCESS; + +error_alloc_reqs: + AcmFreePipes(acm); +error_get_pipes: + AcmCloseInterfaces(acm); +error_open_interfaces: + AcmReleaseInterfaces(acm); +error_claim_interfaces: + UsbExitHostSdk(acm->session); + acm->session = NULL; + return ret; +} + +static void AcmRelease(struct AcmDevice *acm) +{ + if (acm->initFlag == false) { + HDF_LOGE("%{public}s:%{public}d: initFlag is false", __func__, __LINE__); + return; + } + + AcmFreeRequests(acm); + AcmFreePipes(acm); + AcmCloseInterfaces(acm); + AcmReleaseInterfaces(acm); + UsbExitHostSdk(acm->session); + acm->session = NULL; + + acm->initFlag = false; +} + +static int32_t UsbSerialDriverInit(struct HdfDeviceObject *device) +{ + int32_t ret; + struct AcmDevice *acm = NULL; + + if (device == NULL) { + HDF_LOGE("%{public}s: device is null", __func__); + return HDF_ERR_INVALID_OBJECT; + } + acm = (struct AcmDevice *)device->service; + OsalMutexInit(&acm->readLock); + OsalMutexInit(&acm->writeLock); + HDF_LOGD("%{public}s:%{public}d busNum=%{public}d,devAddr=%{public}d", \ + __func__, __LINE__, acm->busNum, acm->devAddr); + + ret = UsbSerialDeviceAlloc(acm); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%{public}s: Serial Device alloc faild", __func__); + } + + acm->initFlag = false; + g_acmReleaseFlag = false; + + HDF_LOGD("%{public}s:%{public}d init ok!", __func__, __LINE__); + + return ret; +} + +static void UsbSerialDriverRelease(struct HdfDeviceObject *device) +{ + struct AcmDevice *acm = NULL; + + if (device == NULL) { + HDF_LOGE("%{public}s: device is NULL", __func__); + return; + } + acm = (struct AcmDevice *)device->service; + if (acm == NULL) { + HDF_LOGE("%{public}s: acm is null", __func__); + return; + } + + g_acmReleaseFlag = true; + + if (acm->initFlag == true) { + HDF_LOGE("%{public}s:%{public}d AcmRelease", __func__, __LINE__); + AcmRelease(acm); + } + UsbSeriaDevicelFree(acm); + OsalMutexDestroy(&acm->writeLock); + OsalMutexDestroy(&acm->readLock); + OsalMutexDestroy(&acm->lock); + OsalMemFree(acm); + acm = NULL; + HDF_LOGD("%{public}s:%{public}d exit", __func__, __LINE__); +} + +struct HdfDriverEntry g_usbSerialDriverEntry = { + .moduleVersion = 1, + .moduleName = "usbhost_acm", //驱动模块名称,必须与hcs文件中配置的名称一致 + .Bind = UsbSerialDriverBind, + .Init = UsbSerialDriverInit, + .Release = UsbSerialDriverRelease, +}; +HDF_INIT(g_usbSerialDriverEntry); +``` + +### Host RAW API驱动开发 + +``` +root { + module = "usb_pnp_device"; + usb_pnp_config { + match_attr = "usb_pnp_match"; + usb_pnp_device_id = "UsbPnpDeviceId"; + UsbPnpDeviceId { + idTableList = [ + "host_acm_rawapi_table" + ]; + host_acm_rawapi_table { //驱动配置匹配表信息 + //驱动模块名,该字段的值必须和驱动入口结构的moduleName一致 + moduleName = "usbhost_acm_rawapi"; + //驱动对外发布服务的名称,必须唯一 + serviceName = "usbhost_acm_rawapi_service"; + //驱动私有数据匹配关键字 + deviceMatchAttr = "usbhost_acm_rawapi_matchAttr"; + //从该字段开始(包含该字段)之后数据长度,以byte为单位 + length = 21; + //USB驱动匹配规则vendorId+productId+interfaceSubClass+interfaceProtocol+interfaceNumber + matchFlag = 0x0303; + //厂商编号 + vendorId = 0x12D1; + //产品编号 + productId = 0x5000; + //设备出厂编号,低16位 + bcdDeviceLow = 0x0000; + //设备出厂编号,高16位 + bcdDeviceHigh = 0x0000; + //USB分配的设备类代码 + deviceClass = 0; + //USB分配的子类代码 + deviceSubClass = 0; + //USB分配的设备协议代码 + deviceProtocol = 0; + //接口类型,根据实际需要可填写多个 + interfaceClass = [0]; + //接口子类型,根据实际需要可填写多个 + interfaceSubClass = [2, 0]; + //接口所遵循的协议,根据实际需要可填写多个 + interfaceProtocol = [1, 2]; + //接口的编号,根据实际需要可填写多个 + interfaceNumber = [2, 3]; + } + } + } +} + +#include "usb_serial_rawapi.h" +#include +#include "osal_mem.h" +#include "osal_time.h" +#include "securec.h" +#include "hdf_base.h" +#include "hdf_log.h" +#include "hdf_usb_pnp_manage.h" + +#define HDF_LOG_TAG USB_HOST_ACM_RAW_API +#define USB_CTRL_REQ_SIZE 64 +#define USB_IO_THREAD_STACK_SIZE 8192 +#define USB_RAW_IO_SLEEP_MS_TIME 100 +#define USB_RAW_IO_STOP_WAIT_MAX_TIME 3 + +static struct UsbRawRequest *g_syncRequest = NULL; +static UsbRawIoProcessStatusType g_stopIoStatus = USB_RAW_IO_PROCESS_RUNNING; +struct OsalMutex g_stopIoLock; +static bool g_rawAcmReleaseFlag = false; +...... + +static int UsbGetConfigDescriptor(UsbRawHandle *devHandle, struct UsbRawConfigDescriptor **config) +{ + UsbRawDevice *dev = NULL; + int activeConfig; + int ret; + + if (devHandle == NULL) { + HDF_LOGE("%{public}s:%{public}d devHandle is NULL", + __func__, __LINE__); + return HDF_ERR_INVALID_PARAM; + } + + ret = UsbRawGetConfiguration(devHandle, &activeConfig); + if (ret) { + HDF_LOGE("%{public}s:%{public}d UsbRawGetConfiguration failed, ret=%{public}d", + __func__, __LINE__, ret); + return HDF_FAILURE; + } + HDF_LOGE("%{public}s:%{public}d activeConfig=%{public}d", __func__, __LINE__, activeConfig); + dev = UsbRawGetDevice(devHandle); + if (dev == NULL) { + HDF_LOGE("%{public}s:%{public}d UsbRawGetDevice failed", + __func__, __LINE__); + return HDF_FAILURE; + } + + ret = UsbRawGetConfigDescriptor(dev, activeConfig, config); + if (ret) { + HDF_LOGE("UsbRawGetConfigDescriptor failed, ret=%{public}d\n", ret); + return HDF_FAILURE; + } + + return HDF_SUCCESS; +} +... +static int UsbAllocWriteRequests(struct AcmDevice *acm) +{ + int i; + + for (i = 0; i < ACM_NW; i++) { + struct AcmWb *snd = &acm->wb[i]; + snd->request = UsbRawAllocRequest(acm->devHandle, 0, acm->dataOutEp->maxPacketSize); + snd->instance = acm; + if (snd->request == NULL) { + HDF_LOGE("%{public}s: UsbRawAllocRequest faild", __func__); + return HDF_ERR_MALLOC_FAIL; + } + } + + return HDF_SUCCESS; +} +... +/* HdfDriverEntry implementations */ +static int32_t UsbSerialDriverBind(struct HdfDeviceObject *device) +{ + struct AcmDevice *acm = NULL; + struct UsbPnpNotifyServiceInfo *info = NULL; + errno_t err; + + if (device == NULL) { + HDF_LOGE("%s: device is null", __func__); + return HDF_ERR_INVALID_OBJECT; + } + + acm = (struct AcmDevice *)OsalMemCalloc(sizeof(*acm)); + if (acm == NULL) { + HDF_LOGE("%s: Alloc usb serial device failed", __func__); + return HDF_FAILURE; + } + if (OsalMutexInit(&acm->lock) != HDF_SUCCESS) { + HDF_LOGE("%s:%d OsalMutexInit fail", __func__, __LINE__); + goto error; + } + + info = (struct UsbPnpNotifyServiceInfo *)device->priv; + if (info != NULL) { + acm->busNum = info->busNum; + acm->devAddr = info->devNum; + acm->interfaceCnt = info->interfaceLength; + err = memcpy_s((void *)(acm->interfaceIndex), USB_MAX_INTERFACES, + (const void*)info->interfaceNumber, info->interfaceLength); + if (err != EOK) { + HDF_LOGE("%s:%d memcpy_s faile err=%d", \ + __func__, __LINE__, err); + goto lock_error; + } + } else { + HDF_LOGE("%s:%d info is NULL!", __func__, __LINE__); + goto lock_error; + } + + device->service = &(acm->service); + device->service->Dispatch = UsbSerialDeviceDispatch; + acm->device = device; + HDF_LOGD("UsbSerialDriverBind=========================OK"); + return HDF_SUCCESS; + +lock_error: + if (OsalMutexDestroy(&acm->lock)) { + HDF_LOGE("%s:%d OsalMutexDestroy fail", __func__, __LINE__); + } +error: + OsalMemFree(acm); + acm = NULL; + return HDF_FAILURE; +} +... +static int UsbAllocReadRequests(struct AcmDevice *acm) +{ + struct UsbRawFillRequestData reqData; + int size = acm->dataInEp->maxPacketSize; + int ret; + + for (int i = 0; i < ACM_NR; i++) { + acm->readReq[i] = UsbRawAllocRequest(acm->devHandle, 0, size); + if (!acm->readReq[i]) { + HDF_LOGE("readReq request faild\n"); + return HDF_ERR_MALLOC_FAIL; + } + + reqData.endPoint = acm->dataInEp->addr; + reqData.numIsoPackets = 0; + reqData.callback = AcmReadBulkCallback; + reqData.userData = (void *)acm; + reqData.timeout = USB_CTRL_SET_TIMEOUT; + reqData.length = size; + + ret = UsbRawFillBulkRequest(acm->readReq[i], acm->devHandle, &reqData); + if (ret) { + HDF_LOGE("%{public}s: FillBulkRequest faile, ret=%{public}d \n", + __func__, ret); + return HDF_FAILURE; + } + } + + return HDF_SUCCESS; +} +... +static int UsbAllocNotifyRequest(struct AcmDevice *acm) +{ + struct UsbRawFillRequestData fillRequestData; + int size = acm->notifyEp->maxPacketSize; + int ret; + + acm->notifyReq = UsbRawAllocRequest(acm->devHandle, 0, size); + if (!acm->notifyReq) { + HDF_LOGE("notifyReq request fail\n"); + return HDF_ERR_MALLOC_FAIL; + } + + fillRequestData.endPoint = acm->notifyEp->addr; + fillRequestData.length = size; + fillRequestData.numIsoPackets = 0; + fillRequestData.callback = AcmNotifyReqCallback; + fillRequestData.userData = (void *)acm; + fillRequestData.timeout = USB_CTRL_SET_TIMEOUT; + + ret = UsbRawFillInterruptRequest(acm->notifyReq, acm->devHandle, &fillRequestData); + if (ret) { + HDF_LOGE("%{public}s: FillInterruptRequest faile, ret=%{public}d", __func__, ret); + return HDF_FAILURE; + } + + return HDF_SUCCESS; +} +... +static int32_t UsbSerialInit(struct AcmDevice *acm) +{ + struct UsbSession *session = NULL; + UsbRawHandle *devHandle = NULL; + int32_t ret; + + if (acm->initFlag == true) { + HDF_LOGE("%{public}s:%{public}d: initFlag is true", __func__, __LINE__); + return HDF_SUCCESS; + } + + ret = UsbRawInit(NULL); + if (ret) { + HDF_LOGE("%{public}s:%{public}d UsbRawInit faild", __func__, __LINE__); + return HDF_ERR_IO; + } + acm->session = session; + + devHandle = UsbRawOpenDevice(session, acm->busNum, acm->devAddr); + if (devHandle == NULL) { + HDF_LOGE("%{public}s:%{public}d UsbRawOpenDevice faild", __func__, __LINE__); + ret = HDF_FAILURE; + goto err_open_device; + } + acm->devHandle = devHandle; + ret = UsbGetConfigDescriptor(devHandle, &acm->config); + if (ret) { + HDF_LOGE("%{public}s:%{public}d UsbGetConfigDescriptor faild", __func__, __LINE__); + ret = HDF_FAILURE; + goto err_get_desc; + } + ret = UsbParseConfigDescriptor(acm, acm->config); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%{public}s:%{public}d UsbParseConfigDescriptor faild", __func__, __LINE__); + ret = HDF_FAILURE; + goto err_parse_desc; + } + + ret = AcmWriteBufAlloc(acm); + if (ret < 0) { + HDF_LOGE("%{public}s:%{public}d AcmWriteBufAlloc faild", __func__, __LINE__); + ret = HDF_FAILURE; + goto err_alloc_write_buf; + } + ret = UsbAllocWriteRequests(acm); + if (ret < 0) { + HDF_LOGE("%{public}s:%{public}d UsbAllocWriteRequests faild", __func__, __LINE__); + ret = HDF_FAILURE; + goto err_alloc_write_reqs; + } + ret = UsbAllocNotifyRequest(acm); + if (ret) { + HDF_LOGE("%{public}s:%{public}d UsbAllocNotifyRequests faild", __func__, __LINE__); + goto err_alloc_notify_req; + } + ret = UsbAllocReadRequests(acm); + if (ret) { + HDF_LOGE("%{public}s:%{public}d UsbAllocReadRequests faild", __func__, __LINE__); + goto err_alloc_read_reqs; + } + ret = UsbStartIo(acm); + if (ret) { + HDF_LOGE("%{public}s:%{public}d UsbAllocReadRequests faild", __func__, __LINE__); + goto err_start_io; + } + + acm->lineCoding.dwDTERate = CpuToLe32(DATARATE); + acm->lineCoding.bCharFormat = CHARFORMAT; + acm->lineCoding.bParityType = USB_CDC_NO_PARITY; + acm->lineCoding.bDataBits = USB_CDC_1_STOP_BITS; + + ret = UsbRawSubmitRequest(acm->notifyReq); + if (ret) { + HDF_LOGE("%{public}s:%{public}d UsbRawSubmitRequest failed", __func__, __LINE__); + goto err_submit_req; + } + + acm->initFlag = true; + + HDF_LOGD("%{public}s:%{public}d=========================OK", __func__, __LINE__); + + return HDF_SUCCESS; + +err_submit_req: + UsbStopIo(acm); +err_start_io: + UsbFreeReadRequests(acm); +err_alloc_read_reqs: + UsbFreeNotifyReqeust(acm); + err_alloc_notify_req: + UsbFreeWriteRequests(acm); +err_alloc_write_reqs: + AcmWriteBufFree(acm); +err_alloc_write_buf: + UsbReleaseInterfaces(acm); +err_parse_desc: + UsbRawFreeConfigDescriptor(acm->config); + acm->config = NULL; +err_get_desc: + (void)UsbRawCloseDevice(devHandle); +err_open_device: + UsbRawExit(acm->session); + + return ret; +} + +static void UsbSerialRelease(struct AcmDevice *acm) +{ + if (acm->initFlag == false) { + HDF_LOGE("%{public}s:%{public}d: initFlag is false", __func__, __LINE__); + return; + } + + /* stop io thread and release all resources */ + UsbStopIo(acm); + if (g_syncRequest != NULL) { + UsbRawFreeRequest(g_syncRequest); + g_syncRequest = NULL; + } + UsbFreeReadRequests(acm); + UsbFreeNotifyReqeust(acm); + UsbFreeWriteRequests(acm); + AcmWriteBufFree(acm); + (void)UsbRawCloseDevice(acm->devHandle); + UsbReleaseInterfaces(acm); + UsbRawFreeConfigDescriptor(acm->config); + acm->config = NULL; + UsbRawExit(acm->session); + + acm->initFlag = false; +} + +static int32_t UsbSerialDriverInit(struct HdfDeviceObject *device) +{ + struct AcmDevice *acm = NULL; + int32_t ret; + + if (device == NULL) { + HDF_LOGE("%{public}s:%{public}d device is null", __func__, __LINE__); + return HDF_ERR_INVALID_OBJECT; + } + acm = (struct AcmDevice *)device->service; + OsalMutexInit(&acm->readLock); + OsalMutexInit(&acm->writeLock); + + ret = UsbSerialDeviceAlloc(acm); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%{public}s:%{public}d UsbSerialDeviceAlloc faild", __func__, __LINE__); + } + + acm->initFlag = false; + g_rawAcmReleaseFlag = false; + + HDF_LOGD("%{public}s:%{public}d init ok!", __func__, __LINE__); + + return ret; +} + +static void UsbSerialDriverRelease(struct HdfDeviceObject *device) +{ + struct AcmDevice *acm = NULL; + if (device == NULL) { + HDF_LOGE("%{public}s: device is NULL", __func__); + return; + } + + acm = (struct AcmDevice *)device->service; + if (acm == NULL) { + HDF_LOGE("%{public}s: acm is null", __func__); + return; + } + + g_rawAcmReleaseFlag = true; + + if (acm->initFlag == true) { + HDF_LOGE("%{public}s:%{public}d UsbSerialRelease", __func__, __LINE__); + UsbSerialRelease(acm); + } + UsbSeriaDevicelFree(acm); + OsalMutexDestroy(&acm->writeLock); + OsalMutexDestroy(&acm->readLock); + OsalMutexDestroy(&acm->lock); + OsalMemFree(acm); + acm = NULL; + HDF_LOGD("%{public}s:%{public}d exit", __func__, __LINE__); +} + +struct HdfDriverEntry g_usbSerialRawDriverEntry = { + .moduleVersion = 1, + .moduleName = "usbhost_acm_rawapi", //驱动模块名称,必须与hcs文件中配置的名称一致 + .Bind = UsbSerialDriverBind, + .Init = UsbSerialDriverInit, + .Release = UsbSerialDriverRelease, +}; +HDF_INIT(g_usbSerialRawDriverEntry); +``` + +### Device DDK API驱动开发 +USB ACM设备核心代码路径为drivers\peripheral\usb\gadget\function\acm\cdcacm.c,其使用示例如下所示,首先根据描述符创建设备,然后获取接口,打开接口,获取Pipe信息,接收Event事件,接着进行USB通信(读写等),设备卸载时候,关闭接口,停止Event接收,删除设备。 + +``` +1、创建设备 +static int32_t AcmCreateFuncDevice(struct UsbAcmDevice *acm, + struct DeviceResourceIface *iface) +{ + struct UsbFnDevice *fnDev = NULL; +struct UsbFnDescriptorData descData; +uint8_t useHcs; + ... +if (useHcs == 0) { + descData.type = USBFN_DESC_DATA_TYPE_DESC; + descData.descriptor = &g_masterFuncDevice; +} else { + descData.type = USBFN_DESC_DATA_TYPE_PROP; + descData.property = device->property; +} +/* 创建设备 */ + fnDev = (struct UsbFnDevice *)UsbFnDeviceCreate(acm->udcName, &descData); + if (fnDev == NULL) { + HDF_LOGE("%{public}s: create usb function device failed", __func__); + return HDF_FAILURE; + } + ... +} +2、获取接口,打开接口,获取Pipe信息 +static int32_t AcmParseEachPipe(struct UsbAcmDevice *acm, struct UsbAcmInterface *iface) +{ + ... + for (i = 0; i < fnIface->info.numPipes; i++) { + struct UsbFnPipeInfo pipeInfo; +/* 获取pipe信息 */ + ret = UsbFnInterfaceGetPipeInfo(fnIface, i, &pipeInfo); + ... + } + return HDF_SUCCESS; +} +/* 获取接口,打开接口获取handle */ +static int32_t AcmParseEachIface(struct UsbAcmDevice *acm, struct UsbFnDevice *fnDev) +{ + ... + for (i = 0; i < fnDev->numInterfaces; i++) { + /* 获取接口 */ + fnIface = (struct UsbFnInterface *)UsbFnDeviceGetInterface(fnDev, i); + ... + /* 打开接口 */ + handle = UsbFnInterfaceOpen(fnIface); + ... + } + return HDF_SUCCESS; +} +3、接收Event事件 +static int32_t AcmAllocCtrlRequests(struct UsbAcmDevice *acm, int num) +{ + ... + req = UsbFnCtrlRequestAlloc(acm->ctrlIface.handle, + sizeof(struct UsbCdcLineCoding) + sizeof(struct UsbCdcLineCoding)); + ... +} +static int32_t AcmDriverInit(struct HdfDeviceObject *device) +{ +... +/* 开始接收Event */ + ret = UsbFnInterfaceStartRecvEvent(acm->ctrlIface.fn, 0xff, UsbAcmEventCallback, acm); + ... +} +4、进行USB通信(读写等) +static int32_t AcmSendNotifyRequest(struct UsbAcmDevice *acm, uint8_t type, + uint16_t value, void *data, uint32_t length) +{ +... +/* 异步发送 */ + ret = UsbFnRequestSubmitAsync(req); + ... +} +5、关闭接口,停止Event接收,删除设备 +static int32_t AcmReleaseFuncDevice(struct UsbAcmDevice *acm) +{ +int32_t ret; +/* 关闭接口 */ + (void)UsbFnInterfaceClose(acm->ctrlIface.handle); +(void)UsbFnInterfaceClose(acm->dataIface.handle); +/* 停止接收Event */ +(void)UsbFnInterfaceStopRecvEvent(acm->ctrlIface.fn); +/* 删除设备 */ + ret = UsbFnDeviceRemove(acm->fnDev); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%{public}s: remove usb function device failed", __func__); + } + return ret; +} +``` + diff --git "a/zh-cn/device-dev/driver/figure/USB-Device\351\251\261\345\212\250\346\250\241\345\236\213\345\233\276.png" "b/zh-cn/device-dev/driver/figure/USB-Device\351\251\261\345\212\250\346\250\241\345\236\213\345\233\276.png" new file mode 100755 index 0000000000000000000000000000000000000000..d10900d14a777bc98d0aa7a74842ecdda98d3320 GIT binary patch literal 38213 zcmeFY2T;>n)Gr!EMMdDKN0EM%CL*9z0RfK%3{~kJlqvzDw9o>0Jc5NHp-DeTlU@@# z!9s^1HG~iX0*N66q=Xho-amN0`|f-9&71dT?)$!(JMTY}$-nNs_S$RxR@?ja10!AT zlL99}AP_hB{#_Fg=x7xP#G!cn7;p!6RcQIOY0`Z+; z{|>q53N0RrFs(=5ngi2(Jo+X|N2t+ZDS48)Jx`ZqUww0*xOC$TDtGcgN6&I3r|K;XnP5leodLP593-@wcLLY0iVLEs)XM%1Al-*`sNL=`Ih>WJ*8XEdZ+JfmX?f6YBQog1AcEQrX2E6WdZVFj}Y zTW-3}otrrFTC{PdvC34&zGE8PczVD0Z7_w2&N+28 zDxN%;T^Vp3yG+bTU3|WtmbOe>4{j7uSYh>kvbm5UE-2EQquHjyCn}IL_*`m{&^3tt zf@_N1(oRx!KfESCkfYsNHrHmL;-5ci18HE*WX}0Oy+s-#o^KU1YrVtvazj8lLh$c? zYKcf3Ywyz%WckB%m5N5T_WON7H&NBDnz*B&naSVSkR^j!WfHTwp9=~IcPV19x&dW| z#Ra9XxjAGUQB@TEex`1<#gNc7pmH+_?oBe5>eF17fi^m;PsX2qa%3A)>ns_hm9vPO zWGzFpR|p$jZ*8Q)oYB^On(Tqkri>p31X{S6HS;}BwIXV_J0{k>$kY(Q4dGk9+jgNZ zXnHuxhP?JhDXtkAUM4SBo*3L`9%w(F>jJ8@y#G?};(t@c?Wucz&&QIpwCB{pzMG9gH&U+N5My}W zK&fiT`HdYca3z6ntq{HD| z*?rHQW7~Jb;yX#R{7FkYLhz@SJ|R`p@cq37e)2==cdeZLw=GZ&5mr!npciyG zq)Nq298BA1I%zhv9a}j8YA?9P24jm_hG;L7yV8w?TL+k0u>_s0n_G=9+P$lJs_nj@ zC?H2&fv&6rWtYwRB&e0G9oq8blnz{}JFPaS&%QpwXO+ujI^vo2fGnh`bM~UKu?tkN#Z%$O!A7#$t@UbdC3AKhl#LL4GsOm zRa*~8R%CjuXh!RPhG}iRH9ant)zR-Q{9oiI&SxMuvS^_U`m0kmZ}-*!Fh@!R9S+{J(N;2*WnOCk+n!KXkQwiQ!b+@&$?g7hB91 zEh}(sqRlbDsGVhZtr|0=t&xu2U18-RTduZ7Y~Nz;R5Cpw`~XVaXT#&vHRdv&2u4ip z`k~+D?Bi^0QK+p}bm`G)B6AbQ*1Mx(Z2Ej|pjQ)En)O!|+qD>?o$KXLqXiV+2SPXk zYF8$)6$v>0C%9!Y*Ojw8A+$Hm=PMh-#?BUC^*2197P(w{h~!FBN6?QF&qg zDMf{fo8vEW3@)3*Y;WQ64r|kBzb%evQvyn!7~!kmdLwI=Q>6FXXi7|5wY;XENL3Og zCae3eO6#n2AK8J2HrOGXbm-z!t<8y9Gb{~5m0vL`4$uI#OB_J7T}eGp(Mq*x`Pj}l z@DQ(l{#0=|{@PNuf*mgX!&!xH@wp1NDvSk55;{3HD z9;^4HBxPjhd4Fs*&@^7wA1vhH4<+SJWvN^YL<|>gy3SxyzS~BRZ1wx=KWnS*;&CZ^ z#z@2A^(WcGuE|551YL_~BefHFq}WKAh<<=3CJZ-`b|pOUPvBR+(EkWt!bSUT)hY^} z4umVhuKHD{fBn?ZFzGEe%L%@`qWMsJTmwd?M<@2a zrX-1lWP@`$~{eSF>R}!>)C!`8?yxd(50s(X&5VKt<5G`3J&Fl2?v4tB-r54 zaJL{W;ZhGyKD>$YJ*sC)4Trq`#{gm6ySWl=#g!JCotags>AYwbC7R(=K3>Ln>K)dl z8|c1)5l=HJ*x3Q5q8-Ii?1j%7@)lO)g%s6!!*uGEVabD*S?bBPj(aeEt!E{aOR2)r z-uSHN-gIoXdU9@;(O6fJe}0{hp~@64VHg$XUl4e@r!VlL0ca-b0JE%$i@#7pXUs4@ zkR$zuV)xo^G~*EhS>x0|_n(+A{3V>kg6N#SA6Z4iBd9weA#O@tH=zM8GqbYD3UUCr zM)Iueq2NK3%QnuKyzHLw=IzAKpI$|QbQ}kNcGJ6BsG;i7ajy3`d* z-;3@0r3}9aAvHw3$3O<1>yDY!L}}ul5GS^_N`Srp&_m4H22?rjg!g@#5p~^R?&N`%Z0;pK{4r3K(1BfeI||6mPPl$~2wRE0P^Qgw z-i{7IzI`>HXI$Cv!yD0{W!gM81?aVM)@+wnR&G6No}Nk-GX~&-niFyvrtNnFrhP4I zc(m9oapUHW-LaGrVXc50aNeuG7s}VrFhG=WEA*EU`XKUK&U>T5#OA~<9`X{3fP2dB z)d{AtX!Cs&|11P+i)GS*1@~h>*XmO08M32As)$YW>1-exIsHzdRsY6vWDYsah z&h^j&085y5U<308;L+%F0zmxKerGYmeGQ7PYImaOKm9%?zwoux(NmTj=eVD|u|FF( znN>`|y;U|cbYH?5yKjH>Z>bbxAHVOzm)Ip;wU%ulu{D+HumKSX3Y>$%Q1_{(Gz z4Fh0sb8eB5>o)ESf2k;JI3Zh#6(H+h2^CZaXqMuUqN=kY;Fwm8L;;F0H?p($+H_sc z$r_MJ)FIH-1Ebtfv9~w4Kb6T?30|3EtT4LJ2;1<@aW!Pe-mq}iPEN!YHh7DWgBcCq z-d`D*YTF@YC~i))`pI=p__^yMz94^w5K>~8J#7-5FZ}b#vvA7JeX&2ZlDTo%rjdnu zADBZ>u)ZEu-ObH!@K`r;CdA1WE)s|+!y}$c0T^Ifq_Ee;si2~qRb33Y6@@A`^Ymy9 zH<7IT@!5T^W-i6R{3I^^&_L#?rA4!TK)zE9*>0kp<#=FO&&oaql_s|ou?Z3FuJqa_ z+HBgiIX2}fqWyKr{rjvP8oDP9+qpIeYs*0jLrj=2XlX6`p_CqfgpKNYJ4zgvg(XgC zt~a-Xk*I1sz+DM#yhr2(wU7B9q!t;5rvL*c2cM~k1tTIDFV8mqbYv=k z<)pv*@KmlW{kT6U>d#eq2vkv>Vao-;74|Lf6!JQHO9PJ(=VSIck}}*`etjZ+VlBr& zmnGR?`qVyy?{SW;^($HSExChRgp9L~BLa3VZnAbU#FIdCC;Opp0EBQq!Wb-7j=Mju zbPSRVlx?XP{Y*s)Q+D99%Ai|A2a$o#g+AKX^A12DEqaiA;dTvwwZEH51Co4^)M&P! z*DxCfP~W%$W0@o7KU=c)4PNb|+t%yVOnED@<@PdcGH#%Y0hLtEcO+WrAzIe>SjKW; zbRwBS+_zvZqq7F%7ia0Z5iBiszn^xzfhlizq#QlLa&Af&Kj`Td5diB*K^wnU`^*3s ziMJ`jIEWkcH0!4=K*7^540>!q*-BW%^m?r}zUc&z?e#mY3%H=>zp*KgV5cQ7-f`Ub zg`5C*=y84fIWIEBDT>D{Qsx%W41mmY8cJ_ZvqKhkw50P2WcMhdZgqJaNNfX+azqG; zK7QsuJ!5$g&Q%GK2My~}06H#LA*mWR`VWE*FPSgHbRwo~9-rI2ol_8*d>R-eFiK2L z#`(v7gzLADu!muBf*WiHt+Pjn@8rdAqd$9Y6giGQA1nwZHaSAr`RxbSh*`TKkP{VL z>i-qF>W+uyZebr)ZB>cs8a|Lcd{TKvsG>tTYT4eD;yqbUKpWyFRg+eaifFBGx9L$+ ztjF|r1qbVcFDI7c$xoJu8LMgw1#tnJ#PQshJQqMt^aIzX=FJ_@(&C^rnn_@uHTmiBN3M8Jw7{_ z?cCSt1)KE9=m$JfjmEw}@;n8)(K=9dT0pX=*zKrYUyn~uo02q(AY%76?dJVsm$O6o zHYYBf2!sTACvgJa!+|eS(K_t3nWPUc2@Y8=Z**QS+~DPQZ85ROHM}breX(X>P!}<+ zw_*^KH9Q@2oDD`opWC*(or=bhjfe`NY@;F%v?Rjw_pi9d#=Ii zic_u-#}SAbFX`K**vZuV;S3F*=78rB8RsdtIYCzT{|=Bxq^C!;C9&REw5QuijZmP& z>90I@rxh21FWhh}exNpHP?)ql1as?*irG4m2oNE0K*Y@g-zLs@m-!9B;ARmIZ4=S! z3e(t()$#q*(x&zw@5I5<@tNUbVhfW?)XAfwrNJTCZhf-e&ikK zJl0BL|A2g3@%M7cweEGNuUM}2b_6~$9(DJGA^9atL^96`!Qw^0FoYSe8-&==_6G=* zdi%gMIongNO7xv5)M)lRu|EH`V}O1^w6ygW%(&52Ut=LAW_+u3!{O7A1PFBXAR}z& zTrh<-%~8{AnP`afbl@*NijDmv^q$-8AHK`BM&WERB0T~?>kiv9nn|Y$31tdns~c6L zuh1Wg1|$MnpbG6B0eqlVT0X%V}1QXv5b{kdyuoFTc zkfo!VSW&!qa|obc^OQEd>}3c+3al4A-|=Z@y)=9rZO2Kn zl(QCDDFap2dCS%g3R{xj=n$*A-pf<92kb^pEqUXMI->4%0Q)8Gow9PteV^Mkp9+I^^r0iO-JpZ6{Af@E#L3}6Am2z zdkvA@nMCj}ye{iLpswjdnQePPjn5%$Mro_hr-l6giYgjFeCdE{A{q&-W!Tvi_l!hwXA;;CBf>w2hiK?mG#SP@d5P}atJnD z4d2caUHCCk&3;1?%-n@CQLen~$5#A@RG#(-%+m41ZIS^EzQWik)C9R1TFCHMnkiU& z6NbyuSM?<3#_qumzjv=O>2ZOqp0TQp{7@xws5JTXrFMxYJe9=t>72g7{X9oZJriAT z1LLTu?zz|Y)jvhgZ`m>XXORqHp>fdGv?q62(D zQFTtEIW-hyCp{KpS<}8-wLuQ-8T1UeybtG8RBKU{GUhF;4Pd;nZ&jAD-X%j(^Pz{P zoXT|D7lFKsoDC!hjs-+|v0T*_#vxa)2VMSAT4%xc0KfmaFqKhMD(k3IGB!D1Kqs%h1G|kYxF_;KI+H{N*Wr^Y` zngmN8u`ikb_COh|mR0Q1LWtkZhKg(Njg5QSTQ(^smG_0m+YmWs^!NcQ#MJZk_#T@n z{7&(*fJscAUtb&PSHk4Kfyp|`aFSJOa( z4VP)3srYc_<~SizCKqx|EU%V$rVXe<6xFJVarDT1;Z(#SRYOCX1T>0jla~0sr=SWC zdvbG+wq+*8&%2C3MsKeJ)+oV$@cc-GEmUE*GgDr64qjh~9!UMrlWX(TyqS=}Y|Ka$ z&w2qB|ALhF081FlZ!8pVl!EDk0RMQA9+~9n z7+&w*@wyY$BWsP`R-Nw1)63#L+uxRG63OxTq5jNgcH_#WVd7c#Z}(1(%Qj%f!leX^ z)x@+tnD)ii%8AL#Gii!-MCTaAlU_qI+pDtCh!o6TP)92G_b-qeXo{XcQ*884{ z?BF6Y45eX@R25GnnwdO3bv&`5nMKzG6xue^J1V}WCyU#w;Y^!d34fUv zswXk+ITfc#c^cN`ZZ(q`tGSzyRQmB-kpuMa+n7C!RGcK5u4pvy&9Qse@oeaRuaI`dvOBu1|?;US@(K92+&wo|o1#*A$RMNV? zBXs#bm9Gj{Eur{4WIZTRMR?}cpGn93eCyJguRk@qLu1SlNyE5Fc! zzCkhsXZ&#)EGg4USt)ft3DzbYOq(d_g!9q<|`%u zsO}xQJRj0%e%dSR@|ce$qb8uN%^5p0_3#hhHsS^ot;e;Mh4B^>Q*+!W1AqU7f0?~LNt5;8M?49Iw^i>ErbKusm1 zsjx-i1)68OxrG)Yfy5Zg6)F*zPWE$bE-3Qx{g94BzcW{y#;TrmP#w?LiQXCk!Ux|f zO5L8PM0DVo+g_0wMoeDhiQrSmK4qCFXdPtfA?yoJ9$LvuY}N~|7{e>cioQ*mn)IrW zmP*yFWC-;Q{rEpRkpqj*!rZlXUgv=k%J?mJY#ty%2GN17GK}K9#;5E@qZvE>c>{z| zT}3+}aQ2Fj=7?YJPA}|oxDo!(`Ivh6QtTtO*#N|p|H#13OGlN8567qdsNOrBUn6=$ zr`NN`4X1tsBAk?A`{fdXk#v^5Js6qZV`8b^Y|`95sHe^k%&0(i>*a4Uekz#sGBZVM z0X^33+H+I~ei?wlpZrr1F?2WT1F8rRSp*TM7=TNx9)X(sRvz5+tho-xd{WExqYTZ* zojAsZC2n&WN+st_0tZ(Zs#%X>Adz?{!?(+-RsW|Ys$l)Da@K+vKYy0sa^~Kj#1#uq|iD zYrne75%W@4)y=Ta0b9Eeg#{-2iu>RXCrO5nVZ<>-g~{YQXOm;LPPw^UQ&W1Fw*T4W zNP({i|7G%iIo&m*8^C&Uyo~i-a(e=96$D`KmN|YrQU*d&+K$qBWkMMkZ<`dwr~@yp zzkevu-|a!#OOQqHIO)V`<65vnTiic2A4LTEa-1{b(r6Qjwg;PY#5zE42g$A#sypjsmhU%#!53MFM5}p_COI}e^8Vx_y-Qv?@F*ZFl#LT606)Q)WmJW^? zS*pvlHafo^5?k-jT(;@W<|B5)r3?H%sLmym$i?G28RFo^o9i7A-wz7-V6}&W0@D@8 z>>d^PdS^a>yh7wROQ|VMrIyF0mFWKv`WWD8%tI%A`+8SWs>JYlfC*Q5A5QU`qI*gN zEclvOv0IHoie={&z?p+we27GhV*ds_E(MdO!w?IEfmyk2Rmo_$u5DVyr07t|*&Q3@ z*Yx9myk<|m0h8*2Sn7^O)%(~M(it@n#?UEI?L zlQIgIhDOX~fG+{FUt*?^o!?xVls^qtR7qN`7+!j^$!%{>kyGXDvj}C^Ic~9>J=m6} zY)tF_X{zftJC)WFxNP9i$J`xf>pUDs{}Cs6rN50nzSTd)T#cW8GI{v-JIc{fGTxeN@4AEdC^J|5)fpz(?IKo7<-pFir3H+ zutlNuQ;BF6V`X6q+m@|Z2?tiN?RbPQ^Rd8GpLXSO&1-_2{_IT1Cl|@3CzC6WW8%#x zkAS&s)s-sQo^6k74A=WIN$bwky5RJ`%J~(o!$$bfg+@LzHUP3YNzdKk^!uoAW94yN z;vIN!Q@TG74<_(9DqYRI&DvSk&I>oK-5iZ~ zvqcDHH0xHNH}UKX-N_0x4Qh^0WH?7)hx!^JFDuty{+I#R=@Z)>Tsz@|2UrH zaLPMd0zG3k9D#qJpq`!(Ja_jB1x*`I9#peiQ%=M@7h6}qiWm6kR`cvrt}7%)z@r=+ zvqbnjq{?k;tit8GBOhPPmS!%5u~%p$!=GmwsNY1nZ0XZBNZ z*YCgew(U(mz{aV6dS3*)G$4vfIY)@tGSQ|k;3 zg|Iyj2L?Lc&57|?c&g{+obLMMsv=mA4r(6y)ark&)os6Xeml+X222vyRvoY&p#O^d zs5yX;e0+=Fp7^b1A2nCRsdQO;oTuGRVWszJ?gzwGe7c9;mKZaz=v?c)Yq>0V1F==_ zll?K^84!&CpX4?2g>zIyc>-o)f%4kdD7zy1>VlY>TD7!oG>;pV1~ zQOgpTaCR&Ph~d~HM%+|lUNCJNb4oNqk%Q}HADPExyI2vdLUN8*`D;0L=i6p1;HIaw zY>qx(nrwqU=Tgq@NsZ3|oAA~^uk~?PN36|>F4z+HLc)QQfnq;8e8ra4iu}s`(Jz7j zbzf7EvGe$2+p}s)H|u8B>uaa@C;GJZmB9)LJ$lNC-kUjP7Hi1Y`Qx$<=vNP(K$kTL zZbm)@2&UNx&fH;INN=XM*kU-scHe9#Wn@u0t&(aNFz5uT^LPmzP+ zTWY3{$h{$E{+IOq89`Q22=x4yK(2 zrc-LU_a%){OC90_oWyT-Tt$vyiym^~J)3YGGJJCgB0@+F&7MrdVrYX>^_e|gz%{Qo z@y1yfN5ChL5BGI<&`5RJXD->~>oY=7nZGg6a2jhM0=}-Tr1=IyOUbDoC3cf?&Je#9 z7KD5_Z!vh{sif!n>E`AXPw$qN6!J7Io`+8s zs;8`aB1Vn8BV7FJD2lcNF0Mev06C*xV4_|+X21i)sjpjcir3yRLpE01>VAb6mVlc? z$zCdsS@`zUwGgjftSWYDbo}#z61P=+dJS7}&p5S`s)sO{CMM_mUfaK8Z&(C3nt$LZ z0an{W?qBj>ko$f^?P1S$ro7SHC$uy2Y-c73v7G&Bi6UL(M{2eJ!s(FhG0zX@7v;An z**11(_?YD#_h6`w4pn$)Opk|~*R^H1Va7_D5Zky%_cpfVDFjw1?-!V448rdySN1dZ zRLA2xP_o{i0`kfO2O)&N1Z{y>JxEHdd_fIJqoyjDh}b*uOf;hdFyGKH|G*cdy_C+ukQ{8=_goY3zXGPVy{`O^&i403g9-t~yzwjZZ zz@K;A=&bNcGqOOiDC8O6v$9Q6T<1EF>Y%A8@=1|}yL$b3+7jwHZA-zjNhs4e zP&}DlZQrbsh}BsY_niDBjDbs7(8qu;HC-aI4L)U&0bC72=k0hc+Cz@mL61z>iK5U+ z@_Xix>a18?z#?JEQ&!a$eI0Na2c|}B98+SEzwI3pK3#K$>*YlI*bvU#2xf`>pjagp z+Dq{fS1kAxlPS`FA2~E@HG&(J9sw#a>YV8+4m{rK&peBISpY?&=t&sIKy4<^ zO+wnG=>>Orf za^v==dXTISuteZyL#Zl}iGletQta&Y z@Orkz`QoV?zO*uV8`qq>a+60rd&I>W8%F-I5LvP~`^SZ56O*qW%KTkD@h|7%iXEsu z-s$&B?wA|co1Q)O6n1f-pOE&13YKi*#i-qk2)YkWxjM4OIC?xcoGi4~HP_xn{vr>o zX#}O8Vb>WCj(?>8T^s;h{U_g^2usZ#e!&}dmshJw_lpFpGoUJm{6m73qEYG(Zqli*JMiLQ3s!rfjKSn{Kk&<*(5nWr+?1I- z|3QFgOVmsjiOX}SZ#Ou(d)`u>Hkd@#xU#_nm&?H~WB zSqLbR=cs7=Hl_S&-2rkLCZL;fQ)L~w<{n#|H0tDpsF9pU=hEKiA(;YpX z{5#D|;YzVTcqBj(kBAbbN4HCyi(4N0RQqS_$ANubk=Fbg?XotdkJGx;t3qnp`7h#m zLJs>V9@&yjnYb0~5ziwcY?eD8en=)PqAvWtqgJRH*)BIAKkK|G&>iH@UrZZGk{b9; zeHd5#N?f`m4)ZY|fyv@t7Bdl1FY%KAtA9wy!ND_$!`%rVd$a$UFe7L$PP0;`_i#RKPKVx+T4QKK4Wh!48{ zi|)`y2W)1k-V5~~rQ<5eW({U0Yvzpi^`pAsz$8{l{>z0P32t6B+8wV1jF{UWVHA^F z1+^o-^GU*&^${QR+ICZP%9bW(Ct2sVm_Vzeu*sYM+!TG#C@lPKEQhYqbvNE){+vu1 zPiBVY?9xBIaFI$MVB}yN(u~ z7NKleT+n5VxyzC~j!nriQ^d4-j3WLLHYX0+nfDg}^DkF?4VccnP|scSW;OF!xr#<* z2alS37-@i&y-p{fUO3mj33FIEa-=(9Gm!Ng!0>UW=m_8s*a@KfXjm^&UbBFc`uC@W z9&j=v5BaBWXejNyI8yiAL)zij3%Ny$a+#OG={c&lX$?_}^emMw4ioqIo%C=#Y#?Dp z!`l#O2c^cYuY*8>mIA|1v%XE{0g_S>e($f^d1Yx{+R&fP2n_-A{8syf&5!Dn8p)#% zzFb*~)%cJ=40i;xt^pq|AOE)jH~Hy_kLnXV*@IcF`C2TkL+3_FK|PkPv<;jVy*9Gr zH>g;%++fBa-;;@jJi;l0zQx<$SX4Gt#{F}Ek&)ard={q`t!c;Y`mawukt?xk%BMu0 z7al{2qY(W5SZbPmEn0sD<+%{l9LRYyN34WEL%1CKiIgkedLIAd6KD2Ghp(prlIman zQuaK`8^kDFsF!ox~!nK+x)8vPd)aEWBO zCpSDI>h+B&GY>6^OT!X$Elm%L@U5-N$@AjDA?+t;AUW>a4`mv(d|k%Kz;w4y{=&nF z-J3=oP z+eC+6#A_A!W#7URL+_+7?4&nJd)A!Cm+##%X%vR_(5z{x}4!{*TM(zC+#{ zp=W3|xT)yB6>kmSiI&-H=S$3ip&sKI^_6Y%;N8i&GIFr?E=G`)!-~-C&0)C-hA^bY zx*Xrq0=#h|K5}Ly@!G53+%o~qRJs_t+C5)dU#15IBuAdL-YH)i4@17p;)da1F{-8U z4AC8D?D?sc@CVxu>DSM(S0=NM$ijm)BM0XmdcQ{g>-HQ_UIl^hHu)^sR{V-6u&=hg zHS*-I6$HRa3RA$DJNOFdTjH0X0+;{a-kzKlZQrM8KwDNs4@WBR|y)S;M`$x{zBt!K}h^t~R@)|#|kZ|JnUvg$~y>jPuZOCqdko@5?s!wAvD5+fWE2e&++FR;G0eN!qOSI8dbRJCbghn$__ z>AKH=mPXQk?OtzB4+ykA_xbGf?hJc8t;X47EXH}}G#OGJd5;IHUO!;6A_NrnDU+5tZdOFb5 z_b1;Q>Dt@o(H6tJ4T|y*k`DDD>h-0yrS`)ul2T!-Wk_R28nh&Q9|^Z>HbQH?Y4Bn5U1KTUu(SqANT*5Q(2f7VO7ZKcdx*Sh1$ki7lkD3Jf-LOKm5AUkeay zl;|(af>@4>e^>cT{72OA;zK{W6CS*rnwxCd(;S zsq%$oVsTQc=u}kz?Gz7oNPwyW3xZ{CZPqvb7?lZ(ptR@Yq|?-a4Y2C)Leu3y(e<-# zwlSL(^5qxbJ5Ow|;Y*#=-NRvC0?{Pj0MjLAYKRpirQjBpg-_NJO8qG^` zV}Ua90TvoCI5?{Y|LO+3T(F)u;q2n%Md+P+wR-LnO0$9Kdy_ zj9=1Sr7{*-XPRZd`On!i8JcDLlJt$Zc&f8TNEnxd&EUiI(fO|e_~`qd7xdWTuAx;C zulcxsq=C@reQQR|CK|8S28lUn5C045zkLgP;*e+@ZXsH0V`hf8uu>n)R4M%+6;iha zu08ikyjZEYHr{Hs%KRD4&Emo!bt%yuJEZlsVmMnhr+M;DsKU=+AD9mnIjLqdpc4<( zR&+z2&lLqM;DkfqHhaArXsSpuWTRSGllhR>efC!B4^?_^8khzTcUV8GIZ2I(`&c^g z(iWRKS`04T4d$lZxk_{I^bh%7t8SY45k^9L?AO7A1o4 z2LDH!ei-D__YJRIti`nPs)VqYOoBk~@>ecEEkw7p*SacX8;JY={LEW)zVd6OH1PO; zzWqVnY*+DphODc5uT5!*sFkR!Yca<7I@OMm8J6)S;JC1H- zhJ!lKS*7Y_lb@m8Zn|5o45dK`TjNzkRD}E6gi5st?k()(pzyy`d8LHPw_}RV8M||w zzoX~8GobvY+aX8NBTT`ghDapBR<4<*=E_4Bb7qJlV|0>3t_ zek{b*wPP=>Zxk-F&|JRtl(B9SR7?Ue`URUL7xly@cD&=A0Q@_70NYX19 zO$LwgR>(#@x|||_y+^#+x-#&fLIZLXd)<{xa;e~=OcNidq*``{J7{Lz3-F1nO;#Oo2iBlkGA+Oe+!)AdFQMb z8{aprn9~=WU413Rywf16z{)H6r&?kERAx=~^hL=p-7*=5GHod;;?-s69Q2vNS8{4g z@|}D8tyf?jJeB49)Qxcrd8yr&dsO~*n_qiXs`Z= z)|p;9Eo>}fIdc0;hj2*OW6UZd|6|F~v8dUTUG^X7woymkN-Js=B`2%Jy4UI*HS@~Y z&lmi=O}R47w|vW^`*`|CU~UJ~_NzGXAs4QwN!`A_^7?`26wep?u~9)y*uP>?b!f|q zDWmN2U*^#ftNg%@wUR#Rmv%8BX&wfc9`bw zTyekLFJTtdOPQq1YysOZ-O?h1HY-;PR?k`DdBJZwlHSD1lr(&|0IeJus_&u%$bzqT zm`E3x*!Ny8N@>5s_TQaC8P8Uwb_by0bD|E3EPBKV%1{Jdm-z^IHg(Td#3^)sy;o*| zJ46CXeFLgC)0va5t-*DZl@kVKa$vi9g1F2zewkZwoxdj{WmCp7Ax*}Dtliyit z$pcjA_DH{|=)0!p`MZ~H-u0gD#t?JH;q~^VNw1wpQA|kL`{@!tHr{(7c(t}~AvV$? z;P+YIzlg4SbEB@=z7yYH?$Tby1CD)Uvo|%=NteKNk(3-qZb=^1aiI7#BdkkktxM3GZQHq}4z%av)4P~nqhGFcldmkP8 zb1m5MnD?>olBE&I=GN#HA%3G;Nb36!nAZ=&KQnw{?WS(>%F4dUD81`5dmx1>*cDb^ zbe&(UHku_bs=({{WL1m3#b>#xoVUuE-*qavM`zqFrdlAXQ1f}<)v<*m(>wlz{g}PK zsW2G+bS)GmUFknrF{+Z(r}k&?mD<(C@1qUP-mh}QCQrPywfU}RTYjgkl|!b^a5|VY z@$-M3E02sq}VVULN@kUz5;IXx(>=V&`4IS$d3O0y@~y%gDq?spo4w1 zL#Z5I;NoX6q(8iRw4pvVz|(GS^JJ{f^x3ekk&0VmXT|la`|pwIYfnmjZ&Z|Joh<6) z;4`&DCv}oD-!-(oukmXW!O2xy8C=KS%qpQJb<$pkXdS znaOpS%o*?->6Bj2FdvCO9w<9PZ)PCK3y-fCbdu{#LfdI?U6uGYY!qHl%*MT7!%t&- zM(+&_xVpO25x?uZDwAz5C;%<3CNXXv$>b1|htLGJ zKP=MICkgpjdDhbok?qH8>lh0!elb4FDbKmneaDXWuz6;oxF@RXPaT)t&yWu{*BEbj zH7D=JwVQ!%%f)EJZzl{|?N?bo459GT_SY?1cP(_Xn6W;VzPmqE-wfx*gs;>7j-U!7 zNos(UDy5139&QE?OZ{cLXB^CjsFqiTb2$u!P7%5MfW1;y!oTvoWnHl!B%$}SqEV<1 zA@<8J*b7Eoo$0GiOA1E6hlgupSfVP}~6f_^@eBQ&P8GbJ@c!A+~05X|6@rPzRFz%Qvf`YQr<8hF#>fA5Wm z-enu%(jQ(}MfF|s^3TQ(JI`cnt83n?QBV?nO)vFLuRm1Xmgt-Jp0ES ziJDXq^uilIe@?Ms6LoTRVD*7crHDdTe8^wRZ!k>Gb>s&}&Abubaz95v$7OFFWI92R zfQ58PiT&B4FJrmG`h$=kQQ{ICi(ljE8TH|1#1=YV2>pSTgz+u!##K%FUhut;%el@XNWFXmP^jMS6OPyW+=`GwQzG%0kz=Fi`jY4x!3<|BW|l z7Fk<&9LFateW2{_HDZ57|KlGwO78`8%)N0yn0@u zNSEG0Kx(McyB!4t>C%-dge0`kLMS34E%Z=B5h1kDLP7~7{|fGN{(Hwg-x&8hU-|zv zhQkb!wbm?ed!BdBcTSoAiuKERP0YfVo?g5>!{@gB`nM4K%tojZcS+v9Z90`>r83kO z8JSa2TF)xwWo$)Pd!b}$O-mJMmNF+S?hJZh(UZv@!Cx}T_*m)qp?Cg%D2s`iHM^$P zfuC%hc)e4Yeqn~mUy$G3ZWxHldP%C+PrP^lsX1xh*8~VuU>75mrV*fzCdU#8Aq-K3 zYj1oqoq2NlE5j@Q1HrQl;rX9CWd!7p9*$+iM2dAv^=zrBHg761o}Vrx;sL4gj(SKd zF$U1qn=$Mu6Vc9-Z~+*JQ$%VW8m-J-Z~3z9?Z}^B-EaKZ!IEm2%pK!Om)0W7C!Q%@ zN;MkR53TrqWp$ht;49I;kjqnJX8&bPk9OZjp-gF=f|P!lGLoK_f$BZc{aOtJ{a?bK zRK?;`Z5|^&4NSJ6|3uVqW`5z$Ai*gTAJtS=aTa)JR-Db&MtxH_5OS?^|AKpFy(!>~ zr|IbV@gf# zkncBd1M>U+@$2rsrTkFk;`sGoHFdbrb5_H!rS2lYWk3rHnRV4OzMis&OrXj|w0_XKwfN_M{TSoA zrxLe>fm$BO*dCS!gzep+^gfFav+SQaBr+V4xTrSN!)cX$nb0z4k%&M-!uiHb% z+Y*mI|4;Ikg&dUQ;N8V3CXxu_jo8d3PKR!+?ndkeDouJ~8y5D%m3XNH$3DUl3FCGA zo$UrT{{`KjFeTu|a^qik(_3$UvYD)a+MOmR7Bo|%A~z2o44woM;sI-W9J|*T!Ve8U ztvKxZj>EUQ*<~P;v|ADXyfr^+zEp^k5}_ZV4g^kp$}qhnFX#wffphQk@Swp_xD=Bb zHSnmlmQyv-j~LS#iW4n{0T zcQTu)>__O12-9Xl#mq>Q@{VYjdf9p#Q`nSZKt~j@n~IB`MiRl{D@nQiRa9K_P_gp< zSt>0O8N2?=_N>bZ3kx3{etK~LwDn*%cV_gE*nL2ykZtz^Ez@?X%c-&HqeP}ivdT`0 zUc_C!Lc@cvHzWF*X-T%4<5Dv~9x#!^o(fTy=Cq%jBdSpFk)yzCG-xc`Q{G>RvDuuF z!NI&p^4_<*YZIFdF`IyI{HXDZp{pzQ;QksxRXYB?)35B~Y!2?_7?bI}wPrHc#z7z< zh>EflkA`hFWFFr+{_D;$mYzSzz9WB-S*g25*gq6*HB$b5%DrFdxXI0^G5QnNq=9YZ z03H5!>a*qtV+D1b)xMcJEu6BJGn#vyMvl1zSaLnW4Y|3xibW0vQ%#Z*45y}x3sr@M zT}Q?VIrcw)K$>^|QnRV|n+nwZ5#2G=#r`@Hpans*O{|yfMX5wlZXiIP&V(c z2Pzz%49re2RLn`a*j*&4ECWjhr;*<##0cgvoGW2qd;Q5z5pEWs>zn=|k``ajHaSI& zCGN~jQ|eiqih17BdOFKlNn{>lSV$qgq~XDDDGjz&<~NGhTQ(=UzkfsIJolvC7%>O? zn);a3c>LZvXG#61*{1UAp3Hw526*c3`r1{9CF~mK!Cf5N54KK|_Z835@)&=71%@Gh z=K@FM0sryPhdxNE{zEJ|rZ@#&gn&eiswqR)t!zeE`YS#dq{m!g;u{U9oqO=Ek3V(8 z1QSwW=4R#WAQxWI5U$F$klgKv4l*``a0n2AYmF#tZvrn__kQVQsGj4F!nt_5YHIS z!!Na7`W?-;nY@4mPsbWdMB@6` z`h{q_plO%%I*e{?zgnR&HMvXjcR0_DQT(Y+fD(X~xi~uN&hvvql~HagOGW|cbKy9@ zfn9q5@Gpi;mk}EiYjrxDdK`>PVk<|M*}9yEqB|^y0c`>32`e<#p8Es!XpE4wtr^Pe z!Z(_Y$Pq1aM_9ilU%oeclX>Z6*p5D*zaQ~u+5iK&}&EJwMKI#m@lr*F!q#%(| z9*AAbZSDRQRqSA1YLL~kpY%tr8tBgz2M*(EY17>XLEQOYBj18r10(^E^lye@{Hgjy z7HmRn0dV?&5h()03+o>`Tfj=Y7HM{y?;O zd}Xe#pBV!rsQ${W;&0gS1Qb0;GcixB_=jvNg5|{d1M_>fxOZdG!C&)W((J+YnD`w& zImiBIu?I|>grVmX8sihmCWM{RWPddJz;((R zzv{*FgVFy8m562TCLY}JN)=9zLYg8BIq#$L!aZ_^F8z!)am zc(*p!0Q{eZ!*XZ4^nzwe5@Jj(Uw-xd#*+Qdq1Q+{J(g&|R^MR1XYsZmxx}+aFdJ=Y zSNzFJ#F9bTFkSoT)n%D>Oi!C_LXMuoCvPn+tK~9<72=s`#*M>mS5t*Vqi-bv0j3Yw zgLTa8>J#)JlLYxVBVJ3TZv@=qpUDu;i-x6wMSz+3N0;Ik>C2f7zo9KGj71DO45t|WF;vO=!yW3v9)JJpM4#!h4Sk^jwZ0_7FW(^fQMz|{q=o)&Fd}AI}FTwp~GjUOMHCq=s27DUhCMZ zXTE7Bz5OJR{;lI;wK(7@yq;8;Y0mrl=5XRH1OBPg^#56>qeBw`Z{msAeB*d@P5deJ z7GNn$!5C(}ZbvPrJkh+!4LA_LhTy2IuUfS~#<|8DtYQOIZ=-s^^{@m z3KOXJ8CbDKf(GgXFUiz^`W5#S*fQTzb3q@2Hv`3MB>qEB^IrW9denWWGnu;=1p-;l z?y^z_RZh7Jz_tMX-1}X}(cg*lUb_a%w-_e=frE099efIgXrZJR(ttPD>Wpy zb*K*_V(>K#H3prBa+Q+MXl)rcCHtjCzuGvi%3YbrIF-yHDce4Lrl@k+{ne=C^i(4> z|GfO-UR7{kBTR{Wjfc_PR~Xl`zjxp@lZEE@{T_y1v9jG!j(^;|-q!F?+HcN0lsYmE zz`Pcc>hWaEt_$dWIPs)F>+PHUnUfcM8_fd6)bFt}DwXw=E1Z({!;eS#AGRlgRWj?T zTh4@rWhMyJTh4p#d*BoKi0|y)nb(4LGc2>F!nW)LxdWs6U&9yzSL-;!$iQDTvIAdX zkO3W5mVltFA6FIv{x8j>I^LDx!LKY0if9;Uzm5CAeGg~5V&C7$9poxga(qm zZn`rUY}pjQX!HtMw}=V)q;W4L5Xiyq0&A&4Ei;XO@)(EVwE|T0rh(ypN*?~3QgBZJ z519lhVxjlJwSoByi#snpm02MSiGk|ij)IlC=GvWJR%8lcr-u|={J2KkhXH|ER>EF+ zs{s)yGb>%L1!Vmx@fRoH9hmIf&|m(1+#u&+Mb-f9soN@|VgE;)yt4agbi#)Kd|av2 zpCh?zFW3R41N}N9V14j}(oFfe(0PL{VVmI-$E&JLXtLKLU1KEVVm^qzZ*YzJjr`r9 z;x8-?H@nUcF1h~d3;P4k3Xky^nbP^Zs-;iy9E!P-V(@uN_-_J5*m=g{KGIvk6ZF?% zJok6ag);kU!L^YwmPW|ygNVwJ>tCr2p!G6MbFI1W2XJc2bCyf`mRRs8D!>Cdx=0VQ zpvPQ~KFuWy=iHod0YPwOa6Lr!aaaxnG(F&5(azRQA7)8o!NcOKz=_O^qv z5DBN;sERIOU_A!}T0rvt6PWPA%eOs#>Ujgb`2&88X32@@hZo zwJ{U>?2K?2Wo@VikM;L1pS3pZDzlICTfWU*oarx61@X{uUr1yrY*4BKlLG6dv(pX< z5R*9dkv;gw4_}ZxDXXV8y99?OpkO1Hd6)p$uH3Yn40p>V&(Nn-rdl<_MDiYvsRYO{ zOZr!u(!aeB9{$(z%-C)7s(Yvgo43QL0I9~B`n$fcc2Kz-%^r=Vc4ctL7xQRb(*e6} zN_Wxoyb4jnc;_2p)-)sN(}n9^CFR!_Z9HLpm*9TULudotpBKp zb2G~xeZ|_h)Z#0qA;u$bFK;XDN9k`H4745Eq8W_QoNxm+s%>z>XA2&z5YEr8L93n- z0{54L(~By@36vvj5z_zAoS%SAHea?z3lcVbofZOT(v9!oPWCxggXN1ePxSfD%>=Gy zaQpi~4-{KC4AU|06=j9?@Bm)5VcwD}l!E{dp^`SQu?|atHu;&^kUEohaAGFdb-00- z%a_9N*hEx;d`d#)_YcCUg@DxL`F{Y5f!zP3xulwF-G#P&C`UG1liQ&`^vso5q z&E+U_{Nnn#3VXQCv0b^?|D~WZgy>X?e$Q* zBGW-n1kZH~PKDwHOk4&F-=~ktr&9tY6;`8p1uIs|7r`b@mds{E8ze$6 zYIKXLG1+3Ay|fkIpuhDB@qJaan1{dUq^7XP)7^;pL9fvXmB#q>jL3uW3%fP3qI z1sG~}w51Z<0q{km$wLIqz0%S^rCnd^_o22L2_G2!vIe$9AFwj|uV(xfVjO5S{ECr( zw`^7F0KK&5Yr^Aitl815@JTgP#WZr=y6?z}H2~)~*M8PoFw?`Z;6s3amXAQ?GbF!Q zUYo>e9p=8}ZI`HEomv=|Rz977M0MFi`}=x3+|1{+wA$@@mJ2d5ALNcX9h%8#2EFyUNIcEWIf2 z@@mYHW1UOivaUhygYl6RVhjfr0tots=cScbZnc3GzNQs%Zj2C<=nCbpkqjzK)9C0e z4DNe0tt<)~0`}~wTWLws3FgTcbYMtlThokSTSM4(R=`1107~NH7jVfKAXOaG=e);L zu*G3q_scNz)n((YHo{}fbC`EW#7|G<1oOgk&iPKM06q;?yi!M@AajtP>HDmA-q{%W z45*lR*&3p7X$96Ctp3+#_Ybe&YauuCt<=A%SUHUPs+excQTe=a<1=rzBf)}?5p5qh zR!8=eQ0Sl^$;@N&mtC6*;O|fu^%q8($ zhcQ*$0Gy37zAPfh3bUQJ|BPs`m4#+nCtacwGVVIZqw8uLzp8jC%0EJF@k1TU$rdNt zQmIFopI?{H6>v`4Vu<=0XK|}VVEb_6+5w%|izEvf#0h5O#a>wb%y4JrL>$p|rsCU( zxn}HAIeE9M+$kpUi-zi#bq1U6nDr+bRn!6wn@O2x0k3v}u}1vN(JFi-Jj_iHY-V^) ze%1OS1L1k9HyUn}Z5#U`UJxa(;(#BUF!d#jo83>yxq#o<#lk8*?;2Bsdu={6xj|G3 zi^v0@lq=SQW`^$=l9SL3HQ|(tNOo#HG_NLP>aj^C9m*Eef=w8nld+mxbMYcJ+|+Q> z5ik;iQ+ccWUtfHqHoVCAWaBkpC8C>{A_S+Uxjnm~;S)EF;Ry!jqk5df{(Uwgfbf#I zp%7D)S77?+@T;S6w~I9CJAW*F7e8U%>=Q@Q)T@1WKEi;BKDk=<*>%U*+H?eQNq@5C z4I>?*v;5_sH{=Jd3=H?nRZ(|(sySCH1hP#G`CYm!URFYjb=eb5aFAZn9bpIn+oGmcKCAdKFcLGN~17*{ZG|RpiX}H#WT?F<42RJW(GKMM8RQLrD@AI65I9;aAmY)_+%DmrUPz zSfi(!czwI9LSGCX^C?F;^efn>A*7cWm+3Gvx?NQRH@ClUs|U=WHS>g&@JMu1d-}!j z5O*~TH+*@Bf99ya28_hDNDz)?eopqcoEn`eWTOQfY{vnI$d_>Wt;+rW^Y?pKaNj8oYrK4|9W*Kl$x)Cj8fokxuTV1U%VNd2sPnq(c z41P)8`TA5+!NlA1W4_+XCe{BNDoworm&|ge&sD<_rzV$WG#Nn+h26*+ZhEFH%)}yD zsLa9n#wDQQFZ{;PoWtN+9#S02_JLkglf>Y!bOFDrv2yScO9r%h0EkK!pb_v(S@0V& zkLixk8zezW@M3Wp3?p@EnY6>s^(%;C+-E&~hUk zZ!h386Ie*QQe~T|1rPJ|qAvQEqPVLN-+HQ@oX^2_nf3Vwio?m!QN#=49kAtCSs5dK z2AVC-`hJI$E(8wNH@HnqZbP&nJd3Ek!$3#lLbUNZrA>*W8#q? z=5>8W8?BIFZqoky7U`FdEh@d)@ncaZ*b4FO&#Zkh*3@PEEI* zsl2x&FM~)#?7cS}xayX|qSkFNR#xCSIrAx-j51y#iE%F4|CORr7jVBCW+w4lMw-=nL`A z`nEQ_$n7_ej){q&DYhJeRjIM@3?s`H#7xto7U>K60-jK9x;1Zc9+XMj(DKwnuu@L= zzLi^uBkZ0F^$M$Zau58IY)IH0FNAIh$)mDtQxm}{=o6tFS;dNHv;eq@(HphX9L-GUyb zTJT(`=Mr~4I-KWoMvwf>x7gNsyS~3bAK8K_p0#lOMY?ul33Qv2olWueXY{<%m5LY$ zKb2fkFP(-kI$M`xdM<;59Ybs{&~2P*MO+3+pFP&=hk##ZtW~R)xG+#@%`>c$S5qF@ zXQ3Mt52Dzlua-|u6o*qyR^5_7!I(Hw1<(E7d*j?`BC4!0fBeid%9oG(L?7RQ-dTc( zM(KHQ&651C$;`v5j2{1cUdE77Xd{?7qkR&Tp=Dl$L9|lB%}uzR0xi!WrC#Tbe7l#A zlHXu@#j~1e>!j-YXz~`Ad@$}I@tmw9W23z#J=Tgi@+$B)?*MQeKOH z!UT%C%0=Q+3pe?d=9)B+^VcVKw~>LHYH-~JKWofy@f5o>zzr+__%k+jgvHF*lb_^P z%2#y|e%pzNq&8ZnI-LU|xcG5fu3FaYjC9PP7|sdH_&bJ7^Z?zi;sbXMsf?d15Nw+6 zW8M`-^qfvS%I77#AbguNHAG>0lSeR-#$-tkkt-g;MVE!@C$Vs#@a6vc3Ct1lhTX(HC9?NB)R0nBCuX;$vpa(%Tdc^cS*UY0yO_skV_ z%dCI6^Nq2XHSnMBqK;*A^YeyOly_p=K5C%mHb6H~_XO<1T6)V(?-<52X`Q?wZ#AS_ zfVMY>Y@rqhUEog6T^8QW&EAyA6@2|ThRo@B#^kae~^Lxx{WvD5<=;P{(#r%gql1ap! z65%!X1)5gU%6>n^7m1gZeDbyUe1BNFzqlVjo~+#yY?nWD?N5MJ{VhD*c?gg$r;P`Y z5I(?W)9{m0G2v$SIpZF7OwBV01LP}@1~c`Gh9^VK2>qwFV!yiPe00frrpVE*WK7J4=6`w(qxs1dFRopit8tXIa;@$9(Vf-Hd4&pSo;+Z zPhxZ|X=Qu%4(e9wu!9oDK02k6k)f8@SxgFu4R37;Hhv~h7aI>hlD zDoNj%7)=`f@}UR*I;wZ}Q7NB>qwx83nLR06JnWJY%R)@;WAcJ!%ia$K zr`dHj335~o_7(fLSye~?#ADXY!!h--Ni)*30HV~1&!k}9Ojz7FhY0w9(qb)^QZU2} zK7@~iR*TkYPRr8;;;C|=+amys!{NPa#vlafg=|@t0W_vy;?D~+QKcFj5psEj-uDG~ zv%%_M_1JS1tAom&Df z0OW(rL8AjOtt`a|MZ^BP0~SFV;$$t;o|!Znq337z>Hd8MONC*Kn`pr$vF2#@M|PWc zP_yS+N9_^p1%TG%TrMvyEOUF}j9wh|W4d`Arb+P45XedTasdIvpUPxN8>eGR%xT;9 zDmDR`X%qFfRBo2lDZG1%kz#RoJ`w@9a3sb#K`E)NC_WkA3mpJ>SaK zm=$fWF&j;6+2T$C^fK83V=(R^GrY33+e&nRgxiRM= zqnsZA+))U1#vl*<+#0}44mI&_Wa*_C8zC3Tq5i?;k8CW%$#B)l7NnbHHB4o7?Vd-( z=k;dZ{Yj6#x`|K#BZhowIHQ?p(Hk*<;M##T{o#kV%3{iepo-LiDEQG<*L%oH`u~dy zG-UJo?ak?Ryk+XBN86z}5o#5*!;3YJN3 zU@W$rjE2odtnh-Fehe(#kx% zozzg{&J=m;Kwsm4I*E*AmD^U-Y1t5}Y(O2c8r<<%z8yDVVKy&&@7niQnRj2uVj95i zEO#o(g`l4peB53?Q{zr|h=oc&ev|P``e7{H-i`=Cg5pIjJer9lBdKQLa~BUNdhqyW$_T#MJmgg$mdlB_H5yq_%?PB4 zKp@nw#0%(S8jYL)uAP6JN^JA@sl@$Sf9F6${!u(z^zU*jOUD)TX%G2FeUksiLzdNm zIx(;=TTTi%@GNMLZBv31R42d-BoAgKwnp3FNG%{y3CqZHEv?-sRaLbVXU71)M zI?D_$3T0#56-Uj!5)D0UmkO4G8tB9~hyv$@Xvs9t18p3=Ywc(@f*moDL(kP|8x&8J zR?dBnyN;|fS5qC7^p8XLJf&kxR*hcY4s|JNJAX4xqgro$R~$`te^Gh(F_#`R8&2bM zo|;%YLQ2!V%;;ojN405ZFr083Du|Hkt%msyKGrYbJHw{LQ7F{l^}Q1z!ykR=g?>(4 z+Lw`5zWFWGLVjYcs6_U#hM1YPXms)m^aQ`EC%Rca!$fX1AW?k2Z7byzO4WR^QNcra zJy7StjM4anh0TL#thrPEkE}kE$AKnJSlbJrmg3|6p2}HH8FSv1MPKrvq|dMYqn~7r zr=QrrqLWlqB0lBzVqVwv4A1mt9`$VyOZ70fXTV!ao*XLuT zA}GB2vx0dR^nu)!TuNa}`WgPNc4vhyeQW`LS8@mcp8iS$*)BC_-b&#_Q#BxRCyz(| zWQk#K+iKob+_Fn>Vy%XuUX|&H>xw{|s{%tC+*s0HK=$h^hE&f7in9%gE&K!5IzNE>D)4nE?ZW3C;s0_zr*}M^ z^7FNC>KLpou5Y8Hi5gPh0f}H;CLEGu5PrkK!Dv5!{W1)^F&0$ZNCs&> z)}#SyiG4`r8!GMUPATLa4+{W2)jK{pBj^~|-O>XF2C3~utPcVkC22yPjrL&hkRip+6j7)*6U zbI>#E!$3w?idIJ8i<^#W`PX#}aZ7xz#kG^f5$C~z<<~o`d*4AIuFT^Bc+|BeROOn2 z(#S4yHIUI+3CR4yt=fgvSIe&|dINFy@MqP#v=HP<=-`ccwwk}s8;z$jCKhQJmeHrh z#OACQaq5bvbMprbc`)&-=}91wd+U5db}0>Gy=%)`Kc;>h-rV(R;vHIE*(2H;b{9h+ zCGj0CGw7-XUigZ9%t)*K6=Vq)bx{{6F;%N^|BjI%!=!if)p)~2zuzN61du&J7*1rr zb`+Zkr4$4T`88e-+g-B8`7{iRBZ%UntNFt^Nd+28B?fxY$@Al#YSC%^S5ewnp^$uB z%JuXD3k{o9qc-{W^)ggE%syFPPQy4irzwZa+h9gtPp_z2kE?FQ2+?_^7c#7qP^rBl z&#zWB)ooJRDG4_j{ArqQl7xQfm)*IAj#CYAj>$-`*OY7+2R1kOE!Fs_9QIL`(Y@4Y~6ACpDBBaKUsz zH#NF^7)N<0buvOSP^H<@oLq5+PVWbTvgT2YtkX?Beoss~cQokyQPrR3<%!)j5%WQY zUlB=I!e6My`iOAXiX7d+OGq|m|CN2EDV_?@ zm=Wtg_0BK023R8uXXf?Kc%^N?v-do0goUluBy^;dsH2SPI*G&SCO9t3DR#%|$~!!O zy*>_MEZTd|CrauhuAgY-iI$mJz%S!(#(-kQEyFG2NWckz6 z{PCb9Zup60uyL*HO7OD{`IvH9jSSb6WuIn}mA;dH(Lhe5+!)jlvF!hi@IF*gq{=v3 z+fS5BAzM;I4&AwZD50BLT*I2{T;lZj#ShPPY$jfEx8yK#d_Zz!y3^^MMjWzf_jPyD zH*%Nm1cy$u(W%A#BT9wzy09;EKVsf*A zFR)4>XJK>mR#cTwSD8(qoj!*_Tv`#dTR$MCAid|_eu_iPT7YL|-Q?>gqThr~eojrU zGfz)0cpCPyAHSH;P0wn2sl8AEni-NYXHu(x>=_!DebLb6ZRSWpo&r6c7N>=0R|VCo zVl(YZ8;;epLA^Q}JDxGUBtAbbJ7Sa4P$E&XQWhDSfGuX{tzH$dw8XX>tIFlrhNen| zvZ9&Ip#>3>uoP_5D$V$YWCRE`Jjhrshq7rTg?N3C*BHWUyRJ04tuKyL>taq}yQKph zyu>d%Do*aTqHC+oouXBPus`eUkuITK5W5sNS#AA{Z)ZRUTC_!NWqch~JsI*tDA%}* z1=q#vs^(6TR~+Xl5vu81?x{sNPF^9^R}NWJyPhNuELndUysj$tKu$i=w{;;S;i8OT zoO5w;K~fD<=<+LjNliJ|o8?K|FYa`;{-x?8D&xf)OcozH;Om{XQE0V_OLbFAy^84b z#A=!5xfrbDPiBGVCJKnIk3)FbLcXq8 z8CJpE{0BZxqJ%38vQbgk_81p~(e7t}IY7!gnh4ay7nPKzY0+YV`_1Da$tDgQsnh*;F{ZC_HTy z*B63P+W>zfqhqaS(5t@Qy(KhH&;vG&EgXjq2vUFrLTOkl8XL^Ddv~bJlT_ELFD5^I z>Pfy+hTWuC5FxJZcLZdF-RUqwY)swkFhoBfvB&< z0_9z9dmV?1;Ii*a%d#?QugmEco~U*P4-Id81jOAl8>MK?P~7d~RK=^Vj_*Bx#!alLUF=4^@xrT!KIce{X60@8FMr2UGdwBZ1SwwpM4tW@vB$wD;G) z#0hDn$r&ZQ2#_8Dg?ZCp6mb1p!0dk?y89nvoEGOVxBndddo$)!8w22Es?dF0%J^Y`VdPy&!{7==?4hw2=#_&00TT4Gin-{a`o{st~*7 z`6u75x z5UlUv@{3U4W)~GDMW6V-=k~Y3XmxlDY8x5@I3Whh4+7mhHX1+){zA_Tw4dK7dDnQ9 zklbK3QQ@;i{DG*Fgs%(}xs%eIXMi6U)98q&U>E&)AJQeD*G0OC+cvsCa9W=sM)w;{Y6m#_KOxw;wTvt-`H+YHFUEZ>;8<4AxJ1}Nm5Gc2T z9Sg7vPgg%)?iKfZU9!SUEYMV1<{S_Ra87sbEgmthscDFTNH;ZKb(V%z(h!|SDTBbv3k*dR*2~zemQ*itkuB7$ri}25>*@H$=Mti)Q}KQ4B6=9s=c) z$mtblPs2$XAsxnruOwer+T8Tgd+9 z8~#^lsejvi{Fj!fsEcwg#<1Hi#?X?(xVYYsLn+?Mb@$<}Oy5rk?B$ z);cmP6QLase}ZdmQw;X;rr^S>a&IzmE~4@DIaF)wpBpPz(?S5AZuC2izXENt1y$w3 zy;OQ45~o6vM^(a;@atkK>RyabHW&ZWv$@Kxa`ZvHwt-IQ>d(#>QNi0qN9$EVfGR6J zrj4Dlj8{{4wrWr~bsTrmMs#Wd9l%&g{SoWBAk*AO$$U)4I1?WOC`p|b;p8UC%q*bs zH{~8whEvTcNBd|Tv(3#;X!hEaCZo+^gu}b*0AoXD7E1fuUx(5&09cJwpAo3V<4;hTGqso+g+vRCR2bnsq?noG!L9rmh^ zU42Bp5-x%<_2j$^dH;Z9+MzT=zY%iOJ=WA*Rj>BghO5#y(l-o6edH6b72}0&=y;(h z$L!5jjWcI8i2RFd-W*^PkIB!&DO6Ct2G)ebsfm=q6|I}XW_l=>sW zQ2hQJC186|V_k7o*J_wk#$=%!q;?{mnZ*iu=wQU7kYNF?naZM6J z>N*Y+)fgn3n>B57&=`qc#l=s^r%LSxZ3b0b`>7kT=SoSZR2^y43C?ZP?NsCdFo>DP z*S7SH$81b=;@Wq;a(;HkESxmKt3)KeaK{Z0oP(Yl@MW*Mp)FGoGMvf2uiq zEX;RRKf-K%vI&vIwaCodePTV-nrg6kf>?4Tfv|Fqar>uFC@~vH_$$bhOpysb8dCgf zQ{SQbpgCJzMX4L@Q$BNMR>l2X*UV5yXew|_=&=B)fa&DE2L$9$_v@q|Q(KoO;xMg^ zKj#0^OGRdA6nJ=U+u0h{rigW5^1ZMfb5F&nu9g(yL9B*;X z%_R3p@7U$O%!zq-&J>SZrP&R3Cb-Ldv!3@+;gy6VRiPE@w3S;f{CT!krQM$xr+K*qf~$X+47$phX*orQ#RHD3@sJ*G3}=TAW=K z;&b{=q8F6cEN6a3?fa2hftUNvcZd1V{*mnzO2WY<&wmPp?X1Fet-NNaj%0206RLT{ z2f9HrQid4aX%1G#|MN8O}} zLEi|}OZPdZP{-F$4{`nP6;36FC zqZ^AH)-eGAP2s74F|Cq*G#yOAHBth6pxonVG(oi-ivUFYNS3J!=ZXJ0@NIsSJVxu~ zJ+f-zAthdv3a;2xiCXA{fEs}UOG8v;$^jR3GP}@}=CHW;|3%v6|8RWn>BkD%D{rC> ztsckIAl66!!D(7Ov&B*}kH08?O?|y6Byb-Dd&AWNG#0ec@&9>v@c$3z4U~KQZ={p{ z$8ZkFZp|@OH2@?A?c&Nl3&h936|&44BN{>NNTVVSX;==3J_VW&CqtX}s0))f&VBPq S1uOyxq@k*NA9nZA^Zy4KVcY!x literal 0 HcmV?d00001 diff --git "a/zh-cn/device-dev/driver/figure/USB-Host\351\251\261\345\212\250\346\250\241\345\236\213\345\233\276.png" "b/zh-cn/device-dev/driver/figure/USB-Host\351\251\261\345\212\250\346\250\241\345\236\213\345\233\276.png" new file mode 100755 index 0000000000000000000000000000000000000000..a36e8829aec7c6f1c37d92766547c9b8ebb620ba GIT binary patch literal 45470 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 literal 0 HcmV?d00001 -- GitLab