一 概述
鸿蒙为了支持多个内核,提出了HDF(HarmonyDiverFoundation),鸿蒙驱动框架。
使用“服务”的概念编写驱动程序:
(1)驱动程序中实现服务;(2)APP要先获得服务,然后调用服务跟驱动函数交互。
liteOS-a中驱动程序也跟linux类似:
linux使用设备树描述硬件信息,驱动程序从设备树中获得这些信息;
liteos-a使用HCS文件描述硬件信息,驱动程序从HCS文件中获得这些信息。
二 驱动程序怎么写?
(1)跟Linux类似:构造注册一个file_operations_vfs结构体
OpenharmonyFor6ull\kernel\liteos_a\kernel\common\virtual_serial.c
STATIC const struct file_operations_vfs g_serialDevOps = {
SerialOpen, /* open */
SerialClose, /* close */
SerialRead, /* read */
SerialWrite,
NULL,
SerialIoctl,
NULL,
#ifndef CONFIG_DISABLE_POLL
SerialPoll,
#endif
NULL,
};
INT32 virtual_serial_init(const CHAR *deviceName)
{
...
(VOID)memset_s(&g_serialFilep, sizeof(struct file), 0, sizeof(struct file));
g_serialFilep.f_oflags = O_RDWR;
g_serialFilep.f_inode = inode;
(VOID)register_driver(SERIAL, &g_serialDevOps, DEFFILEMODE, &g_serialFilep);
...
}
(2)HCS-相当于设备树,设备树与内核代码的映射关系:
OpenharmonyFor6ull\vendor\nxp\imx6ull\config\device_info
root {
device_info {
//1.
match_attr = "hdf_manager";
//2.
template host {
hostName = "";
priority = 100;
template device{
template deviceNode {
policy = 0;
priority = 100;
preload = 0;
permission = 0664;
moduleName = "";
serviceName = "";
deviceMatchAttr = "";
}
}
}
//3.
platform :: host {
hostName = "platform_host";
priority = 50;
//3.1 iic接口设备树描述
device_i2c :: device {
device0 :: deviceNode {
policy = 1;
priority = 50;
permission = 0644;
moduleName = "HDF_PLATFORM_I2C"; //对应的驱动程序名称
serviceName = "HDF_PLATFORM_I2C_0"; //对应本驱动程序对外提供服务的名称,供app程序调用
deviceMatchAttr = "nxp_imx6ull_i2c_0";//对应硬件初始化信息,根据这个名字在设备树中查找相应的节点来描述这些信息
}
}
//3.2 触摸屏接口设备树描述
device_touchscreen :: device {
device0 :: deviceNode {
policy = 1;
priority = 100;
preload = 0;
permission = 0666;
moduleName = "HDF_TOUCHSCREEN";
serviceName = "HDF_TOUCHSCREEN";
}
}
}
//4.
storage :: host {
}
media :: host {
}
}
}
举个例子:找一下上面设备树中用于调用iic设备的HDF_PLATFORM_I2C驱动程序在内核代码中的位置:
OpenharmonyFor6ull\vendor\nxp\imx6ull\driver\imx6ull-i2c\i2c_imx6ull.c
struct HdfDriverEntry g_i2cDriverEntry = {
.moduleVersion = 1,
.Bind = Imx6ullI2cBind,
.Init = Imx6ullI2cInit,
.Release = Imx6ullI2cRelease,
.moduleName = "HDF_PLATFORM_I2C",//这里的名称和设备树中的名称一致,那么就对应起来了
};
HDF_INIT(g_i2cDriverEntry);
继续举例子:找一下上面设备树中用于调用iic设备的硬件描述信息deviceMatchAttr = "nxp_imx6ull_i2c_0"在内核代码中的位置:
OpenharmonyFor6ull\vendor\nxp\imx6ull\config\i2c\i2c_config.hcs
root {
platfrom {
i2c_config {
//描述IIC接口的模板i2c_controller
template i2c_controller {
bus = 0;
match_attr = "";
reg_pbase = 0x021A4000;//IIC控制器基地址
reg_size = 0xd1;//IIC控制器寄存器占用的空间
irq = 0;
freq = 400000;//IIC通信频率400KHz
clk = 50000000;//IIC时钟 50MHz
}
//定义一个设备controller_0x021A4000
//使用上面定义的i2c_controller模板
controller_0x021A4000 :: i2c_controller {
bus = 0;
match_attr = "nxp_imx6ull_i2c_0";//对应top层HCS
}
}
}
}
(3)分析驱动程序源码-新框架
OpenharmonyFor6ull\vendor\nxp\imx6ull\driver\imx6ull-i2c\i2c_imx6ull.c
设备树和驱动程序的对应的源码一致的时候,会导致init函数被调用,即Imx6ullI2cInit函数被调用
struct HdfDriverEntry g_i2cDriverEntry = {
.moduleVersion = 1,
.Bind = Imx6ullI2cBind,
.Init = Imx6ullI2cInit, //init函数被调用
.Release = Imx6ullI2cRelease,
.moduleName = "HDF_PLATFORM_I2C",//HCS和这里名称一致
};
HDF_INIT(g_i2cDriverEntry);
init函数源码:
static int32_t Imx6ullI2cInit(struct HdfDeviceObject *device)
{
(void)device;
return HDF_SUCCESS;
}
bind函数源码:
LINE94~97:服务结构体,放入dispatch函数
LINE80:驱动程序中把g_hello_val这个值放入reply里面。应用程序就可以把reply取出来。
继续分析该驱动程序对应的app程序:
LINE22 定义服务名称,
LINE26 相当于调用了对应驱动程序的bind函数,传入服务名称就获得了这个服务,该名称在HCS文件中有定义 serviceName = "hello_service"; 该函数里面肯定调用了open函数
LINE45 调用这个服务最终导致驱动程序dispatch函数调用,提供给服务(即驱动程序)的数据为data,服务(即驱动程序)回复的数据为reply;该函数里面肯定调用了read write函数
使用这套新框架隐藏了老框架中需要首先open然后write然后read函数,直接使用BInd函数后dispatch即可,更为简便。
(4)分析驱动程序源码-老框架
ps:没有下载韦东山老师的源代码,视频截个图,凑合看看吧
hello_init函数:
g_helloDevOps结构体:就是上文说的file_operations_vfs结构体;
/dev/hello:设备节点,供app程序调用使用
看一下g_helloDevOps结构体的定义吧~跟linux的一毛一样。
继续分析该驱动程序对应的app程序:
注释:若使用本小节介绍的老框架,调用的时候调用设备节点-/dev/hello
三 分析源代码
(1)驱动程序:会发布服务。发布服务就是把该服务传入设备管理器(设备管理器的节点路径为“/dev/dev_mgr”);
(2)APP:从设备管理器(设备管理器的节点路径为“/dev/dev_mgr”)查找并获得服务即调用这个驱动。
如果app提供给设备管理器的服务存在,那么设备管理器会调用下面这个函数,在这个函数中注册驱动程序
OpenharmonyFor6ull\drivers\hdf\lite\adapter\vnode\src\hdf_vnode_adapter.c->HdfIoServiceAdapterObtain”
1.获得服务
应用程序的动作:应用程序调用HdfIoServiceBind函数
struct HdfIoService *service = HdfIoServiceBind("hello_service",0);
分析HdfIoServiceBind函数
OpenharmonyFor6ull\drivers\hdf\frameworks\core\shared\src\hdf_io_service.c
struct HdfIoService *HdfIoServiceBind(const char *serviceName, mode_t permission)
{
return HdfIoServiceAdapterObtain(serviceName, permission);
}
-->
(1)分析一个“HdfIoServiceAdapterObtain”函数:
OpenharmonyFor6ull\drivers\hdf\lite\adapter\syscall\src\hdf_syscall_adapter.c(给应用程序使用的)
struct HdfIoService *HdfIoServiceAdapterObtain(const char *serviceName, mode_t mode)
{
struct HdfSyscallAdapter *adapter = NULL;
struct HdfIoService *ioService = NULL;
char devNodePath[PATH_MAX] = {0};
char realPath[PATH_MAX] = {0};
...
if (realpath(devNodePath, realPath) == NULL) {
//通过服务名称查找并加载驱动,根据上文HCS中的ServiceName定义
//在这之前 /dev/dev_mgr会自动给hello_service注册一个驱动程序
HdfLoadDriverByServiceName(serviceName);
//获得设备节点路径
realpath(devNodePath, realPath);
}
...
//realPath = “/dev/hello_service”
adapter->fd = open(realPath, O_RDWR);
ioService = &adapter->super;
static struct HdfIoDispatcher dispatch = {
.Dispatch = HdfSyscallAdapterDispatch,
};
ioService->dispatcher = &dispatch;
return ioService;
}
--->
OpenharmonyFor6ull\drivers\hdf\lite\adapter\syscall\src\hdf_syscall_adapter.c
#define DEV_NODE_PATH "/dev/"
#define DEV_MGR_NODE "dev_mgr"
static int32_t HdfLoadDriverByServiceName(const char *serviceName)
{
int32_t ret = HDF_FAILURE;
struct HdfSBuf *data = NULL;
//通过访问驱动程序,让驱动程序加载服务发布服务
struct HdfIoService *ioService = HdfIoServiceBind(DEV_MGR_NODE, 0);
...
}
---->回到之前继续执行函数
OpenharmonyFor6ull\drivers\hdf\lite\adapter\vnode\src\hdf_vnode_adapter.c
struct HdfIoService *HdfIoServiceAdapterObtain(const char *serviceName, mode_t mode)
{
// /dev/dev_mgr会给hello_service注册一个驱动程序/dev/hello_service
HdfLoadDriverByServiceName(serviceName);
//获得realPath的值为realPath="/dev/hello_service"
realpath(devNodePath, realPath);
//realPath="/dev/hello_service"
adapter->fd = open(realPath, O_RDWR);
}
(2)分析另一个“HdfIoServiceAdapterObtain”函数:
OpenharmonyFor6ull\drivers\hdf\lite\adapter\vnode\src\hdf_vnode_adapter.c//内核调用
struct HdfIoService *HdfIoServiceAdapterObtain(const char *serviceName, mode_t mode)
{
static const struct file_operations_vfs fileOps = {
.open = HdfVNodeAdapterOpen,
.ioctl = HdfVNodeAdapterIoctl,
.poll = HdfVNodeAdapterPoll,
.close = HdfVNodeAdapterClose,
};
//注册file_operations结构体
//vNodePath就是传进来的“/dev/hello_service”
int ret = register_driver(vnodeAdapter->vNodePath, &fileOps, mode, vnodeAdapter);
}
2.获得服务
应用程序的动作:应用程序调用service->dispatcher->Dispatch(&service->object,0,data,reply)函数
//OpenharmonyFor6ull\drivers\hdf\lite\adapter\syscall\src\hdf_syscall_adapter.c
struct HdfIoService *HdfIoServiceAdapterObtain(const char *serviceName, mode_t mode)
{
...
ioService = &adapter->super;
static struct HdfIoDispatcher dispatch = {
.Dispatch = HdfSyscallAdapterDispatch,
};
ioService->dispatcher = &dispatch;
...
}
->
从上面的代码可知,最终调用函数HdfSyscallAdapterDispatch
OpenharmonyFor6ull\drivers\hdf\lite\adapter\syscall\src\hdf_syscall_adapter.c
tatic int HdfSyscallAdapterDispatch(struct HdfObject *object, int code, struct HdfSBuf *data, struct HdfSBuf *reply)
{
...
int ret = ioctl(ioService->fd, HDF_WRITE_READ, &wrBuf);
...
return ret;
}
->
从上面的代码可知,最终调用了ioctl,该函数最终进入到下面这个函数
OpenharmonyFor6ull\drivers\hdf\lite\adapter\vnode\src\hdf_vnode_adapter.c
static int HdfVNodeAdapterIoctl(struct file *filep, int cmd, unsigned long arg)
{
struct HdfVNodeAdapterClient *client = (struct HdfVNodeAdapterClient *)filep->f_priv;
if (client == NULL) {
return HDF_DEV_ERR_NO_DEVICE;
}
switch (cmd) {
case HDF_WRITE_READ:
if (client->serv == NULL) {
return HDF_DEV_ERR_NO_DEVICE;
}
return HdfVNodeAdapterServCall(client, arg);
...
default:
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
->
继续进入函数HdfVNodeAdapterServCall
OpenharmonyFor6ull\drivers\hdf\lite\adapter\vnode\src\hdf_vnode_adapter.c
static int HdfVNodeAdapterServCall(const struct HdfVNodeAdapterClient *client, unsigned long arg)
{
...
if (LOS_ArchCopyFromUser(&bwr, (void*)bwrUser, sizeof(bwr)) != 0) {
HDF_LOGE("Copy from user failed");
return HDF_FAILURE;
}
...
int ret = client->adapter->ioService.dispatcher->Dispatch(client->adapter->ioService.target,
bwr.cmdCode, data, reply);
...
if (LOS_ArchCopyToUser(bwrUser, &bwr, sizeof(struct HdfWriteReadBuf)) != 0) {
HDF_LOGE("%s: copy bwr fail", __func__);
ret = HDF_FAILURE;
}
}
->
其中的Dispatcher函数进一步调用就是驱动程序的dispatch函数