栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

OpenHarmonyOs / LiteOs-a 驱动开发

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

OpenHarmonyOs / LiteOs-a 驱动开发

相关文章

上一篇:OpenHarmonyOs / LiteOs-a 开发环境搭建
下一篇:OpenHarmonyOs / LiteOs-a 应用开发

文章目录
  • 相关文章
  • 前言
  • 一、LiteOs-a 内核框架
  • 二、HDF(Hardware Driver Foundation)驱动框架
          • 1. 驱动模型
          • 2. HDF驱动结构
          • 3. 编译规则
  • 三、例子
          • 1. 驱动实现
          • 2. 添加配置文件
          • 3. 添加编译规则文件
  • 四、测试
          • 1. 测试方法和结果
          • 2. 测试代码
  • 总结
  • 参考文章
  • 相关文章


前言

本文瞎逼介绍一下在 Ubuntu 20.04 系统进行 OpenHarmonyOs / LiteOs-a 驱动开发。内容有很多搬官方文档。
开发板用的是润和的 ipcamera_hispark_taurus,芯片是 HI3516DV300。

一、LiteOs-a 内核框架

主要可以分成三大块:Host、Manager、Support。
就是内核框架图面积最大的三块:

Host 是管理同类设备的驱动的框架,为同一类设备提供统一的构建设备 Node,绑定驱动,监听设备电源状态信息,发布驱动服务,订阅驱动服务等的功能。用户空间或者内核空间需要获取驱动的服务时,可以调用 Host 的接口,然后 Host 再从 Manager 的服务列表中获取驱动服务。

Manager 是内核的管理者,负责内核内大小事务管理。比如 Host 管理,驱动发布的服务管理(Device Service Manage),设备的电源管理(Power Manager),以及其他。

Support 提供内核的基础功能。Host 和 Manager 都可以用 Support 里的 Platform 和 OSAL (Operating System Abstraction Layer) 提供的驱动接口和基础功能,如 GPIO 控制,I2C 读写,Mutex,Thread 等。而 Platform 则直接跟底层硬件打交道。例如,触摸屏驱动复位控制芯片的时候需要拉低 reset GPIO,则需要调 Platform 里的 GPIO 接口;读取触摸屏坐标的时候需要调用 Platform 里的 I2C 接口。再如驱动中需要进行多线程开发或和线程同步机制,则需要用到 OSAL 中的 Thread 和 Mutex。

各个模块在代码中的位置:

host      //drivers/framework/core/host
manager   //drivers/framework/core/manager
          //drivers/adapter/khdf/liteos/manager
osal      //drivers/framework/support/posix
          //drivers/adapter/khdf/liteos/osal
platform  //drivers/framework/support/platform
          //drivers/adapter/khdf/liteos/platform
二、HDF(Hardware Driver Foundation)驱动框架

HDF(Hardware Driver Foundation)驱动框架为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理和驱动消息机制。

HDF 驱动加载包括按需加载和按序加载。按需加载:HDF 框架支持驱动在系统启动过程中默认加载,或者在系统启动之后动态加载。按序加载:HDF 框架支持驱动在系统启动的过程中按照驱动的优先级进行加载。

HDF 框架可以集中管理驱动服务,开发者可直接通过 HDF 框架对外提供的接口获取驱动相关的服务;并提供统一的驱动消息机制,支持用户态应用向内核态驱动发送消息,也支持内核态驱动向用户态应用发送消息。

1. 驱动模型

HDF 框架将一类设备驱动放在同一个 Host 里面,开发者也可以将驱动功能分层独立开发和部署,支持一个驱动多个 Node,每个 Node 对应一个物理设备。有看到文章说每个 Host 为一个进程,但是没有跟过代码,不能确定。驱动模型如下图所示:

2. HDF驱动结构

前两节内容大部分来自官方文档。
1) 主要三个函数和一个 Entry
Bind(),Init(),Release() 分别实现绑定驱动服务,初始化,释放资源的功能。

#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 框架,实际上是把 g_sampleDriverEntry 内存某个区域中。
加载驱动的时候从这块区域中把各个 Entry 取出来,先跑各驱动的 Bind(),然后 Init(),驱动加载出了问题会跑 Release()。

// 定义驱动入口的对象,必须为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);

2) 配置文件
HDF 用 .hcs 文件作为描述设备的参数表。这个跟 Android / Linux 的设备树的概念差不多。语法参考:这里。
包含两部分,HDF框架定义的驱动设备描述和驱动的私有配置信息。
驱动设备描述:

