irpas技术客

OpenHarmonyOs / LiteOs-a 驱动开发_あル

大大的周 3266

相关文章

上一篇: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。还没有完全整明白,参考例子吧。 编译规则参考文档 这里

三、例子

测试之前将 Hilog 的 buffer 尺寸增大,要不然有的 log 打不出来。 修改

//kernel/liteos_a/kernel/extended/hilog/los_hilog.c

添加

#include "los_vm_lock.h" #include "user_copy.h" -#define HILOG_BUFFER LOSCFG_HILOG_BUFFER_SIZE +#define HILOG_BUFFER 0x1000000 #define DRIVER_MODE 0666 #define HILOG_DRIVER "/dev/hilog"

OHOS 的驱动实现代码放在目录

//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 /* DEMO2_DRIVER_H */

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/*GPIO_VAL_HIGH*/) != 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; } /* const struct Demo1DriverData *demo1Service = (const struct Demo1DriverData *)DevSvcManagerClntGetService("demo_service"); if (demo1Service == NULL) { HDF_LOGE("%s: demo1 service NULL", __func__); } demo1Service->ServiceA(); demo1Service->ServiceB(5); */ 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 <fcntl.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <unistd.h> #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 null\n"); return -1; } if (serv == NULL) { printf("fail to get service\n"); return -1; } struct HdfSBuf *data = HdfSBufObtainDefaultSize(); if (data == NULL) { printf("fail to obtain sbuf data\n"); return -1; } struct HdfSBuf *reply = HdfSBufObtainDefaultSize(); if (reply == NULL) { printf("fail to obtain sbuf reply\n"); ret = HDF_DEV_ERR_NO_MEMORY; goto out; } if (!HdfSbufWriteString(data, testMsg)) { printf("fail to write sbuf\n"); ret = HDF_FAILURE; goto out; } ret = serv->dispatcher->Dispatch(&serv->object, cmd, data, reply); if (ret != HDF_SUCCESS) { printf("fail to send service call\n"); 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 data\n"); 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("\n\t\tHello OHOS!\n"); printf("\n************************************************\n\n"); //struct HdfIoService *serv = HdfIoServiceBind("demo_service"); struct HdfIoService *serv = HdfIoServiceBind("demo2_service"); if (serv == NULL) { printf("fail to get service\n"); return -1; } if (HdfDeviceRegisterEventListener(serv, &listener) != 0) { printf("fail to register event listener\n"); 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 应用开发


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #OpenHarmonyOs #LiteOsa #驱动开发 #相关文章前一篇Ubuntu #2004 #搭建