Hi3516驱动开发全面详解

开发 前端
内核驱动是软件与硬件交互的桥梁,通过文件系统接口访问OpenHarmony内核的硬件资源,是用户与内核之间、进程与进程之间通信的一种方式。

[[410422]]

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

驱动框架介绍

驱动子系统

内核驱动是软件与硬件交互的桥梁,通过文件系统接口访问OpenHarmony内核的硬件资源,是用户与内核之间、进程与进程之间通信的一种方式。每类驱动代表一种能力,用户可以根据需求选择对应驱动,完成数据的传输。OpenHarmony驱动子系统采用C面向对象编程模型构建,通过平台解耦、内核解耦,兼容不同内核,提供了归一化的驱动平台底座,旨在为开发者提供更精准、更高效的开发环境,力求做到一次开发,多系统部署。

1、架构

OpenHarmony驱动框架采用主从架构设计模式,围绕着框架、模型、能力库和工具四个维度能力展开构建。

Hi3516驱动开发-鸿蒙HarmonyOS技术社区

2、目录

  1. drivers 
  2. ├── adapter               #适配平台差异性的代码 
  3. ├── framework             #驱动框架核心代码 
  4. └── peripheral            #外设驱动代码 
  5. └── liteos                #hievent和tzdrivert驱动代码。hievent:事件日志管理驱动;tzdrivert:用于ree/tee切换、通讯,提供应用层访问的设备节点 

3、驱动框架交互流程

Hi3516驱动开发-鸿蒙HarmonyOS技术社区

驱动框架完成大部分驱动加载的动作,用户只需注册自己所需的接口和配置,然后驱动框架就会解析配置的内容,完成驱动加载和初始化动作。

4、安装

OpenHarmony驱动主要部署在内核态,当前主要采用静态链接方式,随内核子系统编译和系统镜像打包。

Hi3516驱动开发-鸿蒙HarmonyOS技术社区

5、HDF驱动加载的过程

Hi3516驱动开发-鸿蒙HarmonyOS技术社区

HDF Framework

1、简介

HDF Framework提供包括驱动框架、配置管理、配置解析、驱动通用框架模型、硬件通用平台能力接口等,驱动框架的架构图:

Hi3516驱动开发-鸿蒙HarmonyOS技术社区

 2、目录

  1. /drivers/framework 
  2. ├── ability        #提供驱动开发的能力支持,如消息模型库等 
  3. │   ├── config     #配置解析代码 
  4. │   └── sbuf       #数据序列化代码 
  5. ├── core           #实现驱动框架的核心代码 
  6. │   ├── adapter    #实现对内核操作接口适配,提供抽象化的接口供开发者使用 
  7. │   ├── common     #驱动框架公共基础代码 
  8. │   ├── host       #驱动宿主环境模块 
  9. │   ├── manager    #驱动框架管理模块 
  10. │   └── shared     #host和manager共享模块代码 
  11. ├── include        #驱动框架对外提供能力的头文件 
  12. │   ├── config     #提供配置解析能力的头文件 
  13. │   ├── core       #驱动框架对外提供的头文件 
  14. │   ├── net        #网络数据操作相关的头文件 
  15. │   ├── osal       #系统适配相关接口的头文件 
  16. │   ├── platform   #平台设备相关接口的头文件 
  17. │   ├── utils      #驱动框架公共能力的头文件 
  18. │   └── wifi       #WLAN对外提供能力的头文件 
  19. ├── model          #提供驱动通用框架模型 
  20. │   ├── display    #显示框架模型 
  21. │   ├── input      #输入框架模型 
  22. │   ├── network    #WLAN框架模型 
  23. │   └── sensor     #Sensor驱动模型 
  24. ├── support        #提系统的基础能力  
  25. │   └── platform   #平台设备驱动框架及访问接口,范围包括GPIO、I2C、SPI等 
  26. ├── tools          #hdf框架工具相关的源码 
  27. │   └── hc-gen     #配置管理工具源码 
  28. └── utils          #提供基础数据结构和算法等 

HDF适配

HDF适配主要实现OpenHarmony驱动子系统内核或用户态驱动框架的代码和编译脚本,提供驱动框架的能力。

2、目录

  1. /drivers/adapter 
  2. ├── khdf/linux          #提供驱动框架对Linux内核依赖适配 
  3. ├── khdf/liteos         #提供驱动框架对LiteOS-A内核依赖适配 
  4. ├── khdf/liteos_m       #提供驱动框架对LiteOS-M内核依赖适配 
  5. ├── uhdf                #提供用户态驱动接口对系统依赖适配 
  6. └── uhdf2               #提供用户态驱动框架对系统依赖适配 