root {
    device_info {
        match_attr = "hdf_manager";
        template host {     // host 模板,继承这个模板的节点(比如下面的 sample_host)如果底下属性使用默认值,则节点字段可缺省
            hostName = "";
            priority = 100;
            uid = "";       // 用户态进程 uid,缺省为空,会被配置为 hostName 的定义值,即普通用户
            gid = "";       // 用户态进程 gid,缺省为空,会被配置为 hostName 的定义值,即普通用户组
            caps = [""];    // 用户态进程 Linux capabilities 配置,缺省为空,需要业务模块按照业务需要进行配置
            template device {
                template deviceNode {
                    policy = 0;
                    priority = 100;
                    preload = 0;
                    permission = 0664;
                    moduleName = "";
                    serviceName = "";
                    deviceMatchAttr = "";
                }
            }
        }
        sample_host :: host{
            hostName = "host0";              // host 名称
            priority = 100;                  // host 启动优先级(0-200),值越大优先级越低,建议默认配100,优先级同则不保证加载顺序
            caps = ["DAC_OVERRIDE", "DAC_READ_SEARCH"];   // 用户态进程 Linux capabilities 配置
            device_sample :: device {        // sample 设备节点
                device0 :: deviceNode {      // sample 驱动的 DeviceNode 节点
                    policy = 1;              // 驱动服务发布的策略
                    priority = 100;          // 驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级同则不保证加载顺序
                    preload = 0;             // 驱动按需加载字段
                    permission = 0664;       // 驱动创建设备节点权限
                    moduleName = "sample_driver";         // 驱动名称,该字段的值必须和驱动入口结构的 moduleName 值一致
                    serviceName = "sample_service";       // 驱动对外发布服务的名称,须唯一
                    deviceMatchAttr = "sample_config";    // 驱动私有数据匹配的关键字,须和驱动私有数据配置表中的 match_attr 值相等
                }
            }
        }
    }
}

说明:
a)uid、gid、caps 等配置项是用户态驱动的启动配置,内核态不用配置。本文介绍的是内核态驱动,暂时先不讨论这几个参数。

b)policy: 驱动发布服务的策略。0,驱动不提供服务;1,驱动对内核态发布服务; 2,对内核态和用户态都发布服务; 3,驱动服务不对外发布服务,但可以被订阅; 4,驱动私有服务不对外发布服务,也不能被订阅。

c)preload:加载选项。0,系统启动过程中默认加载。1,当系统支持快启的时候,则在系统完成之后再加载这一类驱动,否则和0相同。2,默认不加载,支持后续动态加载;当用户态获取驱动服务(参考消息机制)时,如果驱动服务不存在,HDF 框架会尝试动态加载该驱动。

d)priority:加载的优先级。范围0-200,值越大优先级越低。

驱动私有配置可放在驱动的配置 hcs,HDF 框架在加载驱动时,会获取对应配置信息并保存在 HdfDeviceObject 中的 property 里面,通过 Bind() 和 Init() 传递给驱动。驱动的配置信息:

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

配置信息定义之后,需要将该配置文件包含到板级配置入口文件 hdf.hcs。

#include "sample/sample_config.hcs"

做了个驱动模型对应配置文件的图:

host node 内是同一类设备
device 内的设备用同一个驱动
device node 对应一个硬件(同一个 moduleName 加载同一个驱动)

3)获取私有配置信息
以下面的配置为例

root {                                                                                                                                   
    SampleDriverConfig {
        boardConfig {
            match_attr = "Sample_config";
            SampleVal1 = 50; 
            SampleNode {
                SampleVal2 = true;
            }
        }
    }   
}

首先包含头文件

#include "utils/device_resource_if.h"

驱动被加载后, Init() 函数的参数 struct HdfDeviceObject *device 的指针成员 property 指向了驱动私有配置。

int32_t InitSampleDriver(struct HdfDeviceObject *device)
{
   uint32_t sampleVal1;
   bool sampleVal2;
   struct DeviceResourceNode *node = device->property;
   struct DeviceResourceIface *parser = NULL;
   const struct DeviceResourceNode *sampleNode = NULL;
   int ret = 0;
   ...
   parser = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); // 获取一个parser
   ...
   ret = parser->GetUint32(node, "sampleVal1", &sampleVal1, 0); // 解析boardConfig节点内uint32类型的参数SampleVal1
   ...
   sampleNode = parser->GetChildNode(node, "sampleNode"); // 获取子节点SampleNode
   ...
   sampleVal2 = parser->GetBool(sampleNode, "sampleVal2"); // 解析子节点SampleNode内bool类型的参数SampleVal2
   ...
}

其他类型的参数解析可以参考

//drivers/framework/include/utils/device_resource_if.h

4)获取 Support 模块的功能
a)获取 Platform 接口
以 GPIO 为例,需要包含头文件

#include "gpio_if.h"

然后直接调接口就可以了

