当前位置:   article > 正文

liteOS-A学习笔记-5.驱动程序-HDF框架深层知识_liteos hdf

liteos hdf

一 概述

鸿蒙为了支持多个内核,提出了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

  1. STATIC const struct file_operations_vfs g_serialDevOps = {
  2. SerialOpen, /* open */
  3. SerialClose, /* close */
  4. SerialRead, /* read */
  5. SerialWrite,
  6. NULL,
  7. SerialIoctl,
  8. NULL,
  9. #ifndef CONFIG_DISABLE_POLL
  10. SerialPoll,
  11. #endif
  12. NULL,
  13. };
  14. INT32 virtual_serial_init(const CHAR *deviceName)
  15. {
  16. ...
  17. (VOID)memset_s(&g_serialFilep, sizeof(struct file), 0, sizeof(struct file));
  18. g_serialFilep.f_oflags = O_RDWR;
  19. g_serialFilep.f_inode = inode;
  20. (VOID)register_driver(SERIAL, &g_serialDevOps, DEFFILEMODE, &g_serialFilep);
  21. ...
  22. }

(2)HCS-相当于设备树,设备树与内核代码的映射关系:

OpenharmonyFor6ull\vendor\nxp\imx6ull\config\device_info

  1. root {
  2. device_info {
  3. //1.
  4. match_attr = "hdf_manager";
  5. //2.
  6. template host {
  7. hostName = "";
  8. priority = 100;
  9. template device{
  10. template deviceNode {
  11. policy = 0;
  12. priority = 100;
  13. preload = 0;
  14. permission = 0664;
  15. moduleName = "";
  16. serviceName = "";
  17. deviceMatchAttr = "";
  18. }
  19. }
  20. }
  21. //3.
  22. platform :: host {
  23. hostName = "platform_host";
  24. priority = 50;
  25. //3.1 iic接口设备树描述
  26. device_i2c :: device {
  27. device0 :: deviceNode {
  28. policy = 1;
  29. priority = 50;
  30. permission = 0644;
  31. moduleName = "HDF_PLATFORM_I2C"; //对应的驱动程序名称
  32. serviceName = "HDF_PLATFORM_I2C_0"; //对应本驱动程序对外提供服务的名称,供app程序调用
  33. deviceMatchAttr = "nxp_imx6ull_i2c_0";//对应硬件初始化信息,根据这个名字在设备树中查找相应的节点来描述这些信息
  34. }
  35. }
  36. //3.2 触摸屏接口设备树描述
  37. device_touchscreen :: device {
  38. device0 :: deviceNode {
  39. policy = 1;
  40. priority = 100;
  41. preload = 0;
  42. permission = 0666;
  43. moduleName = "HDF_TOUCHSCREEN";
  44. serviceName = "HDF_TOUCHSCREEN";
  45. }
  46. }
  47. }
  48. //4.
  49. storage :: host {
  50. }
  51. media :: host {
  52. }
  53. }
  54. }

举个例子:找一下上面设备树中用于调用iic设备的HDF_PLATFORM_I2C驱动程序在内核代码中的位置:

OpenharmonyFor6ull\vendor\nxp\imx6ull\driver\imx6ull-i2c\i2c_imx6ull.c

  1. struct HdfDriverEntry g_i2cDriverEntry = {
  2. .moduleVersion = 1,
  3. .Bind = Imx6ullI2cBind,
  4. .Init = Imx6ullI2cInit,
  5. .Release = Imx6ullI2cRelease,
  6. .moduleName = "HDF_PLATFORM_I2C",//这里的名称和设备树中的名称一致,那么就对应起来了
  7. };
  8. HDF_INIT(g_i2cDriverEntry);

继续举例子:找一下上面设备树中用于调用iic设备的硬件描述信息deviceMatchAttr = "nxp_imx6ull_i2c_0"在内核代码中的位置:

