上一篇: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。
主要可以分成三大块: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 为一个进程,但是没有跟过代码,不能确定。驱动模型如下图所示:
前两节内容大部分来自官方文档。
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
修改
//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 应用开发