int ret = GpioSetDir(20, GPIO_DIR_OUT);
if (ret) {
    HDF_LOGE("%s: gpio%d setting output failed", __func__, 20);                                                              
    return HDF_FAILURE;
}
if (GpioWrite(20, GPIO_VAL_HIGH) != HDF_SUCCESS) {
    HDF_LOGE("%s: pull gpio%d to %d level failed", __func__, 41, GPIO_VAL_HIGH);
    return HDF_FAILURE;
} 

在路径

//drivers/framework/include/platform/

下运行命令

find -name "*if.h"
./sdio_if.h
./mmc_if.h
./emmc_if.h
./i2c_if.h
./spi_if.h
./gpio_if.h
./rtc_if.h
./i2s_if.h
./mipi_dsi_if.h
./timer_if.h
./uart_if.h
./platform_if.h
./pwm_if.h
./regulator_if.h
./pin_if.h
./pcie_if.h
./adc_if.h
./i3c_if.h
./watchdog_if.h
./mipi_csi_if.h
./dac_if.h
./hdmi_if.h

可以找到提供接口的 Platform 模块

b)获取 OSAL 接口
以 Mutex 为例,添加头文件

#include "osal_mutex.h"

然后调用接口

(void)OsalMutexLock(&drvData->mutex);
data = 1;
(void)OsalMutexUnlock(&drvData->mutex);

在路径

//drivers/framework/include/platform/

下运行命令

find -name "osal*.h"
./osal.h
./osal/osal_firmware.h
./osal/osal_atomic.h
./osal/osal_irq.h
./osal/osal_timer.h
./osal/osal_cdev.h
./osal/osal_mutex.h
./osal/osal_io.h
./osal/osal_mem.h
./osal/osal_thread.h
./osal/osal_sem.h
./osal/osal_spinlock.h
./osal/osal_file.h
./osal/osal_time.h

可以找到提供接口的 OSAL 模块

5)发布服务
在 hcs 文件配置好适当的驱动服务发布策略,驱动里可以对外发布服务。
驱动服务结构体:

struct ISampleDriverService {
    struct IDeviceIoService ioService;       // 服务结构的首个成员必须是IDeviceIoService类型的成员
    int32_t (*ServiceA)(void);               // 驱动的第一个服务接口
    int32_t (*ServiceB)(uint32_t inputCode); // 驱动的第二个服务接口,有多个可以依次往下累加
};

驱动服务接口的实现

int32_t SampleDriverServiceA(void)
{
    // 驱动开发者实现业务逻辑
    return 0;
}

int32_t SampleDriverServiceB(uint32_t inputCode)
{
    // 驱动开发者实现业务逻辑
    return 0;
}

驱动服务绑定到 HDF 框架中,实现 HdfDriverEntry 中的 Bind() 指针函数。

int32_t SampleDriverBind(struct HdfDeviceObject *deviceObject)
{
    // deviceObject为HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口
    if (deviceObject == NULL) {
        HDF_LOGE("Sample device object is null!");
        return -1;
    }
    static struct ISampleDriverService sampleDriverA = {
        .ServiceA = SampleDriverServiceA,
        .ServiceB = SampleDriverServiceB,
    };
    deviceObject->service = &sampleDriverA.ioService;
    return 0;
}

驱动服务的获取有两种方式,HDF 框架提供接口直接获取和 HDF 框架提供订阅机制获取。
注意驱动服务的获取(目前)只能在内核驱动

a)通过 HDF 接口直接获取
当明确驱动已经加载完成时,驱动服务可通过 HDF 框架接口直接获取

const struct ISampleDriverService *sampleService =
        (const struct ISampleDriverService *)DevSvcManagerClntGetService("sample_driver");
if (sampleService == NULL) {
    return -1;
}
sampleService->ServiceA();
sampleService->ServiceB(5);

b)通过 HDF 提供的订阅机制获取
当不明确驱动是否加载完成时,同一个 Host 下的驱动可以通过订阅机制获取驱动服务。当驱动加载完成后会通过 callback 调用服务接口。

订阅机制的原理:

加载早的驱动 A 查找 Host 下的列表上的记录,如果找到需要订阅的服务,则表明发布服务的驱动 B 已经加载,则直接调用服务接口即可;如果找不到需要订阅的服务的记录,说明驱动 B 还没有加载,则新建一个记录,将需要订阅的服务放进 Host 下的列表。

当加载迟的驱动 B 发布服务的时候,遍历 Host 的列表,如果找到记录则说明有订阅者,然后通知驱动 A 跑 callback,然后跑服务;如果没有找到记录则说明没有订阅者,然后新建一个记录,将发布的服务放到列表里。

以下是订阅者(即驱动 A)的代码,实现订阅 sample2_service 服务。