OpenharmonyFor6ull\vendor\nxp\imx6ull\config\i2c\i2c_config.hcs

  1. root {
  2. platfrom {
  3. i2c_config {
  4. //描述IIC接口的模板i2c_controller
  5. template i2c_controller {
  6. bus = 0;
  7. match_attr = "";
  8. reg_pbase = 0x021A4000;//IIC控制器基地址
  9. reg_size = 0xd1;//IIC控制器寄存器占用的空间
  10. irq = 0;
  11. freq = 400000;//IIC通信频率400KHz
  12. clk = 50000000;//IIC时钟 50MHz
  13. }
  14. //定义一个设备controller_0x021A4000
  15. //使用上面定义的i2c_controller模板
  16. controller_0x021A4000 :: i2c_controller {
  17. bus = 0;
  18. match_attr = "nxp_imx6ull_i2c_0";//对应top层HCS
  19. }
  20. }
  21. }
  22. }

(3)分析驱动程序源码-新框架

OpenharmonyFor6ull\vendor\nxp\imx6ull\driver\imx6ull-i2c\i2c_imx6ull.c

设备树和驱动程序的对应的源码一致的时候,会导致init函数被调用,即Imx6ullI2cInit函数被调用

  1. struct HdfDriverEntry g_i2cDriverEntry = {
  2. .moduleVersion = 1,
  3. .Bind = Imx6ullI2cBind,
  4. .Init = Imx6ullI2cInit, //init函数被调用
  5. .Release = Imx6ullI2cRelease,
  6. .moduleName = "HDF_PLATFORM_I2C",//HCS和这里名称一致
  7. };
  8. HDF_INIT(g_i2cDriverEntry);

 

init函数源码:

  1. static int32_t Imx6ullI2cInit(struct HdfDeviceObject *device)
  2. {
  3. (void)device;
  4. return HDF_SUCCESS;
  5. }

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

  1. struct HdfIoService *HdfIoServiceBind(const char *serviceName, mode_t permission)
  2. {
  3.     return HdfIoServiceAdapterObtain(serviceName, permission);
  4. }

-->

(1)分析一个“HdfIoServiceAdapterObtain”函数:

OpenharmonyFor6ull\drivers\hdf\lite\adapter\syscall\src\hdf_syscall_adapter.c(给应用程序使用的)

  1. struct HdfIoService *HdfIoServiceAdapterObtain(const char *serviceName, mode_t mode)
  2. {
  3. struct HdfSyscallAdapter *adapter = NULL;
  4. struct HdfIoService *ioService = NULL;
  5. char devNodePath[PATH_MAX] = {0};
  6. char realPath[PATH_MAX] = {0};
  7. ...
  8. if (realpath(devNodePath, realPath) == NULL) {
  9. //通过服务名称查找并加载驱动,根据上文HCS中的ServiceName定义
  10. //在这之前 /dev/dev_mgr会自动给hello_service注册一个驱动程序
  11. HdfLoadDriverByServiceName(serviceName);
  12. //获得设备节点路径
  13. realpath(devNodePath, realPath);
  14. }
  15. ...
  16. //realPath = “/dev/hello_service”
  17. adapter->fd = open(realPath, O_RDWR);
  18. ioService = &adapter->super;
  19. static struct HdfIoDispatcher dispatch = {
  20. .Dispatch = HdfSyscallAdapterDispatch,
  21. };
  22. ioService->dispatcher = &dispatch;
  23. return ioService;
  24. }

--->

OpenharmonyFor6ull\drivers\hdf\lite\adapter\syscall\src\hdf_syscall_adapter.c

  1. #define DEV_NODE_PATH "/dev/"
  2. #define DEV_MGR_NODE "dev_mgr"
  3. static int32_t HdfLoadDriverByServiceName(const char *serviceName)
  4. {
  5. int32_t ret = HDF_FAILURE;
  6. struct HdfSBuf *data = NULL;
  7. //通过访问驱动程序,让驱动程序加载服务发布服务
  8. struct HdfIoService *ioService = HdfIoServiceBind(DEV_MGR_NODE, 0);
  9. ...
  10. }

---->回到之前继续执行函数

OpenharmonyFor6ull\drivers\hdf\lite\adapter\vnode\src\hdf_vnode_adapter.c

  1. struct HdfIoService *HdfIoServiceAdapterObtain(const char *serviceName, mode_t mode)
  2. {
  3. // /dev/dev_mgr会给hello_service注册一个驱动程序/dev/hello_service
  4. HdfLoadDriverByServiceName(serviceName);
  5. //获得realPath的值为realPath="/dev/hello_service"
  6. realpath(devNodePath, realPath);
  7. //realPath="/dev/hello_service"
  8. adapter->fd = open(realPath, O_RDWR);
  9. }