linux khdf

1、简介

drivers/adapter/khdf/linux下提供OpenHarmony驱动子系统适配linux内核的代码和编译脚本,在linux内核中部署OpenHarmony驱动框架。

2、目录

  1. /drivers/adapter/khdf/linux 
  2. ├── config               #linux内核下编译配置解析代码的编译脚本 
  3. ├── hcs                  #linux内核下HDF的配置管理目录 
  4. ├── manager              #linux内核下启动适配启动HDF框架代码 
  5. ├── model                #驱动模型适配linux代码 
  6. │   ├── display          #显示驱动模型 
  7. │   ├── input            #输入驱动模型 
  8. │   ├── network          #wifi驱动模型 
  9. │   └── sensor           #传感器驱动模型 
  10. ├── network              #适配linux内核网络代码 
  11. ├── osal                 #适配linux内核的posix接口 
  12. ├── platform             #平台设备接口适配linux内核代码 
  13. │   ├── emmc             #emmc操作接口 
  14. │   ├── gpio             #gpio接口 
  15. │   ├── i2c              #i2c接口 
  16. │   ├── mipi_dsi         #mipi dsi接口 
  17. │   ├── pwm              #pwm接口 
  18. │   ├── rtc              #rtc接口 
  19. │   ├── sdio             #sdio接口 
  20. │   ├── spi              #spi接口 
  21. │   ├── uart             #uart接口 
  22. │   └── watchdog         #watchdog接口 

peripheral

1、简介

peripheral主要包含各外设器件驱动相关的HDI(Hardware Driver Interface)接口、HAL实现、驱动模型及测试用例等,根据模块划分不同目录,具体模块的相关信息可参阅各模块子目录下的readme。

2、目录

代码目录为 /drivers/peripheral,其中包含的各子目录简介如下。

  • audio:Audio HDI接口的定义,用于管理声卡驱动的加载和卸载、创建音频播放对象3及录音对象、选择音频场景、设置音频属性及音量、控制音频播放及录音的启停等。
  • codec:Codec HDI接口的定义,这些接口对上层服务提供媒体编解码的驱动能力。
  • display:Display HDI 接口定义及其默认实现,对上层图形服务提供显示驱动能力,包括显示图层的管理、显示内存的管理及硬件图形加速。
  • format:Format HDI接口定义,此类接口对上层服务提供媒体文件复用和解复用的驱动能力。
  • input:Input HDI接口定义及其实现,对上层输入系统服务提供操作input设备的驱动能力,包括input设备管理、业务流控制、数据上报等。
  • sensor:Sensor HDI接口定义与实现,接口主要包括所有Sensor信息查询、Sensor启停、Sensor订阅/去订阅、Sensor参数配置等稳定的接口,简化服务开发。
  • wlan:WLAN HDI接口定义与实现,包括创建和销毁HAL层和WLAN驱动的通道、获取本设备支持的WLAN特性等。

HDF

1、HDF驱动开发简介

HDF框架以组件化的驱动模型作为核心设计思路,为开发者提供更精细化的驱动管理,让驱动开发和部署更加规范。HDF框架将一类设备驱动放在同一个host里面,开发者也可以将驱动功能分层独立开发和部署,支持一个驱动多个node,HDF框架管理驱动模型如下图所示:

Hi3516驱动开发-鸿蒙HarmonyOS技术社区

基于HDF(Hardware Driver Foundation驱动框架)开发驱动,用户只需注册自己所需的接口和配置,然后驱动框架就会解析配置的内容,完成驱动加载和初始化动作。

开发者基于HDF驱动框架开发的驱动主要包含三大部分:

  • 驱动程序部分----完成驱动的功能逻辑。
  • 驱动配置信息----指示驱动的加载信息内容。
  • 驱动资源配置----配置驱动的硬件配置信息。

驱动程序主要是完成驱动功能的逻辑代码:

对于开发者首先看到的是驱动入口部分,驱动入口部分通过DriverEntry进行描述。

其中主要包含Bind, Init 和Release三个接口。

  1. struct HdfDriverEntry g_deviceSample = { 
  2.     .moduleVersion = 1, 
  3.     .moduleName = "sample_driver",  
  4.     .Bind = SampleDriverBind, 
  5.     .Init = SampleDriverInit, 
  6.     .Release = SampleDriverRelease, 
  7. }; 