// 订阅回调函数。object为订阅者的私有数据,service为被订阅的服务对象
int32_t TestDriverSubCallBack(struct HdfDeviceObject *deviceObject, const struct HdfObject *service)
{
    const struct ISampleDriverService *sampleService =
        (const struct ISampleDriverService *)service;
    if (sampleService == NULL) {
        return -1;
    }
    sampleService->ServiceA();
    sampleService->ServiceB(5);
}
// 订阅过程的实现
int32_t TestDriverInit(struct HdfDeviceObject *deviceObject)
{
    if (deviceObject == NULL) {
        HDF_LOGE("Test driver init failed, deviceObject is null!");
        return -1;
    }
    struct SubscriberCallback callBack;
    callBack.deviceObject = deviceObject;
    callBack.OnServiceConnected = TestDriverSubCallBack;
    int32_t ret = HdfDeviceSubscribeService(deviceObject, "sample2_service", callBack);
    if (ret != 0) {
        HDF_LOGE("Test driver subscribe sample driver failed!");
    }
    return ret;
}

6)消息功能
消息机制貌似底层实现是类似 Android / Linux 的 ioctl。
消息机制的功能主要有两种:用户态应用发送消息到驱动,用户态应用接收驱动主动上报事件。

首先将驱动配置信息中服务策略 policy 字段设置为2(参考 policy 定义)。

然后配置驱动信息中的服务设备节点权限(permission 字段),默认是0666。驱动开发者根据驱动的实际使用场景配置驱动设备节点的权限。

a)用户态应用发送消息到驱动
用户态获取服务接口,并发送消息到驱动。下面是用户态驱动或者应用的代码。

#define SAMPLE_WRITE_READ 1
int SendMsg(const char *testMsg)
{
    if (testMsg == NULL) {
        HDF_LOGE("test msg is null");
        return -1;
    }
    struct HdfIoService *serv = HdfIoServiceBind("sample_driver"); // 获取服务
    if (serv == NULL) {
        HDF_LOGE("fail to get service");
        return -1;
    }
    struct HdfSBuf *data = HdfSBufObtainDefaultSize();             // 需要发送的数据
    if (data == NULL) {
        HDF_LOGE("fail to obtain sbuf data");
        return -1;
    }
    struct HdfSBuf *reply = HdfSBufObtainDefaultSize();            // 回复
    if (reply == NULL) {
        HDF_LOGE("fail to obtain sbuf reply");
        ret = HDF_DEV_ERR_NO_MEMORY;
        goto out;
    }
    if (!HdfSbufWriteString(data, testMsg)) {
        HDF_LOGE("fail to write sbuf");
        ret = HDF_FAILURE;
        goto out;
    }                                                              // 发送命令SAMPLE_WRITE_READ和数据data,回复在reply
    int ret = serv->dispatcher->Dispatch(&serv->object, SAMPLE_WRITE_READ, data, reply);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("fail to send service call");
        goto out;
    }
out:
    HdfSBufRecycle(data);
    HdfSBufRecycle(reply);
    HdfIoServiceRecycle(serv);
    return ret;
}

驱动实现 Dispatch() 方法,参数从用户空间传下来。

// Dispatch 是用来处理用户态发下来的消息
int32_t SampleDriverDispatch(struct HdfDeviceIoClient *client, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply)
{
    // do something.
    HDF_LOGE("sample driver lite A dispatch");
    return 0;
}

int32_t SampleDriverBind(struct HdfDeviceObject *device)
{
    HDF_LOGE("test for lite os sample driver A Open!");
    if (device == NULL) {
        HDF_LOGE("test for lite os sample driver A Open failed!");
        return -1;
    }
    static struct ISampleDriverService sampleDriverA = {
        .ioService.Dispatch = SampleDriverDispatch,
        ...
    };
    device->service = (struct IDeviceIoService *)(&sampleDriverA);
    return 0;
}

b)驱动给用户空间上报事件
驱动调用 HdfDeviceSendEvent() 接口,例如在 Dispatch() 中调用

// 这里的参数注意跟官方文档的例子不同
int32_t SampleDriverDispatch(struct HdfDeviceIoClient *client, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply)
{
    ... // process api call here
    return HdfDeviceSendEvent(client->device, cmdCode, data);
}

用户态实现驱动上报消息的处理函数,然后注册监听器。

static int OnDevEventReceived(void *priv,  uint32_t id, struct HdfSBuf *data)
{
    ...
    const char *string = HdfSbufReadString(data);
    if (string == NULL) {
        HDF_LOGE("fail to read string in event data");
        return -1;
    }
    HDF_LOGE("%s: dev event received: %d %s",  (char *)priv, id, string);
    return 0;
}