(2)分析另一个“HdfIoServiceAdapterObtain”函数:

OpenharmonyFor6ull\drivers\hdf\lite\adapter\vnode\src\hdf_vnode_adapter.c//内核调用

  1. struct HdfIoService *HdfIoServiceAdapterObtain(const char *serviceName, mode_t mode)
  2. {
  3. static const struct file_operations_vfs fileOps = {
  4. .open = HdfVNodeAdapterOpen,
  5. .ioctl = HdfVNodeAdapterIoctl,
  6. .poll = HdfVNodeAdapterPoll,
  7. .close = HdfVNodeAdapterClose,
  8. };
  9. //注册file_operations结构体
  10. //vNodePath就是传进来的“/dev/hello_service”
  11. int ret = register_driver(vnodeAdapter->vNodePath, &fileOps, mode, vnodeAdapter);
  12. }

2.获得服务

应用程序的动作:应用程序调用service->dispatcher->Dispatch(&service->object,0,data,reply)函数

  1. //OpenharmonyFor6ull\drivers\hdf\lite\adapter\syscall\src\hdf_syscall_adapter.c
  2. struct HdfIoService *HdfIoServiceAdapterObtain(const char *serviceName, mode_t mode)
  3. {
  4. ...
  5. ioService = &adapter->super;
  6. static struct HdfIoDispatcher dispatch = {
  7. .Dispatch = HdfSyscallAdapterDispatch,
  8. };
  9. ioService->dispatcher = &dispatch;
  10. ...
  11. }

 

->

从上面的代码可知,最终调用函数HdfSyscallAdapterDispatch

OpenharmonyFor6ull\drivers\hdf\lite\adapter\syscall\src\hdf_syscall_adapter.c

  1. tatic int HdfSyscallAdapterDispatch(struct HdfObject *object, int code, struct HdfSBuf *data, struct HdfSBuf *reply)
  2. {
  3. ...
  4. int ret = ioctl(ioService->fd, HDF_WRITE_READ, &wrBuf);
  5. ...
  6. return ret;
  7. }

->

从上面的代码可知,最终调用了ioctl,该函数最终进入到下面这个函数

OpenharmonyFor6ull\drivers\hdf\lite\adapter\vnode\src\hdf_vnode_adapter.c

  1. static int HdfVNodeAdapterIoctl(struct file *filep, int cmd, unsigned long arg)
  2. {
  3. struct HdfVNodeAdapterClient *client = (struct HdfVNodeAdapterClient *)filep->f_priv;
  4. if (client == NULL) {
  5. return HDF_DEV_ERR_NO_DEVICE;
  6. }
  7. switch (cmd) {
  8. case HDF_WRITE_READ:
  9. if (client->serv == NULL) {
  10. return HDF_DEV_ERR_NO_DEVICE;
  11. }
  12. return HdfVNodeAdapterServCall(client, arg);
  13. ...
  14. default:
  15. return HDF_FAILURE;
  16. }
  17. return HDF_SUCCESS;
  18. }

->

继续进入函数HdfVNodeAdapterServCall

OpenharmonyFor6ull\drivers\hdf\lite\adapter\vnode\src\hdf_vnode_adapter.c

  1. static int HdfVNodeAdapterServCall(const struct HdfVNodeAdapterClient *client, unsigned long arg)
  2. {
  3. ...
  4. if (LOS_ArchCopyFromUser(&bwr, (void*)bwrUser, sizeof(bwr)) != 0) {
  5. HDF_LOGE("Copy from user failed");
  6. return HDF_FAILURE;
  7. }
  8. ...
  9. int ret = client->adapter->ioService.dispatcher->Dispatch(client->adapter->ioService.target,
  10. bwr.cmdCode, data, reply);
  11. ...
  12. if (LOS_ArchCopyToUser(bwrUser, &bwr, sizeof(struct HdfWriteReadBuf)) != 0) {
  13. HDF_LOGE("%s: copy bwr fail", __func__);
  14. ret = HDF_FAILURE;
  15. }
  16. }

->

其中的Dispatcher函数进一步调用就是驱动程序的dispatch函数

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/246626
推荐阅读
相关标签
  

闽ICP备14008679号