Bind接口描述:该接口的作用主要是完成驱动设备和设备服务接口的bind动作。

  1. int32_t SampleDriverBind(struct HdfDeviceObject *deviceObject) 
  2.     // TODO: Bind device service to device object. 
  3.     // And you can also initialize device resources here. 
  4.     return HDF_SUCCESS; 

Init接口描述:当框架完成设备绑定动作后,就开始调用驱动初始化接口,当初始化成功后,驱动框架根据配置文件决定是否对外创建设备服务接口,还是只是对当前服务接口可见。如果Init初始化失败的话,驱动框架就会主动释放创建的设备接口等信息。

  1. int32_t SampleDriverInit(struct HdfDeviceObject *deviceObject) 
  2.     // TODO: Init hardware or other resources here. 
  3.     return HDF_SUCCESS; 

Release接口描述:当用户需要卸载驱动时,驱动框架先通过该接口通知驱动程序释放资源。然后在执行其他内部资源释放。

  1. void SampleDriverRelease(struct HdfDeviceObject *deviceObject) 
  2.     // Release all resources. 
  3.     return

2、驱动加载

HDF驱动加载包括按需加载和按序加载。

  • 按需加载

HDF框架支持驱动在系统启动过程中默认加载,或者在系统启动之后动态加载。

  1. typedef enum {    DEVICE_PRELOAD_ENABLE = 0,    DEVICE_PRELOAD_ENABLE_STEP2,    DEVICE_PRELOAD_DISABLE,    DEVICE_PRELOAD_INVALID} DevicePreload; 
  2. 1. 
  3. 1. 
  4. 1. 

复制配置文件中preload 字段配成 0 (DEVICE_PRELOAD_ENABLE ),则系统启动过程中默认加载;配成1(DEVICE_PRELOAD_ENABLE_STEP2),当系统支持快启的时候,则在系统系统完成之后再加载这一类驱动,否则和DEVICE_PRELOAD_ENABLE 含义相同;

配成2(DEVICE_PRELOAD_DISABLE),则系统启动过程中默认不加载,支持后续动态加载,当用户态获取驱动服务(参考消息机制)时,如果驱动服务不存在时,HDF框架会尝试动态加载该驱动。

  • 按序加载

HDF框架支持驱动在系统启动的过程中按照驱动的优先级进行加载。

配置文件中的priority(取值范围为整数0到200)是用来表示host和驱动的优先级,不同的host内的驱动,host的priority值越小,驱动加载优先级越高;同一个host内驱动的priority值越小,加载优先级越高。

3、驱动服务管理

HDF框架可以集中管理驱动服务,开发者可直接通过HDF框架对外提供的能力接口获取驱动相关的服务。

4、驱动消息机制

HDF框架提供统一的驱动消息机制,支持用户态应用向内核态驱动发送消息,也支持内核态驱动向用户态应用发送消息。

驱动开发步骤

驱动实现

驱动实现包含驱动业务代码和驱动入口注册,具体写法如下:

驱动业务代码

  1. #include "hdf_device_desc.h"  // HDF框架对驱动开放相关能力接口的头文件#include "hdf_log.h"          // HDF 框架提供的日志接口头文件#define HDF_LOG_TAG "sample_driver"   // 打印日志所包含的标签,如果不定义则用默认定义的HDF_TAG标签//驱动对外提供的服务能力,将相关的服务接口绑定到HDF框架int32_t HdfSampleDriverBind(struct HdfDeviceObject *deviceObject){    HDF_LOGD("Sample driver bind success");    return 0;}// 驱动自身业务初始的接口int32_t HdfSampleDriverInit(struct HdfDeviceObject *deviceObject){    HDF_LOGD("Sample driver Init success");    return 0;}// 驱动资源释放的接口void HdfSampleDriverRelease(struct HdfDeviceObject *deviceObject){    HDF_LOGD("Sample driver release success");    return;} 

驱动入口注册到HDF框架

  1. // 定义驱动入口的对象,必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量struct HdfDriverEntry g_sampleDriverEntry = {    .moduleVersion = 1,    .moduleName = "sample_driver",    .Bind = HdfSampleDriverBind,    .Init = HdfSampleDriverInit,    .Release = HdfSampleDriverRelease,};// 调用HDF_INIT将驱动入口注册到HDF框架中,在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动,当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。HDF_INIT(g_sampleDriverEntry); 

驱动编译