int RegisterListen()
{
    struct HdfIoService *serv = HdfIoServiceBind("sample_driver");
    if (serv == NULL) {
        HDF_LOGE("fail to get service");
        return -1;
    }
    static struct HdfDevEventlistener listener = {
        .callBack = OnDevEventReceived,
        .priv ="Service0"
    };
    if (HdfDeviceRegisterEventListener(serv, &listener) != 0) {
        HDF_LOGE("fail to register event listener");
        return -1;
    }
    ...
    HdfDeviceUnregisterEventListener(serv, &listener);
    HdfIoServiceRecycle(serv);
    return 0;
}
3. 编译规则

涉及到 Make 和 GN (Generate Ninja) 编译,需要编写 Makefile 和 BUILD.gn。还没有完全整明白,参考例子吧。
编译规则参考文档 这里

三、例子

驱动实现的代码放在目录

//drivers/framework/model/

参考 vibrator 驱动。

1. 驱动实现

a)添加头文件

新建

//drivers/framework/model/misc/demo2/driver/include/demo2_driver.h

添加

#ifndef DEMO2_DRIVER_H                                                                                                                   
#define DEMO2_DRIVER_H
 
#include "osal_mutex.h"
#include "hdf_device_desc.h"
#include "hdf_workqueue.h"
 
enum Demo2DrvIoCmd {
        DEMO2_DRV_IO_START_ONCE    = 0,
        DEMO2_DRV_IO_STOP          = 1,
        DEMO2_DRV_IO_END,
};
 
typedef int32_t (*Demo2CmdHandle)(struct HdfSBuf *reqData, struct HdfSBuf *reply);
 
struct Demo2CmdHandleList {
        int32_t cmd;
        Demo2CmdHandle func;
};
 
struct Demo2DriverData {
        struct IDeviceIoService ioService;
        int32_t (*ServiceA)(void);
        int32_t (*ServiceB)(int32_t val);
        struct HdfDeviceObject *device;
        int32_t data;
        uint32_t demoVal1;
        bool demoVal2;
        HdfWorkQueue workQueue;
        HdfWork work;
        struct OsalMutex mutex;
};
 
#endif 

b)添加 c 代码

新建

//drivers/framework/model/misc/demo2/driver/src/demo2_driver.c

添加

#include "hdf_base.h"
#include "hdf_device_desc.h"
#include "osal_mem.h"
#include "hdf_log.h"
#include "gpio_if.h"
#include "utils/device_resource_if.h"
#include "hdf_device_node.h"
#include "devhost_service.h"
#include "demo2_driver.h"

#define HDF_LOG_TAG    "demo2_driver"

#define DEMO2_WORK_QUEUE_NAME    "demo2_queue"
#define DEMO2_START_TIME    10

struct Demo2DriverData *g_demo2DrvData = NULL;

static struct Demo2DriverData *GetDemo2DrvData(void)
{
	return g_demo2DrvData;
}

static void Demo2WorkEntry(void *para)
{
	struct Demo2DriverData *drvData = (struct Demo2DriverData *)para;
	if (NULL == drvData) {
		HDF_LOGE("%s: drvData NULL", __func__);
		return;
	}

	HDF_LOGE("%s: data %d", __func__, drvData->data);
}

static int32_t StartOnce(struct HdfSBuf *data, struct HdfSBuf *reply)
{
	struct Demo2DriverData *drvData = GetDemo2DrvData();
	if (NULL == drvData) {
		HDF_LOGE("%s: drvData NULL", __func__);
		return HDF_FAILURE;
	}

	(void)OsalMutexLock(&drvData->mutex);
	drvData->data = 1;
	(void)OsalMutexUnlock(&drvData->mutex);

	HdfAddWork(&drvData->workQueue, &drvData->work);

	return HDF_SUCCESS;
}

static int32_t Stop(struct HdfSBuf *data, struct HdfSBuf *reply)
{
	struct Demo2DriverData *drvData = GetDemo2DrvData();
	if (NULL == drvData) {
		HDF_LOGE("%s: drvData NULL", __func__);
		return HDF_FAILURE;
	}

	(void)OsalMutexLock(&drvData->mutex);
	drvData->data = 0;
	(void)OsalMutexUnlock(&drvData->mutex);

	HdfAddWork(&drvData->workQueue, &drvData->work);

	return HDF_SUCCESS;
}

static struct Demo2CmdHandleList g_demo2CmdHandle[] = {
	{DEMO2_DRV_IO_START_ONCE, StartOnce},
	{DEMO2_DRV_IO_STOP, Stop},
};

static int32_t DispatchDemo2(struct HdfDeviceIoClient *client,
		int32_t cmd, struct HdfSBuf *data, struct HdfSBuf *reply)
{
	struct Demo2DriverData *drvData = GetDemo2DrvData();

	int ret = GpioSetDir((uint32_t)drvData->gpio, GPIO_DIR_OUT);
	if (ret) {
		HDF_LOGE("%s: gpio%d setting output failed", __func__, drvData->gpio);
		return HDF_FAILURE;
	}
	
	if (GpioWrite((uint32_t)drvData->gpio, !!cmd) != HDF_SUCCESS) {
		HDF_LOGE("%s: pull gpio%d to %d level failed", __func__, drvData->gpio, GPIO_VAL_HIGH);
		return HDF_FAILURE;
	}

	int32_t loop;
	for (loop = 0; loop < sizeof(g_demo2CmdHandle) / sizeof(g_demo2CmdHandle[0]); ++loop) {
		if ((cmd == g_demo2CmdHandle[loop].cmd) && (g_demo2CmdHandle[loop].func != NULL)) {
			return g_demo2CmdHandle[loop].func(data, reply);
		}
	}

	//return HDF_SUCCESS;
	HDF_LOGE("%s: demo2 driver lite A dispatch, %d, %s", __func__, cmd, HdfSbufReadString(data));
	return HdfDeviceSendEvent(client->device, cmd, data);
}

int32_t Demo2DriverServiceA(void)
{
	HDF_LOGE("%s: service A", __func__);
	return 0;
}

int32_t Demo2DriverServiceB(int32_t val)
{
	HDF_LOGE("%s: service B, %d", __func__, val);
	return 0;
}

int32_t BindDemo2Driver(struct HdfDeviceObject *device)
{
	struct Demo2DriverData *drvData = NULL;

	HDF_LOGE("%s: bind", __func__);

	if (NULL == device) {
		HDF_LOGE("%s: device NULL", __func__);
		return HDF_FAILURE;
	}

	drvData = (struct Demo2DriverData *)OsalMemCalloc(sizeof(*drvData));
	if (NULL == drvData) {
		HDF_LOGE("%s: drvData NULL", __func__);
		return HDF_FAILURE;
	}

	drvData->ioService.Dispatch = DispatchDemo2;
	drvData->ServiceA = Demo2DriverServiceA;
	drvData->ServiceB = Demo2DriverServiceB;
	drvData->device = device;
	device->service = &drvData->ioService;
	g_demo2DrvData = drvData;

	return HDF_SUCCESS;
}

static int32_t GetDemo2ConfigData(const struct DeviceResourceNode *node)
{
	struct DeviceResourceIface *parser = NULL;
	const struct DeviceResourceNode *demoNode = NULL;
	struct Demo2DriverData *drvData = GetDemo2DrvData();
	int ret = 0;

	if (NULL == node) {
		HDF_LOGE("%s: node NULL", __func__);
		return HDF_ERR_INVALID_PARAM;
	}

	parser = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);
	if (NULL == parser) {
		HDF_LOGE("%s: parser NULL", __func__);
		return HDF_ERR_INVALID_PARAM;
	}

	ret = parser->GetUint32(node, "gpio", &drvData->gpio, 0);
	if (ret != HDF_SUCCESS) {
		HDF_LOGE("%s: gpio NULL", __func__);
		return HDF_ERR_INVALID_PARAM;
	}

	demoNode = parser->GetChildNode(node, "demoNode");
	if (NULL == demoNode) {
		HDF_LOGE("%s: demoNode NULL", __func__);
		return HDF_ERR_INVALID_PARAM;
	}

	drvData->demoVal2 = parser->GetBool(demoNode, "demoVal2");

	HDF_LOGE("%s: %d %d", __func__, drvData->gpio, drvData->demoVal2);

	return HDF_SUCCESS;
}

struct Demo1DriverData {
	struct IDeviceIoService ioService;
	int32_t (*ServiceA)(void);
	int32_t (*ServiceB)(uint32_t val);
};

int32_t Demo2DriverSubCallBack(struct HdfDeviceObject *deviceObject, const struct HdfObject *hdfObj)
{
	(void)*deviceObject;
	const struct Demo1DriverData *service = (const struct Demo1DriverData *)hdfObj;
	if (service == NULL) {
		HDF_LOGE("%s: service NULL", __func__);
		return -1;
	}
	service->ServiceA();
	service->ServiceB(5);
	return 0;
}