驱动代码的编译必须要使用HDF框架提供的Makefile模板进行编译。

  1. include $(LITEOSTOPDIR)/../../drivers/adapter/lite/khdf/lite.mk #导入hdf预定义内容,必需MODULE_NAME :=    #生成的结果文件LOCAL_INCLUDE :=  #本驱动的头文件目录LOCAL_SRCS :=     #本驱动的源代码文件LOCAL_CFLAGS :=  #自定义的编译选项include $(HDF_DRIVER) #导入模板makefile完成编译 

编译结果文件链接到内核镜像,添加到device目录下的lite.mk里面,示例如下:

  1. LITEOS_BASELIB +=  -lxxx  #链接生成的静态库LIB_SUBDIRS    +=         #驱动代码Makefile的目录 

驱动配置

HDF使用HCS作为配置描述源码,HCS详细介绍参考配置管理介绍。

驱动配置包含两部分,HDF框架定义的驱动设备描述和驱动的私有配置信息,具体写法如下:

  • 驱动设备描述(必选)

HDF框架加载驱动所需要的信息来源于HDF框架定义的驱动设备描述,因此基于HDF框架开发的驱动必须要在HDF框架定义的device_info.hcs配置文件中添加对应的设备描述,驱动的设备描述填写如下所示:

  1. root {    device_info {        match_attr = "hdf_manager";        template host {       // host模板,继承该模板的节点(如下sample_host)如果使用模板中的默认值,则节点字段可以缺省            hostName = "";            priority = 100;            template device {                template deviceNode {                    policy = 0;                    priority = 100;                    preload = 0;                    permission = 0664;                    moduleName = "";                    serviceName = "";                    deviceMatchAttr = "";                }            }        }        sample_host :: host{            hostName = "host0";    // host名称,host节点是用来存放某一类驱动的容器            priority = 100;        // host启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证host的加载顺序            device_sample :: device {        // sample设备节点                device0 :: deviceNode {      // sample驱动的DeviceNode节点                    policy = 1;              // policy字段是驱动服务发布的策略,在驱动服务管理章节有详细介绍                    priority = 100;          // 驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序                    preload = 0;             // 驱动按需加载字段,在本章节最后的说明有详细介绍                    permission = 0664;       // 驱动创建设备节点权限                    moduleName = "sample_driver";   // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致                    serviceName = "sample_service";    // 驱动对外发布服务的名称,必须唯一                    deviceMatchAttr = "sample_config"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等                }            }        }    }} 
  • 驱动私有配置信息(可选)

如果驱动有私有配置,则可以添加一个驱动的配置文件,用来填写一些驱动的默认配置信息,HDF框架在加载驱动的时候,会将对应的配置信息获取并保存在HdfDeviceObject 中的property里面,通过Bind和Init(参考驱动开发)传递给驱动,驱动的配置信息示例如下:

  1. root {    SampleDriverConfig {        sample_version = 1;        sample_bus = "I2C_0";        match_attr = "sample_config";   //该字段的值必须和device_info.hcs中的deviceMatchAttr值一致    }} 

 配置信息定义之后,需要将该配置文件添加到板级配置入口文件hdf.hcs(这一块可以通过OpenHarmony驱动子系统在DevEco集成驱动开发套件工具一键式配置,具体使用方法参考驱动开发套件中的介绍),示例如下:

  1. #include "device_info/device_info.hcs"#include "sample/sample_config.hcs" 

驱动开发示例

下面基于HDF框架,提供一个简单的UART(Universal Asynchronous Receiver/Transmitter)平台驱动开发样例,包含配置文件的添加,驱动代码的实现以及用户态程序和驱动交互的流程。驱动程序源码位于vendor/huawei/hdf/sample目录

添加配置

HCS(HDF Configuration Source)是HDF驱动框架的配置描述源码,内容以Key-Value为主要形式。它实现了配置代码与驱动代码解耦,便于开发者进行配置管理。

HC-GEN**(HDF Configuration Generator)**是HCS配置转换工具,可以将HDF配置文件转换为软件可读取的文件格式:

在弱性能环境中,转换为配置树源码,驱动可直接调用C代码获取配置。

在高性能环境中,转换为HCB(HDF Configuration Binary)二进制文件,驱动可使用HDF框架提供的配置解析接口获取配置。

设备配置文件和驱动配置文件的路径定义在板级配置入口文件hdf.hcs(vendor\hisilicon\hispark_taurus\config\hdf.hcs)

  1. #include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/i2c/i2c_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/gpio/gpio_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/uart/uart_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/sdio/sdio_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/emmc/emmc_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/watchdog/watchdog_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/usb/usb_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/rtc/rtc_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/spi/spi_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/pwm/pwm_config.hcs"#include "../../../../device/hisilicon/hispark_taurus/sdk_liteos/config/dmac/dmac_config.hcs"#include "device_info/device_info.hcs"#include "wifi/wlan_platform.hcs"#include "wifi/wlan_chip_hi3881.hcs"#include "lcd/lcd_config.hcs"#include "input/input_config.hcs"#include "sensor/sensor_config.hcs"root {    module = "hisilicon,hi35xx_chip";} 

 1、在HDF框架的驱动配置文件(例如device\hisilicon\hispark_taurus\sdk_liteos\config\uart\uart_config.hcs)中添加该驱动的配置信息,如下所示:

  1. root {    platform {        uart_sample {            num = 5;            base = 0x120a0000;  // UART base register address            irqNum = 38;            baudrate = 115200;            uartClk = 24000000; // 24 M            wlen = 0x60;        // 8 bit width            parity = 0;            stopBit = 0;            match_attr = "sample_uart_5";        }    }} 

 2、在HDF框架的设备配置文件(例如vendor\hisilicon\hispark_taurus\config\device_info\device_info.hcs)中添加该驱动的设备节点信息,如下所示:

  1. root {    device_info {        platform :: host {            hostName = "platform_host";            priority = 50;            device_uart :: device {                device5 :: deviceNode {                    policy = 2;                    priority = 10;                    permission = 0660;                    moduleName = "UART_SAMPLE";                      serviceName = "HDF_PLATFORM_UART_5";                    deviceMatchAttr = "sample_uart_5";                }            }        }    }} 

注册uart驱动入口

基于HDF框架注册UART驱动的入口HdfDriverEntry,代码如下:

  1. // 绑定UART驱动接口到HDF框架static int32_t SampleUartDriverBind(struct HdfDeviceObject *device){    struct UartHost *uartHost = NULL;    if (device == NULL) {        return HDF_ERR_INVALID_OBJECT;    }    HDF_LOGI("Enter %s:", __func__);    uartHost = UartHostCreate(device);    if (uartHost == NULL) {        HDF_LOGE("%s: UartHostCreate failed", __func__);        return HDF_FAILURE;    }    uartHost->service.Dispatch = SampleDispatch;    return HDF_SUCCESS;} // 从UART驱动的HCS中获取配置信息static uint32_t GetUartDeviceResource(    struct UartDevice *device, const struct DeviceResourceNode *resourceNode){    struct UartResource *resource = &device->resource;    struct DeviceResourceIface *dri = NULL;    dri = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);    if (dri == NULL || dri->GetUint32 == NULL) {        HDF_LOGE("DeviceResourceIface is invalid");        return HDF_FAILURE;    }    if (dri->GetUint32(resourceNode, "num", &resource->num, 0) != HDF_SUCCESS) {        HDF_LOGE("uart config read num fail");        return HDF_FAILURE;    }    if (dri->GetUint32(resourceNode, "base", &resource->base, 0) != HDF_SUCCESS) {        HDF_LOGE("uart config read base fail");        return HDF_FAILURE;    }    resource->physBase = (unsigned long)OsalIoRemap(resource->base, 0x48);    if (resource->physBase == 0) {        HDF_LOGE("uart config fail to remap physBase");        return HDF_FAILURE;    }    if (dri->GetUint32(resourceNode, "irqNum", &resource->irqNum, 0) != HDF_SUCCESS) {        HDF_LOGE("uart config read irqNum fail");        return HDF_FAILURE;    }    if (dri->GetUint32(resourceNode, "baudrate", &resource->baudrate, 0) != HDF_SUCCESS) {        HDF_LOGE("uart config read baudrate fail");        return HDF_FAILURE;    }    if (dri->GetUint32(resourceNode, "wlen", &resource->wlen, 0) != HDF_SUCCESS) {        HDF_LOGE("uart config read wlen fail");        return HDF_FAILURE;    }    if (dri->GetUint32(resourceNode, "parity", &resource->parity, 0) != HDF_SUCCESS) {        HDF_LOGE("uart config read parity fail");        return HDF_FAILURE;    }    if (dri->GetUint32(resourceNode, "stopBit", &resource->stopBit, 0) != HDF_SUCCESS) {        HDF_LOGE("uart config read stopBit fail");        return HDF_FAILURE;    }    if (dri->GetUint32(resourceNode, "uartClk", &resource->uartClk, 0) != HDF_SUCCESS) {        HDF_LOGE("uart config read uartClk fail");        return HDF_FAILURE;    }    return HDF_SUCCESS;} // 将UART驱动的配置和接口附加到HDF驱动框架static int32_t AttachUartDevice(struct UartHost *host, struct HdfDeviceObject *device){    int32_t ret;    struct UartDevice *uartDevice = NULL;    if (device->property == NULL) {        HDF_LOGE("%s: property is NULL", __func__);        return HDF_FAILURE;    }    uartDevice = (struct UartDevice *)OsalMemCalloc(sizeof(struct UartDevice));    if (uartDevice == NULL) {        HDF_LOGE("%s: OsalMemCalloc uartDevice error", __func__);        return HDF_ERR_MALLOC_FAIL;    }    ret = GetUartDeviceResource(uartDevice, device->property);    if (ret != HDF_SUCCESS) {        (void)OsalMemFree(uartDevice);        return HDF_FAILURE;    }    host->num = uartDevice->resource.num;    host->priv = uartDevice;    AddUartDevice(host);    return InitUartDevice(uartDevice);} // 初始化UART驱动static int32_t SampleUartDriverInit(struct HdfDeviceObject *device){    int32_t ret;    struct UartHost *host = NULL;    if (device == NULL) {        HDF_LOGE("%s: device is NULL", __func__);        return HDF_ERR_INVALID_OBJECT;    }    HDF_LOGI("Enter %s:", __func__);    host = UartHostFromDevice(device);    if (host == NULL) {        HDF_LOGE("%s: host is NULL", __func__);        return HDF_FAILURE;    }    ret = AttachUartDevice(host, device);    if (ret != HDF_SUCCESS) {        HDF_LOGE("%s: attach error", __func__);        return HDF_FAILURE;    }    host->method = &g_sampleUartHostMethod;    return ret;} static void DeinitUartDevice(struct UartDevice *device){    struct UartRegisterMap *regMap = (struct UartRegisterMap *)device->resource.physBase;    /* wait for uart enter idle. */    while (UartPl011IsBusy(regMap));    UartPl011ResetRegisters(regMap);    uart_clk_cfg(0, false);    OsalIoUnmap((void *)device->resource.physBase);    device->state = UART_DEVICE_UNINITIALIZED;} // 解绑并释放UART驱动static void DetachUartDevice(struct UartHost *host){    struct UartDevice *uartDevice = NULL;    if (host->priv == NULL) {        HDF_LOGE("%s: invalid parameter", __func__);        return;    }    uartDevice = host->priv;    DeinitUartDevice(uartDevice);    (void)OsalMemFree(uartDevice);    host->priv = NULL;} // 释放UART驱动static void SampleUartDriverRelease(struct HdfDeviceObject *device){    struct UartHost *host = NULL;    HDF_LOGI("Enter %s:", __func__);    if (device == NULL) {        HDF_LOGE("%s: device is NULL", __func__);        return;    }    host = UartHostFromDevice(device);    if (host == NULL) {        HDF_LOGE("%s: host is NULL", __func__);        return;    }    if (host->priv != NULL) {        DetachUartDevice(host);    }    UartHostDestroy(host);} struct HdfDriverEntry g_sampleUartDriverEntry = {    .moduleVersion = 1,    .moduleName = "UART_SAMPLE",    .Bind = SampleUartDriverBind,    .Init = SampleUartDriverInit,    .Release = SampleUartDriverRelease,}; HDF_INIT(g_sampleUartDriverEntry); 

注册uart驱动接口

HDF框架提供了UART驱动接口的模板方法UartHostMethod,实现UART驱动接口的代码如下:

  1. static int32_t SampleUartHostInit(struct UartHost *host){    HDF_LOGI("%s: Enter", __func__);    if (host == NULL) {        HDF_LOGE("%s: invalid parameter", __func__);        return HDF_ERR_INVALID_PARAM;    }    return HDF_SUCCESS;}static int32_t SampleUartHostDeinit(struct UartHost *host){    HDF_LOGI("%s: Enter", __func__);    if (host == NULL) {        HDF_LOGE("%s: invalid parameter", __func__);        return HDF_ERR_INVALID_PARAM;    }    return HDF_SUCCESS;}// 向UART中写入数据static int32_t SampleUartHostWrite(struct UartHost *host, uint8_t *data, uint32_t size){    HDF_LOGI("%s: Enter", __func__);    uint32_t idx;    struct UartRegisterMap *regMap = NULL;    struct UartDevice *device = NULL;    if (host == NULL || data == NULL || size == 0) {        HDF_LOGE("%s: invalid parameter", __func__);        return HDF_ERR_INVALID_PARAM;    }    device = (struct UartDevice *)host->priv;    if (device == NULL) {        HDF_LOGE("%s: device is NULL", __func__);        return HDF_ERR_INVALID_PARAM;    }    regMap = (struct UartRegisterMap *)device->resource.physBase;    for (idx = 0; idx < size; idx++) {        UartPl011Write(regMap, data[idx]);    }    return HDF_SUCCESS;} // 设置UART的波特率static int32_t SampleUartHostSetBaud(struct UartHost *host, uint32_t baudRate){    HDF_LOGI("%s: Enter", __func__);    struct UartDevice *device = NULL;    struct UartRegisterMap *regMap = NULL;    UartPl011Error err;    if (host == NULL) {        HDF_LOGE("%s: invalid parameter", __func__);        return HDF_ERR_INVALID_PARAM;    }    device = (struct UartDevice *)host->priv;    if (device == NULL) {        HDF_LOGE("%s: device is NULL", __func__);        return HDF_ERR_INVALID_PARAM;    }    regMap = (struct UartRegisterMap *)device->resource.physBase;    if (device->state != UART_DEVICE_INITIALIZED) {        return UART_PL011_ERR_NOT_INIT;    }    if (baudRate == 0) {        return UART_PL011_ERR_INVALID_BAUD;    }    err = UartPl011SetBaudrate(regMap, device->uartClk, baudRate);    if (err == UART_PL011_ERR_NONE) {        device->baudrate = baudRate;    }    return err;} // 获取UART的波特率static int32_t SampleUartHostGetBaud(struct UartHost *host, uint32_t *baudRate){    HDF_LOGI("%s: Enter", __func__);    struct UartDevice *device = NULL;    if (host == NULL) {        HDF_LOGE("%s: invalid parameter", __func__);        return HDF_ERR_INVALID_PARAM;    }    device = (struct UartDevice *)host->priv;    if (device == NULL) {        HDF_LOGE("%s: device is NULL", __func__);        return HDF_ERR_INVALID_PARAM;    }    *baudRate = device->baudrate;    return HDF_SUCCESS;} // 在HdfUartSampleInit方法中绑定struct UartHostMethod g_sampleUartHostMethod = {    .Init = SampleUartHostInit,    .Deinit = SampleUartHostDeinit,    .Read = NULL,    .Write = SampleUartHostWrite,    .SetBaud = SampleUartHostSetBaud,    .GetBaud = SampleUartHostGetBaud,    .SetAttribute = NULL,    .GetAttribute = NULL,    .SetTransMode = NULL,}; 

 uart驱动Makefile如下:

  1. include $(LITEOSTOPDIR)/config.mkinclude $(LITEOSTOPDIR)/../../drivers/adapter/khdf/liteos/lite.mkMODULE_NAME := hdf_uart_sampleLOCAL_CFLAGS += $(HDF_INCLUDE)LOCAL_SRCS += src/uart_sample.c \              src/uart_pl011_sample.c \              src/buf_fifo.c \              src/uart_dev_sample.c \              src/uart_dispatch_sample.c \LOCAL_INCLUDE := ./includeLOCAL_CFLAGS += -fstack-protector-stronginclude $(HDF_DRIVER) 

 在device/hisilicon/drivers/lite.mk编译脚本中增加示例UART驱动模块,代码如下: 

  1. LITEOS_BASELIB += -lhdf_uart_sampleLIB_SUBDIRS += $(LITEOS_SOURCE_ROOT)/vendor/huawei/hdf/sample/platform/uart 

用户程序和驱动交互代码

UART驱动成功初始化后,会创建/dev/uartdev-5设备节点,通过设备节点与UART驱动交互的代码如下:

  1. #include #include #include #include "hdf_log.h"#define HDF_LOG_TAG "hello_uart"#define INFO_SIZE 16int main(void){ int ret; int fd; const char info[INFO_SIZE] = {" HELLO UART! "}; fd = open("/dev/uartdev-5", O_RDWR); if (fd < 0) { HDF_LOGE("hello_uart uartdev-5 open failed %d", fd); return -1; } ret = write(fd, info, INFO_SIZE); if (ret != 0) { HDF_LOGE("hello_uart write uartdev-5 ret is %d", ret); } ret = close(fd); if (ret != 0) { HDF_LOGE("hello_uart uartdev-5 close failed %d", fd); return -1; } return ret;} 

将应用程序编译进hello_uart_sample组件,应用的编译文件:

(1)vendor/huawei/hdf/sample/platform/uart/BUILD.gn

  1. import("//build/lite/config/component/lite_component.gni")lite_component("hello_uart_sample") { features = [ "dev:hello_uart""dispatch:hello_uart_dispatch", ]} 

(2)vendor/huawei/hdf/sample/platform/uart/dev/BUILD.gn:

  1. HDF_FRAMEWORKS = "//drivers/framework"executable("hello_uart") { sources = [ "hello_uart_dev.c" ] include_dirs = [ "$HDF_FRAMEWORKS/ability/sbuf/include""$HDF_FRAMEWORKS/core/shared/include""$HDF_FRAMEWORKS/core/host/include""$HDF_FRAMEWORKS/core/master/include""$HDF_FRAMEWORKS/include/core""$HDF_FRAMEWORKS/include/utils""$HDF_FRAMEWORKS/utils/include""$HDF_FRAMEWORKS/include/osal""//drivers/adapter/uhdf/posix/include""//third_party/bounds_checking_function/include""//base/hiviewdfx/hilog_lite/interfaces/native/innerkits", ] deps = [ "//base/hiviewdfx/hilog_lite/frameworks/featured:hilog_shared""//drivers/adapter/uhdf/manager:hdf_core""//drivers/adapter/uhdf/posix:hdf_posix_osal", ] public_deps = [ "//third_party/bounds_checking_function:libsec_shared" ] defines = [ "__USER__" ] cflags = [ "-Wall""-Wextra""-Wno-format""-Wno-format-extra-args", ]} 

在build/lite/components/drivers.json驱动配置中hdf_hi3516dv300_liteos_a组件下的targets中增加hello_uart_sample组件,代码如下:

  1. "components": [ { "component""hdf_hi3516dv300_liteos_a""description""""optional""false""dirs": ["vendor/huawei/hdf/sample/platform/uart"], "targets": [ "//vendor/huawei/hdf/sample/platform/uart:hello_uart_sample" ], "rom""""ram""""output": [], "adapted_board": [], "adapted_kernel": [ "liteos_a" ], "features": [], "deps": { "third_party": [ "bounds_checking_function" ], "components": [ "hilog" ] } } ]} 

修改单板配置文件(vendor/hisilicon/hispark_taurus/config.json),新增hdf_hi3516dv300_liteos_a组件的条目,如下所示代码片段为driver子系统配置

  1. "subsystem""drivers""components": [ { "component""adapter_uhdf""features":[] }, { "component""peripheral_display""features":[] }, { "component""hdf_hi3516dv300_liteos_a""features":[] }, { "component""peripheral_input""features":[] }, { "component""peripheral_sensor""features":[] }, { "component""peripheral_wlan""features":[] } ] }, 

编译

如果Linux编译环境通过Docker方式安装,具体编译过程请参见Docker方式获取编译环境的编译操作。如果Linux编译环境通过软件包方式安装,请进入源码根目录,执行如下命令进行编译:

  1. hb set(设置编译路径).(选择当前路径)选择ipcamera_hispark_taurus并回车hb build -f(执行编译) 

运行

烧录成功之后,可执行文件在bin目录下(/bin/hello_uart),运行:

Hi3516驱动开发-鸿蒙HarmonyOS技术社区

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2021-05-25 14:47:43

鸿蒙HarmonyOS应用

2021-03-29 15:36:46

鸿蒙HarmonyOS应用

2021-04-09 09:45:21

鸿蒙HarmonyOS应用

2021-03-16 09:49:16

鸿蒙HarmonyOS应用

2021-11-09 15:28:41

鸿蒙HarmonyOS应用

2021-06-25 09:28:46

鸿蒙HarmonyOS应用

2022-04-15 14:45:49

Hi3516系统类型烧录鸿蒙

2021-09-24 10:20:42

鸿蒙HarmonyOS应用

2021-12-03 09:50:39

鸿蒙HarmonyOS应用

2022-02-16 16:01:02

Hi3516开发板鸿蒙

2021-07-21 09:58:50

鸿蒙HarmonyOS应用

2021-03-02 14:30:20

鸿蒙HarmonyOS应用

2021-10-09 10:12:39

鸿蒙HarmonyOS应用

2021-07-19 15:34:05

鸿蒙HarmonyOS应用

2022-08-10 15:58:54

LED灯鸿蒙

2021-08-06 15:09:22

鸿蒙HarmonyOS应用

2021-07-07 09:45:20

鸿蒙HarmonyOS应用

2022-03-14 15:26:59

Hi3516Ark子系统鸿蒙

2021-07-08 16:16:59

鸿蒙HarmonyOS应用

2021-07-05 09:35:36

鸿蒙HarmonyOS应用
点赞
收藏

51CTO技术栈公众号