int32_t InitDemo2Driver(struct HdfDeviceObject *device)
{
	struct Demo2DriverData *drvData = NULL;

	HDF_LOGE("%s: init", __func__);

	if (NULL == device) {
		HDF_LOGE("%s: device NULL", __func__);
		return HDF_FAILURE;
	}

	if (GetDemo2ConfigData(device->property) != HDF_SUCCESS) {
		HDF_LOGE("%s: get demo2 config fail!", __func__);
		return HDF_FAILURE;
	}

	drvData = (struct Demo2DriverData *)device->service;
	if (NULL == drvData) {
		HDF_LOGE("%s: drvData NULL", __func__);
		return HDF_FAILURE;
	}

	if (OsalMutexInit(&drvData->mutex) != HDF_SUCCESS) {
		HDF_LOGE("%s: init mutex fail!", __func__);
		return HDF_FAILURE;
	}

	if (HdfWorkQueueInit(&drvData->workQueue, DEMO2_WORK_QUEUE_NAME) != HDF_SUCCESS) {
		HDF_LOGE("%s: init workQueue fail!", __func__);
		return HDF_FAILURE;
	}

	if (HdfWorkInit(&drvData->work, Demo2WorkEntry, (void*)drvData) != HDF_SUCCESS) {
		HDF_LOGE("%s: init workQueue fail!", __func__);
		return HDF_FAILURE;
	}
	
	struct SubscriberCallback callBack;
	callBack.deviceObject = device;
	callBack.OnServiceConnected = Demo2DriverSubCallBack;
	int32_t ret = HdfDeviceSubscribeService(device, "demo_service", callBack);
	if (ret != 0) {
		HDF_LOGE("%s: Demo driver subscribe failed!", __func__);
	}

	return HDF_SUCCESS;
}

void ReleaseDemo2Driver(struct HdfDeviceObject *device)
{
	struct Demo2DriverData *drvData = NULL;

	HDF_LOGE("%s: release", __func__);

	if (device == NULL) {
		HDF_LOGE("%s: device is null", __func__);
		return;
	}

	drvData = (struct Demo2DriverData *)device->service;
	if (drvData == NULL) {
		HDF_LOGE("%s: drvData is null", __func__);
		return;
	}

	HdfWorkDestroy(&drvData->work);
	HdfWorkQueueDestroy(&drvData->workQueue);
	(void)OsalMutexDestroy(&drvData->mutex);
	OsalMemFree(drvData);
	g_demo2DrvData = NULL;
}

struct HdfDriverEntry g_demo2DriverEntry = {
	.moduleVersion = 1,
	.moduleName = "demo2_module",
	.Bind = BindDemo2Driver,
	.Init = InitDemo2Driver,
	.Release = ReleaseDemo2Driver,
};

HDF_INIT(g_demo2DriverEntry);
2. 添加配置文件

a)添加驱动私有配置

新建文件

//vendor/hisilicon/hispark_taurus/hdf_config/demo2/demo2_config.hcs

添加

root {
    Demo2DriverConfig {
        boardConfig {
            match_attr = "demo2_config";
            demoVal1 = 50; 
            demoNode {
                demoVal2 = true;
            }
        }
    }
}

b)添加驱动配置描述

新建文件

//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs

在 root {} 节点内添加 host 节点:

demo2_host :: host {
    hostName = "demo2_host";
        priority = 100;
        device_demo2 :: device {
            device0 :: deviceNode {
            policy = 2;
            priority = 100;
            preload = 0;
            permission = 0666;
            moduleName = "demo2_module";
            serviceName = "demo2_service";
            deviceMatchAttr = "demo2_config";
        }
    }
}

c)配置文件包含到板级配置入口文件

修改

//vendor/hisilicon/hispark_taurus/hdf_config/hdf.hcs

添加

#include "demo2/demo2_config.hcs"
3. 添加编译规则文件

a)
新建目录

//drivers/adapter/khdf/liteos/model/misc/demo2/

新建文件

BUILD.gn

添加

import("//drivers/adapter/khdf/liteos/hdf.gni")

module_switch = true
module_name = "demo2_module"
hdf_driver(module_name) {
  FRAMEWORKS_DEMO2_ROOT = "$HDF_FRAMEWORKS_PATH/model/misc/demo2/driver"

  sources = [   
    "$FRAMEWORKS_DEMO2_ROOT/src/demo2_driver.c",
  ]

  include_dirs = [ 
    "$FRAMEWORKS_DEMO2_ROOT/include",
  ]
}

新建文件

Makefile

添加

include $(LITEOSTOPDIR)/../../drivers/adapter/khdf/liteos/lite.mk
 
MODULE_NAME := demo2_driver
 
FRAMEWORKS_DEMO2_ROOT = $(LITEOSTOPDIR)/../../drivers/framework/model/misc/demo2/driver
 
LOCAL_INCLUDE := $(FRAMEWORKS_DEMO2_ROOT)/include
 
LOCAL_SRCS += $(FRAMEWORKS_DEMO2_ROOT)/src/demo2_driver.c
 
include $(HDF_DRIVER)

b)
修改文件

//drivers/adapter/khdf/liteos/model/BUILD.gn

在 modules = [ ] 里添加

    "misc/demo2",

c)
修改文件

//drivers/adapter/khdf/liteos/hdf_lite.mk

添加

LITEOS_BASELIB += -ldemo2_driver
LIB_SUBDIRS    += $(LITEOS_DRIVERS_HDF)/model/misc/demo2
四、测试 1. 测试方法和结果

添加代码,编译刷机,系统运行后会在

/bin/

目录下有一个可执行文件 helloworld2。在串口终端执行该文件测试驱动,驱动正常跑的话可以控制绿色 led 灯。驱动打印接收到的命令和信息。

./bin/helloworld2


2. 测试代码

修改

//drivers/adapter/BUILD.gn

添加“+”号后面内容

if (defined(ohos_lite)) {
	group("uhdf_entry") {
		deps = [
+			"//applications/sample/myApp2:helloworld2",
			...
		]
	}
	...
}

新建路径

//applications/sample/myApp2/

增加文件BUILD.gn

HDF_FRAMEWORKS = "//drivers/framework"
   
executable("helloworld2") {
	output_name = "helloworld2"
	sources = [ "src/helloworld2.c" ]
	
	cflags_c = []
	ldflags = []
	
	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",
	]
}

新建路径

//applications/sample/myApp2/src/

增加文件 helloworld2.c

#include 
#include 
#include 
#include 
#include "hdf_log.h"
#include "hdf_sbuf.h"
#include "hdf_io_service_if.h"
#include "osal_time.h"

int SendMsg(struct HdfIoService *serv, uint32_t cmd, const char *testMsg)
{
	int ret = 0;
	
	if (testMsg == NULL) {
		printf("test msg is nulln");
		return -1;
	}
	
	if (serv == NULL) {
		printf("fail to get servicen");
		return -1;
	}
	
	struct HdfSBuf *data = HdfSBufObtainDefaultSize();
	if (data == NULL) {
		printf("fail to obtain sbuf datan");
		return -1;
	}
	
	struct HdfSBuf *reply = HdfSBufObtainDefaultSize();
	if (reply == NULL) {
		printf("fail to obtain sbuf replyn");
		ret = HDF_DEV_ERR_NO_MEMORY;
		goto out;
	}
	
	if (!HdfSbufWriteString(data, testMsg)) {
		printf("fail to write sbufn");
		ret = HDF_FAILURE;
		goto out;
	}
	
	ret = serv->dispatcher->Dispatch(&serv->object, cmd, data, reply);
	if (ret != HDF_SUCCESS) {
		printf("fail to send service calln");
		goto out;
	}
out:
	HdfSBufRecycle(data);
	HdfSBufRecycle(reply);
	//HdfIoServiceRecycle(serv);
	return ret;
}

static int OnDevEventReceived(void *priv,  uint32_t id, struct HdfSBuf *data)
{
	const char *string = HdfSbufReadString(data);
	OsalTimespec time;

	OsalGetTime(&time);
	HDF_LOGE("%s received event at %llu.%llu", (char *)priv, time.sec, time.usec);

	if (string == NULL) {
		printf("fail to read string in event datan");
		return -1;
	}
	printf("%s: dev event received: %d %s",  (char *)priv, id, string);
	return 0;
}

static struct HdfDevEventlistener listener = {
	.callBack = OnDevEventReceived,
	.priv ="Service0"
};

int main(int argc, char **argv)
{
	int ret = 0;

	printf("n************************************************n");
	printf("nttHello OHOS!n");
	printf("n************************************************nn");

	//struct HdfIoService *serv = HdfIoServiceBind("demo_service");
	struct HdfIoService *serv = HdfIoServiceBind("demo2_service");
	if (serv == NULL) {
		printf("fail to get servicen");
		return -1;
	}

	if (HdfDeviceRegisterEventListener(serv, &listener) != 0) {
		printf("fail to register event listenern");
		return -1;
	}

	//ret = SendMsg(serv, "Hello.");
	//sleep(1);
	static int cmd = 0;

	while (1) {
		if (SendMsg(serv, cmd, "Hello.")) {
			HDF_LOGE("fail to send event");
			return HDF_FAILURE;
		}
		sleep(1);
		if (cmd++ == 2)
			cmd = 0;
	}

	HdfDeviceUnregisterEventListener(serv, &listener);
	HdfIoServiceRecycle(serv);

	return 0;
}

总结

下一篇讲讲 HDI 和应用。

参考文章

用鸿蒙开发AI应用(七)触摸屏控制LED 作者:bluishfish
驱动使用指南 鸿蒙官方文档

相关文章

上一篇:OpenHarmonyOs / LiteOs-a 开发环境搭建
下一篇:OpenHarmonyOs / LiteOs-a 应用开发

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/851537.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号