diff --git a/zh-cn/device-dev/driver/driver-platform-adc-des.md b/zh-cn/device-dev/driver/driver-platform-adc-des.md index d478bd0a69d0a4dfd188541e8139799eb0d7caf2..6711f7e39e7ed7a5e73ae2e9102b873a368fc766 100755 --- a/zh-cn/device-dev/driver/driver-platform-adc-des.md +++ b/zh-cn/device-dev/driver/driver-platform-adc-des.md @@ -4,29 +4,30 @@ ### 功能简介 -ADC(Analog to Digital Converter),即模拟-数字转换器,可将模拟信号转换成对应的数字信号,便于存储与计算等操作。除电源线和地线之外,ADC只需要1根线与被测量的设备进行连接,其物理连线如图1: +ADC(Analog to Digital Converter),即模拟-数字转换器,可将模拟信号转换成对应的数字信号,便于存储与计算等操作。除电源线和地线之外,ADC只需要1根线与被测量的设备进行连接,其物理连线如图1所示: **图 1** ADC物理连线示意图 -![](figures/ADC物理连线示意图.png "ADC物理连线示意图") +![ADC物理连线示意图](figures/ADC物理连线示意图.png) ADC接口定义了完成AD转换的通用方法集合,包括: - ADC设备管理:打开或关闭ADC设备。 + - ADC读取转换结果:读取AD转换结果。 ### 基本概念 - 分辨率 - 分辨率指的是ADC模块能够转换的二进制位数,位数越多分辨率越高。 + 分辨率指的是ADC模块能够转换的二进制位数,位数越多分辨率越高。 - 转换误差 - 转换误差通常是以输出误差的最大值形式给出。它表示A/D转换器实际输出的数字量和理论上的输出数字量之间的差别。常用最低有效位的倍数表示。 + 转换误差通常是以输出误差的最大值形式给出。它表示A/D转换器实际输出的数字量和理论上的输出数字量之间的差别。常用最低有效位的倍数表示。 - 转换时间 - 转换时间是指A/D转换器从转换控制信号到来开始,到输出端得到稳定的数字信号所经过的时间。 + 转换时间是指A/D转换器从转换控制信号到来开始,到输出端得到稳定的数字信号所经过的时间。 ### 运作机制 @@ -50,18 +51,18 @@ ADC模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/ -| 接口名 | 接口描述 | +| 接口名 | 接口描述 | | -------- | ---------------- | -| DevHandle AdcOpen(uint32_t number) | 打开ADC设备 | -| void AdcClose(DevHandle handle) | 关闭ADC设备 | -| int32_t AdcRead(DevHandle handle, uint32_t channel, uint32_t \*val) | 读取AD转换结果值 | +| DevHandle AdcOpen(uint32_t number) | 打开ADC设备 | +| void AdcClose(DevHandle handle) | 关闭ADC设备 | +| int32_t AdcRead(DevHandle handle, uint32_t channel, uint32_t \*val) | 读取AD转换结果值 | ### 开发步骤 使用ADC设备的一般流程如图2所示。 - **图 2** ADC使用流程图 -![](figures/ADC使用流程图.png "ADC使用流程图") +**图 2** ADC使用流程图 +![ADC使用流程图](figures/ADC使用流程图.png) #### 打开ADC设备 @@ -76,23 +77,23 @@ DevHandle AdcOpen(int16_t number); -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | ----------------- | -| number | ADC设备号 | -| **返回值** | **返回值描述** | -| NULL | 打开ADC设备失败 | -| 设备句柄 | 打开的ADC设备句柄 | +| number | int16_t类型,ADC设备号 | +| **返回值** | **返回值描述** | +| NULL | 打开ADC设备失败 | +| 设备句柄 | 打开的ADC设备句柄 | 假设系统中存在2个ADC设备,编号从0到1,那么我们现在打开1号设备。 ```c -DevHandle adcHandle = NULL; /* ADC设备句柄 / +DevHandle adcHandle = NULL; // ADC设备句柄 -/* 打开ADC设备 */ +// 打开ADC设备 adcHandle = AdcOpen(1); if (adcHandle == NULL) { HDF_LOGE("AdcOpen: fail\n"); - return; + return NULL; } ``` @@ -106,14 +107,14 @@ int32_t AdcRead(DevHandle handle, uint32_t channel, uint32_t *val); -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | -------------- | -| handle | ADC设备句柄 | -| channel | ADC设备通道号 | -| val | AD转换结果 | +| handle | DevHandle类型,ADC设备句柄 | +| channel| uint32_t类型,ADC设备通道号 | +| val | uint32_t类型指针,AD转换结果 | | **返回值** | **返回值描述** | -| 0 | 读取成功 | -| 负数 | 读取失败 | +| HDF_SUCCESS | 读取成功 | +| 负数 | 读取失败 | 读取转换结果示例(以通道1为例): @@ -122,9 +123,9 @@ uint32_t value; int32_t ret; ret = AdcRead(adcHandle, 1, &value); -if (ret != 0) { +if (ret != HDF_SUCCESS) { HDF_LOGE("ADC read fail!\n"); - return; + return ret; } ``` @@ -138,16 +139,16 @@ void AdcClose(DevHandle handle); -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ------ | ----------- | -| handle | ADC设备句柄 | +| handle | DevHandle类型,ADC设备句柄 | | 返回值 | 返回值描述 | -| 无 | 无 | +| 无 | 无 | 关闭ADC设备示例: ```c -AdcClose(adcHandle); /* 关闭ADC设备 */ +AdcClose(adcHandle); // 关闭ADC设备 ``` ### 使用实例 @@ -163,15 +164,15 @@ AdcClose(adcHandle); /* 关闭ADC设备 */ 示例如下: ```c -#include "adc_if.h" /* ADC标准接口头文件 */ -#include "hdf_log.h" /* 标准日志打印头文件 */ +#include "adc_if.h" // ADC标准接口头文件 +#include "hdf_log.h" // 标准日志打印头文件 -/* 设备号0,通道号1 */ +/// 设备号0,通道号1 #define ADC_DEVICE_NUM 0 #define ADC_CHANNEL_NUM 1 #define ADC_TEST_NUM 30 -/* ADC例程总入口 */ +// ADC例程总入口 static int32_t TestCaseAdc(void) { int32_t i; @@ -179,14 +180,14 @@ static int32_t TestCaseAdc(void) DevHandle adcHandle = NULL; uint32_t readBuf[ADC_TEST_NUM] = {0}; - /* 打开ADC设备 */ + // 打开ADC设备 adcHandle = AdcOpen(ADC_DEVICE_NUM); if (adcHandle == NULL) { HDF_LOGE("%s: Open ADC%u fail!", __func__, ADC_DEVICE_NUM); return -1; } - /* 连续进行30次AD转换并读取转换结果 */ + // 连续进行30次AD转换并读取转换结果 for (i = 0; i < ADC_TEST_NUM; i++) { ret = AdcRead(adcHandle, ADC_CHANNEL_NUM, &readBuf[i]); if (ret != HDF_SUCCESS) { @@ -197,7 +198,7 @@ static int32_t TestCaseAdc(void) } HDF_LOGI("%s: ADC read successful!", __func__); - /* 访问完毕关闭ADC设备 */ + // 访问完毕关闭ADC设备 AdcClose(adcHandle); return 0; diff --git a/zh-cn/device-dev/driver/driver-platform-adc-develop.md b/zh-cn/device-dev/driver/driver-platform-adc-develop.md index 21843636451da3b08f8d2d0cfaf437ee2da1bf8f..c254b470ada7afbc80604809f91d79dbe2a195bb 100755 --- a/zh-cn/device-dev/driver/driver-platform-adc-develop.md +++ b/zh-cn/device-dev/driver/driver-platform-adc-develop.md @@ -4,37 +4,42 @@ ### 功能简介 -ADC(Analog to Digital Converter),即模拟-数字转换器,是一种将模拟信号转换成对应数字信号的设备。 +ADC(Analog to Digital Converter),即模拟-数字转换器,可将模拟信号转换成对应的数字信号,便于存储与计算等操作。除电源线和地线之外,ADC只需要1根线与被测量的设备进行连接,其物理连线如图1所示: + +**图 1** ADC物理连线示意图 +![ADC物理连线示意图](figures/ADC物理连线示意图.png) ### 基本概念 - 分辨率 - 分辨率指的是ADC模块能够转换的二进制位数,位数越多分辨率越高。 + 分辨率指的是ADC模块能够转换的二进制位数,位数越多分辨率越高。 - 转换误差 - 转换误差通常是以输出误差的最大值形式给出。它表示A/D转换器实际输出的数字量和理论上的输出数字量之间的差别。常用最低有效位的倍数表示。 + 转换误差通常是以输出误差的最大值形式给出。它表示A/D转换器实际输出的数字量和理论上的输出数字量之间的差别。常用最低有效位的倍数表示。 - 转换时间 - 转换时间是指A/D转换器从转换控制信号到来开始,到输出端得到稳定的数字信号所经过的时间。 + 转换时间是指A/D转换器从转换控制信号到来开始,到输出端得到稳定的数字信号所经过的时间。 ### 运作机制 -在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型配置器),若采用独立服务模式,则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。ADC模块即采用统一服务模式(如图1)。 +在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型配置器),若采用独立服务模式,则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。ADC模块即采用统一服务模式(如图2所示)。 ADC模块各分层的作用为: - 接口层:提供打开设备,写入数据,关闭设备的能力。 + - 核心层:主要负责服务绑定、初始化以及释放管理器,并提供添加、删除以及获取控制器的能力。 + - 适配层:由驱动适配者实现与硬件相关的具体功能,如控制器的初始化等。 在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层,因此这种模式下驱动无需再为每个控制器发布服务。 -**图1** ADC统一服务模式结构图 -![image](figures/统一服务模式结构图.png "ADC统一服务模式结构图") +**图 2** ADC统一服务模式结构图 +![ADC统一服务模式结构图](figures/统一服务模式结构图.png) ## 使用指导 @@ -90,348 +95,374 @@ static const struct AdcLockMethod g_adcLockOpsDefault = { 若实际情况不允许使用Spinlock,驱动适配者可以考虑使用其他类型的锁来实现一个自定义的AdcLockMethod。一旦实现了自定义的AdcLockMethod,默认的AdcLockMethod将被覆盖。 - **表1** AdcMethod结构体成员的钩子函数功能说明 +**表 1** AdcMethod结构体成员的钩子函数功能说明 | 函数成员 | 入参 | 出参 | 返回值 | 功能 | | -------- | -------- | -------- | -------- | -------- | -| read | device:结构体指针,核心层ADC控制器
channel:uint32_t,传入的通道号 | val:uint32_t指针,要传出的信号数据 | HDF_STATUS相关状态 | 读取ADC采样的信号数据 | +| read | device:结构体指针,核心层ADC控制器
channel:uint32_t类型,传入的通道号 | val:uint32_t类型指针,要传出的信号数据 | HDF_STATUS相关状态 | 读取ADC采样的信号数据 | | stop | device:结构体指针,核心层ADC控制器 | 无 | HDF_STATUS相关状态 | 关闭ADC设备 | | start | device:结构体指针,核心层ADC控制器 | 无 | HDF_STATUS相关状态 | 开启ADC设备 | -**表2** AdcLockMethod结构体成员函数功能说明 +**表 2** AdcLockMethod结构体成员函数功能说明 | 函数成员 | 入参 | 出参 | 返回值 | 功能 | | -------- | -------- | -------- | -------- | -------- | | lock | device:结构体指针,核心层ADC设备对象。 | 无 | HDF_STATUS相关状态 | 获取临界区锁 | -| unlock | devicie:结构体指针,核心层ADC设备对象。 | 无 | HDF_STATUS相关状态 | 释放临界区锁 | +| unlock | device:结构体指针,核心层ADC设备对象。 | 无 | HDF_STATUS相关状态 | 释放临界区锁 | ### 开发步骤 -ADC模块适配必选的三个环节是实例化驱动入口,配置属性文件,以及实例化核心层接口函数。 +ADC模块适配包含以下四个步骤: 1. 实例化驱动入口 - - 实例化HdfDriverEntry结构体成员。 - - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 + + - 实例化HdfDriverEntry结构体成员。 + + - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 2. 配置属性文件 - - 在device_info.hcs文件中添加deviceNode描述。 - - 【可选】添加adc_config.hcs器件属性文件。 + + - 在device_info.hcs文件中添加deviceNode描述。 + + - 【可选】添加adc_config.hcs器件属性文件。 3. 实例化核心层接口函数 - - 初始化AdcDevice成员。 - - 实例化AdcDevice成员AdcMethod。 - > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 实例化AdcDevice成员AdcMethod,其定义和成员说明见[接口说明](#接口说明)。 + + - 初始化AdcDevice成员。 + + - 实例化AdcDevice成员AdcMethod。 + + > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 实例化AdcDevice成员AdcMethod,其定义和成员说明见[接口说明](#接口说明)。 + +4. 驱动调试 + + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的测试用例是否成功等。 ### 开发实例 - 接下来以Hi3516DV300的驱动//device/soc/hisilicon/common/platform/adc/adc_hi35xx.c为例, 展示需要驱动适配者提供哪些内容来完整实现设备功能。 +下方将基于Hi3516DV300开发板以//device/soc/hisilicon/common/platform/adc/adc_hi35xx.c驱动为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。 -1. 驱动开发首先需要实例化驱动入口。 +1. 实例化驱动入口 - 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 + 驱动入口必须为HdfDriverEntry(在//drivers/hdf_core/interfaces/inner_api/host/shared/hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 - 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 + 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 - ADC驱动入口参考: + ADC驱动入口参考: - ADC控制器会出现多个设备挂接的情况,因而在HDF框架中首先会为此类型的设备创建一个管理器对象。这样,需要打开某个设备时,管理器对象会根据指定参数查找到指定设备。 + ADC控制器会出现多个设备挂接的情况,因而在HDF框架中首先会为此类型的设备创建一个管理器对象。这样,需要打开某个设备时,管理器对象会根据指定参数查找到指定设备。 - ADC管理器的驱动由核心层实现,驱动适配者不需要关注这部分内容的实现,但在实现Init函数的时候需要调用核心层的AdcDeviceAdd函数,它会实现相应功能。 + ADC管理器的驱动由核心层实现,驱动适配者不需要关注这部分内容的实现,但在实现Init函数的时候需要调用核心层的AdcDeviceAdd函数,它会实现相应功能。 ```c static struct HdfDriverEntry g_hi35xxAdcDriverEntry = { .moduleVersion = 1, .Init = Hi35xxAdcInit, .Release = Hi35xxAdcRelease, - .moduleName = "hi35xx_adc_driver", //【必要且与device_info.hcs文件内的模块名匹配】 + .moduleName = "hi35xx_adc_driver", // 【必要且与device_info.hcs文件内的模块名匹配】 }; HDF_INIT(g_hi35xxAdcDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 - - /* 核心层adc_core.c管理器服务的驱动入口 */ + + // 核心层adc_core.c管理器服务的驱动入口 struct HdfDriverEntry g_adcManagerEntry = { .moduleVersion = 1, - .Init = AdcManagerInit, - .Release = AdcManagerRelease, + .Bind = AdcManagerBind, // ADC不需要实现Bind,本例是一个空实现,驱动适配者可根据自身需要添加相关操作 + .Init = AdcManagerInit, // 见Init参考 + .Release = AdcManagerRelease, // 见Release参考 .moduleName = "HDF_PLATFORM_ADC_MANAGER", // 这与device_info.hcs文件中device0对应 }; - HDF_INIT(g_adcManagerEntry); + HDF_INIT(g_adcManagerEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 ``` -2. 完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在adc_config.hcs中配置器件属性。 +2. 配置属性文件 + + 完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在adc_config.hcs中配置器件属性。 deviceNode信息与驱动入口注册相关,器件属性值对于驱动适配者的驱动实现以及核心层AdcDevice相关成员的默认值或限制范围有密切关系。 - 统一服务模式的特点是device_info.hcs文件中第一个设备节点必须为ADC管理器,其各项参数必须如下设置: + 统一服务模式的特点是device_info.hcs文件中第一个设备节点必须为ADC管理器,其各项参数如表3所示: + + **表 3** device_info.hcs节点参数说明 | 成员名 | 值 | | -------- | -------- | - | moduleName | 固定为HDF_PLATFORM_ADC_MANAGER | - | serviceName | 无 | - | policy | 具体配置为0,不发布服务 | - | deviceMatchAttr | 没有使用,可忽略 | - - 从第二个节点开始配置具体ADC控制器信息,第一个节点并不表示某一路ADC控制器,而是代表一个资源性质设备,用于描述一类ADC控制器的信息。本例只有一个ADC设备,如有多个设备,则需要在device_info.hcs文件增加deviceNode信息,以及在adc_config文件中增加对应的器件属性。 - - - device_info.hcs配置参考 - - ```c - root { - device_info { - platform :: host { - device_adc :: device { - device0 :: deviceNode { - policy = 0; - priority = 50; - permission = 0644; - moduleName = "HDF_PLATFORM_ADC_MANAGER"; - serviceName = "HDF_PLATFORM_ADC_MANAGER"; - } - device1 :: deviceNode { - policy = 0; // 等于0,不需要发布服务。 - priority = 55; // 驱动启动优先级。 - permission = 0644; // 驱动创建设备节点权限。 - moduleName = "hi35xx_adc_driver"; //【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。 - serviceName = "HI35XX_ADC_DRIVER"; //【必要】驱动对外发布服务的名称,必须唯一。 - deviceMatchAttr = "hisilicon_hi35xx_adc"; //【必要】用于配置控制器私有数据,要与adc_config.hcs中对应控制器保持一致, - // 具体的控制器信息在adc_config.hcs中。 - } - } - } - } - } - ``` - - - adc_config.hcs配置参考 - - 此处以Hi3516DV300为例,给出HCS配置参考。其中部分字段为Hi3516DV300特有功能,驱动适配者可根据需要进行删除或添加字段。 - - ```c - root { - platform { - adc_config_hi35xx { - match_attr = "hisilicon_hi35xx_adc"; - template adc_device { - regBasePhy = 0x120e0000; // 寄存器物理基地址 - regSize = 0x34; // 寄存器位宽 - deviceNum = 0; // 设备号 - validChannel = 0x1; // 有效通道 - dataWidth = 10; // AD转换后的数据位宽,即分辨率 - scanMode = 1; // 扫描模式 - delta = 0; // 转换结果误差范围 - deglitch = 0; // 滤毛刺开关 - glitchSample = 5000; // 滤毛刺时间窗口 - rate = 20000; // 转换速率 - } - device_0 :: adc_device { - deviceNum = 0; - validChannel = 0x2; - } - } - } - } - ``` - - 需要注意的是,新增adc_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。 - - 例如:本例中adc_config.hcs所在路径为//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/adc/adc_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句: - - ```c - #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/adc/adc_config.hcs" // 配置文件相对路径 - ``` - - 本例基于Hi3516DV300开发板的小型系统LiteOS内核运行,对应的hdf.hcs文件路径为vendor/hisilicon/hispark_taurus/hdf_config/hdf.hcs以及//device/hisilicon/hispark_taurus/sdk_liteos/hdf_config/hdf.hcs。驱动适配者需根据实际情况选择对应路径下的文件进行修改。 - -3. 完成驱动入口注册之后,下一步就是以核心层AdcDevice对象的初始化为核心,包括初始化驱动适配者自定义结构体(传递参数和数据),实例化AdcDevice成员AdcMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。 - - - 自定义结构体参考。 - - 从驱动的角度看,自定义结构体是参数和数据的载体,而且adc_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,其中一些重要数值(例如设备号、总线号等)也会传递给核心层AdcDevice对象。 - - ```c - struct Hi35xxAdcDevice { - struct AdcDevice device; //【必要】是核心层控制对象,必须作为自定义结构体的首个成员,其具体描述见下方。 - volatile unsigned char *regBase; //【必要】寄存器基地址 - volatile unsigned char *pinCtrlBase; - uint32_t regBasePhy; //【必要】寄存器物理基地址 - uint32_t regSize; //【必要】寄存器位宽 - uint32_t deviceNum; //【必要】设备号 - uint32_t dataWidth; //【必要】信号接收的数据位宽 - uint32_t validChannel; //【必要】有效通道 - uint32_t scanMode; //【必要】扫描模式 - uint32_t delta; - uint32_t deglitch; - uint32_t glitchSample; - uint32_t rate; //【必要】采样率 - }; - - /* AdcDevice是核心层控制器结构体,其中的成员在Init函数中会被赋值。*/ - struct AdcDevice { - const struct AdcMethod *ops; - OsalSpinlock spin; - uint32_t devNum; - uint32_t chanNum; - const struct AdcLockMethod *lockOps; - void *priv; - }; - ``` - - - AdcDevice成员钩子函数结构体AdcMethod的实例化。 - - AdcLockMethod钩子函数结构体本例未实现,若要实例化,可参考I2C驱动开发,其他成员在Init函数中初始化。 - - ```c - static const struct AdcMethod g_method = { - .read = Hi35xxAdcRead, - .stop = Hi35xxAdcStop, - .start = Hi35xxAdcStart, - }; - ``` - - - Init函数开发参考 - - 入参: - - HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 - - 返回值: - - HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 - - | 状态(值) | 问题描述 | - | -------- | -------- | - | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | - | HDF_ERR_INVALID_PARAM | 参数非法 | - | HDF_ERR_MALLOC_FAIL | 内存分配失败 | - | HDF_ERR_IO | I/O错误 | - | HDF_SUCCESS | 传输成功 | - | HDF_FAILURE | 传输失败 | - - 函数说明: - - 初始化自定义结构体对象,初始化AdcDevice成员,并调用核心层AdcDeviceAdd函数。 - - ```c - static int32_t Hi35xxAdcInit(struct HdfDeviceObject *device) - { - int32_t ret; - struct DeviceResourceNode *childNode = NULL; - ... - /* 遍历、解析adc_config.hcs中的所有配置节点,并分别调用Hi35xxAdcParseInit函数来初始化device。*/ - DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { - ret = Hi35xxAdcParseInit(device, childNode); // 函数定义见下方 - ... - } - return ret; - } - - static int32_t Hi35xxAdcParseInit(struct HdfDeviceObject *device, struct DeviceResourceNode *node) - { - int32_t ret; - struct Hi35xxAdcDevice *hi35xx = NULL; //【必要】自定义结构体对象 - (void)device; - - hi35xx = (struct Hi35xxAdcDevice *)OsalMemCalloc(sizeof(*hi35xx)); //【必要】内存分配 - ... - ret = Hi35xxAdcReadDrs(hi35xx, node); //【必要】将adc_config文件的默认值填充到结构体中,函数定义见下方 - ... - hi35xx->regBase = OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize); //【必要】地址映射 - ... - hi35xx->pinCtrlBase = OsalIoRemap(HI35XX_ADC_IO_CONFIG_BASE, HI35XX_ADC_IO_CONFIG_SIZE); - ... - Hi35xxAdcDeviceInit(hi35xx); //【必要】ADC设备的初始化 - hi35xx->device.priv = (void *)node; //【必要】存储设备属性 - hi35xx->device.devNum = hi35xx->deviceNum; //【必要】初始化AdcDevice成员 - hi35xx->device.ops = &g_method; //【必要】AdcMethod的实例化对象的挂载 - ret = AdcDeviceAdd(&hi35xx->device); //【必要且重要】调用此函数填充核心层结构体,返回成功信号后驱动才完全接入平台核心层。 - ... - return HDF_SUCCESS; - - __ERR__: - if (hi35xx != NULL) { // 若不成功,需要执行去初始化相关函数。 - if (hi35xx->regBase != NULL) { - OsalIoUnmap((void *)hi35xx->regBase); - hi35xx->regBase = NULL; - } - AdcDeviceRemove(&hi35xx->device); - OsalMemFree(hi35xx); - } - return ret; - } - - static int32_t Hi35xxAdcReadDrs(struct Hi35xxAdcDevice *hi35xx, const struct DeviceResourceNode *node) - { - int32_t ret; - struct DeviceResourceIface *drsOps = NULL; + | policy | 驱动服务发布的策略,ADC管理器具体配置为2,表示驱动对内核态和用户态都发布服务 | + | priority | 驱动启动优先级(0-200),值越大优先级越低。ADC管理器具体配置为50 | + | permission | 驱动创建设备节点权限,ADC管理器具体配置为0664 | + | moduleName | 驱动名称,ADC管理器固定为HDF_PLATFORM_ADC_MANAGER | + | serviceName | 驱动对外发布服务的名称,ADC管理器服务名设置为HDF_PLATFORM_ADC_MANAGER | + | deviceMatchAttr | 驱动私有数据匹配的关键字,ADC管理器没有使用,可忽略 | + + 从第二个节点开始配置具体ADC控制器信息,第一个节点并不表示某一路ADC控制器,而是代表一个资源性质设备,用于描述一类ADC控制器的信息。本例只有一个ADC设备,如有多个设备,则需要在device_info.hcs文件增加deviceNode信息,以及在adc_config.hcs文件中增加对应的器件属性。 + + - device_info.hcs配置参考 + + ```c + root { + device_info { + platform :: host { + device_adc :: device { + device0 :: deviceNode { + policy = 2; + priority = 50; + permission = 0644; + moduleName = "HDF_PLATFORM_ADC_MANAGER"; + serviceName = "HDF_PLATFORM_ADC_MANAGER"; + } + device1 :: deviceNode { + policy = 0; // 等于0,不需要发布服务。 + priority = 55; // 驱动启动优先级。 + permission = 0644; // 驱动创建设备节点权限。 + moduleName = "hi35xx_adc_driver"; //【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。 + serviceName = "HI35XX_ADC_DRIVER"; //【必要】驱动对外发布服务的名称,必须唯一。 + deviceMatchAttr = "hisilicon_hi35xx_adc"; //【必要】用于配置控制器私有数据,要与adc_config.hcs中对应控制器保持一致,具体的控制器信息在adc_config.hcs中。 + } + } + } + } + } + ``` + + - adc_config.hcs配置参考 + + 此处以Hi3516DV300为例,给出HCS配置参考。其中部分字段为Hi3516DV300特有功能,驱动适配者可根据需要进行删除或添加字段。 + + ```c + root { + platform { + adc_config_hi35xx { + match_attr = "hisilicon_hi35xx_adc"; + template adc_device { + regBasePhy = 0x120e0000; // 寄存器物理基地址 + regSize = 0x34; // 寄存器位宽 + deviceNum = 0; // 设备号 + validChannel = 0x1; // 有效通道 + dataWidth = 10; // AD转换后的数据位宽,即分辨率 + scanMode = 1; // 扫描模式 + delta = 0; // 转换结果误差范围 + deglitch = 0; // 滤毛刺开关 + glitchSample = 5000; // 滤毛刺时间窗口 + rate = 20000; // 转换速率 + } + device_0 :: adc_device { + deviceNum = 0; + validChannel = 0x2; + } + } + } + } + ``` + + 需要注意的是,新增adc_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。 + + 例如:本例中adc_config.hcs所在路径为//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/adc/adc_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句: + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/adc/adc_config.hcs" // 配置文件相对路径 + ``` + + 本例基于Hi3516DV300开发板的小型系统LiteOS内核运行,对应的hdf.hcs文件路径为vendor/hisilicon/hispark_taurus/hdf_config/hdf.hcs以及//device/hisilicon/hispark_taurus/sdk_liteos/hdf_config/hdf.hcs。驱动适配者需根据实际情况选择对应路径下的文件进行修改。 + +3. 实例化核心层函数 + + 完成驱动入口注册之后,下一步就是以核心层AdcDevice对象的初始化为核心,包括初始化驱动适配者自定义结构体(传递参数和数据),实例化AdcDevice成员AdcMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。 + + - 自定义结构体参考。 + + 从驱动的角度看,自定义结构体是参数和数据的载体,而且adc_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,其中一些重要数值(例如设备号、总线号等)也会传递给核心层AdcDevice对象。 + + ```c + struct Hi35xxAdcDevice { + struct AdcDevice device; // 【必要】是核心层控制对象,必须作为自定义结构体的首个成员,其具体描述见下方。 + volatile unsigned char *regBase; // 【必要】寄存器基地址 + volatile unsigned char *pinCtrlBase; + uint32_t regBasePhy; // 【必要】寄存器物理基地址 + uint32_t regSize; // 【必要】寄存器位宽 + uint32_t deviceNum; // 【必要】设备号 + uint32_t dataWidth; // 【必要】信号接收的数据位宽 + uint32_t validChannel; // 【必要】有效通道 + uint32_t scanMode; // 【必要】扫描模式 + uint32_t delta; + uint32_t deglitch; + uint32_t glitchSample; + uint32_t rate; // 【必要】采样率 + }; + + // AdcDevice是核心层控制器结构体,其中的成员在Init函数中会被赋值。 + struct AdcDevice { + const struct AdcMethod *ops; + OsalSpinlock spin; + uint32_t devNum; + uint32_t chanNum; + const struct AdcLockMethod *lockOps; + void *priv; + }; + ``` + + - AdcDevice成员钩子函数结构体AdcMethod的实例化。 + + AdcLockMethod钩子函数结构体本例未实现,若要实例化,可参考I2C驱动开发。 + + ```c + static const struct AdcMethod g_method = { + .read = Hi35xxAdcRead, + .stop = Hi35xxAdcStop, + .start = Hi35xxAdcStart, + }; + ``` + + - Init函数开发参考 + + 入参: + + HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 + + 返回值: + + HDF_STATUS相关状态(表4为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 + + **表 4** HDF_STATUS相关状态说明 + + | 状态(值) | 问题描述 | + | -------- | -------- | + | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | + | HDF_ERR_INVALID_PARAM | 参数非法 | + | HDF_ERR_MALLOC_FAIL | 内存分配失败 | + | HDF_ERR_IO | I/O错误 | + | HDF_SUCCESS | 传输成功 | + | HDF_FAILURE | 传输失败 | + + 函数说明: + + 初始化自定义结构体对象,初始化AdcDevice成员,并调用核心层AdcDeviceAdd函数。 - /* 获取drsOps方法 */ - drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - if (drsOps == NULL || drsOps->GetUint32 == NULL) { - HDF_LOGE("%s: invalid drs ops", __func__); - return HDF_ERR_NOT_SUPPORT; - } - /* 将配置参数依次读出,并填充至结构体中 */ - ret = drsOps->GetUint32(node, "regBasePhy", &hi35xx->regBasePhy, 0); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read regBasePhy failed", __func__); - return ret; - } - ret = drsOps->GetUint32(node, "regSize", &hi35xx->regSize, 0); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read regSize failed", __func__); - return ret; - } - ··· - return HDF_SUCCESS; - } - ``` - - - Release函数开发参考 - - 入参: - - HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 - - 返回值: - - 无。 - - 函数说明: - - 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 - - ```c - static void Hi35xxAdcRelease(struct HdfDeviceObject *device) - { - const struct DeviceResourceNode *childNode = NULL; - ... - /* 遍历、解析adc_config.hcs中的所有配置节点,并分别进行Release操作。*/ - DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { - Hi35xxAdcRemoveByNode(childNode);// 函数定义见下 - } - } + ```c + static int32_t Hi35xxAdcInit(struct HdfDeviceObject *device) + { + int32_t ret; + struct DeviceResourceNode *childNode = NULL; + ...... + // 遍历、解析adc_config.hcs中的所有配置节点,并分别调用Hi35xxAdcParseInit函数来初始化device。 + DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { + ret = Hi35xxAdcParseInit(device, childNode); // 函数定义见下方 + ...... + } + HDF_LOGI("%s: adc init success.", __func__); + return ret; + } + + static int32_t Hi35xxAdcParseInit(struct HdfDeviceObject *device, struct DeviceResourceNode *node) + { + int32_t ret; + struct Hi35xxAdcDevice *hi35xx = NULL; //【必要】自定义结构体对象 + + (void)device; + hi35xx = (struct Hi35xxAdcDevice *)OsalMemCalloc(sizeof(*hi35xx)); //【必要】内存分配 + ...... + ret = Hi35xxAdcReadDrs(hi35xx, node); //【必要】将adc_config文件的默认值填充到结构体中,函数定义见下方 + ...... + hi35xx->regBase = OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize); //【必要】地址映射 + ...... + hi35xx->pinCtrlBase = OsalIoRemap(HI35XX_ADC_IO_CONFIG_BASE, HI35XX_ADC_IO_CONFIG_SIZE); + ...... + Hi35xxAdcDeviceInit(hi35xx); // 【必要】ADC设备的初始化 + hi35xx->device.priv = (void *)node; // 【必要】存储设备属性 + hi35xx->device.devNum = hi35xx->deviceNum; // 【必要】初始化AdcDevice成员 + hi35xx->device.ops = &g_method; // 【必要】AdcMethod的实例化对象的挂载 + ret = AdcDeviceAdd(&hi35xx->device); // 【必要且重要】调用此函数填充核心层结构体,返回成功信号后驱动才完全接入平台核心层。 + ...... + return HDF_SUCCESS; + + __ERR__: + if (hi35xx != NULL) { // 若不成功,需要执行去初始化相关函数。 + if (hi35xx->regBase != NULL) { + OsalIoUnmap((void *)hi35xx->regBase); + hi35xx->regBase = NULL; + } + AdcDeviceRemove(&hi35xx->device); + OsalMemFree(hi35xx); + } + return ret; + } + + static int32_t Hi35xxAdcReadDrs(struct Hi35xxAdcDevice *hi35xx, const struct DeviceResourceNode *node) + { + int32_t ret; + struct DeviceResourceIface *drsOps = NULL; + + // 获取drsOps方法 + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + if (drsOps == NULL || drsOps->GetUint32 == NULL) { + HDF_LOGE("%s: invalid drs ops", __func__); + return HDF_ERR_NOT_SUPPORT; + } + // 将配置参数依次读出,并填充至结构体中 + ret = drsOps->GetUint32(node, "regBasePhy", &hi35xx->regBasePhy, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read regBasePhy failed", __func__); + return ret; + } + ret = drsOps->GetUint32(node, "regSize", &hi35xx->regSize, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read regSize failed", __func__); + return ret; + } + ...... + return HDF_SUCCESS; + } + ``` + + - Release函数开发参考 + + 入参: + + HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 + + 返回值: + + 无。 + + 函数说明: + + 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 + + ```c + static void Hi35xxAdcRelease(struct HdfDeviceObject *device) + { + const struct DeviceResourceNode *childNode = NULL; + ...... + // 遍历、解析adc_config.hcs中的所有配置节点,并分别进行Release操作。 + DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { + Hi35xxAdcRemoveByNode(childNode);// 函数定义见下 + } + } - static void Hi35xxAdcRemoveByNode(const struct DeviceResourceNode *node) - { - int32_t ret; - int32_t deviceNum; - struct AdcDevice *device = NULL; - struct Hi35xxAdcDevice *hi35xx = NULL; - struct DeviceResourceIface *drsOps = NULL; - - drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - ... - ret = drsOps->GetUint32(node, "deviceNum", (uint32_t *)&deviceNum, 0); - ... - /* 可以调用AdcDeviceGet函数通过设备的deviceNum获取AdcDevice对象,以及调用AdcDeviceRemove函数来释放AdcDevice对象的内容。*/ - device = AdcDeviceGet(deviceNum); - if (device != NULL && device->priv == node) { - AdcDevicePut(device); - AdcDeviceRemove(device); //【必要】主要是从管理器驱动那边移除AdcDevice对象。 - hi35xx = (struct Hi35xxAdcDevice *)device; //【必要】通过强制转换获取自定义的对象并进行Release操作。这一步的前提是device必须作为自定义结构体的首个成员。 - OsalIoUnmap((void *)hi35xx->regBase); - OsalMemFree(hi35xx); - } - return; - } - ``` + static void Hi35xxAdcRemoveByNode(const struct DeviceResourceNode *node) + { + int32_t ret; + int32_t deviceNum; + struct AdcDevice *device = NULL; + struct Hi35xxAdcDevice *hi35xx = NULL; + struct DeviceResourceIface *drsOps = NULL; + + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + ...... + ret = drsOps->GetUint32(node, "deviceNum", (uint32_t *)&deviceNum, 0); + ...... + // 可以调用AdcDeviceGet函数通过设备的deviceNum获取AdcDevice对象,以及调用AdcDeviceRemove函数来释放AdcDevice对象的内容。 + device = AdcDeviceGet(deviceNum); + if (device != NULL && device->priv == node) { + AdcDevicePut(device); + AdcDeviceRemove(device); //【必要】主要是从管理器驱动那边移除AdcDevice对象。 + hi35xx = (struct Hi35xxAdcDevice *)device; //【必要】通过强制转换获取自定义的对象并进行Release操作。这一步的前提是device必须作为自定义结构体的首个成员。 + OsalIoUnmap((void *)hi35xx->regBase); + OsalMemFree(hi35xx); + } + return; + } + ``` + +4. 驱动调试 + + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的测试用例是否成功等。 diff --git a/zh-cn/device-dev/driver/driver-platform-dac-des.md b/zh-cn/device-dev/driver/driver-platform-dac-des.md index 4f4e4b47f12e04e5559af1794dc0ac2dde1e2b4b..6922c00e85ea14558147ca2b278785d5783a4a57 100644 --- a/zh-cn/device-dev/driver/driver-platform-dac-des.md +++ b/zh-cn/device-dev/driver/driver-platform-dac-des.md @@ -7,29 +7,32 @@ DAC(Digital to Analog Converter)是一种通过电流、电压或电荷的形式将数字信号转换为模拟信号的设备,主要用于: - 作为过程控制计算机系统的输出通道,与执行器相连,实现对生产过程的自动控制。 + - 在利用反馈技术的模数转换器设计中,作为重要的功能模块呈现。 DAC接口定义了完成DAC传输的通用方法集合,包括: + - DAC设备管理:打开或关闭DAC设备。 + - DAC设置目标值:设置DAC设备需要将数字信号转成模拟信号的目标值。 ### 基本概念 - 分辨率 - 分辨率指的是DAC模块能够转换的二进制位数,位数越多分辨率越高。 + 分辨率指的是DAC模块能够转换的二进制位数,位数越多分辨率越高。 - 转换精度 - 精度是指输入端加有最大数值时,DAC的实际输出值和理论计算值之差,DAC转换器的转换精度与DAC转换器的集成芯片结构和接口电路配置有关。理想情况下,DAC的转换精度越小越好,因此为了获得更高精度的DAC转换结果,首先要保证选择的DAC转换器具备足够高的分辨率。其次,接口电路的器件或电源存在误差时,会造成DAC转换的误差,若这些误差超过一定程度,就会导致DAC转换错误。 + 精度是指输入端加有最大数值时,DAC的实际输出值和理论计算值之差,DAC转换器的转换精度与DAC转换器的集成芯片结构和接口电路配置有关。理想情况下,DAC的转换精度越小越好,因此为了获得更高精度的DAC转换结果,首先要保证选择的DAC转换器具备足够高的分辨率。其次,接口电路的器件或电源存在误差时,会造成DAC转换的误差,若这些误差超过一定程度,就会导致DAC转换错误。 - 转换速度 - 转换速度一般由建立时间决定。从输入由全0突变为全1时开始,到输出电压稳定在FSR±½LSB范围(或以FSR±x%FSR指明范围)内为止,这段时间称为建立时间,它是DAC的最大响应时间,所以用它衡量转换速度的快慢。 + 转换速度一般由建立时间决定。从输入由全0突变为全1时开始,到输出电压稳定在FSR±½LSB范围(或以FSR±x%FSR指明范围)内为止,这段时间称为建立时间,它是DAC的最大响应时间,所以用它衡量转换速度的快慢。 - 满量程范围FSR(Full Scale Range),是指DAC输出信号幅度的最大范围,不同的DAC有不同的满量程范围,该范围可以用正、负电流或者正、负电压来限制。 + 满量程范围FSR(Full Scale Range),是指DAC输出信号幅度的最大范围,不同的DAC有不同的满量程范围,该范围可以用正、负电流或者正、负电压来限制。 - 最低有效位LSB(Least Significant Byte),指的是一个二进制数字中的第0位(即最低位)。 + 最低有效位LSB(Least Significant Byte),指的是一个二进制数字中的第0位(即最低位)。 ### 运作机制 @@ -59,10 +62,10 @@ DAC模块提供的主要接口如下所示,具体API详见//drivers/hdf_core/f **表 1** DAC驱动API接口功能介绍 -| 接口名 | 接口描述 | +| 接口名 | 接口描述 | | ------------------------------------------------------------------ | ------------ | -| DevHandle DacOpen(uint32_t number) | 打开DAC设备。 | -| void DacClose(DevHandle handle) | 关闭DAC设备。 | +| DevHandle DacOpen(uint32_t number) | 打开DAC设备。| +| void DacClose(DevHandle handle) | 关闭DAC设备。| | int32_t DacWrite(DevHandle handle, uint32_t channel, uint32_t val) | 设置DA目标值。 | ### 开发步骤 @@ -76,78 +79,80 @@ DAC模块提供的主要接口如下所示,具体API详见//drivers/hdf_core/f 在进行DA转换之前,首先要调用DacOpen打开DAC设备,打开函数如下所示: -```c++ +```c DevHandle DacOpen(uint32_t number); ``` **表 2** DacOpen参数和返回值描述 -| 参数 | 参数描述 | +| 参数 | 参数描述 | | --------- | ---------------- | -| number | DAC设备号。 | -| **返回值** | **返回值描述** | -| NULL | 打开DAC设备失败。 | -| 设备句柄 | 打开的DAC设备句柄。 | +| number | uint32_t类型,DAC设备号。 | +| **返回值** | **返回值描述** | +| NULL | 打开DAC设备失败。 | +| 设备句柄 | 打开的DAC设备句柄。 | 假设系统中存在2个DAC设备,编号从0到1,现在打开1号设备。 -```c++ -DevHandle dacHandle = NULL; // DAC设备句柄 +```c +DevHandle dacHandle = NULL; // DAC设备句柄 -/* 打开DAC设备 */ +// 打开DAC设备 dacHandle = DacOpen(1); if (dacHandle == NULL) { - HDF_LOGE("DacOpen: failed\n"); - return; + HDF_LOGE("DacOpen: open dac fail.\n"); + return NULL; } ``` #### 设置DA目标值 -```c++ +```c int32_t DacWrite(DevHandle handle, uint32_t channel, uint32_t val); ``` **表 3** DacWrite参数和返回值描述 -| 参数 | 参数描述 | +| 参数 | 参数描述 | | --------- | ------------ | -| handle | DAC设备句柄。 | -| channel | DAC设备通道号。| -| val | 设置DA的值。 | +| handle | DevHandle类型,DAC设备句柄。 | +| channel | uint32_t类型,DAC设备通道号。 | +| val | uint32_t类型,设置DA的值。 | | **返回值** | **返回值描述** | -| 0 | 写入成功。 | -| 负数 | 写入失败。 | +| HDF_SUCCESS | 写入DA目标值成功 | +| 负数 | 写入DA目标值失败 | -```c++ -/* 通过DAC_CHANNEL_NUM设备通道写入目标val值 */ +```c +// 通过DAC_CHANNEL_NUM设备通道写入目标val值 +int32_t ret; ret = DacWrite(dacHandle, DAC_CHANNEL_NUM, val); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: tp DAC write reg fail!:%d", __func__, ret); + HDF_LOGE("DacWrite: tp DAC write reg fail!,ret:%d", ret); DacClose(dacHandle); - return -1; + return ret; } ``` #### 关闭DAC设备 DAC通信完成之后,需要关闭DAC设备,关闭函数如下所示: -```c++ + +```c void DacClose(DevHandle handle); ``` **表 4** DacClose参数和返回值描述 -| 参数 | 参数描述 | +| 参数 | 参数描述 | | --------- | ------------ | -| handle | DAC设备句柄。 | +| handle | DAC设备句柄。 | | **返回值** | **返回值描述** | -| void | 无 | +| 无 | 无 | 关闭DAC设备示例: -```c++ -DacClose(dacHandle); /* 关闭DAC设备 */ +```c +DacClose(dacHandle); // 关闭DAC设备 ``` ## 使用实例 @@ -155,20 +160,22 @@ DacClose(dacHandle); /* 关闭DAC设备 */ DAC设备的具体使用方式可以参考如下示例代码,示例代码步骤主要如下: 1. 根据设备号DAC_DEVICE_NUM打开DAC设备得到设备句柄。 + 2. 通过DAC的设备号以及设备通道设置val的值,如果写入失败则关闭设备句柄。 + 3. 访问完毕DAC设备后,则关闭该设备句柄。 运行结果:根据输入的val通过打印日志得到输出的结果。 -```c++ -#include "dac_if.h" /* DAC标准接口头文件 */ -#include "hdf_log.h" /* 标准日志打印头文件 */ +```c +#include "dac_if.h" // DAC标准接口头文件 +#include "hdf_log.h" // 标准日志打印头文件 -/* 设备号0,通道号1 */ +// 设备号0,通道号1 #define DAC_DEVICE_NUM 0 #define DAC_CHANNEL_NUM 1 -/* DAC例程总入口 */ +// DAC例程总入口 static int32_t TestCaseDac(void) { // 设置要写入的val值 @@ -176,14 +183,14 @@ static int32_t TestCaseDac(void) int32_t ret; DevHandle dacHandle; - /* 打开DAC设备 */ + // 打开DAC设备 dacHandle = DacOpen(DAC_DEVICE_NUM); if (dacHandle == NULL) { HDF_LOGE("%s: Open DAC%u fail!", __func__, DAC_DEVICE_NUM); return -1; } - /* 写入数据 */ + // 写入数据 ret = DacWrite(dacHandle, DAC_CHANNEL_NUM, val); if (ret != HDF_SUCCESS) { HDF_LOGE("%s: tp DAC write reg fail!:%d", __func__, ret); @@ -191,9 +198,9 @@ static int32_t TestCaseDac(void) return -1; } - /* 访问完毕关闭DAC设备 */ + // 访问完毕关闭DAC设备 DacClose(dacHandle); - + HDF_LOGI("%s: function tests end.", __func__); return 0; } -``` \ No newline at end of file +``` diff --git a/zh-cn/device-dev/driver/driver-platform-dac-develop.md b/zh-cn/device-dev/driver/driver-platform-dac-develop.md index b2d5bfb0cb57d7910bbdc5dc26a616b24c569f26..93f6fd2463cefe8e4c47aa5dcc7808f8fd439ab8 100644 --- a/zh-cn/device-dev/driver/driver-platform-dac-develop.md +++ b/zh-cn/device-dev/driver/driver-platform-dac-develop.md @@ -9,42 +9,46 @@ DAC(Digital to Analog Converter)是一种通过电流、电压或电荷的 DAC模块支持数模转换的开发。它主要用于: - 作为过程控制计算机系统的输出通道,与执行器相连,实现对生产过程的自动控制。 + - 在利用反馈技术的模数转换器设计中,作为重要的功能模块呈现。 ### 基本概念 - 分辨率 - 分辨率指的是D/A转换器能够转换的二进制位数,位数越多分辨率越高。 + 分辨率指的是D/A转换器能够转换的二进制位数,位数越多分辨率越高。 - 转换精度 - 精度是指输入端加有最大数值时,DAC的实际输出值和理论计算值之差,DAC转换器的转换精度与DAC转换器的集成芯片结构和接口电路配置有关。理想情况下,DAC的转换精度越小越好,因此为了获得更高精度的DAC转换结果,首先要保证选择的DAC转换器具备足够高的分辨率。其次,要保证接口电路的器件或电源误差最小或者不存在误差,否则会造成DAC转换的误差,若这些误差超过一定程度,就会导致DAC转换错误。 + 精度是指输入端加有最大数值时,DAC的实际输出值和理论计算值之差,DAC转换器的转换精度与DAC转换器的集成芯片结构和接口电路配置有关。理想情况下,DAC的转换精度越小越好,因此为了获得更高精度的DAC转换结果,首先要保证选择的DAC转换器具备足够高的分辨率。其次,要保证接口电路的器件或电源误差最小或者不存在误差,否则会造成DAC转换的误差,若这些误差超过一定程度,就会导致DAC转换错误。 - 转换速度 - 转换速度一般由建立时间决定。从输入由全0突变为全1时开始,到输出电压稳定在FSR±½LSB范围(或以FSR±x%FSR指明范围)内为止,这段时间称为建立时间,它是DAC的最大响应时间,所以用它衡量转换速度的快慢。 + 转换速度一般由建立时间决定。从输入由全0突变为全1时开始,到输出电压稳定在FSR±½LSB范围(或以FSR±x%FSR指明范围)内为止,这段时间称为建立时间,它是DAC的最大响应时间,所以用它衡量转换速度的快慢。 - - 满量程范围FSR(Full Scale Range),是指DAC输出信号幅度的最大范围,不同的DAC有不同的满量程范围,该范围可以用正、负电流或者正、负电压来限制。 + - 满量程范围FSR(Full Scale Range),是指DAC输出信号幅度的最大范围,不同的DAC有不同的满量程范围,该范围可以用正、负电流或者正、负电压来限制。 - - 最低有效位LSB(Least Significant Byte),指的是一个二进制数字中的第0位(即最低位)。 + - 最低有效位LSB(Least Significant Byte),指的是一个二进制数字中的第0位(即最低位)。 ### 运作机制 -在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型配置器),若采用独立服务模式,则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。DAC模块即采用统一服务模式(如图1)。 +在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型配置器),若采用独立服务模式,则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。DAC模块即采用统一服务模式(如图1所示)。 DAC模块各分层的作用为: + - 接口层:提供打开设备、写入数据和关闭设备接口的能力。 + - 核心层:主要提供绑定设备、初始化设备以及释放设备的能力。 + - 适配层:由驱动适配者实现与硬件相关的具体功能,如控制器的初始化等。 在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层,因此这种模式下驱动无需再为每个控制器发布服务。 ![](../public_sys-resources/icon-note.gif) 说明:
核心层可以调用接口层的函数,也可以通过钩子函数调用适配层函数,从而使得适配层间接的可以调用接口层函数,但是不可逆转接口层调用适配层函数。 -**图 1** 统一服务模式结构图 +**图 1** DAC统一服务模式结构图 -![](figures/统一服务模式结构图.png "DAC统一服务模式") +![DAC统一服务模式结构图](figures/统一服务模式结构图.png) ### 约束与限制 @@ -62,13 +66,13 @@ DAC模块主要在设备中数模转换、音频输出和电机控制等设备 DacMethod和DacLockMethod定义: -```c++ +```c struct DacMethod { - /* 写入数据的钩子函数 */ + // 写入数据的钩子函数 int32_t (*write)(struct DacDevice *device, uint32_t channel, uint32_t val); - /* 启动DAC设备的钩子函数 */ + // 启动DAC设备的钩子函数 int32_t (*start)(struct DacDevice *device); - /* 停止DAC设备的钩子函数 */ + // 停止DAC设备的钩子函数 int32_t (*stop)(struct DacDevice *device); }; @@ -77,6 +81,7 @@ struct DacLockMethod { void (*unlock)(struct DacDevice *device); }; ``` + 在适配层中,DacMethod必须被实现,DacLockMethod可根据实际情况考虑是否实现。核心层提供了默认的DacLockMethod,其中使用Spinlock作为保护临界区的锁: ```c @@ -106,76 +111,91 @@ static const struct DacLockMethod g_dacLockOpsDefault = { 若实际情况不允许使用Spinlock,驱动适配者可以考虑使用其他类型的锁来实现一个自定义的DacLockMethod。一旦实现了自定义的DacLockMethod,默认的DacLockMethod将被覆盖。 -**表 1** DacMethod结构体成员的钩子函数功能说明 +**表 1** DacMethod结构体成员的钩子函数功能说明 -| 函数成员 | 入参 | 出参 | 返回值 | 功能 | +| 函数成员 | 入参 | 出参 | 返回值 | 功能 | | -------- | ------------------------------------------------------------ | ---- | ------------------ | -------------- | -| write | device:结构体指针,核心层DAC控制器
channel:uint32_t,传入的通道号
val:uint32_t,要传入的数据 | 无 | HDF_STATUS相关状态 | 写入DA的目标值 | -| start | device:结构体指针,核心层DAC控制器 | 无 | HDF_STATUS相关状态 | 开启DAC设备 | -| stop | device:结构体指针,核心层DAC控制器 | 无 | HDF_STATUS相关状态 | 关闭DAC设备 | +| write | device:结构体指针,核心层DAC控制器
channel:uint32_t类型,传入的通道号
val:uint32_t类型,要传入的数据 | 无 | HDF_STATUS相关状态 | 写入DA的目标值 | +| start | device:结构体指针,核心层DAC控制器 | 无 | HDF_STATUS相关状态 | 开启DAC设备 | +| stop | device:结构体指针,核心层DAC控制器 | 无 | HDF_STATUS相关状态 | 关闭DAC设备 | -**表2** DacLockMethod结构体成员函数功能说明 +**表 2** DacLockMethod结构体成员函数功能说明 | 函数成员 | 入参 | 出参 | 返回值 | 功能 | | -------- | -------- | -------- | -------- | -------- | | lock | device:结构体指针,核心层DAC设备对象。 | 无 | HDF_STATUS相关状态 | 获取临界区锁 | -| unlock | devicie:结构体指针,核心层DAC设备对象。 | 无 | HDF_STATUS相关状态 | 释放临界区锁 | +| unlock | device:结构体指针,核心层DAC设备对象。 | 无 | HDF_STATUS相关状态 | 释放临界区锁 | ### 开发步骤 DAC模块适配包含以下四个步骤: -1. 实例化驱动入口。 -2. 配置属性文件。 -3. 实例化核心层接口函数。 -4. 驱动调试。 +1. 实例化驱动入口 + +2. 配置属性文件 + +3. 实例化核心层函数 + +4. 驱动调试 ### 开发实例 下方将Hi3516DV300的驱动//device/soc/hisilicon/common/platform/dac/dac_hi35xx.c为例,展示驱动适配者需要提供哪些内容来完整实现设备功能。 -1. 实例化驱动入口: +1. 实例化驱动入口 驱动开发首先需要实例化驱动入口,驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs中保持一致。HDF框架会汇总所有加载的驱动的HdfDriverEntry对象入口,形成一个类似数组的段地址空间,方便上层调用。 一般在加载驱动时HDF会先调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 - ```c++ + ```c static struct HdfDriverEntry g_dacDriverEntry = { .moduleVersion = 1, .Init = VirtualDacInit, .Release = VirtualDacRelease, - .moduleName = "virtual_dac_driver", //【必要且与HCS里面的名字匹配】 + .moduleName = "virtual_dac_driver", //【必要且与device_info.hcs文件内的模块名匹配】 }; - HDF_INIT(g_dacDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 + HDF_INIT(g_dacDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 + + // 核心层dac_core.c管理器服务的驱动入口 + struct HdfDriverEntry g_dacManagerEntry = { + .moduleVersion = 1, + .Bind = DacManagerBind, // DAC不需要实现Bind,本例是一个空实现,驱动适配者可根据自身需要添加相关操作 + .Init = DacManagerInit, // 见Init参考 + .Release = DacManagerRelease, // 见Release参考 + .moduleName = "HDF_PLATFORM_DAC_MANAGER", // 这与device_info.hcs文件中device0对应 + }; + HDF_INIT(g_dacManagerEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 ``` -2. 配置属性文件: +2. 配置属性文件 + + - 添加//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs器件属性文件。 - - 添加//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs器件属性文件。 + 器件属性值对于驱动适配者的驱动实现以及核心层DacDevice相关成员的默认值或限制范围有密切关系,比如设备通道的个数以及传输速率的最大值,会影响DacDevice相关成员的默认值。 - 器件属性值对于驱动适配者的驱动实现以及核心层DacDevice相关成员的默认值或限制范围有密切关系,比如设备通道的个数以及传输速率的最大值,会影响DacDevice相关成员的默认值。 + 由于采用了统一服务模式,device_info.hcs文件中第一个设备节点必须为DAC管理器,其各项参数如表3所示: - 由于采用了统一服务模式,device_info.hcs文件中第一个设备节点必须为DAC管理器,其各项参数必须如下设置: + **表 3** device_info.hcs节点参数说明 - | 成员名 | 值 | - | --------------- | ------------------------------------------------------------------- | - | policy | 具体配置为0,不发布服务 | - | priority | 驱动启动优先级(0-200),值越大优先级越低,优先级相同则不保证device的加载顺序。| - | permission | 驱动权限 | - | moduleName | 固定为HDF_PLATFORM_DAC_MANAGER | - | serviceName | 固定为HDF_PLATFORM_DAC_MANAGER | - | deviceMatchAttr | 没有使用,可忽略 | + | 成员名 | 值 | + | --------------- | ------------------------------------------------------------------- | + | policy | 驱动服务发布的策略,DAC管理器具体配置为2,表示驱动对内核态和用户态都发布服务 | + | priority | 驱动启动优先级(0-200),值越大优先级越低,优先级相同则不保证device的加载顺序。DAC管理器具体配置为52 | + | permission | 驱动创建设备节点权限,DAC管理器具体配置为0664 | + | moduleName | 驱动名称,DAC管理器固定为HDF_PLATFORM_DAC_MANAGER | + | serviceName | 驱动对外发布服务的名称,DAC管理器服务名设置为HDF_PLATFORM_DAC_MANAGER | + | deviceMatchAttr | 驱动私有数据匹配的关键字,DAC管理器没有使用,可忽略 | - 从第二个节点开始配置具体DAC控制器信息,此节点并不表示某一路DAC控制器,而是代表一个资源性质设备,用于描述一类DAC控制器的信息。本例只有一个DAC设备,如有多个设备,则需要在device_info.hcs文件增加deviceNode信息,以及在dac_config.hcs文件中增加对应的器件属性。 + 从第二个节点开始配置具体DAC控制器信息,此节点并不表示某一路DAC控制器,而是代表一个资源性质设备,用于描述一类DAC控制器的信息。本例只有一个DAC设备,如有多个设备,则需要在device_info.hcs文件增加deviceNode信息,以及在dac_config.hcs文件中增加对应的器件属性。 - device_info.hcs配置参考: + - device_info.hcs配置参考 ```hcs root { device_dac :: device { - /* device0是DAC管理器 */ + // device0是DAC管理器 device0 :: deviceNode { policy = 0; priority = 52; @@ -184,27 +204,27 @@ DAC模块适配包含以下四个步骤: moduleName = "HDF_PLATFORM_DAC_MANAGER"; } } - /* dac_virtual是DAC控制器 */ + // dac_virtual是DAC控制器 dac_virtual :: deviceNode { policy = 0; priority = 56; permission = 0644; - moduleName = "virtual_dac_driver"; //【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。 - serviceName = "VIRTUAL_DAC_DRIVER"; //【必要】驱动对外发布服务的名称,必须唯一。 - deviceMatchAttr = "virtual_dac"; //【必要】用于配置控制器私有数据,要与dac_config.hcs中对应控制器保持一致。 - } + moduleName = "virtual_dac_driver"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。 + serviceName = "VIRTUAL_DAC_DRIVER"; // 【必要】驱动对外发布服务的名称,必须唯一。 + deviceMatchAttr = "virtual_dac"; // 【必要】用于配置控制器私有数据,要与dac_config.hcs中对应控制器保持一致。 + } } ``` - 添加dac_test_config.hcs器件属性文件。 - 在具体产品对应目录下新增文件用于驱动配置参数(例如hispark_taurus开发板:vendor/hisilicon/hispark_taurus/hdf_config/hdf_test/dac_test_config.hcs),其中配置参数如下: + 在具体产品对应目录下新增文件用于驱动配置参数(例如hispark_taurus开发板:vendor/hisilicon/hispark_taurus/hdf_config/hdf_test/dac_test_config.hcs),其中配置参数如下: ```hcs root { platform { dac_config { - match_attr = "virtual_dac"; //【必要】需要和device_info.hcs中的deviceMatchAttr值一致 + match_attr = "virtual_dac"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致 template dac_device { deviceNum = 0; // 设备号 validChannel = 0x1; // 有效通道1 @@ -219,117 +239,117 @@ DAC模块适配包含以下四个步骤: } ``` - 需要注意的是,新增dac_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。 + 需要注意的是,新增dac_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。 - 例如:本例中dac_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/dac/dac_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句: + 例如:本例中dac_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/dac/dac_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句: ```c #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/dac/dac_config.hcs" // 配置文件相对路径 ``` -3. 实例化核心层接口函数: +3. 实例化核心层函数 - 初始化DacDevice成员。 在VirtualDacParseAndInit函数中对DacDevice成员进行初始化操作。 - ```c++ - /* 虚拟驱动自定义结构体 */ + ```c + // 虚拟驱动自定义结构体 struct VirtualDacDevice { - /*DAC设备结构体 */ + // DAC设备结构体 struct DacDevice device; - /* DAC设备号 */ + // DAC设备号 uint32_t deviceNum; - /* 有效通道 */ + // 有效通道 uint32_t validChannel; - /* DAC速率 */ + // DAC速率 uint32_t rate; }; - /* 解析并且初始化核心层DacDevice对象 */ + // 解析并且初始化核心层DacDevice对象 static int32_t VirtualDacParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node) { - /* 定义返回值 */ + // 定义返回值 int32_t ret; - /* DAC设备虚拟指针 */ + // DAC设备虚拟指针 struct VirtualDacDevice *virtual = NULL; (void)device; - /* 给virtual指针开辟空间 */ + // 给virtual指针开辟空间 virtual = (struct VirtualDacDevice *)OsalMemCalloc(sizeof(*virtual)); - if (virtual == NULL) { - /*为空则返回错误参数 */ - HDF_LOGE("%s: Malloc virtual fail!", __func__); - return HDF_ERR_MALLOC_FAIL; - } - /* 读取属性文件配置参数 */ - ret = VirtualDacReadDrs(virtual, node); - if (ret != HDF_SUCCESS) { - /* 读取失败 */ - HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret); - /* 释放virtual空间 */ - OsalMemFree(virtual); - /* 指针置为0 */ - virtual = NULL; - return ret; - } - /* 初始化虚拟指针 */ - VirtualDacDeviceInit(virtual); - /* 对DacDevice中priv对象初始化 */ - virtual->device.priv = (void *)node; - /* 对DacDevice中devNum对象初始化 */ - virtual->device.devNum = virtual->deviceNum; - /* 对DacDevice中ops对象初始化 */ - virtual->device.ops = &g_method; - /* 添加DAC设备 */ - ret = DacDeviceAdd(&virtual->device); - if (ret != HDF_SUCCESS) { - /* 添加设备失败 */ - HDF_LOGE("%s: add Dac controller failed! ret = %d", __func__, ret); - /* 释放virtual空间 */ - OsalMemFree(virtual); - /* 虚拟指针置空 */ - virtual = NULL; - return ret; - } - - return HDF_SUCCESS; + if (virtual == NULL) { + // 为空则返回错误参数 + HDF_LOGE("%s: Malloc virtual fail!", __func__); + return HDF_ERR_MALLOC_FAIL; + } + // 读取属性文件配置参数 + ret = VirtualDacReadDrs(virtual, node); + if (ret != HDF_SUCCESS) { + // 读取失败 + HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret); + // 释放virtual空间 + OsalMemFree(virtual); + // 指针置为NULL + virtual = NULL; + return ret; + } + // 初始化虚拟指针 + VirtualDacDeviceInit(virtual); + // 对DacDevice中priv对象初始化 + virtual->device.priv = (void *)node; + // 对DacDevice中devNum对象初始化 + virtual->device.devNum = virtual->deviceNum; + // 对DacDevice中ops对象初始化 + virtual->device.ops = &g_method; + // 添加DAC设备 + ret = DacDeviceAdd(&virtual->device); + if (ret != HDF_SUCCESS) { + // 添加设备失败 + HDF_LOGE("%s: add Dac controller failed! ret = %d", __func__, ret); + // 释放virtual空间 + OsalMemFree(virtual); + // 虚拟指针置空 + virtual = NULL; + return ret; + } + + return HDF_SUCCESS; } ``` - 自定义结构体参考。 - 通过自定义结构体定义DAC数模转换必要的参数,在定义结构体时需要根据设备的功能参数来实现自定义结构体,从驱动的角度看,自定义结构体是参数和数据的载体,dac_config.hcs文件中传递的参数和数据会被HDF驱动模块的DacTestReadConfig函数读入,通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层DacDevice对象,例如设备号、总线号等。 + 通过自定义结构体定义DAC数模转换必要的参数,在定义结构体时需要根据设备的功能参数来实现自定义结构体,从驱动的角度看,自定义结构体是参数和数据的载体,dac_config.hcs文件中传递的参数和数据会被HDF驱动模块的DacTestReadConfig函数读入,通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层DacDevice对象,例如设备号、总线号等。 - ```c++ - struct VirtualDacDevice { - struct DacDevice device; //【必要】是核心层控制对象,具体描述见下面 - uint32_t deviceNum; //【必要】设备号 - uint32_t validChannel; //【必要】有效通道 - uint32_t rate; //【必要】采样率 - }; - - /* DacDevice是核心层控制器结构体,其中的成员在Init函数中会被赋值。 */ - struct DacDevice { - const struct DacMethod *ops; - OsalSpinlock spin; // 自旋锁 - uint32_t devNum; // 设备号 - uint32_t chanNum; // 设备通道号 - const struct DacLockMethod *lockOps; - void *priv; - }; - ``` + ```c + struct VirtualDacDevice { + struct DacDevice device; //【必要】是核心层控制对象,具体描述见下面 + uint32_t deviceNum; //【必要】设备号 + uint32_t validChannel; //【必要】有效通道 + uint32_t rate; //【必要】采样率 + }; + + // DacDevice是核心层控制器结构体,其中的成员在Init函数中会被赋值。 + struct DacDevice { + const struct DacMethod *ops; + OsalSpinlock spin; // 自旋锁 + uint32_t devNum; // 设备号 + uint32_t chanNum; // 设备通道号 + const struct DacLockMethod *lockOps; + void *priv; + }; + ``` - 实例化DacDevice成员DacMethod。 - + VirtualDacWrite、VirtualDacStop、VirtualDacStart函数会在dac_virtual.c文件中进行模块功能的实例化。 - - ```c++ + + ```c static const struct DacMethod g_method = { .write = VirtualDacWrite, // DAC设备写入值 .stop = VirtualDacStop, // 停止DAC设备 .start = VirtualDacStart, // 开始启动DAC设备 }; ``` - + ![](../public_sys-resources/icon-note.gif) **说明:**
DacDevice成员DacMethod的定义和成员说明见[接口说明](#接口说明)。 @@ -341,180 +361,182 @@ DAC模块适配包含以下四个步骤: 返回值: - HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 - - | 状态(值) | 问题描述 | + HDF_STATUS相关状态(表4为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 + + **表 4** HDF_STATUS相关状态说明 + + | 状态(值) | 问题描述 | | ---------------------- | ------------- | - | HDF_ERR_INVALID_OBJECT | 控制器对象非法。 | - | HDF_ERR_INVALID_PARAM | 参数非法。 | - | HDF_ERR_MALLOC_FAIL | 内存分配失败。 | - | HDF_ERR_IO | I/O 错误。 | - | HDF_SUCCESS | 传输成功。 | - | HDF_FAILURE | 传输失败。 | + | HDF_ERR_INVALID_OBJECT | 控制器对象非法。| + | HDF_ERR_INVALID_PARAM | 参数非法。| + | HDF_ERR_MALLOC_FAIL | 内存分配失败。| + | HDF_ERR_IO | I/O 错误。| + | HDF_SUCCESS | 传输成功。| + | HDF_FAILURE | 传输失败。| 函数说明: - 初始化自定义结构体对象,初始化DacDevice成员,并调用核心层DacDeviceAdd函数。 + 初始化自定义结构体对象,初始化DacDevice成员,并调用核心层DacDeviceAdd函数。 - ```c++ - static int32_t VirtualDacParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node) - { - /* 定义返回值参数 */ - int32_t ret; - /* DAC设备的结构体指针 */ - struct VirtualDacDevice *virtual = NULL; - (void)device; - /* 分配指定大小的内存 */ - virtual = (struct VirtualDacDevice *)OsalMemCalloc(sizeof(*virtual)); - if (virtual == NULL) { - /* 分配内存失败 */ - HDF_LOGE("%s: Malloc virtual fail!", __func__); - return HDF_ERR_MALLOC_FAIL; - } - /* 读取hcs中的node节点参数,函数定义见下方 */ - ret = VirtualDacReadDrs(virtual, node); - if (ret != HDF_SUCCESS) { - /* 读取节点失败 */ - HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret); - goto __ERR__; - } - /* 初始化DAC设备指针 */ - VirtualDacDeviceInit(virtual); - /* 节点数据传入私有数据 */ - virtual->device.priv = (void *)node; - /* 传入设备号 */ - virtual->device.devNum = virtual->deviceNum; - /* 传入方法 */ - virtual->device.ops = &g_method; - /* 添加DAC设备 */ - ret = DacDeviceAdd(&virtual->device); - if (ret != HDF_SUCCESS) { - /* 添加DAC设备失败 */ - HDF_LOGE("%s: add Dac controller failed! ret = %d", __func__, ret); - goto __ERR__; - } - /* 成功添加DAC设备 */ - return HDF_SUCCESS; - __ERR__: - /* 如果指针为空 */ - if (virtual != NULL) { - /* 释放内存 */ - OsalMemFree(virtual); - /* 指针置空 */ - virtual = NULL; - } - - return ret; - } - - static int32_t VirtualDacInit(struct HdfDeviceObject *device) - { - /* 定义返回值参数 */ - int32_t ret; - /* 设备结构体子节点 */ - const struct DeviceResourceNode *childNode = NULL; - /* 入参指针进行判断 */ - if (device == NULL || device->property == NULL) { - /* 入参指针为空 */ - HDF_LOGE("%s: device or property is NULL", __func__); - return HDF_ERR_INVALID_OBJECT; - } - /* 入参指针不为空 */ - ret = HDF_SUCCESS; - DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { - /* 解析子节点 */ - ret = VirtualDacParseAndInit(device, childNode); - if (ret != HDF_SUCCESS) { - /* 解析失败 */ - break; - } - } - /* 解析成功 */ - return ret; - } + ```c + static int32_t VirtualDacParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node) + { + // 定义返回值参数 + int32_t ret; + // DAC设备的结构体指针 + struct VirtualDacDevice *virtual = NULL; + (void)device; + // 分配指定大小的内存 + virtual = (struct VirtualDacDevice *)OsalMemCalloc(sizeof(*virtual)); + if (virtual == NULL) { + // 分配内存失败 + HDF_LOGE("%s: Malloc virtual fail!", __func__); + return HDF_ERR_MALLOC_FAIL; + } + // 读取hcs中的node节点参数,函数定义见下方 + ret = VirtualDacReadDrs(virtual, node); + if (ret != HDF_SUCCESS) { + // 读取节点失败 + HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret); + goto __ERR__; + } + // 初始化DAC设备指针 + VirtualDacDeviceInit(virtual); + // 节点数据传入私有数据 + virtual->device.priv = (void *)node; + // 传入设备号 + virtual->device.devNum = virtual->deviceNum; + // 传入方法 + virtual->device.ops = &g_method; + // 添加DAC设备 + ret = DacDeviceAdd(&virtual->device); + if (ret != HDF_SUCCESS) { + // 添加DAC设备失败 + HDF_LOGE("%s: add Dac controller failed! ret = %d", __func__, ret); + goto __ERR__; + } + // 成功添加DAC设备 + return HDF_SUCCESS; + __ERR__: + // 如果指针为空 + if (virtual != NULL) { + // 释放内存 + OsalMemFree(virtual); + // 指针置空 + virtual = NULL; + } + + return ret; + } - static int32_t VirtualDacReadDrs(struct VirtualDacDevice *virtual, const struct DeviceResourceNode *node) - { - struct DeviceResourceIface *drsOps = NULL; + static int32_t VirtualDacInit(struct HdfDeviceObject *device) + { + // 定义返回值参数 + int32_t ret; + // 设备结构体子节点 + const struct DeviceResourceNode *childNode = NULL; + // 入参指针进行判断 + if (device == NULL || device->property == NULL) { + // 入参指针为空 + HDF_LOGE("%s: device or property is NULL", __func__); + return HDF_ERR_INVALID_OBJECT; + } + // 入参指针不为空 + ret = HDF_SUCCESS; + DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { + // 解析子节点 + ret = VirtualDacParseAndInit(device, childNode); + if (ret != HDF_SUCCESS) { + // 解析失败 + break; + } + } + // 解析成功 + return ret; + } - /* 获取drsOps方法 */ - drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) { - HDF_LOGE("%s: Invalid drs ops fail!", __func__); - return HDF_FAILURE; - } - /* 将配置参数依次读出,并填充至结构体中 */ - if (drsOps->GetUint32(node, "deviceNum", &virtual->deviceNum, 0) != HDF_SUCCESS) { - HDF_LOGE("%s: Read deviceNum fail!", __func__); - return HDF_ERR_IO; - } - if (drsOps->GetUint32(node, "validChannel", &virtual->validChannel, 0) != HDF_SUCCESS) { - HDF_LOGE("%s: Read validChannel fail!", __func__); - return HDF_ERR_IO; - } - if (drsOps->GetUint32(node, "rate", &virtual->rate, 0) != HDF_SUCCESS) { - HDF_LOGE("%s: Read rate fail!", __func__); - return HDF_ERR_IO; - } - return HDF_SUCCESS; - } - ``` + static int32_t VirtualDacReadDrs(struct VirtualDacDevice *virtual, const struct DeviceResourceNode *node) + { + struct DeviceResourceIface *drsOps = NULL; + + // 获取drsOps方法 + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) { + HDF_LOGE("%s: Invalid drs ops fail!", __func__); + return HDF_FAILURE; + } + // 将配置参数依次读出,并填充至结构体中 + if (drsOps->GetUint32(node, "deviceNum", &virtual->deviceNum, 0) != HDF_SUCCESS) { + HDF_LOGE("%s: Read deviceNum fail!", __func__); + return HDF_ERR_IO; + } + if (drsOps->GetUint32(node, "validChannel", &virtual->validChannel, 0) != HDF_SUCCESS) { + HDF_LOGE("%s: Read validChannel fail!", __func__); + return HDF_ERR_IO; + } + if (drsOps->GetUint32(node, "rate", &virtual->rate, 0) != HDF_SUCCESS) { + HDF_LOGE("%s: Read rate fail!", __func__); + return HDF_ERR_IO; + } + return HDF_SUCCESS; + } + ``` - - Release函数开发参考 + - Release函数开发参考 - 入参: + 入参: - HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 + HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 - 返回值: + 返回值: - 无。 + 无。 - 函数说明: + 函数说明: - 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 + 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 - ![](../public_sys-resources/icon-note.gif) **说明:**
- 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。 + ![](../public_sys-resources/icon-note.gif) **说明:**
+ 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。 - ```c++ + ```c static void VirtualDacRemoveByNode(const struct DeviceResourceNode *node) { - /* 定义返回值参数 */ + // 定义返回值参数 int32_t ret; - /* 定义DAC设备号 */ + // 定义DAC设备号 int16_t devNum; - /* DAC设备结构体指针 */ + // DAC设备结构体指针 struct DacDevice *device = NULL; - /* DAC虚拟结构体指针 */ + // DAC虚拟结构体指针 struct VirtualDacDevice *virtual = NULL; - /* 设备资源接口结构体指针 */ + // 设备资源接口结构体指针 struct DeviceResourceIface *drsOps = NULL; - /* 通过实例入口获取设备资源 */ + // 通过实例入口获取设备资源 drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - /* 入参指判空 */ + // 入参指判空 if (drsOps == NULL || drsOps->GetUint32 == NULL) { - /* 指针为空 */ + // 指针为空 HDF_LOGE("%s: invalid drs ops fail!", __func__); return; } - /* 获取devNum节点的数据 */ + // 获取devNum节点的数据 ret = drsOps->GetUint16(node, "devNum", (uint16_t *)&devNum, 0); if (ret != HDF_SUCCESS) { - /* 获取失败 */ + // 获取失败 HDF_LOGE("%s: read devNum fail!", __func__); return; } - /* 获取DAC设备号 */ + // 获取DAC设备号 device = DacDeviceGet(devNum); - /* 判断DAC设备号以及数据是否为空 */ + // 判断DAC设备号以及数据是否为空 if (device != NULL && device->priv == node) { - /* 为空释放DAC设备号 */ + // 为空释放DAC设备号 DacDevicePut(device); - /* 移除DAC设备号 */ + // 移除DAC设备号 DacDeviceRemove(device); virtual = (struct VirtualDacDevice *)device; - /* 释放虚拟指针 */ + // 释放虚拟指针 OsalMemFree(virtual); } return; @@ -522,22 +544,22 @@ DAC模块适配包含以下四个步骤: static void VirtualDacRelease(struct HdfDeviceObject *device) { - /* 定义设备资源子节点结构体指针 */ + // 定义设备资源子节点结构体指 const struct DeviceResourceNode *childNode = NULL; - /* 入参指针判空 */ + // 入参指针判空 if (device == NULL || device->property == NULL) { - /* 入参指针为空 */ + // 入参指针为空 HDF_LOGE("%s: device or property is NULL", __func__); return; } DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { - /* 通过节点移除DAC */ + // 通过节点移除DAC VirtualDacRemoveByNode(childNode); } } ``` -4. 驱动调试: +4. 驱动调试 - 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的测试用例是否成功等。 \ No newline at end of file + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的测试用例是否成功等。 diff --git a/zh-cn/device-dev/driver/driver-platform-gpio-des.md b/zh-cn/device-dev/driver/driver-platform-gpio-des.md index a92baaada7ff503ab66fb1b10d95ee2a6e4c73e8..0ae9512755d4fc16c6c8f32d75f55e60b36a79bd 100644 --- a/zh-cn/device-dev/driver/driver-platform-gpio-des.md +++ b/zh-cn/device-dev/driver/driver-platform-gpio-des.md @@ -8,10 +8,13 @@ GPIO(General-purpose input/output)即通用型输入输出。通常,GPIO GPIO接口定义了操作GPIO管脚的标准方法集合,包括: -- 设置管脚方向:方向可以是输入或者输出(暂不支持高阻态)。 -- 读写管脚电平值:电平值可以是低电平或高电平。 -- 设置管脚中断服务函数:设置一个管脚的中断响应函数,以及中断触发方式。 -- 使能和禁止管脚中断:禁止或使能管脚中断。 +- 设置、获取管脚方向:方向可以是输入或者输出(暂不支持高阻态)。 + +- 读、写管脚电平值:电平值可以是低电平或高电平。 + +- 设置、取消管脚中断服务函数:设置一个管脚的中断响应函数,以及中断触发方式。取消一个管脚的中断服务函数。 + +- 使能、禁止管脚中断:禁止或使能管脚中断。 ### 基本概念 @@ -19,11 +22,11 @@ GPIO又俗称为I/O口,I指的是输入(in),O指的是输出(out)。 - GPIO输入 - 输入是检测各个引脚上的电平状态,高电平或者低电平状态。常见的输入模式有:模拟输入、浮空输入、上拉输入、下拉输入。 + 输入是检测各个引脚上的电平状态,高电平或者低电平状态。常见的输入模式有:模拟输入、浮空输入、上拉输入、下拉输入。 - GPIO输出 - 输出是当需要控制引脚电平的高低时需要用到输出功能。常见的输出模式有:开漏输出、推挽输出、复用开漏输出、复用推挽输出。 + 输出是当需要控制引脚电平的高低时需要用到输出功能。常见的输出模式有:开漏输出、推挽输出、复用开漏输出、复用推挽输出。 ### 运作机制 @@ -34,7 +37,9 @@ GPIO又俗称为I/O口,I指的是输入(in),O指的是输出(out)。 GPIO模块各分层作用: - 接口层提供操作GPIO管脚的标准方法。 + - 核心层主要提供GPIO管脚资源匹配,GPIO管脚控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互,供芯片厂家快速接入HDF框架。 + - 适配层主要是将钩子函数的功能实例化,实现具体的功能。 **图 1** GPIO统一服务模式结构图 @@ -45,36 +50,36 @@ GPIO模块各分层作用: ### 场景介绍 -GPIO仅是一个软件层面的概念,主要工作是GPIO管脚资源管理。开发者可以使用提供的GPIO操作接口,实现对管脚控制。 +GPIO主要是对GPIO管脚资源进行管理。开发者可以使用提供的GPIO操作接口,实现对管脚控制的具体控制。 ### 接口说明 -GPIO模块提供的主要接口如表1所示。 +GPIO模块提供的主要接口如表1所示。具体API详见//drivers/hdf_core/framework/include/platform/gpio_if.h。 -**表1** GPIO驱动API接口功能介绍 +**表 1** GPIO驱动API接口功能介绍 -| 接口名 | 描述 | +| 接口名 | 描述 | | ------------------------------------------------------------ | ------------------------------ | -| GpioGetByName(const char *gpioName) | 获取GPIO管脚ID | -| int32_t GpioRead(uint16_t gpio, uint16_t *val) | 读GPIO管脚电平值 | -| int32_t GpioWrite(uint16_t gpio, uint16_t val) | 写GPIO管脚电平值 | -| int32_t GpioGetDir(uint16_t gpio, uint16_t *dir) | 获取GPIO管脚方向 | -| int32_t GpioSetDir(uint16_t gpio, uint16_t dir) | 设置GPIO管脚方向 | -| int32_t GpioUnsetIrq(uint16_t gpio, void *arg); | 取消GPIO管脚对应的中断服务函数 | -| int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void *arg) | 设置GPIO管脚对应的中断服务函数 | -| int32_t GpioEnableIrq(uint16_t gpio) | 使能GPIO管脚中断 | -| int32_t GpioDisableIrq(uint16_t gpio) | 禁止GPIO管脚中断 | +| GpioGetByName(const char \*gpioName) | 获取GPIO管脚ID | +| int32_t GpioRead(uint16_t gpio, uint16_t \*val) | 读GPIO管脚电平值 | +| int32_t GpioWrite(uint16_t gpio, uint16_t val) | 写GPIO管脚电平值 | +| int32_t GpioGetDir(uint16_t gpio, uint16_t \*dir) | 获取GPIO管脚方向 | +| int32_t GpioSetDir(uint16_t gpio, uint16_t dir) | 设置GPIO管脚方向 | +| int32_t GpioUnsetIrq(uint16_t gpio, void \*arg) | 取消GPIO管脚对应的中断服务函数 | +| int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void \*arg) | 设置GPIO管脚对应的中断服务函数 | +| int32_t GpioEnableIrq(uint16_t gpio) | 使能GPIO管脚中断 | +| int32_t GpioDisableIrq(uint16_t gpio) | 禁止GPIO管脚中断 | >![](../public_sys-resources/icon-note.gif) **说明:**
>本文涉及GPIO的所有接口,支持内核态及用户态使用。 ### 开发步骤 -GPIO标准API通过GPIO管脚号来操作指定管脚,使用GPIO的一般流程如下图所示。 +GPIO标准API通过GPIO管脚号来操作指定管脚,使用GPIO的一般流程如图2所示。 -**图1** GPIO使用流程图 +**图 2** GPIO使用流程图 -![image](figures/GPIO使用流程图.png "GPIO使用流程图") +![GPIO使用流程图](figures/GPIO使用流程图.png) #### 确定GPIO管脚号 @@ -82,31 +87,31 @@ GPIO标准API通过GPIO管脚号来操作指定管脚,使用GPIO的一般流 - 根据SOC芯片规则进行计算 - 不同SOC芯片由于其GPIO控制器型号、参数、以及控制器驱动的不同,GPIO管脚号的换算方式不一样。 + 不同SOC芯片由于其GPIO控制器型号、参数、以及控制器驱动的不同,GPIO管脚号的换算方式不一样。 - - Hi3516DV300 + - Hi3516DV300 - 控制器管理12组GPIO管脚,每组8个。 + 控制器管理12组GPIO管脚,每组8个。 - GPIO号 = GPIO组索引 (0~11) \* 每组GPIO管脚数(8) + 组内偏移 + GPIO号 = GPIO组索引 (0~11) \* 每组GPIO管脚数(8) + 组内偏移 - 举例:GPIO10_3的GPIO号 = 10 \* 8 + 3 = 83 + 举例:GPIO10_3的GPIO号 = 10 \* 8 + 3 = 83 - - Hi3518EV300 + - Hi3518EV300 - 控制器管理10组GPIO管脚,每组10个。 + 控制器管理10组GPIO管脚,每组10个。 - GPIO号 = GPIO组索引 (0~9) \* 每组GPIO管脚数(10) + 组内偏移 + GPIO号 = GPIO组索引 (0~9) \* 每组GPIO管脚数(10) + 组内偏移 - 举例:GPIO7_3的GPIO管脚号 = 7 \* 10 + 3 = 73 + 举例:GPIO7_3的GPIO管脚号 = 7 \* 10 + 3 = 73 - 通过管脚别名获取 - 调用接口GpioGetByName进行获取,入参是该管脚的别名,接口返回值是管脚的全局ID。 + 调用接口GpioGetByName进行获取,入参是该管脚的别名,接口返回值是管脚的全局ID。 - ```c - GpioGetByName(const char *gpioName); - ``` + ```c + GpioGetByName(const char *gpioName); + ``` #### 设置GPIO管脚方向 @@ -116,15 +121,15 @@ GPIO标准API通过GPIO管脚号来操作指定管脚,使用GPIO的一般流 int32_t GpioSetDir(uint16_t gpio, uint16_t dir); ``` -**表2** GpioSetDir参数和返回值描述 +**表 2** GpioSetDir参数和返回值描述 -| **参数** | **参数描述** | +| **参数** | **参数描述** | | ---------- | ------------------ | -| gpio | GPIO管脚号 | -| dir | 待设置的方向值 | -| **返回值** | **返回值描述** | -| 0 | 设置成功 | -| 负数 | 设置失败 | +| gpio | uint16_t类型,GPIO管脚号 | +| dir | uint16_t类型,待设置的方向值 | +| **返回值** | **返回值描述** | +| HDF_SUCCESS | 设置GPIO管脚方向成功 | +| 负数 | 设置GPIO管脚方向失败 | 假设需要将GPIO管脚3的方向配置为输出,其使用示例如下: @@ -132,8 +137,8 @@ int32_t GpioSetDir(uint16_t gpio, uint16_t dir); int32_t ret; ret = GpioSetDir(3, GPIO_DIR_OUT); // 将3号GPIO管脚配置为输出 -if (ret != 0) { - HDF_LOGE("GpioSetDir: failed, ret %d\n", ret); +if (ret != HDF_SUCCESS) { + HDF_LOGE("GpioSetDir: gpio set dir fail, ret:%d\n", ret); return ret; } ``` @@ -146,15 +151,15 @@ if (ret != 0) { int32_t GpioGetDir(uint16_t gpio, uint16_t *dir); ``` -**表3** GpioGetDir参数和返回值描述 +**表 3** GpioGetDir参数和返回值描述 -| **参数** | **参数描述** | +| **参数** | **参数描述** | | ---------- | ------------------ | -| gpio | GPIO管脚号 | -| dir | 获取到的方向值指针 | -| **返回值** | **返回值描述** | -| 0 | 设置成功 | -| 负数 | 设置失败 | +| gpio | uint16_t类型,GPIO管脚号 | +| dir | uint16_t类型指针,获取到的方向值 | +| **返回值** | **返回值描述** | +| HDF_SUCCESS | 获取GPIO管脚方向成功 | +| 负数 | 获取GPIO管脚方向失败 | 假设需要获取GPIO管脚3的方向,其使用示例如下: @@ -163,8 +168,8 @@ int32_t ret; uin16_t dir; ret = GpioGetDir(3, &dir); // 获取3号GPIO管脚方向 -if (ret != 0) { - HDF_LOGE("GpioGetDir: failed, ret %d\n", ret); +if (ret != HDF_SUCCESS) { + HDF_LOGE("GpioGetDir: gpio get dir fail, ret:%d\n", ret); return ret; } ``` @@ -177,15 +182,15 @@ if (ret != 0) { int32_t GpioRead(uint16_t gpio, uint16_t *val); ``` -**表4** GpioRead参数和返回值描述 +**表 4** GpioRead参数和返回值描述 -| **参数** | **参数描述** | +| **参数** | **参数描述** | | ---------- | -------------------- | -| gpio | GPIO管脚号 | -| val | 接收读取电平值的指针 | -| **返回值** | **返回值描述** | -| 0 | 读取成功 | -| 负数 | 读取失败 | +| gpio | uint16_t类型,GPIO管脚号 | +| val | uint16_t类型指针,接收读取电平值 | +| **返回值** | **返回值描述** | +| HDF_SUCCESS | 读取GPIO管脚电平值成功 | +| 负数 | 读取GPIO管脚电平值失败 | 假设需要读取GPIO管脚3的电平值,其使用示例如下: @@ -194,8 +199,8 @@ int32_t ret; uint16_t val; ret = GpioRead(3, &val); // 读取3号GPIO管脚电平值 -if (ret != 0) { - HDF_LOGE("GpioRead: failed, ret %d\n", ret); +if (ret != HDF_SUCCESS) { + HDF_LOGE("GpioRead: gpio read fail, ret:%d\n", ret); return ret; } ``` @@ -208,15 +213,15 @@ if (ret != 0) { int32_t GpioWrite(uint16_t gpio, uint16_t val); ``` -**表5** GpioWrite参数和返回值描述 +**表 5** GpioWrite参数和返回值描述 -| **参数** | **参数描述** | +| **参数** | **参数描述** | | ---------- | ------------------ | -| gpio | GPIO管脚号 | -| val | 待写入的电平值 | -| **返回值** | **返回值描述** | -| 0 | 写入成功 | -| 负数 | 写入失败 | +| gpio | uint16_t类型,GPIO管脚号 | +| val | uint16_t类型,待写入的电平值 | +| **返回值** | **返回值描述** | +| HDF_SUCCESS | 写入GPIO管脚电平值成功 | +| 负数 | 写入GPIO管脚电平值失败 | 假设需要给GPIO管脚3写入低电平值,其使用示例如下: @@ -224,8 +229,8 @@ int32_t GpioWrite(uint16_t gpio, uint16_t val); int32_t ret; ret = GpioWrite(3, GPIO_VAL_LOW); // 给3号GPIO管脚写入低电平值 -if (ret != 0) { - HDF_LOGE("GpioRead: failed, ret %d\n", ret); +if (ret != HDF_SUCCESS) { + HDF_LOGE("GpioWrite: gpio write fail, ret:%d\n", ret); return ret; } ``` @@ -238,17 +243,17 @@ if (ret != 0) { int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void *arg); ``` -**表6** GpioSetIrq参数和返回值描述 +**表 6** GpioSetIrq参数和返回值描述 -| **参数** | **参数描述** | +| **参数** | **参数描述** | | ---------- | ------------------------ | -| gpio | GPIO管脚号 | -| mode | 中断触发模式 | -| func | 中断服务程序 | -| arg | 传递给中断服务程序的入参 | -| **返回值** | **返回值描述** | -| 0 | 设置成功 | -| 负数 | 设置失败 | +| gpio | uint16_t类型,GPIO管脚号 | +| mode | uint16_t类型,中断触发模式 | +| func | 函数指针,中断服务程序 | +| arg | 无类型指针,传递给中断服务程序的入参 | +| **返回值** | **返回值描述** | +| HDF_SUCCESS | 设置GPIO管脚中断成功 | +| 负数 | 设置GPIO管脚中断失败 | > ![icon-caution.gif](public_sys-resources/icon-caution.gif) **注意:**
> 同一时间,只能为某个GPIO管脚设置一个中断服务函数,如果重复调用GpioSetIrq函数,则之前设置的中断服务函数会被取代。 @@ -261,15 +266,15 @@ int32_t GpioSetIrq(uint16_t gpio, uint16_t mode, GpioIrqFunc func, void *arg); int32_t GpioUnsetIrq(uint16_t gpio, void *arg); ``` -**表7** GpioUnsetIrq参数和返回值描述 +**表 7** GpioUnsetIrq参数和返回值描述 -| **参数** | **参数描述** | +| **参数** | **参数描述** | | ---------- | -------------- | -| gpio | GPIO管脚号 | -| arg | GPIO中断数据 | +| gpio | uint16_t类型,GPIO管脚号 | +| arg | 无类型指针,GPIO中断数据 | | **返回值** | **返回值描述** | -| 0 | 取消成功 | -| 负数 | 取消失败 | +| HDF_SUCCESS | 取消GPIO管脚中断成功 | +| 负数 | 取消GPIO管脚中断失败 | #### 使能GPIO管脚中断 @@ -279,14 +284,14 @@ int32_t GpioUnsetIrq(uint16_t gpio, void *arg); int32_t GpioEnableIrq(uint16_t gpio); ``` -**表8** GpioEnableIrq参数和返回值描述 +**表 8** GpioEnableIrq参数和返回值描述 -| **参数** | **参数描述** | +| **参数** | **参数描述** | | ---------- | -------------- | -| gpio | GPIO管脚号 | +| gpio | uint16_t类型,GPIO管脚号 | | **返回值** | **返回值描述** | -| 0 | 使能成功 | -| 负数 | 使能失败 | +| HDF_SUCCESS | 使能GPIO管脚中断成功 | +| 负数 | 使能GPIO管脚中断失败 | > ![icon-caution.gif](public_sys-resources/icon-caution.gif) **注意:**
> 必须通过此函数使能管脚中断,之前设置的中断服务函数才能被正确响应。 @@ -298,51 +303,51 @@ int32_t GpioEnableIrq(uint16_t gpio); ```c int32_t GpioDisableIrq(uint16_t gpio); ``` -**表9** GpioDisableIrq参数和返回值描述 +**表 9** GpioDisableIrq参数和返回值描述 -| **参数** | **参数描述** | +| **参数** | **参数描述**| | ---------- | -------------- | -| gpio | GPIO管脚号 | +| gpio | uint16_t类型,GPIO管脚号 | | **返回值** | **返回值描述** | -| 0 | 禁止成功 | -| 负数 | 禁止失败 | +| HDF_SUCCESS | 禁止GPIO管脚中断成功 | +| 负数 | 禁止GPIO管脚中断失败 | 中断相关操作示例: ```c -/* 中断服务函数*/ +// 中断服务函数 int32_t MyCallBackFunc(uint16_t gpio, void *data) { - HDF_LOGI("%s: gpio:%u interrupt service in data\n", __func__, gpio); - return 0; + HDF_LOGI("MyCallBackFunc: gpio:%u interrupt service in data.\n", gpio); + return HDF_SUCCESS; } int32_t ret; -/* 设置中断服务程序为MyCallBackFunc,入参为NULL,中断触发模式为上升沿触发 */ +// 设置中断服务程序为MyCallBackFunc,入参为NULL,中断触发模式为上升沿触发 ret = GpioSetIrq(3, OSAL_IRQF_TRIGGER_RISING, MyCallBackFunc, NULL); -if (ret != 0) { - HDF_LOGE("GpioSetIrq: failed, ret %d\n", ret); +if (ret != HDF_SUCCESS) { + HDF_LOGE("GpioSetIrq: gpio set irq fail, ret:%d\n", ret); return ret; } -/* 使能3号GPIO管脚中断 */ +// 使能3号GPIO管脚中断 ret = GpioEnableIrq(3); -if (ret != 0) { - HDF_LOGE("GpioEnableIrq: failed, ret %d\n", ret); +if (ret != HDF_SUCCESS) { + HDF_LOGE("GpioEnableIrq: gpio enable irq fail, ret:%d\n", ret); return ret; } -/* 禁止3号GPIO管脚中断 */ +// 禁止3号GPIO管脚中断 ret = GpioDisableIrq(3); -if (ret != 0) { - HDF_LOGE("GpioDisableIrq: failed, ret %d\n", ret); +if (ret != HDF_SUCCESS) { + HDF_LOGE("GpioDisableIrq: gpio disable irqfail, ret:%d\n", ret); return ret; } -/* 取消3号GPIO管脚中断服务程序 */ +// 取消3号GPIO管脚中断服务程序 ret = GpioUnsetIrq(3, NULL); -if (ret != 0) { - HDF_LOGE("GpioUnSetIrq: failed, ret %d\n", ret); +if (ret != HDF_SUCCESS) { + HDF_LOGE("GpioUnSetIrq: gpio unset irq fail, ret:%d\n", ret); return ret; } ``` @@ -363,65 +368,66 @@ if (ret != 0) { static uint32_t g_irqCnt; -/* 中断服务函数*/ +// 中断服务函数 static int32_t TestCaseGpioIrqHandler(uint16_t gpio, void *data) { - HDF_LOGE("%s: irq triggered! on gpio:%u, in data", __func__, gpio); - g_irqCnt++; /* 如果中断服务函数触发执行,则将全局中断计数加1 */ + HDF_LOGE("TestCaseGpioIrqHandler: irq triggered! on gpio:%u, in data", gpio); + g_irqCnt++; // 如果中断服务函数触发执行,则将全局中断计数加1 return GpioDisableIrq(gpio); } -/* 测试用例函数 */ +// 测试用例函数 static int32_t TestCaseGpioIrqEdge(void) { int32_t ret; uint16_t valRead; uint16_t mode; - uint16_t gpio = 83; /* 待测试的GPIO管脚号 */ + uint16_t gpio = 84; // 待测试的GPIO管脚号 uint32_t timeout; - /* 将管脚方向设置为输出 */ + // 将管脚方向设置为输出 ret = GpioSetDir(gpio, GPIO_DIR_OUT); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: set dir fail! ret:%d\n", __func__, ret); + HDF_LOGE("TestCaseGpioIrqEdge: set dir fail! ret:%d\n", ret); return ret; } - /* 先禁止该管脚中断 */ + // 先禁止该管脚中断 ret = GpioDisableIrq(gpio); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: disable irq fail! ret:%d\n", __func__, ret); + HDF_LOGE("TestCaseGpioIrqEdge: disable irq fail! ret:%d\n", ret); return ret; } - /* 为管脚设置中断服务函数,触发模式为上升沿和下降沿共同触发 */ + // 为管脚设置中断服务函数,触发模式为上升沿和下降沿共同触发 mode = OSAL_IRQF_TRIGGER_RISING | OSAL_IRQF_TRIGGER_FALLING; - HDF_LOGE("%s: mode:%0x\n", __func__, mode); + HDF_LOGE("TestCaseGpioIrqEdge: mode:%0x\n", mode); ret = GpioSetIrq(gpio, mode, TestCaseGpioIrqHandler, NULL); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: set irq fail! ret:%d\n", __func__, ret); + HDF_LOGE("TestCaseGpioIrqEdge: set irq fail! ret:%d\n", ret); return ret; } - /* 使能此管脚中断 */ + // 使能此管脚中断 ret = GpioEnableIrq(gpio); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: enable irq fail! ret:%d\n", __func__, ret); + HDF_LOGE("TestCaseGpioIrqEdge: enable irq fail! ret:%d\n", ret); (void)GpioUnsetIrq(gpio, NULL); return ret; } - g_irqCnt = 0; /* 清除全局计数器 */ - timeout = 0; /* 等待时间清零 */ - /* 等待此管脚中断服务函数触发,等待超时时间为1000毫秒 */ + g_irqCnt = 0; // 清除全局计数器 + timeout = 0; // 等待时间清零 + // 等待此管脚中断服务函数触发,等待超时时间为1000毫秒 while (g_irqCnt <= 0 && timeout < 1000) { (void)GpioRead(gpio, &valRead); (void)GpioWrite(gpio, (valRead == GPIO_VAL_LOW) ? GPIO_VAL_HIGH : GPIO_VAL_LOW); - HDF_LOGE("%s: wait irq timeout:%u\n", __func__, timeout); - OsalMDelay(200); /* wait for irq trigger */ + HDF_LOGE("TestCaseGpioIrqEdge: wait irq timeout:%u\n", timeout); + OsalMDelay(200); // 等待中断触发 timeout += 200; } (void)GpioUnsetIrq(gpio, NULL); + HDF_LOGI("TestCaseGpioIrqEdge: function tests end, g_irqCnt:%u", g_irqCnt); return (g_irqCnt > 0) ? HDF_SUCCESS : HDF_FAILURE; } -``` \ No newline at end of file +``` diff --git a/zh-cn/device-dev/driver/driver-platform-gpio-develop.md b/zh-cn/device-dev/driver/driver-platform-gpio-develop.md index 7b15fc2935bfbde69ee7622b11ab9ad88d6f54f2..5c8a2feea00c28ed0493de269cadef113ea06f47 100755 --- a/zh-cn/device-dev/driver/driver-platform-gpio-develop.md +++ b/zh-cn/device-dev/driver/driver-platform-gpio-develop.md @@ -12,23 +12,25 @@ GPIO又俗称为I/O口,I指的是输入(in),O指的是输出(out)。 - GPIO输入 - 输入是检测各个引脚上的电平状态,高电平或者低电平状态。常见的输入模式有:模拟输入、浮空输入、上拉输入、下拉输入。 + 输入是检测各个引脚上的电平状态,高电平或者低电平状态。常见的输入模式有:模拟输入、浮空输入、上拉输入、下拉输入。 - GPIO输出 - 输出是当需要控制引脚电平的高低时需要用到输出功能。常见的输出模式有:开漏输出、推挽输出、复用开漏输出、复用推挽输出。 + 输出是当需要控制引脚电平的高低时需要用到输出功能。常见的输出模式有:开漏输出、推挽输出、复用开漏输出、复用推挽输出。 ### 运作机制 -在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型配置器),若采用独立服务模式,则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。GPIO模块接口适配模式采用统一服务模式(如图1所示)。 +在HDF框架中,同类型设备对象较多时(可能同时存在十几个同类型配置器),若采用独立服务模式,则需要配置更多的设备节点,且相关服务会占据更多的内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。GPIO模块采用统一服务模式(如图1所示)。 在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层,因此这种模式下驱动无需再为每个控制器发布服务。 GPIO模块各分层作用: -- 接口层提供操作GPIO管脚的标准方法。 -- 核心层主要提供GPIO管脚资源匹配,GPIO管脚控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互,供芯片厂家快速接入HDF框架。 -- 适配层主要是将钩子函数的功能实例化,实现具体的功能。 +- 接口层:提供操作GPIO管脚的标准方法。 + +- 核心层:提供GPIO管脚资源匹配,GPIO管脚控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互,供芯片厂家快速接入HDF框架。 + +- 适配层:由驱动适配者将钩子函数的功能实例化,实现与硬件相关的具体功能。 **图 1** GPIO统一服务模式结构图 @@ -38,7 +40,7 @@ GPIO模块各分层作用: ### 场景介绍 -GPIO仅是一个软件层面的概念,主要工作是GPIO管脚资源管理。驱动开发者可以使用GPIO模块提供的操作接口,实现对管脚的控制。当驱动开发者需要将GPIO适配到OpenHarmony时,需要进行GPIO驱动适配。下文将介绍如何进行GPIO驱动适配。 +GPIO主要是对GPIO管脚资源进行管理。驱动开发者可以使用GPIO模块提供的操作接口,实现对管脚的具体控制。当驱动开发者需要将GPIO适配到OpenHarmony时,需要进行GPIO驱动适配。下文将介绍如何进行GPIO驱动适配。 ### 接口说明 @@ -55,388 +57,424 @@ struct GpioMethod { int32_t (*setDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t dir); int32_t (*getDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *dir); int32_t (*toIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *irq); // 【预留】 - int32_t (*setIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t mode, GpioIrqFunc func, void *arg); + int32_t (*setIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t mode); int32_t (*unsetIrq)(struct GpioCntlr *cntlr, uint16_t local); int32_t (*enableIrq)(struct GpioCntlr *cntlr, uint16_t local); int32_t (*disableIrq)(struct GpioCntlr *cntlr, uint16_t local); } ``` -**表1** GpioMethod结构体成员的钩子函数功能说明 +**表 1** GpioMethod结构体成员的钩子函数功能说明 -| 函数成员 | 入参 | 出参 | 返回值 | 功能 | +| 函数成员 | 入参 | 出参 | 返回值 | 功能 | | -------- | -------- | -------- | -------- | -------- | -| write | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号
val:uint16_t类型,电平传入值 | 无 | HDF_STATUS相关状态 | GPIO引脚写入电平值 | -| read | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识 | val:uint16_t类型指针,用于传出电平值。 | HDF_STATUS相关状态 | GPIO引脚读取电平值 | -| setDir | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号
dir:uint16_t类型,管脚方向传入值 | 无 | HDF_STATUS相关状态 | 设置GPIO引脚输入/输出方向 | -| getDir | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号 | dir:uint16_t类型指针,用于传出管脚方向值 | HDF_STATUS相关状态 | 读GPIO引脚输入/输出方向 | -| setIrq | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号
mode:uint16_t类型,表示触发模式(边沿或电平)
func:函数指针,中断服务程序;
arg:void指针,中断服务程序入参 | 无 | HDF_STATUS相关状态 | 将GPIO引脚设置为中断模式 | -| unsetIrq | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号 | 无 | HDF_STATUS相关状态 | 取消GPIO中断设置 | -| enableIrq | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号 | 无 | HDF_STATUS相关状态 | 使能GPIO管脚中断 | -| disableIrq | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号 | 无 | HDF_STATUS相关状态 | 禁止GPIO管脚中断 | +| write | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号
val:uint16_t类型,电平传入值 | 无 | HDF_STATUS相关状态 | GPIO引脚写入电平值 | +| read | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号 | val:uint16_t类型指针,用于传出电平值。 | HDF_STATUS相关状态 | GPIO引脚读取电平值 | +| setDir | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号
dir:uint16_t类型,管脚方向传入值 | 无 | HDF_STATUS相关状态 | 设置GPIO引脚输入/输出方向 | +| getDir | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号 | dir:uint16_t类型指针,用于传出管脚方向值 | HDF_STATUS相关状态 | 读GPIO引脚输入/输出方向 | +| setIrq | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号
mode:uint16_t类型,表示触发模式(边沿或电平) | 无 | HDF_STATUS相关状态 | 将GPIO引脚设置为中断模式 | +| unsetIrq | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号 | 无 | HDF_STATUS相关状态 | 取消GPIO中断设置 | +| enableIrq | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号 | 无 | HDF_STATUS相关状态 | 使能GPIO管脚中断 | +| disableIrq | cntlr:结构体指针,核心层GPIO控制器
local:uint16_t类型,GPIO端口标识号 | 无 | HDF_STATUS相关状态 | 禁止GPIO管脚中断 | ### 开发步骤 GPIO模块适配包含以下四个步骤: -- 实例化驱动入口。 -- 配置属性文件。 -- 实例化GPIO控制器对象。 -- 驱动调试。 +- 实例化驱动入口 + +- 配置属性文件 + +- 实例化GPIO控制器对象 + +- 驱动调试 ### 开发实例 下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/gpio/gpio_hi35xx.c驱动为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。 -1. 实例化驱动入口。 - - 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 - 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 - - GPIO驱动入口开发参考: - - ```c - struct HdfDriverEntry g_gpioDriverEntry = { - .moduleVersion = 1, - .Bind = Pl061GpioBind, // GPIO不需要实现Bind,本例是一个空实现,驱动适配者可根据自身需要添加相关操作 - .Init = Pl061GpioInit, // 见Init参考 - .Release = Pl061GpioRelease, // 见Release参考 - .moduleName = "hisi_pl061_driver", // 【必要且需要与HCS文件中里面的moduleName匹配】 - }; - HDF_INIT(g_gpioDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 - ``` - -2. 配置属性文件。 - - 完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以一个GPIO控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加deviceNode信息。器件属性值与核心层GpioCntlr成员的默认值或限制范围有密切关系,需要在gpio_config.hcs中配置器件属性。 - - - device_info.hcs 配置参考: - - 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。 - - ```c - root { - device_info { - platform :: host { - hostName = "platform_host"; - priority = 50; - device_gpio :: device { - device0 :: deviceNode { - policy = 0; // 等于0,不需要发布服务 - priority = 10; // 驱动启动优先级 - permission = 0644; // 驱动创建设备节点权限 - moduleName = "hisi_pl061_driver"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致 - deviceMatchAttr = "hisilicon_hi35xx_pl061"; // 【必要】用于配置控制器私有数据,要与gpio_config.hcs中 - // 对应控制器保持一致,其他控制器信息也在文件中 - } - } - } - } - } - ``` - - - gpio_config.hcs配置参考: - - 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/gpio/gpio_config.hcs文件配置器件属性,其中配置参数如下: - - ```c - root { - platform { - gpio_config { - controller_0x120d0000 { - match_attr = "hisilicon_hi35xx_pl061"; // 【必要】必须和device_info.hcs中的deviceMatchAttr值一致 - groupNum = 12; // 【必要】GPIO组索引,需要根据设备情况填写 - bitNum = 8; // 【必要】每组GPIO管脚数 - regBase = 0x120d0000; // 【必要】物理基地址 - regStep = 0x1000; // 【必要】寄存器偏移步进 - irqStart = 48; // 【必要】开启中断 - irqShare = 0; // 【必要】共享中断 - } - ... - } - } - } - ``` - - 需要注意的是,新增gpio_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。 - - ```c - #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/gpio/gpio_config.hcs" // 配置文件相对路径 - ``` - -3. 实例化GPIO控制器对象。 - - 完成驱动入口注册之后,下一步就是以核心层GpioCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化GpioCntlr成员GpioMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。 - - - 驱动适配者自定义结构体参考。 - - 从驱动的角度看,自定义结构体是参数和数据的载体,而且gpio_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层GpioCntlr对象,例如索引、管脚数等。 - - ```c - //GPIO分组信息定义 - struct Pl061GpioGroup { - struct GpioCntlr cntlr; // 【必要】是核心层控制对象,其成员定义见下面。 - volatile unsigned char *regBase; // 【必要】寄存器基地址。 - unsigned int index; - unsigned int irq; - OsalIRQHandle irqFunc; - OsalSpinlock lock; - uint32_t irqSave; - bool irqShare; - struct PlatformDumper *dumper; - char *dumperName; - }; - - struct Pl061GpioData { - volatile unsigned char *regBase; // 【必要】寄存器基地址。 - uint32_t phyBase; // 【必要】物理基址。 - uint32_t regStep; // 【必要】寄存器偏移步进。 - uint32_t irqStart; // 【必要】中断开启。 - uint16_t groupNum; // 【必要】用于描述厂商的GPIO端口号的参数。 - uint16_t bitNum; // 【必要】用于描述厂商的GPIO端口号的参数。 - uint8_t irqShare; // 【必要】共享中断。 - struct Pl061GpioGroup *groups; // 【可选】根据厂商需要设置。 - struct GpioInfo *gpioInfo; - void *priv; - }; - - struct GpioInfo { - struct GpioCntlr *cntlr; - char name[GPIO_NAME_LEN]; - OsalSpinlock spin; - uint32_t irqSave; - struct GpioIrqRecord *irqRecord; - }; - // GpioCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。 - struct GpioCntlr { - struct PlatformDevice device; - struct GpioMethod *ops; - uint16_t start; - uint16_t count; - struct GpioInfo *ginfos; - bool isAutoAlloced; - void *priv; - }; - ``` - - - GpioCntlr成员钩子函数结构体GpioMethod的实例化,其他成员在Init函数中初始化。 - - ```c - //GpioMethod结构体成员都是钩子函数,驱动适配者需要根据表1完成相应的函数功能。 - static struct GpioMethod g_method = { - .request = NULL, - .release = NULL, - .write = Pl061GpioWrite, // 写管脚 - .read = Pl061GpioRead, // 读管脚 - .setDir = Pl061GpioSetDir, // 设置管脚方向 - .getDir = Pl061GpioGetDir, // 获取管脚方向 - .toIrq = NULL, - .setIrq = Pl061GpioSetIrq, // 设置管脚中断,如不具备此能力可忽略 - .unsetIrq = Pl061GpioUnsetIrq, // 取消管脚中断设置,如不具备此能力可忽略 - .enableIrq = Pl061GpioEnableIrq, // 使能管脚中断,如不具备此能力可忽略 - .disableIrq = Pl061GpioDisableIrq, // 禁止管脚中断,如不具备此能力可忽略 - }; - ``` - - - Init函数开发参考 - - 入参: - - HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 - - 返回值: - - HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 - - **表2** Init函数说明 - - | 状态(值) | 问题描述 | - | -------- | -------- | - | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | - | HDF_ERR_MALLOC_FAIL | 内存分配失败 | - | HDF_ERR_INVALID_PARAM | 参数非法 | - | HDF_ERR_IO | I/O 错误 | - | HDF_SUCCESS | 初始化成功 | - | HDF_FAILURE | 初始化失败 | - - 函数说明: - - 初始化自定义结构体对象,初始化GpioCntlr成员,调用核心层GpioCntlrAdd函数,接入VFS(可选)。 - - ```c - static struct Pl061GpioData g_pl061 = { - .groups = NULL, - .groupNum = PL061_GROUP_MAX, - .bitNum = PL061_BIT_MAX, - }; - - static int32_t Pl061GpioInitGroups(struct Pl061GpioData *pl061) - { - int32_t ret; - uint16_t i; - struct Pl061GpioGroup *groups = NULL; - - if (pl061 == NULL) { - return HDF_ERR_INVALID_PARAM; - } - - groups = (struct Pl061GpioGroup *)OsalMemCalloc(sizeof(*groups) * pl061->groupNum); - if (groups == NULL) { - return HDF_ERR_MALLOC_FAIL; - } - pl061->groups = groups; - - for (i = 0; i < pl061->groupNum; i++) { - // 相关信息初始化 - groups[i].index = i; - groups[i].regBase = pl061->regBase + i * pl061->regStep; - groups[i].irq = pl061->irqStart + i; - groups[i].irqShare = pl061->irqShare; - groups[i].cntlr.start = i * pl061->bitNum; - groups[i].cntlr.count = pl061->bitNum; - groups[i].cntlr.ops = &g_method; - groups[i].cntlr.ginfos = &pl061->gpioInfo[i * pl061->bitNum]; - - if ((ret = OsalSpinInit(&groups[i].lock)) != HDF_SUCCESS) { - goto ERR_EXIT; - } - - ret = GpioCntlrAdd(&groups[i].cntlr); // 向HDF core中添加相关信息 - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: err add controller(%hu:%hu):%d", __func__, - groups[i].cntlr.start, groups[i].cntlr.count, ret); - (void)OsalSpinDestroy(&groups[i].lock); - goto ERR_EXIT; - } - } - return HDF_SUCCESS; - - ERR_EXIT: - while (i-- > 0) { - GpioCntlrRemove(&groups[i].cntlr); - (void)OsalSpinDestroy(&groups[i].lock); - } - pl061->groups = NULL; - OsalMemFree(groups); - return ret; - } - - static int32_t Pl061GpioInit(struct HdfDeviceObject *device) - { - int32_t ret; - struct Pl061GpioData *pl061 = &g_pl061; - - if (device == NULL || device->property == NULL) { - HDF_LOGE("%s: device or property null!", __func__); - return HDF_ERR_INVALID_OBJECT; - } - - pl061->gpioInfo = OsalMemCalloc(sizeof(struct GpioInfo) * GPIO_MAX_INFO_NUM); - if (pl061->gpioInfo == NULL) { - HDF_LOGE("%s: failed to calloc gpioInfo!", __func__); - return HDF_ERR_MALLOC_FAIL; - } - - ret = Pl061GpioReadDrs(pl061, device->property); // 利用从gpio_config.HCS文件读取的属性值来初始化自定义结构体对象成员 - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: failed to read drs:%d", __func__, ret); - return ret; - } - - if (pl061->groupNum > PL061_GROUP_MAX || pl061->groupNum <= 0 || - pl061->bitNum > PL061_BIT_MAX || pl061->bitNum <= 0) { - HDF_LOGE("%s: err groupNum:%hu, bitNum:%hu", __func__, pl061->groupNum, pl061->bitNum); - return HDF_ERR_INVALID_PARAM; - } - - pl061->regBase = OsalIoRemap(pl061->phyBase, pl061->groupNum * pl061->regStep); //地址映射 - if (pl061->regBase == NULL) { - HDF_LOGE("%s: err remap phy:0x%x", __func__, pl061->phyBase); - return HDF_ERR_IO; - } - - ret = Pl061GpioInitGroups(pl061); //group信息初始化,并添加到HDF核心层 - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: err init groups:%d", __func__, ret); - OsalIoUnmap((void *)pl061->regBase); - pl061->regBase = NULL; - return ret; - } - pl061->priv = (void *)device->property; - device->priv = (void *)pl061; - Pl061GpioDebug(pl061); - - #ifdef PL061_GPIO_USER_SUPPORT - if (GpioAddVfs(pl061->bitNum) != HDF_SUCCESS) { - HDF_LOGE("%s: add vfs fail!", __func__); - } - #endif - HDF_LOGI("%s: dev service:%s init success!", __func__, HdfDeviceGetServiceName(device)); - return HDF_SUCCESS; - } - ``` - - - Release函数开发参考 - - 入参: - - HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 - - 返回值: - - 无。 - - 函数说明: - - 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 - - > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 所有强制转换获取相应对象的操作**前提**是在Init函数中具备对应赋值的操作。 - - ```c - static void Pl061GpioUninitGroups(struct Pl061GpioData *pl061) - { - uint16_t i; - struct Pl061GpioGroup *group = NULL; - - for (i = 0; i < pl061->groupNum; i++) { - group = &pl061->groups[i]; - GpioDumperDestroy(&pl061->groups[i]); - GpioCntlrRemove(&group->cntlr); // 从HDF核心层删除 - } - - OsalMemFree(pl061->groups); - pl061->groups = NULL; - } - - static void Pl061GpioRelease(struct HdfDeviceObject *device) - { - struct Pl061GpioData *pl061 = NULL; - - HDF_LOGI("%s: enter", __func__); - if (device == NULL) { - HDF_LOGE("%s: device is null!", __func__); - return; - } - - #ifdef PL061_GPIO_USER_SUPPORT - GpioRemoveVfs(); - #endif - - pl061 = (struct Pl061GpioData *)device->priv; - if (pl061 == NULL) { - HDF_LOGE("%s: device priv is null", __func__); - return; - } - - Pl061GpioUninitGroups(pl061); - OsalMemFree(pl061->gpioInfo); - pl061->gpioInfo = NULL; - OsalIoUnmap((void *)pl061->regBase); - pl061->regBase = NULL; - } - ``` - -4. 驱动调试。 - - 【可选】针对新增驱动程序,建议验证驱动基本功能,例如GPIO控制状态,中断响应情况等。 \ No newline at end of file +1. 实例化驱动入口 + + 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 + + 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 + + GPIO驱动入口开发参考: + + ```c + struct HdfDriverEntry g_gpioDriverEntry = { + .moduleVersion = 1, + .Bind = Pl061GpioBind, // GPIO不需要实现Bind,本例是一个空实现,驱动适配者可根据自身需要添加相关操作 + .Init = Pl061GpioInit, // 挂接Gpio模块Init实例化 + .Release = Pl061GpioRelease, // 挂接Gpio模块Release实例化 + .moduleName = "hisi_pl061_driver", // 【必要且需要与HCS文件中里面的moduleName匹配】 + }; + HDF_INIT(g_gpioDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 + ``` + +2. 配置属性文件 + + 完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以一个GPIO控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加deviceNode信息,以及在gpio_config.hcs文件中增加对应的器件属性。器件属性值与核心层GpioCntlr成员的默认值或限制范围有密切关系,需要在gpio_config.hcs中配置器件属性。 + + 统一服务模式的特点是device_info.hcs文件中第一个设备节点必须为GPIO管理器,其各项参数如表2所示: + + **表 2** device_info.hcs节点参数说明 + + | 成员名 | 值 | + | -------- | -------- | + | policy | 驱动服务发布的策略,GPIO管理器具体配置为2,表示驱动对内核态和用户态都发布服务 | + | priority | 驱动启动优先级(0-200),值越大优先级越低。GPIO管理器具体配置为10 | + | permission | 驱动创建设备节点权限,GPIO管理器具体配置为0664 | + | moduleName | 驱动名称,GPIO管理器固定为HDF_PLATFORM_GPIO_MANAGER | + | serviceName | 驱动对外发布服务的名称,GPIO管理器服务名设置为HDF_PLATFORM_GPIO_MANAGER | + | deviceMatchAttr | 驱动私有数据匹配的关键字,GPIO管理器没有使用,可忽略 | + + - device_info.hcs 配置参考: + + 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。 + + ```c + root { + device_info { + platform :: host { + hostName = "platform_host"; + priority = 50; + device_gpio :: device { + device0 :: deviceNode { + policy = 2; + priority = 10; + permission = 0644; + moduleName = "HDF_PLATFORM_GPIO_MANAGER"; + serviceName = "HDF_PLATFORM_GPIO_MANAGER"; + } + device1 :: deviceNode { + policy = 0; // 等于0,不需要发布服务 + priority = 10; // 驱动启动优先级 + permission = 0644; // 驱动创建设备节点权限 + moduleName = "hisi_pl061_driver"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致 + deviceMatchAttr = "hisilicon_hi35xx_pl061"; // 【必要】用于配置控制器私有数据,要与gpio_config.hcs中对应控制器保持一致,其他控制器信息也在文件中 + } + } + } + } + } + ``` + + - gpio_config.hcs配置参考: + + 此处以Hi3516DV300为例,给出HCS配置参考。其中部分字段为Hi3516DV300特有功能,驱动适配者可根据需要进行删除或添加字段。 + + ```c + root { + platform { + gpio_config { + controller_0x120d0000 { + match_attr = "hisilicon_hi35xx_pl061"; // 【必要】必须和device_info.hcs中的deviceMatchAttr值一致 + groupNum = 12; // 【必要】GPIO组索引,需要根据设备情况填写 + bitNum = 8; // 【必要】每组GPIO管脚数 + regBase = 0x120d0000; // 【必要】物理基地址 + regStep = 0x1000; // 【必要】寄存器偏移步进 + irqStart = 48; // 【必要】开启中断 + irqShare = 0; // 【必要】共享中断 + } + template gpio_info { // gpio_info模板 + gpioCustomName = ""; // gpio管脚默认名称 + } + GPIO0 :: gpio_info { + gpioCustomName = "GPIO0_0"; + } + ...... + } + } + } + ``` + + 需要注意的是,新增gpio_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。 + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/gpio/gpio_config.hcs" // 配置文件相对路径 + ``` + + 本例基于Hi3516DV300开发板的小型系统LiteOS内核运行,对应的hdf.hcs文件路径为vendor/hisilicon/hispark_taurus/hdf_config/hdf.hcs以及//device/hisilicon/hispark_taurus/sdk_liteos/hdf_config/hdf.hcs。驱动适配者需根据实际情况选择对应路径下的文件进行修改。 + +3. 实例化GPIO控制器对象 + + 完成驱动入口注册之后,下一步就是以核心层GpioCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化GpioCntlr成员GpioMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。 + + - 驱动适配者自定义结构体参考。 + + 从驱动的角度看,自定义结构体是参数和数据的载体,而且gpio_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层GpioCntlr对象,例如索引、管脚数等。 + + ```c + //GPIO分组信息定义 + struct Pl061GpioGroup { + struct GpioCntlr cntlr; // 【必要】是核心层控制对象,其成员定义见下面。 + volatile unsigned char *regBase; // 【必要】寄存器基地址。 + unsigned int index; + unsigned int irq; + OsalIRQHandle irqFunc; + OsalSpinlock lock; + uint32_t irqSave; + bool irqShare; + struct PlatformDumper *dumper; + char *dumperName; + }; + + struct Pl061GpioData { + volatile unsigned char *regBase; // 【必要】寄存器基地址。 + uint32_t phyBase; // 【必要】物理基址。 + uint32_t regStep; // 【必要】寄存器偏移步进。 + uint32_t irqStart; // 【必要】中断开启。 + uint16_t groupNum; // 【必要】用于描述厂商的GPIO端口号的参数。 + uint16_t bitNum; // 【必要】用于描述厂商的GPIO端口号的参数。 + uint8_t irqShare; // 【必要】共享中断。 + struct Pl061GpioGroup *groups; // 【可选】根据厂商需要设置。 + struct GpioInfo *gpioInfo; + void *priv; + }; + + struct GpioInfo { + struct GpioCntlr *cntlr; + char name[GPIO_NAME_LEN]; + OsalSpinlock spin; + uint32_t irqSave; + struct GpioIrqRecord *irqRecord; + }; + // GpioCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。 + struct GpioCntlr { + struct PlatformDevice device; + struct GpioMethod *ops; + uint16_t start; + uint16_t count; + struct GpioInfo *ginfos; + bool isAutoAlloced; + void *priv; + }; + ``` + + - GpioCntlr成员钩子函数结构体GpioMethod的实例化。 + + ```c + //GpioMethod结构体成员都是钩子函数,驱动适配者需要根据表1完成相应的函数功能。 + static struct GpioMethod g_method = { + .request = NULL, + .release = NULL, + .write = Pl061GpioWrite, // 写管脚 + .read = Pl061GpioRead, // 读管脚 + .setDir = Pl061GpioSetDir, // 设置管脚方向 + .getDir = Pl061GpioGetDir, // 获取管脚方向 + .toIrq = NULL, + .setIrq = Pl061GpioSetIrq, // 设置管脚中断,如不具备此能力可忽略 + .unsetIrq = Pl061GpioUnsetIrq, // 取消管脚中断设置,如不具备此能力可忽略 + .enableIrq = Pl061GpioEnableIrq, // 使能管脚中断,如不具备此能力可忽略 + .disableIrq = Pl061GpioDisableIrq, // 禁止管脚中断,如不具备此能力可忽略 + }; + ``` + + - Init函数开发参考 + + 入参: + + HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 + + 返回值: + + HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 + + **表 3** HDF_STATUS相关状态说明 + + | 状态(值) | 问题描述 | + | -------- | -------- | + | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | + | HDF_ERR_MALLOC_FAIL | 内存分配失败 | + | HDF_ERR_INVALID_PARAM | 参数非法 | + | HDF_ERR_IO | I/O 错误 | + | HDF_SUCCESS | 初始化成功 | + | HDF_FAILURE | 初始化失败 | + + 函数说明: + + 初始化自定义结构体对象,初始化GpioCntlr成员,调用核心层GpioCntlrAdd函数,接入VFS(可选)。 + + ```c + static struct Pl061GpioData g_pl061 = { + .groups = NULL, + .groupNum = PL061_GROUP_MAX, + .bitNum = PL061_BIT_MAX, + }; + + static int32_t Pl061GpioInitGroups(struct Pl061GpioData *pl061) + { + int32_t ret; + uint16_t i; + struct Pl061GpioGroup *groups = NULL; + + if (pl061 == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + groups = (struct Pl061GpioGroup *)OsalMemCalloc(sizeof(*groups) * pl061->groupNum); + if (groups == NULL) { + return HDF_ERR_MALLOC_FAIL; + } + pl061->groups = groups; + + for (i = 0; i < pl061->groupNum; i++) { + // 相关信息初始化 + groups[i].index = i; + groups[i].regBase = pl061->regBase + i * pl061->regStep; + groups[i].irq = pl061->irqStart + i; + groups[i].irqShare = pl061->irqShare; + groups[i].cntlr.start = i * pl061->bitNum; + groups[i].cntlr.count = pl061->bitNum; + groups[i].cntlr.ops = &g_method; + groups[i].cntlr.ginfos = &pl061->gpioInfo[i * pl061->bitNum]; + + if ((ret = OsalSpinInit(&groups[i].lock)) != HDF_SUCCESS) { + goto ERR_EXIT; + } + + ret = GpioCntlrAdd(&groups[i].cntlr); // 向HDF core中添加相关信息 + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: err add controller(%hu:%hu):%d", __func__, + groups[i].cntlr.start, groups[i].cntlr.count, ret); + (void)OsalSpinDestroy(&groups[i].lock); + goto ERR_EXIT; + } + ret = GpioDumperCreate(&pl061->groups[i]); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: create dumper failed:%d", __func__, ret); + return ret; + } + } + return HDF_SUCCESS; + + ERR_EXIT: + while (i-- > 0) { + GpioCntlrRemove(&groups[i].cntlr); + (void)OsalSpinDestroy(&groups[i].lock); + } + pl061->groups = NULL; + OsalMemFree(groups); + return ret; + } + + static int32_t Pl061GpioInit(struct HdfDeviceObject *device) + { + int32_t ret; + struct Pl061GpioData *pl061 = &g_pl061; + + if (device == NULL || device->property == NULL) { + HDF_LOGE("%s: device or property null!", __func__); + return HDF_ERR_INVALID_OBJECT; + } + + pl061->gpioInfo = OsalMemCalloc(sizeof(struct GpioInfo) * GPIO_MAX_INFO_NUM); + if (pl061->gpioInfo == NULL) { + HDF_LOGE("%s: failed to calloc gpioInfo!", __func__); + return HDF_ERR_MALLOC_FAIL; + } + + ret = Pl061GpioReadDrs(pl061, device->property); // 利用从gpio_config.HCS文件读取的属性值来初始化自定义结构体对象成员 + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: failed to read drs:%d", __func__, ret); + return ret; + } + + if (pl061->groupNum > PL061_GROUP_MAX || pl061->groupNum <= 0 || + pl061->bitNum > PL061_BIT_MAX || pl061->bitNum <= 0) { + HDF_LOGE("%s: err groupNum:%hu, bitNum:%hu", __func__, pl061->groupNum, pl0 61->bitNum); + return HDF_ERR_INVALID_PARAM; + } + + pl061->regBase = OsalIoRemap(pl061->phyBase, pl061->groupNum * pl061->regStep); // 地址映射 + if (pl061->regBase == NULL) { + HDF_LOGE("%s: err remap phy:0x%x", __func__, pl061->phyBase); + return HDF_ERR_IO; + } + + ret = Pl061GpioInitGroups(pl061); // group信息初始化,并添加到HDF核心层 + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: err init groups:%d", __func__, ret); + OsalIoUnmap((void *)pl061->regBase); + pl061->regBase = NULL; + return ret; + } + pl061->priv = (void *)device->property; + device->priv = (void *)pl061; + Pl061GpioDebug(pl061); + + #ifdef PL061_GPIO_USER_SUPPORT + if (GpioAddVfs(pl061->bitNum) != HDF_SUCCESS) { + HDF_LOGE("%s: add vfs fail!", __func__); + } + #endif + HDF_LOGI("%s: dev service:%s init success!", __func__, HdfDeviceGetServiceName(device)); + return HDF_SUCCESS; + } + ``` + + - Release函数开发参考 + + 入参: + + HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 + + 返回值: + + 无。 + + 函数说明: + + 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 + + > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 所有强制转换获取相应对象的操作**前提**是在Init函数中具备对应赋值的操作。 + + ```c + static void Pl061GpioUninitGroups(struct Pl061GpioData *pl061) + { + uint16_t i; + struct Pl061GpioGroup *group = NULL; + + for (i = 0; i < pl061->groupNum; i++) { + group = &pl061->groups[i]; + GpioDumperDestroy(&pl061->groups[i]); + GpioCntlrRemove(&group->cntlr); // 从HDF核心层删除 + } + + OsalMemFree(pl061->groups); + pl061->groups = NULL; + } + + static void Pl061GpioRelease(struct HdfDeviceObject *device) + { + struct Pl061GpioData *pl061 = NULL; + + HDF_LOGI("%s: enter", __func__); + if (device == NULL) { + HDF_LOGE("%s: device is null!", __func__); + return; + } + + #ifdef PL061_GPIO_USER_SUPPORT + GpioRemoveVfs(); + #endif + + pl061 = (struct Pl061GpioData *)device->priv; + if (pl061 == NULL) { + HDF_LOGE("%s: device priv is null", __func__); + return; + } + + Pl061GpioUninitGroups(pl061); + OsalMemFree(pl061->gpioInfo); + pl061->gpioInfo = NULL; + OsalIoUnmap((void *)pl061->regBase); + pl061->regBase = NULL; + } + ``` + +4. 驱动调试 + + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如GPIO控制状态,中断响应情况等。 diff --git a/zh-cn/device-dev/driver/driver-platform-hdmi-des.md b/zh-cn/device-dev/driver/driver-platform-hdmi-des.md index e67370bda2be9dc82836a1f8743acbc5076aaf51..aafcaea77cd202545e38fccad77e384ca1a7d0fc 100755 --- a/zh-cn/device-dev/driver/driver-platform-hdmi-des.md +++ b/zh-cn/device-dev/driver/driver-platform-hdmi-des.md @@ -1,18 +1,23 @@ # HDMI - ## 概述 ### 功能简介 HDMI(High Definition Multimedia Interface),即高清多媒体接口,主要用于DVD、机顶盒等音视频Source到TV、显示器等Sink设备的传输。 + HDMI以主从方式工作,通常有一个Source端和一个Sink端。 + HDMI接口定义了完成HDMI传输的通用方法集合,包括: - HDMI控制器管理:打开或关闭HDMI控制器 + - HDMI启动/停止传输:启动或停止HDMI传输 + - HDMI控制器设置:设置音频、视频及HDR属性,设置色彩深度、声音图像消隐等 + - HDMI读取EDID:读取Sink端原始的EDID数据 + - HDMI热插拔:注册/注销热插拔回调函数 ### 基本概念 @@ -20,25 +25,50 @@ HDMI接口定义了完成HDMI传输的通用方法集合,包括: HDMI是Hitachi、Panasonic、Philips、Silicon Image、Sony、Thomson、Toshiba共同发布的一款音视频传输协议。传输过程遵循TMDS(Transition Minimized Differential Signaling)协议。 - TMDS(Transition Minimized Differential signal):过渡调制差分信号,也被称为最小化传输差分信号,用于发送音频、视频及各种辅助数据。 + - DDC(Display Data Channel):显示数据通道,发送端与接收端可利用DDC通道得知彼此的发送与接收能力,但HDMI仅需单向获知接收端(显示器)的能力。 + - CEC(Consumer Electronics Control):消费电子控制,该功能应该能够在连接HDMI的发送设备与接收设备之间实现交互操作。 + - FRL(Fixed Rate Link):TMDS 的架构进行讯号传输时,最高带宽可达 18Gbps,而FRL模式的带宽则提升到48 Gbps。 + - HDCP(High-bandwidth Digital Content Protection):即高带宽数字内容保护技术,当用户对高清晰信号进行非法复制时,该技术会进行干扰,降低复制出来的影像的质量,从而对内容进行保护。 + - EDID(Extended Display Identification Data):扩展显示标识数据,通常存储在显示器的固件中,标识供应商信息、EDID版本信息、最大图像大小、颜色设置、厂商预设置、频率范围的限制以及显示器名和序列号的字符串。 ### 运作机制 +在HDF框架中,HDMI模块接口适配模式拟采用独立服务模式,如图1所示。在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDF设备管理器的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。 + +独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为: + +- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。 + +- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。 + +**图 1** HDMI独立服务模式结构图 + +![HDMI独立服务模式结构图](figures/独立服务模式结构图.png) + +HDMI模块各分层作用: + +- 接口层提供打开HDMI设备、启动HDMI传输、停止HDMI传输、声音图像消隐设置、设置色彩深度、获取色彩深度、设置视频属性、获取视频属性、设置HDR属性、读取Sink端原始EDID数据、注册HDMI热插拔检测回调函数、注销HDMI热插拔检测回调函数、关闭HDMI设备的接口。 + +- 核心层主要提供HDMI控制器的打开、关闭及管理的能力,通过钩子函数与适配层交互。 + +- 适配层主要是将钩子函数的功能实例化,实现具体的功能。 + HDMI的Source端提供+5V和GND,用于DDC和CEC通信。通过DDC通道,Source端可以读取Sink端的各项参数,如接受能力等;CEC为可选通道,用于同步Source端与Sink端的控制信号,改善用户体验。TMDS通道有四组差分信号,TMDS Clock Channel为TMDS提供时钟信号,其余三组传输音视频数据及各种辅助数据;HDP为热插拔检测端口,当有Sink端接入时,Source端会通过中断服务程序进行响应。 -HDMI物理连接如图1所示: +HDMI物理连接如图2所示: -**图 1** HDMI物理连线示意图 +**图 2** HDMI物理连线示意图 -![](figures/HDMI物理连线示意图.png "HDMI物理连线示意图") +![HDMI物理连线示意图](figures/HDMI物理连线示意图.png) ### 约束与限制 -HDMI模块当前仅支持轻量和小型系统内核(LiteOS)。 +HDMI模块当前仅支持轻量和小型系统内核(LiteOS),暂无实际适配驱动 。 ## 使用指导 @@ -48,32 +78,33 @@ HDMI具有体积小,传输速率高,传输带宽宽,兼容性好,能同 ### 接口说明 -**表 1** HDMI驱动API接口功能介绍 +HDMI模块提供的主要接口如下所示,具体API详见//drivers/hdf_core/framework/include/platform/hdmi_if.h。 +**表 1** HDMI驱动API接口功能介绍 -| 接口名 | 描述 | +| 接口名 | 描述 | | ----------------------------- | -------------------------- | -| HdmiOpen | 打开HDMI控制器 | -| HdmiClose | 关闭HDMI控制器 | -| HdmiStart | 启动HDMI传输 | -| HdmiStop | 停止HDMI传输 | -| HdmiAvmuteSet | 声音图像消隐设置 | -| HdmiDeepColorSet | 设置色彩深度 | -| HdmiDeepColorGet | 获取色彩深度 | -| HdmiSetVideoAttribute | 设置视频属性 | -| HdmiSetAudioAttribute | 设置音频属性 | -| HdmiSetHdrAttribute | 设置HDR属性 | -| HdmiReadSinkEdid | 读取Sink端原始EDID数据 | -| HdmiRegisterHpdCallbackFunc | 注册HDMI热插拔检测回调函数 | +| HdmiOpen | 打开HDMI控制器 | +| HdmiClose | 关闭HDMI控制器 | +| HdmiStart | 启动HDMI传输 | +| HdmiStop | 停止HDMI传输 | +| HdmiAvmuteSet | 声音图像消隐设置 | +| HdmiDeepColorSet | 设置色彩深度 | +| HdmiDeepColorGet | 获取色彩深度 | +| HdmiSetVideoAttribute | 设置视频属性 | +| HdmiSetAudioAttribute | 设置音频属性 | +| HdmiSetHdrAttribute | 设置HDR属性 | +| HdmiReadSinkEdid | 读取Sink端原始EDID数据 | +| HdmiRegisterHpdCallbackFunc | 注册HDMI热插拔检测回调函数 | | HdmiUnregisterHpdCallbackFunc | 注销HDMI热插拔检测回调函数 | ### 开发步骤 -使用HDMI设备的一般流程如图2所示。 +使用HDMI设备的一般流程如图3所示。 -**图 2** HDMI设备使用流程图 +**图 3** HDMI设备使用流程图 -![](figures/HDMI使用流程图.png "HDMI使用流程图") +![HDMI设备使用流程图](figures/HDMI使用流程图.png) #### 打开HDMI控制器 @@ -83,25 +114,25 @@ HDMI具有体积小,传输速率高,传输带宽宽,兼容性好,能同 DevHandle HdmiOpen(int16_t number); ``` -**表 2** HdmiOpen参数和返回值描述 +**表 2** HdmiOpen参数和返回值描述 -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | -------------------- | -| number | HDMI控制器号 | -| **返回值** | **返回值描述** | -| NULL | 打开HDMI控制器失败 | +| number | int16_t类型,HDMI控制器号 | +| **返回值** | **返回值描述** | +| NULL | 打开HDMI控制器失败 | | 控制器句柄 | 打开的HDMI控制器句柄 | 假设系统中存在2个HDMI控制器,编号从0到1,以下代码示例为获取0号控制器: ```c -DevHandle hdmiHandle = NULL; /* HDMI控制器句柄 / +DevHandle hdmiHandle = NULL; // HDMI控制器句柄 -/* 打开HDMI控制器 */ +// 打开HDMI控制器 hdmiHandle = HdmiOpen(0); if (hdmiHandle == NULL) { - HDF_LOGE("HdmiOpen: failed\n"); - return; + HDF_LOGE("HdmiOpen: hdmi open fail!\n"); + return NULL; } ``` @@ -111,20 +142,20 @@ if (hdmiHandle == NULL) { int32_t HdmiRegisterHpdCallbackFunc(DevHandle handle, struct HdmiHpdCallbackInfo *callback); ``` -**表 3** HdmiRegisterHpdCallbackFunc参数和返回值描述 +**表 3** HdmiRegisterHpdCallbackFunc参数和返回值描述 -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | ------------------ | -| handle | HDMI控制器句柄 | -| callback | 热插拔回调函数信息 | -| **返回值** | **返回值描述** | -| 0 | 注册成功 | -| 负数 | 注册失败 | +| handle | DevHandle类型,HDMI控制器句柄 | +| callback | 结构体指针,热插拔回调函数信息 | +| **返回值** | **返回值描述** | +| HDF_SUCCESS | 注册成功 | +| 负数 | 注册失败 | 注册热插拔检测回调函数示例: ```c -/* 热插拔检测回调函数定义 */ +// 热插拔检测回调函数定义 static void HdmiHpdHandle(void *data, bool hpd) { if (data == NULL) { @@ -133,23 +164,22 @@ static void HdmiHpdHandle(void *data, bool hpd) } if (hpd == true) { HDF_LOGD("HdmiHpdHandle: hot plug"); - /* 调用者添加相关处理 */ + // 调用者添加相关处理 } else { HDF_LOGD("HdmiHpdHandle: hot unplug"); - /* 调用者添加相关处理 */ + // 调用者添加相关处理 } } -/* 热插拔检测回调函数注册示例 */ -··· +// 热插拔检测回调函数注册示例 struct HdmiHpdCallbackInfo info = {0}; info.data = handle; info.callbackFunc = HdmiHpdHandle; ret = HdmiRegisterHpdCallbackFunc(hdmiHandle, info); -if (ret != 0) { - HDF_LOGE("HdmiRegisterHpdCallbackFunc: Register failed."); +if (ret != HDF_SUCCESS) { + HDF_LOGE("HdmiRegisterHpdCallbackFunc: Register hpd callback func fail, ret:%d", ret); + return ret; } -··· ``` #### 读取EDID @@ -158,16 +188,16 @@ if (ret != 0) { int32_t HdmiReadSinkEdid(DevHandle handle, uint8_t *buffer, uint32_t len); ``` -**表 4** HdmiReadSinkEdid参数和返回值描述 +**表 4** HdmiReadSinkEdid参数和返回值描述 -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | ---------------------- | -| handle | HDMI控制器句柄 | -| buffer | 数据缓冲区 | -| len | 数据长度 | -| **返回值** | **返回值描述** | -| 正整数 | 成功读取的原始EDID数据 | -| 负数或0 | 读取失败 | +| handle | DevHandle类型,HDMI控制器句柄 | +| buffer | uint8_t类型指针,数据缓冲区 | +| len | uint32_t类型,数据长度 | +| **返回值** | **返回值描述** | +| 正整数 | 成功读取的原始EDID数据 | +| 负数或0 | 读取失败 | 读取Sink端的原始EDID数据示例: @@ -177,7 +207,8 @@ uint8_t edid[HDMI_EDID_MAX_LEN] = {0}; len = HdmiReadSinkEdid(hdmiHandle, edid, HDMI_EDID_MAX_LEN); if (len <= 0) { - HDF_LOGE("%s: HdmiReadSinkEdid failed len = %d.", __func__, len); + HDF_LOGE("HdmiReadSinkEdid: hdmi read sink edid fail, len = %d.", len); + return HDF_FAILURE; } ``` @@ -187,16 +218,15 @@ if (len <= 0) { int32_t HdmiSetAudioAttribute(DevHandle handle, struct HdmiAudioAttr *attr); ``` -**表 5** HdmiSetAudioAttribute参数和返回值描述 +**表 5** HdmiSetAudioAttribute参数和返回值描述 - -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ------ | -------------- | -| handle | HDMI控制器句柄 | -| attr | 音频属性 | -| 返回值 | 返回值描述 | -| 0 | 设置成功 | -| 负数 | 设置失败 | +| handle | DevHandle类型,HDMI控制器句柄 | +| attr | 结构体指针,音频属性 | +| 返回值 | 返回值描述 | +| HDF_SUCCESS | 设置成功 | +| 负数 | 设置失败 | 设置音频属性示例: @@ -210,8 +240,9 @@ audioAttr.bitDepth = HDMI_ADIO_BIT_DEPTH_16; audioAttr.sampleRate = HDMI_SAMPLE_RATE_8K; audioAttr.channels = HDMI_AUDIO_FORMAT_CHANNEL_3; ret = HdmiSetAudioAttribute(handle, &audioAttr); -if (ret != 0) { - HDF_LOGE("HdmiSetAudioAttribute failed."); +if (ret != HDF_SUCCESS) { + HDF_LOGE("HdmiSetAudioAttribute: hdmi set audio attribute fail!, ret:%d", ret); + return ret; } ``` @@ -221,16 +252,15 @@ if (ret != 0) { int32_t HdmiSetVideoAttribute(DevHandle handle, struct HdmiVideoAttr *attr); ``` -**表 6** HdmiSetVideoAttribute参数和返回值描述 - +**表 6** HdmiSetVideoAttribute参数和返回值描述 -| 参数 | 参数描述 | +| 参数| 参数描述| | ---------- | -------------- | -| handle | HDMI控制器句柄 | -| attr | 视频属性 | +| handle | DevHandle类型,HDMI控制器句柄 | +| attr | 结构体指针,视频属性 | | **返回值** | **返回值描述** | -| 0 | 设置成功 | -| 负数 | 设置失败 | +| HDF_SUCCESS | 设置成功 | +| 负数 | 设置失败 | 设置视频属性示例: @@ -243,8 +273,9 @@ videoAttr.colorimetry = HDMI_COLORIMETRY_EXTENDED; videoAttr.extColorimetry = HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM; videoAttr.quantization = HDMI_QUANTIZATION_RANGE_FULL; ret = HdmiSetVideoAttribute(handle, &videoAttr); -if (ret != 0) { - HDF_LOGE("HdmiSetVideoAttribute failed."); +if (ret != HDF_SUCCESS) { + HDF_LOGE("HdmiSetVideoAttribute: hdmi set video attribute fail, ret:%d.", ret); + return ret; } ``` @@ -254,16 +285,15 @@ if (ret != 0) { int32_t HdmiSetHdrAttribute(DevHandle handle, struct HdmiHdrAttr *attr); ``` -**表 7** HdmiSetHdrAttribute参数和返回值描述 +**表 7** HdmiSetHdrAttribute参数和返回值描述 - -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | -------------- | -| handle | HDMI控制器句柄 | -| attr | HDR属性 | +| handle | DevHandle类型,HDMI控制器句柄 | +| attr | 结构体指针,HDR属性 | | **返回值** | **返回值描述** | -| 0 | 设置成功 | -| 负数 | 设置失败 | +| HDF_SUCCESS | 设置成功 | +| 负数 | 设置失败 | 设置HDR属性示例: @@ -277,8 +307,9 @@ hdrAttr.eotfType = HDMI_EOTF_SMPTE_ST_2048; hdrAttr.metadataType = HDMI_DRM_STATIC_METADATA_TYPE_1; hdrAttr.colorimetry = HDMI_HDR_EXTENDED_COLORIMETRY_XV_YCC_709; ret = HdmiSetHdrAttribute(handle, &hdrAttr); -if (ret != 0) { - HDF_LOGE("HdmiSetHdrAttribute failed."); +if (ret != HDF_SUCCESS) { + HDF_LOGE("HdmiSetHdrAttribute: hdmi set hdr attribute fail, ret:%d", ret); + return ret; } ``` @@ -288,16 +319,15 @@ if (ret != 0) { int32_t HdmiAvmuteSet(DevHandle handle, bool enable); ``` -**表 8** HdmiAvmuteSet参数和返回值描述 - +**表 8** HdmiAvmuteSet参数和返回值描述 -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | ----------------- | -| handle | HDMI控制器句柄 | -| enable | 使能/去使能avmute | -| **返回值** | **返回值描述** | -| 0 | 设置成功 | -| 负数 | 设置失败 | +| handle | DevHandle类型,HDMI控制器句柄 | +| enable | 布尔值,使能/去使能avmute | +| **返回值** | **返回值描述** | +| HDF_SUCCESS | 设置成功 | +| 负数 | 设置失败 | 设置声音图像消隐示例: @@ -305,8 +335,9 @@ int32_t HdmiAvmuteSet(DevHandle handle, bool enable); int32_t ret; ret = HdmiAvmuteSet(hdmiHandle, true); -if (ret != 0) { - HDF_LOGE("HdmiAvmuteSet failed."); +if (ret != HDF_SUCCESS) { + HDF_LOGE("HdmiAvmuteSet: hdmi avmute set fail, ret:%d", ret); + return ret; } ``` @@ -316,16 +347,15 @@ if (ret != 0) { int32_t HdmiDeepColorSet(DevHandle handle, enum HdmiDeepColor color); ``` -**表 9** HdmiDeepColorSet参数和返回值描述 +**表 9** HdmiDeepColorSet参数和返回值描述 - -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | -------------- | -| handle | HDMI控制器句柄 | -| color | 色彩深度 | +| handle | DevHandle类型,HDMI控制器句柄 | +| color | 枚举类型,色彩深度 | | **返回值** | **返回值描述** | -| 0 | 设置成功 | -| 负数 | 设置失败 | +| HDF_SUCCESS | 设置成功 | +| 负数 | 设置失败 | 设置色彩深度示例: @@ -333,8 +363,9 @@ int32_t HdmiDeepColorSet(DevHandle handle, enum HdmiDeepColor color); int32_t ret; ret = HdmiDeepColorSet(handle, HDMI_DEEP_COLOR_48BITS); -if (ret != 0) { - HDF_LOGE("HdmiDeepColorSet failed."); +if (ret != HDF_SUCCESS) { + HDF_LOGE("HdmiDeepColorSet: hdmi deep color set fail, ret:%d.", ret); + return ret; } ``` @@ -344,16 +375,15 @@ if (ret != 0) { int32_t HdmiDeepColorGet(DevHandle handle, enum HdmiDeepColor *color); ``` -**表 10** HdmiDeepColorGet参数和返回值描述 - +**表 10** HdmiDeepColorGet参数和返回值描述 -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | -------------- | -| handle | HDMI控制器句柄 | -| color | 色彩深度 | +| handle | DevHandle类型,HDMI控制器句柄 | +| color | 枚举类型指针,色彩深度 | | **返回值** | **返回值描述** | -| 0 | 获取成功 | -| 负数 | 获取失败 | +| HDF_SUCCESS | 获取成功 | +| 负数 | 获取失败 | 获取色彩深度示例: @@ -362,8 +392,9 @@ enum HdmiDeepColor color; int32_t ret; ret = HdmiDeepColorGet(handle, &color); -if (ret != 0) { - HDF_LOGE("HdmiDeepColorGet failed."); +if (ret != HDF_SUCCESS) { + HDF_LOGE("HdmiDeepColorGet: hdmi deep color get fail, ret:%d", ret); + return ret; } ``` @@ -373,15 +404,14 @@ if (ret != 0) { int32_t HdmiStart(DevHandle handle); ``` -**表 11** HdmiStart参数和返回值描述 +**表 11** HdmiStart参数和返回值描述 - -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | -------------- | -| handle | HDMI控制器句柄 | +| handle | DevHandle类型,HDMI控制器句柄 | | **返回值** | **返回值描述** | -| 0 | 启动成功 | -| 负数 | 启动失败 | +| HDF_SUCCESS | 启动成功 | +| 负数 | 启动失败 | 启动HDMI传输示例: @@ -389,8 +419,9 @@ int32_t HdmiStart(DevHandle handle); int32_t ret; ret = HdmiStart(hdmiHandle); -if (ret != 0) { - HDF_LOGE("start transmission failed."); +if (ret != HDF_SUCCESS) { + HDF_LOGE("HdmiStart: start transmission fail, ret:%d", ret); + return ret; } ``` @@ -400,15 +431,14 @@ if (ret != 0) { int32_t HdmiStop(DevHandle handle); ``` -**表 12** HdmiStop参数和返回值描述 - +**表 12** HdmiStop参数和返回值描述 -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | -------------- | -| handle | HDMI控制器句柄 | +| handle | DevHandle类型,HDMI控制器句柄 | | **返回值** | **返回值描述** | -| 0 | 停止成功 | -| 负数 | 停止失败 | +| HDF_SUCCESS | 停止成功 | +| 负数 | 停止失败 | 停止HDMI传输示例: @@ -416,8 +446,9 @@ int32_t HdmiStop(DevHandle handle); int32_t ret; ret = HdmiStop(hdmiHandle); -if (ret != 0) { - HDF_LOGE("stop transmission failed."); +if (ret != HDF_SUCCESS) { + HDF_LOGE("HdmiStop: stop transmission fail, ret:%d.", ret); + return ret; } ``` @@ -427,15 +458,14 @@ if (ret != 0) { int32_t HdmiUnregisterHpdCallbackFunc(DevHandle handle); ``` -**表 13** HdmiUnregisterHpdCallbackFunc参数和返回值描述 - +**表 13** HdmiUnregisterHpdCallbackFunc参数和返回值描述 -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | -------------- | -| handle | HDMI控制器句柄 | +| handle | DevHandle类型,HDMI控制器句柄 | | **返回值** | **返回值描述** | -| 0 | 注销成功 | -| 负数 | 注销失败 | +| HDF_SUCCESS | 注销成功 | +| 负数 | 注销失败 | 注销热插拔检测回调函数示例: @@ -443,8 +473,9 @@ int32_t HdmiUnregisterHpdCallbackFunc(DevHandle handle); int32_t ret; ret = HdmiUnregisterHpdCallbackFunc(hdmiHandle); -if (ret != 0) { - HDF_LOGE("unregister failed."); +if (ret != HDF_SUCCESS) { + HDF_LOGE("HdmiUnregisterHpdCallbackFunc:unregister fail, ret:%d.", ret); + return ret; } ``` @@ -456,10 +487,9 @@ void HdmiClose(DevHandle handle); **表 14** HdmiClose参数和返回值描述 - -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | -------------- | -| handle | HDMI控制器句柄 | +| handle | DevHandle类型,HDMI控制器句柄 | 关闭HDMI控制器示例: @@ -473,9 +503,9 @@ HdmiClose(hdmiHandle); 本例拟在Hi3516DV300开发板上对虚拟驱动进行简单的传输操作: -- SOC:hi3516dv300。 +- SOC:hi3516dv300。 -- HDMI控制器:使用0号HDMI控制器。 +- HDMI控制器:使用0号HDMI控制器。 示例如下: @@ -615,4 +645,4 @@ static int32_t TestCaseHdmi(void) return 0; } -``` \ No newline at end of file +``` diff --git a/zh-cn/device-dev/driver/driver-platform-hdmi-develop.md b/zh-cn/device-dev/driver/driver-platform-hdmi-develop.md index 2568e2e3cd54ff1aff5e8874141b75b258057d12..7486bf20f30e642cb061bd71b65a4d07c7a22039 100755 --- a/zh-cn/device-dev/driver/driver-platform-hdmi-develop.md +++ b/zh-cn/device-dev/driver/driver-platform-hdmi-develop.md @@ -1,5 +1,4 @@ -# HDMI - +# HDMI ## 概述 @@ -19,18 +18,25 @@ HDMI(High Definition Multimedia Interface),即高清多媒体接口,是H - HDCP(High-bandwidth Digital Content Protection):即高带宽数字内容保护技术,当用户对高清晰信号进行非法复制时,该技术会进行干扰,降低复制出来的影像的质量,从而对内容进行保护。 - ### 运作机制 -在HDF框架中,HDMI的接口适配模式采用独立服务模式(如图1)。在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用率。 +在HDF框架中,HDMI的接口适配模式拟采用独立服务模式(如图1)。在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用率。 + +HDMI模块各分层作用: + +- 接口层提供打开HDMI设备、启动HDMI传输、停止HDMI传输、声音图像消隐设置、设置色彩深度、获取色彩深度、设置视频属性、获取视频属性、设置HDR属性、读取Sink端原始EDID数据、注册HDMI热插拔检测回调函数、注销HDMI热插拔检测回调函数、关闭HDMI设备的接口。 + +- 核心层主要提供HDMI控制器的打开、关闭及管理的能力,通过钩子函数与适配层交互。 - **图 1** HDMI独立服务模式 +- 适配层主要是将钩子函数的功能实例化,实现具体的功能。 -![image1](figures/独立服务模式结构图.png) +**图 1** HDMI独立服务模式 + +![HDMI独立服务模式](figures/独立服务模式结构图.png) ### 约束与限制 -HDMI模块当前仅支持轻量和小型系统内核(LiteOS) 。 +HDMI模块当前仅支持轻量和小型系统内核(LiteOS),暂无实际适配驱动。 ## 开发指导 @@ -41,6 +47,7 @@ HDMI具有体积小、传输速率高、传输带宽宽、兼容性好、能同 ### 接口说明 HdmiCntlrOps定义: + ```c struct HdmiCntlrOps { void (*hardWareInit)(struct HdmiCntlr *cntlr); @@ -80,17 +87,17 @@ struct HdmiCntlrOps { }; ``` -**表1** HdmiCntlrOps结构体成员的回调函数功能说明 +**表 1** HdmiCntlrOps结构体成员的回调函数功能说明 -| 函数成员 | 入参 | 出参 | 返回值 | 功能 | +| 函数成员 | 入参 | 出参 | 返回值 | 功能 | | ------------------------ | ------------------------------------------------------------ | -------------------------------------- | ------------------ | -------------------------------------------------- | -| hardWareInit | **cntlr**:结构体指针,核心层HDMI控制器 | 无 | 无 | 初始化HDMI硬件 | -| hardWareStatusGet | **cntlr**:结构体指针,核心层HDMI控制器
| **status**:HDMI硬件状态 ; | 无 | 获取HDMI当前硬件状态 | -| controllerReset | **cntlr**:结构体指针,核心层HDMI控制器 | 无 | 无 | 复位HDMI控制器 | -| hotPlugStateGet | **cntlr**:结构体指针,核心层HDMI控制器 | 无 | bool:HDMI热插拔状态 | 获取HDMI热插拔状态 | +| hardWareInit | **cntlr**:结构体指针,核心层HDMI控制器 | 无 | 无 | 初始化HDMI硬件 | +| hardWareStatusGet | **cntlr**:结构体指针,核心层HDMI控制器
| **status**:HDMI硬件状态 ; | 无 | 获取HDMI当前硬件状态 | +| controllerReset | **cntlr**:结构体指针,核心层HDMI控制器 | 无 | 无 | 复位HDMI控制器 | +| hotPlugStateGet | **cntlr**:结构体指针,核心层HDMI控制器 | 无 | bool:HDMI热插拔状态 | 获取HDMI热插拔状态 | | hotPlugInterruptStateGet | **cntlr**:结构体指针,核心层HDMI控制器 | 无 | bool:HDMI热插拔中断状态 | 获取HDMI热插拔中断状态 | -| lowPowerSet | **cntlr**:结构体指针,核心层HDMI控制器
**enable**:bool,使能/去使能 | 无 | 无 | 使能/去使能低功耗 | -| tmdsModeSet | **cntlr**:结构体指针,核心层HDMI控制器
**mode**:TMDS模式 | 无 | 无 | 设置TMDS模式 | +| lowPowerSet | **cntlr**:结构体指针,核心层HDMI控制器
**enable**:bool,使能/去使能 | 无 | 无 | 使能/去使能低功耗 | +| tmdsModeSet | **cntlr**:结构体指针,核心层HDMI控制器
**mode**:TMDS模式 | 无 | 无 | 设置TMDS模式 | | tmdsConfigSet | **cntlr**:结构体指针,核心层HDMI控制器
**mode**:TMDS参数 | 无 | HDF_STATUS相关状态 | 配置TMDS参数 | | infoFrameEnable | **cntlr**:结构体指针,核心层HDMI控制器
**infoFrameType**:packet类型
**enable**:bool,使能/去使能 | 无 | 无 | 使能/去使能infoFrame | | infoFrameSend | **cntlr**:结构体指针,核心层HDMI控制器
**infoFrameType**:packet类型
**data**:infoFrame数据
**len**:数据长度 | 无 | HDF_STATUS相关状态 | 发送infoFrame | @@ -120,20 +127,30 @@ struct HdmiCntlrOps { ### 开发步骤 -HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以及实例化HDMI控制器对象。 +HDMI模块适配包含以下四个步骤: + +- 实例化驱动入口 -- 实例化驱动入口: - 实例化HdfDriverEntry结构体成员。 + - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 -- 配置属性文件: +- 配置属性文件 + - 在device_info.hcs文件中添加deviceNode描述。 + - 【可选】添加hdmi_config.hcs器件属性文件。 -- 实例化HDMI控制器对象: +- 实例化HDMI控制器对象 + - 初始化HdmiCntlr成员。 + - 实例化HdmiCntlr成员HdmiCntlrOps方法集合。 +- 驱动调试 + + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,HDMI传输等。 + 1. 实例化驱动入口 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 @@ -148,16 +165,14 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以 .Bind = HdmiAdapterBind, .Init = HdmiAdapterInit, .Release = HdmiAdapterRelease, - .moduleName = "adapter_hdmi_driver",//【必要】与HCS里面的名字匹配 + .moduleName = "adapter_hdmi_driver", // 【必要且与HCS文件中里面的moduleName匹配】 }; - HDF_INIT(g_hdmiDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 + HDF_INIT(g_hdmiDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 ``` 2. 配置属性文件 - 完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,并在hdmi_config.hcs中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值对于厂商驱动的实现以及核心层HdmiCntlr相关成员的默认值或限制范围有密切关系。 - - 从第一个节点开始配置具体HDMI控制器信息,此节点并不表示某一路HDMI控制器,而是代表一个资源性质设备,用于描述一类HDMI控制器的信息。本例只有一个HDMI控制器,如有多个控制器,则需要在device_info文件增加deviceNode信息,以及在hdmi_config文件中增加对应的器件属性。 + 完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,并在hdmi_config.hcs中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值对于厂商驱动的实现以及核心层HdmiCntlr相关成员的默认值或限制范围有密切关系。从第一个节点开始配置具体HDMI控制器信息,此节点并不表示某一路HDMI控制器,而是代表一个资源性质设备,用于描述一类HDMI控制器的信息。本例只有一个HDMI控制器,如有多个控制器,则需要在device_info文件增加deviceNode信息,以及在hdmi_config文件中增加对应的器件属性。 - device_info.hcs配置参考 @@ -181,8 +196,8 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以 - hdmi_config.hcs 配置参考 ```c - root { - platform { + root { + platform { hdmi_config { template hdmi_controller { // 模板公共参数,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省。 match_attr = ""; //【必要】需要和device_info.hcs中的deviceMatchAttr值一致。 @@ -227,7 +242,7 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以 } ``` -3. 实例化控制器对象 +3. 实例化HDMI控制器对象 最后一步,完成驱动入口注册之后,要以核心层HdmiCntlr对象的初始化为核心,包括厂商自定义结构体(传递参数和数据),实例化HdmiCntlr成员HdmiCntlrOps(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。 @@ -245,7 +260,6 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以 uint32_t irqNum; //【必要】中断号 }; - /* HdmiCntlr是核心层控制器结构体,其中的成员在Init函数中被赋值。 */ struct HdmiCntlr { struct IDeviceIoService service; struct HdfDeviceObject *hdfDevObj; @@ -320,16 +334,17 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以 **返回值:** - HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/framework/include/utils/hdf_base.h中HDF_STATUS 定义) + HDF_STATUS相关状态 (表2为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS的定义)。 + + **表 2** HDF_STATUS相关状态说明 - |状态(值)|状态描述| - |:-|:-| - |HDF_ERR_INVALID_OBJECT|控制器对象非法| - |HDF_ERR_INVALID_PARAM |参数非法| - |HDF_ERR_MALLOC_FAIL |内存分配失败| - |HDF_ERR_IO |I/O错误| - |HDF_SUCCESS |传输成功| - |HDF_FAILURE |传输失败| + | 状态(值) | 问题描述 | + | -------- | -------- | + | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | + | HDF_ERR_MALLOC_FAIL | 内存分配失败 | + | HDF_ERR_IO | I/O 错误 | + | HDF_SUCCESS | 初始化成功 | + | HDF_FAILURE | 初始化失败 | **函数说明:** @@ -358,13 +373,13 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以 cntlr->hdfDevObj = obj; //【必要】使HdfDeviceObject与HdmiCntlr可以相互转化的前提 obj->service = &cntlr->service; //【必要】使HdfDeviceObject与HdmiCntlr可以相互转化的前提 ret = HdmiAdapterCntlrParse(cntlr, obj); //【必要】初始化cntlr,失败则goto __ERR。 - ... + ...... ret = HdmiAdapterHostParse(host, obj); //【必要】初始化host对象的相关属性,失败则goto __ERR。 - ... + ...... ret = HdmiAdapterHostInit(host, cntlr); // 厂商自定义的初始化,失败则goto __ERR。 - ... + ...... ret = HdmiCntlrAdd(cntlr); // 调用核心层函数,失败则goto __ERR。 - ... + ...... HDF_LOGD("HdmiAdapterBind: success."); return HDF_SUCCESS; __ERR: @@ -416,11 +431,13 @@ HDMI模块适配的三个环节是实例化驱动入口、配置属性文件以 static void HdmiAdapterRelease(struct HdfDeviceObject *obj) { struct HdmiCntlr *cntlr = NULL; - ... + ...... cntlr = (struct HdmiCntlr *)obj->service; // 这里有HdfDeviceObject到HdmiCntlr的强制转化,通过service成员,赋值见Bind函数。 - ... + ...... HimciDeleteHost((struct HimciAdapterHost *)cntlr->priv);// 厂商自定义的内存释放函数,这里有HdmiCntlr到HimciAdapterHost的强制转化。 } ``` - +4. 驱动调试 + + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,HDMI传输等。 diff --git a/zh-cn/device-dev/driver/driver-platform-i2c-des.md b/zh-cn/device-dev/driver/driver-platform-i2c-des.md index c4324343a044891d354a1f2cf1938034be16ebc1..3a2e93a8fd5ecffb91195668028d08bc041f179f 100644 --- a/zh-cn/device-dev/driver/driver-platform-i2c-des.md +++ b/zh-cn/device-dev/driver/driver-platform-i2c-des.md @@ -1,6 +1,5 @@ # I2C - ## 概述 ### 功能简介 @@ -16,12 +15,14 @@ I2C数据的传输必须以一个起始信号作为开始条件,以一个结 I2C总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的地址,当主设备需要和某一个从设备通信时,通过广播的方式,将从设备地址写到总线上,如果某个从设备符合此地址,将会发出应答信号,建立传输。 I2C接口定义了完成I2C传输的通用方法集合,包括: + - I2C控制器管理:打开或关闭I2C控制器 + - I2C消息传输:通过消息传输结构体数组进行自定义传输 - **图1** I2C物理连线示意图 +**图 1** I2C物理连线示意图 - ![image](figures/I2C物理连线示意图.png "I2C物理连线示意图") +![I2C物理连线示意图](figures/I2C物理连线示意图.png) ## 使用指导 @@ -33,9 +34,9 @@ I2C通常用于与各类支持I2C协议的传感器、执行器或输入输出 I2C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/i2c_if.h。 -**表1** I2C驱动API接口功能介绍 +**表 1** I2C驱动API接口功能介绍 -| 接口名 | 接口描述 | +| 接口名 | 接口描述 | | -------- | -------- | | DevHandle I2cOpen(int16_t number) | 打开I2C控制器 | | void I2cClose(DevHandle handle) | 关闭I2C控制器 | @@ -45,9 +46,9 @@ I2C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/ 使用I2C设备的一般流程如下图所示。 -**图2** I2C设备使用流程图 +**图 2** I2C设备使用流程图 -![image](figures/I2C设备使用流程图.png "I2C设备使用流程图") +![I2C设备使用流程图](figures/I2C设备使用流程图.png) #### 打开I2C控制器 @@ -58,11 +59,11 @@ I2C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/ DevHandle I2cOpen(int16_t number); ``` - **表2** I2cOpen参数和返回值描述 +**表 2** I2cOpen参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| number | I2C控制器号 | +| number | int16_t类型,I2C控制器号 | | **返回值** | **返回值描述** | | NULL | 打开I2C控制器失败 | | 设备句柄 | 打开的I2C控制器设备句柄 | @@ -70,57 +71,55 @@ DevHandle I2cOpen(int16_t number); 假设系统中存在8个I2C控制器,编号从0到7,以下代码示例为获取3号控制器: ```c -DevHandle i2cHandle = NULL; /* I2C控制器句柄 / +DevHandle i2cHandle = NULL; // I2C控制器句柄 -/* 打开I2C控制器 */ +// 打开I2C控制器 i2cHandle = I2cOpen(3); if (i2cHandle == NULL) { - HDF_LOGE("I2cOpen: failed\n"); - return; + HDF_LOGE("I2cOpen: i2c open fail.\n"); + return NULL; } ``` - #### 进行I2C通信 消息传输 ```c -int32_t I2cTransfer(DevHandle handle, struct I2cMsg \*msgs, int16_t count); +int32_t I2cTransfer(DevHandle handle, struct I2cMsg *msgs, int16_t count); ``` - **表3** I2cTransfer参数和返回值描述 +**表 3** I2cTransfer参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| handle | I2C控制器设备句柄 | -| msgs | 待传输数据的消息结构体数组 | -| count | 消息数组长度 | +| handle | DevHandle类型,I2C控制器设备句柄 | +| msgs | 结构体指针,待传输数据的消息结构体数组 | +| count | int16_t类型,消息数组长度 | | **返回值** | **返回值描述** | | 正整数 | 成功传输的消息结构体数目 | | 负数 | 执行失败 | I2C传输消息类型为I2cMsg,每个传输消息结构体表示一次读或写,通过一个消息数组,可以执行若干次的读写组合操作。组合读写示例: - ```c int32_t ret; uint8_t wbuff[2] = { 0x12, 0x13 }; uint8_t rbuff[2] = { 0 }; -struct I2cMsg msgs[2]; /* 自定义传输的消息结构体数组 */ -msgs[0].buf = wbuff; /* 写入的数据 */ -msgs[0].len = 2; /* 写入数据长度为2 */ -msgs[0].addr = 0x5A; /* 写入设备地址为0x5A */ -msgs[0].flags = 0; /* 传输标记为0,默认为写 */ -msgs[1].buf = rbuff; /* 要读取的数据 */ -msgs[1].len = 2; /* 读取数据长度为2 */ -msgs[1].addr = 0x5A; /* 读取设备地址为0x5A */ -msgs[1].flags = I2C_FLAG_READ /* I2C_FLAG_READ置位 */ -/* 进行一次自定义传输,传输的消息个数为2 */ +struct I2cMsg msgs[2]; // 自定义传输的消息结构体数组 +msgs[0].buf = wbuff; // 写入的数据 +msgs[0].len = 2; // 写入数据长度为2 +msgs[0].addr = 0x5A; // 写入设备地址为0x5A +msgs[0].flags = 0; // 传输标记为0,默认为写 +msgs[1].buf = rbuff; // 要读取的数据 +msgs[1].len = 2; // 读取数据长度为2 +msgs[1].addr = 0x5A; // 读取设备地址为0x5A +msgs[1].flags = I2C_FLAG_READ // I2C_FLAG_READ置位 +// 进行一次自定义传输,传输的消息个数为2 ret = I2cTransfer(i2cHandle, msgs, 2); if (ret != 2) { - HDF_LOGE("I2cTransfer: failed, ret %d\n", ret); - return; + HDF_LOGE("I2cTransfer: i2c transfer fail, ret:%d\n", ret); + return HDF_FAILURE; } ``` @@ -142,19 +141,18 @@ I2C通信完成之后,需要关闭I2C控制器,关闭函数如下所述: void I2cClose(DevHandle handle); ``` - **表4** I2cClose参数和返回值描述 +**表 4** I2cClose参数和返回值描述 | 参数 | 参数描述 | | -------- | -------- | -| handle | I2C控制器设备句柄 | +| handle | DevHandle类型,I2C控制器设备句柄 | 关闭I2C控制器示例: ```c -I2cClose(i2cHandle); /* 关闭I2C控制器 */ +I2cClose(i2cHandle); // 关闭I2C控制器 ``` - ### 使用示例 本例程以操作开发板上的I2C设备为例,详细展示I2C接口的完整使用流程。 diff --git a/zh-cn/device-dev/driver/driver-platform-i2c-develop.md b/zh-cn/device-dev/driver/driver-platform-i2c-develop.md index cf8fdfa72c847a500739343cf7fb49bc4aefbc76..ef65858fef86b272e756ff7e45905ab87be5f3b9 100755 --- a/zh-cn/device-dev/driver/driver-platform-i2c-develop.md +++ b/zh-cn/device-dev/driver/driver-platform-i2c-develop.md @@ -15,11 +15,14 @@ I2C(Inter Integrated Circuit)总线是由Philips公司开发的一种简单 I2C模块各分层的作用为: - 接口层:提供打开设备,数据传输以及关闭设备的能力。 + - 核心层:主要负责服务绑定、初始化以及释放管理器,并提供添加、删除以及获取控制器的能力。 + - 适配层:由驱动适配者实现与硬件相关的具体功能,如控制器的初始化等。 -**图1** I2C统一服务模式结构图 -![image](figures/统一服务模式结构图.png "I2C统一服务模式结构图") +**图 1** I2C统一服务模式结构图 + +![I2C统一服务模式结构图](figures/统一服务模式结构图.png) ## 使用指导 @@ -71,13 +74,13 @@ static const struct I2cLockMethod g_i2cLockOpsDefault = { 若实际情况不允许使用mutex(例如使用者可能在中断上下文调用I2C接口,mutex可能导致休眠,而中断上下文不允许休眠)时,驱动适配者可以考虑使用其他类型的锁来实现一个自定义的I2cLockMethod。一旦实现了自定义的I2cLockMethod,默认的I2cLockMethod将被覆盖。 - **表1** I2cMethod结构体成员函数功能说明 +**表 1** I2cMethod结构体成员函数功能说明 | 函数成员 | 入参 | 出参 | 返回值 | 功能 | | -------- | -------- | -------- | -------- | -------- | -| transfer | cntlr:结构体指针,核心层I2C控制器。
msgs:结构体指针,用户消息。
count:uint16_t,消息数量。 | 无 | HDF_STATUS相关状态 | 传递用户消息 | +| transfer | cntlr:结构体指针,核心层I2C控制器。
msgs:结构体指针,用户消息。
count:uint16_t类型,消息数量。 | 无 | HDF_STATUS相关状态 | 传递用户消息 | - **表2** I2cLockMethod结构体成员函数功能说明 +**表 2** I2cLockMethod结构体成员函数功能说明 | 函数成员 | 入参 | 出参 | 返回值 | 功能 | | -------- | -------- | -------- | -------- | -------- | @@ -86,296 +89,306 @@ static const struct I2cLockMethod g_i2cLockOpsDefault = { ### 开发步骤 -I2C模块适配的三个必选环节是实例化驱动入口,配置属性文件,以及实例化核心层接口函数。 +I2C模块适配包含以下四个步骤: 1. 实例化驱动入口 - - 实例化HdfDriverEntry结构体成员。 - - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 + - 实例化HdfDriverEntry结构体成员。 + + - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 2. 配置属性文件 - - 在device_info.hcs文件中添加deviceNode描述。 - - 【可选】添加i2c_config.hcs器件属性文件。 + - 在device_info.hcs文件中添加deviceNode描述。 + + - 【可选】添加i2c_config.hcs器件属性文件。 3. 实例化I2C控制器对象 - - 初始化I2cCntlr成员。 - - 实例化I2cCntlr成员I2cMethod和I2cLockMethod。 - > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 实例化I2cCntlr成员I2cMethod和I2cLockMethod,详见[接口说明](#接口说明)。 + - 初始化I2cCntlr成员。 + + - 实例化I2cCntlr成员I2cMethod和I2cLockMethod。 + + > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 实例化I2cCntlr成员I2cMethod和I2cLockMethod,详见[接口说明](#接口说明)。 4. 驱动调试 - 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,消息传输的成功与否等。 + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,消息传输的成功与否等。 ### 开发实例 下方将以Hi3516DV300的驱动//device/soc/hisilicon/common/platform/i2c/i2c_hi35xx.c为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。 -1. 驱动开发首先需要实例化驱动入口。 +1. 实例化驱动入口 - 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 + 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 - 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 + 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 - I2C驱动入口开发参考: + I2C驱动入口开发参考: - I2C控制器会出现很多个设备挂接的情况,因而在HDF框架中首先会为此类型的设备创建一个管理器对象,并同时对外发布一个管理器服务来统一处理外部访问。这样,用户需要打开某个设备时,会先获取到管理器服务,然后管理器服务根据用户指定参数查找到指定设备。 + I2C控制器会出现很多个设备挂接的情况,因而在HDF框架中首先会为此类型的设备创建一个管理器对象,并同时对外发布一个管理器服务来统一处理外部访问。这样,用户需要打开某个设备时,会先获取到管理器服务,然后管理器服务根据用户指定参数查找到指定设备。 - I2C管理器服务的驱动由核心层实现,驱动适配者不需要关注这部分内容的实现,但在实现Init函数的时候需要调用核心层的I2cCntlrAdd函数,它会实现相应功能。 + I2C管理器服务的驱动由核心层实现,驱动适配者不需要关注这部分内容的实现,但在实现Init函数的时候需要调用核心层的I2cCntlrAdd函数,它会实现相应功能。 ```c struct HdfDriverEntry g_i2cDriverEntry = { - .moduleVersion = 1, - .Init = Hi35xxI2cInit, - .Release = Hi35xxI2cRelease, - .moduleName = "hi35xx_i2c_driver", // 【必要且与config.hcs文件里面匹配】 + .moduleVersion = 1, + .Init = Hi35xxI2cInit, + .Release = Hi35xxI2cRelease, + .moduleName = "hi35xx_i2c_driver", // 【必要且与config.hcs文件里面匹配】 }; - HDF_INIT(g_i2cDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 - - /* 核心层i2c_core.c管理器服务的驱动入口 */ + HDF_INIT(g_i2cDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 + + // 核心层i2c_core.c管理器服务的驱动入口 struct HdfDriverEntry g_i2cManagerEntry = { .moduleVersion = 1, .Bind = I2cManagerBind, .Init = I2cManagerInit, .Release = I2cManagerRelease, - .moduleName = "HDF_PLATFORM_I2C_MANAGER", // 这与device_info.hcs文件中device0对应 + .moduleName = "HDF_PLATFORM_I2C_MANAGER", // 这与device_info.hcs文件中device0对应 }; HDF_INIT(g_i2cManagerEntry); ``` -2. 完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在i2c_config.hcs中配置器件属性。 +2. 配置属性文件 + + 完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在i2c_config.hcs中配置器件属性。 - deviceNode信息与驱动入口注册相关,器件属性值对于驱动适配者的驱动实现以及核心层I2cCntlr相关成员的默认值或限制范围有密切关系。 + deviceNode信息与驱动入口注册相关,器件属性值对于驱动适配者的驱动实现以及核心层I2cCntlr相关成员的默认值或限制范围有密切关系。 - 统一服务模式的特点是device_info.hcs文件中第一个设备节点必须为I2C管理器,其各项参数必须如表2设置: + 统一服务模式的特点是device_info.hcs文件中第一个设备节点必须为I2C管理器,其各项参数必须如表3设置: - **表3** 统一服务模式的特点 + **表 3** device_info.hcs节点参数说明 - | 成员名 | 值 | + | 成员名 | 值 | | -------- | -------- | - | moduleName | 固定为HDF_PLATFORM_I2C_MANAGER | - | serviceName | 固定为HDF_PLATFORM_I2C_MANAGER | - | policy | 具体配置为1或2取决于是否对用户态可见 | - | deviceMatchAttr | 没有使用,可忽略 | + | policy | 驱动服务发布的策略,I2C管理器具体配置为2,表示驱动对内核态和用户态都发布服务 | + | priority | 驱动启动优先级(0-200),值越大优先级越低。I2C管理器具体配置为50 | + | permission | 驱动创建设备节点权限,I2C管理器具体配置为0664 | + | moduleName | 驱动名称,I2C管理器固定为HDF_PLATFORM_I2C_MANAGER | + | serviceName | 驱动对外发布服务的名称,I2C管理器服务名设置为HDF_PLATFORM_I2C_MANAGER | + | deviceMatchAttr | 驱动私有数据匹配的关键字,I2C管理器设置为hdf_platform_i2c_manager | 从第二个节点开始配置具体I2C控制器信息,此节点并不表示某一路I2C控制器,而是代表一个资源性质设备,用于描述一类I2C控制器的信息。多个控制器之间相互区分的参数是busId和reg_pbase,这在i2c_config.hcs文件中有所体现。 - - device_info.hcs配置参考 - - ```c - root { - device_info { - match_attr = "hdf_manager"; - device_i2c :: device { - device0 :: deviceNode { - policy = 2; - priority = 50; - permission = 0644; - moduleName = "HDF_PLATFORM_I2C_MANAGER"; - serviceName = "HDF_PLATFORM_I2C_MANAGER"; - deviceMatchAttr = "hdf_platform_i2c_manager"; - } - device1 :: deviceNode { - policy = 0; // 等于0,不需要发布服务。 - priority = 55; // 驱动启动优先级。 - permission = 0644; // 驱动创建设备节点权限。 - moduleName = "hi35xx_i2c_driver"; //【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。 - serviceName = "HI35XX_I2C_DRIVER"; //【必要】驱动对外发布服务的名称,必须唯一。 - deviceMatchAttr = "hisilicon_hi35xx_i2c"; //【必要】用于配置控制器私有数据,要与i2c_config.hcs中对应控制器保持一致, - // 具体的控制器信息在 i2c_config.hcs中。 - } - } - } - } - ``` - - - i2c_config.hcs配置参考 - - ```c - root { - platform { - i2c_config { - match_attr = "hisilicon_hi35xx_i2c"; //【必要】需要和device_info.hcs中的deviceMatchAttr值一致 - template i2c_controller { // 模板公共参数,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省。 - bus = 0; //【必要】i2c识别号 - reg_pbase = 0x120b0000; //【必要】物理基地址 - reg_size = 0xd1; //【必要】寄存器位宽 - irq = 0; //【可选】中断号,由控制器的中断特性决定是否需要 - freq = 400000; //【可选】频率,初始化硬件控制器的可选参数 - clk = 50000000; //【可选】控制器时钟,由控制器时钟的初始化流程决定是否需要 - } - controller_0x120b0000 :: i2c_controller { - bus = 0; - } - controller_0x120b1000 :: i2c_controller { - bus = 1; - reg_pbase = 0x120b1000; - } - ... - } - } - } - ``` - - 需要注意的是,新增i2c_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。 - - 例如:本例中i2c_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/i2c/i2c_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句: - - ```c - #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/i2c/i2c_config.hcs" // 配置文件相对路径 - ``` - -3. 完成驱动入口注册之后,下一步就是以核心层I2cCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化I2cCntlr成员I2cMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。 - - - 自定义结构体参考 - - 从驱动的角度看,自定义结构体是参数和数据的载体,而且i2c_config.hcs文件中的数值会被HDF读入通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层I2cCntlr对象,例如设备号、总线号等。 - - ```c - /* 驱动适配者自定义结构体 */ - struct Hi35xxI2cCntlr { - struct I2cCntlr cntlr; // 【必要】是核心层控制对象,具体描述见下面。 - OsalSpinlock spin; // 【必要】驱动适配者需要基于此锁变量对各个i2c操作函数实现对应的加锁解锁。 - volatile unsigned char *regBase; // 【必要】寄存器基地址 - uint16_t regSize; // 【必要】寄存器位宽 - int16_t bus; // 【必要】i2c_config.hcs文件中可读取具体值 - uint32_t clk; // 【可选】驱动适配者自定义 - uint32_t freq; // 【可选】驱动适配者自定义 - uint32_t irq; // 【可选】驱动适配者自定义 - uint32_t regBasePhy; // 【必要】寄存器物理基地址 - }; - - /* I2cCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。*/ - struct I2cCntlr { - struct OsalMutex lock; - void *owner; - int16_t busId; - void *priv; - const struct I2cMethod *ops; - const struct I2cLockMethod *lockOps; - }; - ``` - - - I2cCntlr成员钩子函数结构体I2cMethod的实例化,和锁机制钩子函数结构体I2cLockMethod实例化,其他成员在Init函数中初始化。 - - ```c - /* i2c_hi35xx.c中的示例 */ - static const struct I2cMethod g_method = { - .transfer = Hi35xxI2cTransfer, - }; - - static const struct I2cLockMethod g_lockOps = { - .lock = Hi35xxI2cLock, // 加锁函数 - .unlock = Hi35xxI2cUnlock, // 解锁函数 - }; - ``` - - - Init函数开发参考 + - device_info.hcs配置参考 + + ```c + root { + device_info { + match_attr = "hdf_manager"; + device_i2c :: device { + device0 :: deviceNode { + policy = 2; + priority = 50; + permission = 0644; + moduleName = "HDF_PLATFORM_I2C_MANAGER"; + serviceName = "HDF_PLATFORM_I2C_MANAGER"; + deviceMatchAttr = "hdf_platform_i2c_manager"; + } + device1 :: deviceNode { + policy = 0; // 等于0,不需要发布服务。 + priority = 55; // 驱动启动优先级。 + permission = 0644; // 驱动创建设备节点权限。 + moduleName = "hi35xx_i2c_driver"; //【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。 + serviceName = "HI35XX_I2C_DRIVER"; //【必要】驱动对外发布服务的名称,必须唯一。 + deviceMatchAttr = "hisilicon_hi35xx_i2c"; //【必要】用于配置控制器私有数据,要与i2c_config.hcs中对应控制器保持一致, + // 具体的控制器信息在 i2c_config.hcs中。 + } + } + } + } + ``` + + - i2c_config.hcs配置参考 + + ```c + root { + platform { + i2c_config { + match_attr = "hisilicon_hi35xx_i2c"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致 + template i2c_controller { // 模板公共参数,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省。 + bus = 0; // 【必要】i2c识别号 + reg_pbase = 0x120b0000; // 【必要】物理基地址 + reg_size = 0xd1; // 【必要】寄存器位宽 + irq = 0; // 【可选】中断号,由控制器的中断特性决定是否需要 + freq = 400000; // 【可选】频率,初始化硬件控制器的可选参数 + clk = 50000000; // 【可选】控制器时钟,由控制器时钟的初始化流程决定是否需要 + } + controller_0x120b0000 :: i2c_controller { + bus = 0; + } + controller_0x120b1000 :: i2c_controller { + bus = 1; + reg_pbase = 0x120b1000; + } + ... + } + } + } + ``` + + 需要注意的是,新增i2c_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。 + + 例如:本例中i2c_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/i2c/i2c_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句: + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/i2c/i2c_config.hcs" // 配置文件相对路径 + ``` - 入参: - - HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 - - 返回值: - - HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 +3. 实例化I2C控制器对象 - **表4** Init函数入参及返回值参考 + 完成驱动入口注册之后,下一步就是以核心层I2cCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化I2cCntlr成员I2cMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。 + + - 自定义结构体参考 + + 从驱动的角度看,自定义结构体是参数和数据的载体,而且i2c_config.hcs文件中的数值会被HDF读入通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层I2cCntlr对象,例如设备号、总线号等。 + + ```c + // 驱动适配者自定义结构体 + struct Hi35xxI2cCntlr { + struct I2cCntlr cntlr; // 【必要】是核心层控制对象,具体描述见下面。 + OsalSpinlock spin; // 【必要】驱动适配者需要基于此锁变量对各个i2c操作函数实现对应的加锁解锁。 + volatile unsigned char *regBase; // 【必要】寄存器基地址 + uint16_t regSize; // 【必要】寄存器位宽 + int16_t bus; // 【必要】i2c_config.hcs文件中可读取具体值 + uint32_t clk; // 【可选】驱动适配者自定义 + uint32_t freq; // 【可选】驱动适配者自定义 + uint32_t irq; // 【可选】驱动适配者自定义 + uint32_t regBasePhy; // 【必要】寄存器物理基地址 + }; - | 状态(值) | 问题描述 | - | -------- | -------- | - | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | - | HDF_ERR_INVALID_PARAM | 参数非法 | - | HDF_ERR_MALLOC_FAIL | 内存分配失败 | - | HDF_ERR_IO | I/O 错误 | - | HDF_SUCCESS | 传输成功 | - | HDF_FAILURE | 传输失败 | - - 函数说明: - - 初始化自定义结构体对象,初始化I2cCntlr成员,调用核心层I2cCntlrAdd函数,接入VFS(可选)。 - - ```c - static int32_t Hi35xxI2cInit(struct HdfDeviceObject *device) - { - ... - /* 遍历、解析i2c_config.hcs中的所有配置节点,并分别进行初始化,需要调用Hi35xxI2cParseAndInit函数。*/ - DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { - ret = Hi35xxI2cParseAndInit(device, childNode);//函数定义见下 - ... - } - ... - } - - static int32_t Hi35xxI2cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node) - { - struct Hi35xxI2cCntlr *hi35xx = NULL; - ... // 入参判空 - hi35xx = (struct Hi35xxI2cCntlr *)OsalMemCalloc(sizeof(*hi35xx)); // 内存分配 - ... // 返回值校验 - hi35xx->regBase = OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize); // 地址映射 - ... // 返回值校验 - Hi35xxI2cCntlrInit(hi35xx); // 【必要】i2c设备的初始化 - - hi35xx->cntlr.priv = (void *)node; // 【必要】存储设备属性 - hi35xx->cntlr.busId = hi35xx->bus; // 【必要】初始化I2cCntlr成员busId - hi35xx->cntlr.ops = &g_method; // 【必要】I2cMethod的实例化对象的挂载 - hi35xx->cntlr.lockOps = &g_lockOps; // 【必要】I2cLockMethod的实例化对象的挂载 - (void)OsalSpinInit(&hi35xx->spin); // 【必要】锁的初始化 - ret = I2cCntlrAdd(&hi35xx->cntlr); // 【必要】调用此函数将控制器对象添加至平台核心层,返回成功信号后驱动才完全接入平台核心层。 - ... - #ifdef USER_VFS_SUPPORT - (void)I2cAddVfsById(hi35xx->cntlr.busId); // 【可选】若支持用户级的虚拟文件系统,则接入。 - #endif - return HDF_SUCCESS; - __ERR__: // 若不成功,需要回滚函数内已执行的操作(如取消IO映射、释放内存等),并返回错误码 - if (hi35xx != NULL) { - if (hi35xx->regBase != NULL) { - OsalIoUnmap((void *)hi35xx->regBase); - hi35xx->regBase = NULL; - } - OsalMemFree(hi35xx); - hi35xx = NULL; - } - return ret; - } - ``` - - - Release函数开发参考 - - 入参: - - HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 - - 返回值: - - 无。 - - 函数说明: - - 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 + // I2cCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。 + struct I2cCntlr { + struct OsalMutex lock; + void *owner; + int16_t busId; + void *priv; + const struct I2cMethod *ops; + const struct I2cLockMethod *lockOps; + }; + ``` + + - I2cCntlr成员钩子函数结构体I2cMethod的实例化,和锁机制钩子函数结构体I2cLockMethod实例化,其他成员在Init函数中初始化。 + + ```c + // i2c_hi35xx.c中的示例 + static const struct I2cMethod g_method = { + .transfer = Hi35xxI2cTransfer, + }; + + static const struct I2cLockMethod g_lockOps = { + .lock = Hi35xxI2cLock, // 加锁函数 + .unlock = Hi35xxI2cUnlock, // 解锁函数 + }; + ``` + + - Init函数开发参考 + + 入参: + + HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 + + 返回值: + + HDF_STATUS相关状态(表4为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 + + **表 4** HDF_STATUS相关状态说明 + + | 状态(值) | 问题描述 | + | -------- | -------- | + | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | + | HDF_ERR_INVALID_PARAM | 参数非法 | + | HDF_ERR_MALLOC_FAIL | 内存分配失败 | + | HDF_ERR_IO | I/O错误 | + | HDF_SUCCESS | 传输成功 | + | HDF_FAILURE | 传输失败 | + + 函数说明: + + 初始化自定义结构体对象,初始化I2cCntlr成员,调用核心层I2cCntlrAdd函数,接入VFS(可选)。 + + ```c + static int32_t Hi35xxI2cInit(struct HdfDeviceObject *device) + { + ...... + // 遍历、解析i2c_config.hcs中的所有配置节点,并分别进行初始化,需要调用Hi35xxI2cParseAndInit函数。 + DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { + ret = Hi35xxI2cParseAndInit(device, childNode);//函数定义见下 + ...... + } + ...... + } + + static int32_t Hi35xxI2cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node) + { + struct Hi35xxI2cCntlr *hi35xx = NULL; + ...... // 入参判空 + hi35xx = (struct Hi35xxI2cCntlr *)OsalMemCalloc(sizeof(*hi35xx)); // 内存分配 + ...... // 返回值校验 + hi35xx->regBase = OsalIoRemap(hi35xx->regBasePhy, hi35xx->regSize); // 地址映射 + ...... // 返回值校验 + Hi35xxI2cCntlrInit(hi35xx); // 【必要】i2c设备的初始化 + + hi35xx->cntlr.priv = (void *)node; // 【必要】存储设备属性 + hi35xx->cntlr.busId = hi35xx->bus; // 【必要】初始化I2cCntlr成员busId + hi35xx->cntlr.ops = &g_method; // 【必要】I2cMethod的实例化对象的挂载 + hi35xx->cntlr.lockOps = &g_lockOps; // 【必要】I2cLockMethod的实例化对象的挂载 + (void)OsalSpinInit(&hi35xx->spin); // 【必要】锁的初始化 + ret = I2cCntlrAdd(&hi35xx->cntlr); // 【必要】调用此函数将控制器对象添加至平台核心层,返回成功信号后驱动才完全接入平台核心层。 + ...... + #ifdef USER_VFS_SUPPORT + (void)I2cAddVfsById(hi35xx->cntlr.busId); // 【可选】若支持用户级的虚拟文件系统,则接入。 + #endif + return HDF_SUCCESS; + __ERR__: // 若不成功,需要回滚函数内已执行的操作(如取消IO映射、释放内存等),并返回错误码 + if (hi35xx != NULL) { + if (hi35xx->regBase != NULL) { + OsalIoUnmap((void *)hi35xx->regBase); + hi35xx->regBase = NULL; + } + OsalMemFree(hi35xx); + hi35xx = NULL; + } + return ret; + } + ``` + + - Release函数开发参考 + + 入参: + + HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 + + 返回值: + + 无。 + + 函数说明: + + 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 + + ```c + static void Hi35xxI2cRelease(struct HdfDeviceObject *device) + { + ...... + // 与Hi35xxI2cInit一样,需要将每个节点分别进行释放。 + DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { + Hi35xxI2cRemoveByNode(childNode); // 函数定义如下 + } + } - ```c - static void Hi35xxI2cRelease(struct HdfDeviceObject *device) - { - ... - /* 与Hi35xxI2cInit一样,需要将每个节点分别进行释放。*/ - DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { - Hi35xxI2cRemoveByNode(childNode); // 函数定义如下 - } - } - static void Hi35xxI2cRemoveByNode(const struct DeviceResourceNode *node) { - ... - /* 【必要】可以调用I2cCntlrGet函数通过设备的bus号获取I2cCntlr对象的指针,以及调用I2cCntlrRemove函数将I2cCntlr对象从平台核心层移除。*/ + ...... + // 【必要】可以调用I2cCntlrGet函数通过设备的bus号获取I2cCntlr对象的指针,以及调用I2cCntlrRemove函数将I2cCntlr对象从平台核心层移除。 cntlr = I2cCntlrGet(bus); if (cntlr != NULL && cntlr->priv == node) { - ... + ...... I2cCntlrRemove(cntlr); - /* 【必要】解除地址映射,释放锁和内存。*/ + // 【必要】解除地址映射,释放锁和内存。 hi35xx = (struct Hi35xxI2cCntlr *)cntlr; OsalIoUnmap((void *)hi35xx->regBase); (void)OsalSpinDestroy(&hi35xx->spin); @@ -384,3 +397,7 @@ I2C模块适配的三个必选环节是实例化驱动入口,配置属性文 return; } ``` + +4. 驱动调试 + + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,消息传输的成功与否等。 diff --git a/zh-cn/device-dev/driver/driver-platform-i3c-des.md b/zh-cn/device-dev/driver/driver-platform-i3c-des.md index 0926a9a2be4976b86ab3c107cf356480d5b3a88c..4310c03a28e742414a11a1eb6f93715135983420 100755 --- a/zh-cn/device-dev/driver/driver-platform-i3c-des.md +++ b/zh-cn/device-dev/driver/driver-platform-i3c-des.md @@ -9,38 +9,49 @@ I3C(Improved Inter Integrated Circuit)总线是由MIPI Alliance开发的一 I3C是两线双向串行总线,针对多个传感器从设备进行了优化,并且一次只能由一个I3C主设备控制。相比于I2C,I3C总线拥有更高的速度、更低的功耗,支持带内中断、从设备热接入以及切换当前主设备,同时向后兼容I2C从设备。I3C增加了带内中断(In-Bind Interrupt)功能,支持I3C设备进行热接入操作,弥补了I2C总线需要额外增加中断线来完成中断的不足。I3C总线上允许同时存在I2C设备、I3C从设备和I3C次级主设备。 I3C接口定义了完成I3C传输的通用方法集合,包括: + - I3C控制器管理:打开或关闭I3C控制器。 + - I3C控制器配置:获取或配置I3C控制器参数。 + - I3C消息传输:通过消息传输结构体数组进行自定义传输。 + - I3C带内中断:请求或释放带内中断。 ### 基本概念 - IBI(In-Band Interrupt)
- 带内中断。在SCL线没有启动信号时,I3C从设备可以通过拉低SDA线使主设备发出SCL启动信号,从而发出带内中断请求。若有多个从机同时发出中断请求,I3C主机则通过从机地址进行仲裁,低地址优先相应。 + + 带内中断。在SCL线没有启动信号时,I3C从设备可以通过拉低SDA线使主设备发出SCL启动信号,从而发出带内中断请求。若有多个从机同时发出中断请求,I3C主机则通过从机地址进行仲裁,低地址优先相应。 - DAA(Dynamic Address Assignment)
- 动态地址分配。I3C支持对从设备地址进行动态分配从而避免地址冲突。在分配动态地址之前,连接到I3C总线上的每个I3C设备都应以两种方式之一来唯一标识: + + 动态地址分配。I3C支持对从设备地址进行动态分配从而避免地址冲突。在分配动态地址之前,连接到I3C总线上的每个I3C设备都应以两种方式之一来唯一标识: + 1)设备可能有一个符合I2C规范的静态地址,主机可以使用此静态地址; + 2)在任何情况下,设备均应具有48位的临时ID。 除非设备具有静态地址且主机使用静态地址,否则主机应使用此48位临时ID。 - CCC(Common Command Code)
- 通用命令代码,所有I3C设备均支持CCC,可以直接将其传输到特定的I3C从设备,也可以同时传输到所有I3C从设备。 + + 通用命令代码,所有I3C设备均支持CCC,可以直接将其传输到特定的I3C从设备,也可以同时传输到所有I3C从设备。 - BCR(Bus Characteristic Register)
- 总线特性寄存器,每个连接到 I3C 总线的 I3C 设备都应具有相关的只读总线特性寄存器 (BCR),该寄存器描述了I3C兼容设备在动态地址分配和通用命令代码中的作用和功能。 + + 总线特性寄存器,每个连接到 I3C 总线的 I3C 设备都应具有相关的只读总线特性寄存器 (BCR),该寄存器描述了I3C兼容设备在动态地址分配和通用命令代码中的作用和功能。 - DCR(Device Characteristic Register)
- 设备特性寄存器,连接到 I3C 总线的每个 I3C 设备都应具有相关的只读设备特性寄存器 (DCR)。 该寄存器描述了用于动态地址分配和通用命令代码的 I3C 兼容设备类型(例如,加速度计、陀螺仪等)。 + + 设备特性寄存器,连接到 I3C 总线的每个 I3C 设备都应具有相关的只读设备特性寄存器 (DCR)。 该寄存器描述了用于动态地址分配和通用命令代码的 I3C 兼容设备类型(例如,加速度计、陀螺仪等)。 ### 运作机制 在HDF框架中,I3C模块接口适配模式采用统一服务模式,这需要一个设备服务来作为I3C模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如I3C可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问(这会在配置文件中有所体现),实现便捷管理和节约资源的目的。 - 相比于I2C,I3C总线拥有更高的速度、更低的功耗,支持带内中断、从设备热接入以及切换当前主设备,同时向后兼容I2C从设备。一路I3C总线上,可以连接多个设备,这些设备可以是I2C从设备、I3C从设备和I3C次级主设备,但只能同时存在一个主设备,一般为控制器本身。 +相比于I2C,I3C总线拥有更高的速度、更低的功耗,支持带内中断、从设备热接入以及切换当前主设备,同时向后兼容I2C从设备。一路I3C总线上,可以连接多个设备,这些设备可以是I2C从设备、I3C从设备和I3C次级主设备,但只能同时存在一个主设备,一般为控制器本身。 -**图 1** I3C物理连线示意图 -![](figures/I3C物理连线示意图.png "I3C物理连线示意图") +**图 1** I3C物理连线示意图 +![I3C物理连线示意图](figures/I3C物理连线示意图.png) ### 约束与限制 @@ -53,25 +64,26 @@ I3C模块当前仅支持轻量和小型系统内核(LiteOS-A),不支持在 I3C可连接单个或多个I3C、I2C从器件,它主要用于: - 与传感器通信,如陀螺仪、气压计或支持I3C协议的图像传感器等; + - 通过软件或硬件协议转换,与其他接口(如 UART 串口等)的设备进行通信。 ### 接口说明 I3C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/i3c_if.h。 -**表 1** I3C驱动API接口功能介绍 +**表 1** I3C驱动API接口功能介绍 -| 接口名 | 接口描述 | +| 接口名 | 接口描述 | | ------------- | ----------------- | -| DevHandle I3cOpen(int16_t number) | 打开I3C控制器 | -| void I3cClose(DevHandle handle) | 关闭I3C控制器 | -| int32_t I3cTransfer(DevHandle handle, struct I3cMsg \*msg, int16_t count, enum TransMode mode) | 自定义传输 | -| int32_t I3cSetConfig(DevHandle handle, struct I3cConfig \*config) | 配置I3C控制器 | -| int32_t I3cGetConfig(DevHandle handle, struct I3cConfig \*config) | 获取I3C控制器配置 | -| int32_t I3cRequestIbi(DevHandle handle, uint16_t addr, I3cIbiFunc func, uint32_t payload) | 请求带内中断 | -| int32_t I3cFreeIbi(DevHandle handle, uint16_t addr) | 释放带内中断 | +| DevHandle I3cOpen(int16_t number) | 打开I3C控制器 | +| void I3cClose(DevHandle handle) | 关闭I3C控制器 | +| int32_t I3cTransfer(DevHandle handle, struct I3cMsg \*msg, int16_t count, enum TransMode mode) | 自定义传输 | +| int32_t I3cSetConfig(DevHandle handle, struct I3cConfig \*config) | 配置I3C控制器 | +| int32_t I3cGetConfig(DevHandle handle, struct I3cConfig \*config) | 获取I3C控制器配置 | +| int32_t I3cRequestIbi(DevHandle handle, uint16_t addr, I3cIbiFunc func, uint32_t payload) | 请求带内中断 | +| int32_t I3cFreeIbi(DevHandle handle, uint16_t addr) | 释放带内中断 | >![](../public_sys-resources/icon-note.gif) **说明:**
>本文涉及的所有接口,仅限内核态使用,不支持在用户态使用。 @@ -80,8 +92,8 @@ I3C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/ I3C的使用流程如图2所示。 -**图 2** I3C使用流程图 -![](figures/I3C使用流程图.png "I3C使用流程图") +**图 2** I3C使用流程图 +![I3C使用流程图](figures/I3C使用流程图.png) #### 打开I3C控制器 @@ -90,27 +102,27 @@ I3C的使用流程如图2所示。 DevHandle I3cOpen(int16_t number); ``` -**表 2** I3cOpen参数和返回值描述 +**表 2** I3cOpen参数和返回值描述 -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | ------------------- | -| number | I3C控制器号 | -| **返回值** | **返回值描述** | -| NULL | 打开I3C控制器失败 | +| number | int16_t类型,I3C控制器号 | +| **返回值** | **返回值描述** | +| NULL | 打开I3C控制器失败 | | 控制器句柄 | 打开的I3C控制器句柄 | 假设系统中存在8个I3C控制器,编号从0到7,以下示例代码为打开1号控制器: ```c -DevHandle i3cHandle = NULL; /* I3C控制器句柄 / +DevHandle i3cHandle = NULL; // I3C控制器句柄 -/* 打开I3C控制器 */ +// 打开I3C控制器 i3cHandle = I3cOpen(1); if (i3cHandle == NULL) { - HDF_LOGE("I3cOpen: failed\n"); - return; + HDF_LOGE("I3cOpen: i3c open fail.\n"); + return NULL; } ``` @@ -120,27 +132,27 @@ if (i3cHandle == NULL) { int32_t I3cGetConfig(DevHandle handle, struct I3cConfig *config); ``` -**表 3** I3cGetConfig参数和返回值描述 +**表 3** I3cGetConfig参数和返回值描述 -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | -------------- | -| handle | I3C控制器句柄 | -| config | I3C控制器配置 | +| handle | DevHandle类型,I3C控制器句柄 | +| config | 结构体指针,I3C控制器配置 | | **返回值** | **返回值描述** | -| 0 | 获取成功 | -| 负数 | 获取失败 | +| HDF_SUCCESS | 获取成功 | +| 负数 | 获取失败 | 获取I3C控制器配置示例: ```c struct I3cConfig config; -ret = I3cGetConfig(i3cHandle, &config); +int32_t ret = I3cGetConfig(i3cHandle, &config); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: Get config fail!", __func__); - return HDF_FAILURE; + HDF_LOGE("I3cGetConfig: get config fail, ret:%d", ret); + return ret; } ``` @@ -150,17 +162,17 @@ if (ret != HDF_SUCCESS) { int32_t I3cSetConfig(DevHandle handle, struct I3cConfig *config); ``` -**表 4** I3cSetConfig参数和返回值描述 +**表 4** I3cSetConfig参数和返回值描述 -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | -------------- | -| handle | I3C控制器句柄 | -| config | I3C控制器配置 | +| handle | DevHandle类型,I3C控制器句柄 | +| config | 结构体指针,I3C控制器配置 | | **返回值** | **返回值描述** | -| 0 | 配置成功 | -| 负数 | 配置失败 | +| HDF_SUCCESS | 配置成功 | +| 负数 | 配置失败 | 配置I3C控制器示例: @@ -169,10 +181,10 @@ struct I3cConfig config; config->busMode = I3C_BUS_HDR_MODE; config->curMaster = NULL; -ret = I3cSetConfig(i3cHandle, &config); +int32_t ret = I3cSetConfig(i3cHandle, &config); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: Set config fail!", __func__); - return HDF_FAILURE; + HDF_LOGE("I3cSetConfig: set config fail, ret:%d", ret); + return ret; } ``` @@ -183,19 +195,19 @@ if (ret != HDF_SUCCESS) { int32_t I3cTransfer(DevHandle handle, struct I3cMsg *msgs, int16_t count, enum TransMode mode); ``` -**表 5** I3cTransfer参数和返回值描述 +**表 5** I3cTransfer参数和返回值描述 -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | -------------------------------------------- | -| handle | I3C控制器句柄 | -| msgs | 待传输数据的消息结构体数组 | -| count | 消息数组长度 | -| mode | 传输模式,0:I2C模式;1:I3C模式;2:发送CCC | -| **返回值** | **返回值描述** | -| 正整数 | 成功传输的消息结构体数目 | -| 负数 | 执行失败 | +| handle | DevHandle类型,I3C控制器句柄 | +| msgs | 结构体指针,待传输数据的消息结构体数组 | +| count | int16_t类型,消息数组长度 | +| mode | 枚举类型,传输模式,0:I2C模式;1:I3C模式;2:发送CCC | +| **返回值** | **返回值描述** | +| 正整数 | 成功传输的消息结构体数目 | +| 负数 | 执行失败 | I3C传输消息类型为I3cMsg,每个传输消息结构体表示一次读或写,通过一个消息数组,可以执行若干次的读写组合操作。 @@ -203,20 +215,20 @@ I3C传输消息类型为I3cMsg,每个传输消息结构体表示一次读或 int32_t ret; uint8_t wbuff[2] = { 0x12, 0x13 }; uint8_t rbuff[2] = { 0 }; -struct I3cMsg msgs[2]; /* 自定义传输的消息结构体数组 */ -msgs[0].buf = wbuff; /* 写入的数据 */ -msgs[0].len = 2; /* 写入数据长度为2 */ -msgs[0].addr = 0x3F; /* 写入设备地址为0x3F */ -msgs[0].flags = 0; /* 传输标记为0,默认为写 */ -msgs[1].buf = rbuff; /* 要读取的数据 */ -msgs[1].len = 2; /* 读取数据长度为2 */ -msgs[1].addr = 0x3F; /* 读取设备地址为0x3F */ -msgs[1].flags = I3C_FLAG_READ /* I3C_FLAG_READ置位 */ -/* 进行一次I2C模式自定义传输,传输的消息个数为2 */ +struct I3cMsg msgs[2]; // 自定义传输的消息结构体数组 +msgs[0].buf = wbuff; // 写入的数据 +msgs[0].len = 2; // 写入数据长度为2 +msgs[0].addr = 0x3F; // 写入设备地址为0x3F +msgs[0].flags = 0; // 传输标记为0,默认为写 +msgs[1].buf = rbuff; // 要读取的数据 +msgs[1].len = 2; // 读取数据长度为2 +msgs[1].addr = 0x3F; // 读取设备地址为0x3F +msgs[1].flags = I3C_FLAG_READ // I3C_FLAG_READ置位 +// 进行一次I2C模式自定义传输,传输的消息个数为2 ret = I3cTransfer(i3cHandle, msgs, 2, I2C_MODE); if (ret != 2) { - HDF_LOGE("I3cTransfer: failed, ret %d\n", ret); - return; + HDF_LOGE("I3cTransfer: transfer fail, ret:%d\n", ret); + return HDF_FAILURE; } ``` @@ -232,19 +244,19 @@ if (ret != 2) { int32_t I3cRequestIbi(DevHandle handle, uint16_t addr, I3cIbiFunc func, uint32_t payload); ``` -**表 6** I3cRequestIbi参数和返回值描述 +**表 6** I3cRequestIbi参数和返回值描述 -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | -------------- | -| handle | I3C控制器句柄 | -| addr | I3C设备地址 | -| func | IBI回调函数 | -| payload | IBI有效载荷 | +| handle | DevHandle类型,I3C控制器句柄 | +| addr | uint16_t类型,I3C设备地址 | +| func | 函数指针,IBI回调函数 | +| payload | IBI有效载荷 | | **返回值** | **返回值描述** | -| 0 | 请求成功 | -| 负数 | 请求失败 | +| HDF_SUCCESS | 请求成功 | +| 负数 | 请求失败 | 请求带内中断示例: @@ -253,9 +265,9 @@ static int32_t TestI3cIbiFunc(DevHandle handle, uint16_t addr, struct I3cIbiData { (void)handle; (void)addr; - HDF_LOGD("%s: %.16s", __func__, (char *)data.buf); + HDF_LOGD("TestI3cIbiFunc: %.16s", (char *)data.buf); - return 0; + return HDF_SUCCESS; } int32_t I3cTestRequestIbi(void) @@ -263,22 +275,22 @@ int32_t I3cTestRequestIbi(void) DevHandle i3cHandle = NULL; int32_t ret; - /* 打开I3C控制器 */ + // 打开I3C控制器 i3cHandle = I3cOpen(1); if (i3cHandle == NULL) { - HDF_LOGE("I3cOpen: failed\n"); - return; -} + HDF_LOGE("I3cOpen: i3c open fail.\n"); + return; + } ret = I3cRequestIbi(i3cHandle, 0x3F, TestI3cIbiFunc, 16); - if (ret != 0) { + if (ret != HDF_SUCCESS) { HDF_LOGE("%s: Request IBI failed!", __func__); - return -1; + return ret; } I3cClose(i3cHandle); - HDF_LOGD("%s: Done", __func__); + HDF_LOGD("I3cTestRequestIbi: done"); - return 0; + return HDF_SUCCESS; } ``` @@ -288,22 +300,22 @@ int32_t I3cTestRequestIbi(void) int32_t I3cFreeIbi(DevHandle handle, uint16_t addr); ``` -**表 7** I3cFreeIbi参数和返回值描述 +**表 7** I3cFreeIbi参数和返回值描述 -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | -------------- | -| handle | I3C控制器句柄 | -| addr | I3C设备地址 | +| handle | DevHandle类型,I3C控制器句柄 | +| addr | uint16_t类型,I3C设备地址 | | **返回值** | **返回值描述** | -| 0 | 释放成功 | -| 负数 | 释放失败 | +| HDF_SUCCESS | 释放成功 | +| 负数 | 释放失败 | 释放带内中断示例: ```c -I3cFreeIbi(i3cHandle, 0x3F); /* 释放带内中断 */ +I3cFreeIbi(i3cHandle, 0x3F); // 释放带内中断 ``` #### 关闭I3C控制器 @@ -313,57 +325,57 @@ I3C通信完成之后,需要关闭I3C控制器,关闭函数如下所示: void I3cClose(DevHandle handle); ``` -**表 8** I3cClose参数和返回值描述 +**表 8** I3cClose参数和返回值描述 -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | -------------- | -| handle | I3C控制器句柄 | +| handle | DevHandle类型,I3C控制器句柄 | 关闭I3C控制器实例: ```c -I3cClose(i3cHandle); /* 关闭I3C控制器 */ +I3cClose(i3cHandle); // 关闭I3C控制器 ``` ## 使用实例 本例程以操作Hi3516DV300开发板上的I3C虚拟设备为例,详细展示I3C接口的完整使用流程,基本硬件信息如下。 -- SOC:hi3516dv300。 +- SOC:hi3516dv300。 -- 虚拟I3C设备:I3C地址为0x3f, 寄存器位宽为1字节。 +- 虚拟I3C设备:I3C地址为0x3f, 寄存器位宽为1字节。 -- 硬件连接:虚拟I3C设备挂接在18号和19号I3C控制器下。 +- 硬件连接:虚拟I3C设备挂接在18号和19号I3C控制器下。 本例程进行简单的I3C传输,测试I3C通路是否正常。 示例如下: ```c -#include "i3c_if.h" /* I3C标准接口头文件 */ -#include "hdf_log.h" /* 标准日志打印头文件 */ -#include "osal_io.h" /* 标准IO读写接口头文件 */ -#include "osal_time.h" /* 标准延迟&睡眠接口头文件 */ +#include "i3c_if.h" // I3C标准接口头文件 +#include "hdf_log.h" // 标准日志打印头文件 +#include "osal_io.h" // 标准IO读写接口头文件 +#include "osal_time.h" // 标准延迟&睡眠接口头文件 -/* 定义一个表示设备的结构体,存储信息 */ +// 定义一个表示设备的结构体,存储信息 struct TestI3cDevice { - uint16_t busNum; /* I3C总线号 */ - uint16_t addr; /* I3C设备地址 */ - uint16_t regLen; /* 寄存器字节宽度 */ - DevHandle i3cHandle; /* I3C控制器句柄 */ + uint16_t busNum; // I3C总线号 + uint16_t addr; // I3C设备地址 + uint16_t regLen; // 寄存器字节宽度 + DevHandle i3cHandle; // I3C控制器句柄 }; -/* 基于I3cTransfer方法封装一个寄存器读写的辅助函数, 通过flag表示读或写 */ -static int TestI3cReadWrite(struct TestI3cDevice *testDevice, unsigned int regAddr, +// 基于I3cTransfer方法封装一个寄存器读写的辅助函数,通过flag表示读或写 +static int32_t TestI3cReadWrite(struct TestI3cDevice *testDevice, unsigned int regAddr, unsigned char *regData, unsigned int dataLen, uint8_t flag) { int index = 0; unsigned char regBuf[4] = {0}; struct I3cMsg msgs[2] = {0}; - /* 单双字节寄存器长度适配 */ + // 单双字节寄存器长度适配 if (testDevice->regLen == 1) { regBuf[index++] = regAddr & 0xFF; } else { @@ -371,81 +383,80 @@ static int TestI3cReadWrite(struct TestI3cDevice *testDevice, unsigned int regAd regBuf[index++] = regAddr & 0xFF; } - /* 填充I3cMsg消息结构 */ + // 填充I3cMsg消息结构 msgs[0].addr = testDevice->addr; - msgs[0].flags = 0; /* 标记为0,表示写入 */ + msgs[0].flags = 0; // 标记为0,表示写入 msgs[0].len = testDevice->regLen; msgs[0].buf = regBuf; msgs[1].addr = testDevice->addr; - msgs[1].flags = (flag == 1) ? I3C_FLAG_READ : 0; /* 添加读标记位,表示读取 */ + msgs[1].flags = (flag == 1) ? I3C_FLAG_READ : 0; // 添加读标记位,表示读取 msgs[1].len = dataLen; msgs[1].buf = regData; if (I3cTransfer(testDevice->i3cHandle, msgs, 2, I2C_MODE) != 2) { - HDF_LOGE("%s: i3c read err", __func__); + HDF_LOGE("TestI3cReadWrite: i3c transfer err."); return HDF_FAILURE; } return HDF_SUCCESS; } -/* 寄存器读函数 */ -static inline int TestI3cReadReg(struct TestI3cDevice *testDevice, unsigned int regAddr, +// 寄存器读函数 +static inline int32_t TestI3cReadReg(struct TestI3cDevice *testDevice, unsigned int regAddr, unsigned char *regData, unsigned int dataLen) { return TestI3cReadWrite(testDevice, regAddr, regData, dataLen, 1); } -/* 寄存器写函数 */ -static inline int TestI3cWriteReg(struct TestI3cDevice *testDevice, unsigned int regAddr, +// 寄存器写函数 +static inline int32_t TestI3cWriteReg(struct TestI3cDevice *testDevice, unsigned int regAddr, unsigned char *regData, unsigned int dataLen) { return TestI3cReadWrite(testDevice, regAddr, regData, dataLen, 0); } -/* I3C例程总入口 */ +// I3C例程总入口 static int32_t TestCaseI3c(void) { - int32_t i; int32_t ret; unsigned char bufWrite[7] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xA, 0xB, 0xC }; unsigned char bufRead[7] = {0}; static struct TestI3cDevice testDevice; - /* 设备信息初始化 */ + // 设备信息初始化 testDevice.busNum = 18; testDevice.addr = 0x3F; - testDevice.regLen = 1; + testDevice.regLen = 2; testDevice.i3cHandle = NULL; - /* 打开I3C控制器 */ + // 打开I3C控制器 testDevice.i3cHandle = I3cOpen(testDevice.busNum); if (testDevice.i3cHandle == NULL) { - HDF_LOGE("%s: Open I3c:%u fail!", __func__, testDevice.busNum); - return -1; + HDF_LOGE("TestCaseI3c: open I3c:%u fail!", testDevice.busNum); + return HDF_FAILURE; } - /* 向地址为0x3F的设备连续写7字节数据 */ + // 向地址为0x3F的设备连续写7字节数据 ret = TestI3cWriteReg(&testDevice, 0x3F, bufWrite, 7); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: test i3c write reg fail!:%d", __func__, ret); + HDF_LOGE("TestCaseI3c: test i3c write reg fail, ret:%d", ret); I3cClose(testDevice.i3cHandle); - return -1; + return ret; } OsalMSleep(10); - /* 从地址为0x3F的设备连续读7字节数据 */ + // 从地址为0x3F的设备连续读7字节数据 ret = TestI3cReadReg(&testDevice, 0x3F, bufRead, 7); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: test i3c read reg fail!:%d", __func__, ret); + HDF_LOGE("TestCaseI3c: test i3c read reg fail, ret:%d", ret); I3cClose(testDevice.i3cHandle); - return -1; + return ret; } - HDF_LOGI("%s: test i3c write&read reg success!", __func__); - - /* 访问完毕关闭I3C控制器 */ + HDF_LOGD("TestCaseI3c: test i3c write&read reg success!"); + HDF_LOGD("TestCaseI3c: function tests end."); + // 访问完毕关闭I3C控制器 I3cClose(testDevice.i3cHandle); - return 0; + return HDF_SUCCESS; } ``` diff --git a/zh-cn/device-dev/driver/driver-platform-i3c-develop.md b/zh-cn/device-dev/driver/driver-platform-i3c-develop.md index 125f67535d37a6a4ae9bbbf8303a69155fde5b0a..e48fac00702b4bc09fc96f75e71e639cccce5ac0 100755 --- a/zh-cn/device-dev/driver/driver-platform-i3c-develop.md +++ b/zh-cn/device-dev/driver/driver-platform-i3c-develop.md @@ -12,27 +12,27 @@ I3C是两线双向串行总线,针对多个传感器从设备进行了优化 - IBI(In-Band Interrupt):带内中断。 - 在SCL线没有启动信号时,I3C从设备可以通过拉低SDA线使主设备发出SCL启动信号,从而发出带内中断请求。若有多个从设备同时发出中断请求,I3C主设备则通过从设备地址进行仲裁,低地址优先相应。 + 在SCL线没有启动信号时,I3C从设备可以通过拉低SDA线使主设备发出SCL启动信号,从而发出带内中断请求。若有多个从设备同时发出中断请求,I3C主设备则通过从设备地址进行仲裁,低地址优先相应。 - DAA(Dynamic Address Assignment):动态地址分配。 - I3C支持对从设备地址进行动态分配从而避免地址冲突。在分配动态地址之前,连接到I3C总线上的每个I3C/I2C设备都应以两种方式之一来唯一标识: + I3C支持对从设备地址进行动态分配从而避免地址冲突。在分配动态地址之前,连接到I3C总线上的每个I3C/I2C设备都应以两种方式之一来唯一标识: - - 设备可能有一个符合I2C规范的静态地址,主机可以使用此静态地址。 + - 设备可能有一个符合I2C规范的静态地址,主机可以使用此静态地址。 - - 在任何情况下,I3C设备均应具有48位的临时ID。除非设备具有静态地址且主机使用静态地址,否则主机应使用此48位临时ID。 + - 在任何情况下,I3C设备均应具有48位的临时ID。除非设备具有静态地址且主机使用静态地址,否则主机应使用此48位临时ID。 - CCC(Common Command Code):通用命令代码。 - 所有I3C设备均支持CCC,可以直接将其传输到特定的I3C从设备,也可以同时传输到所有I3C从设备。 + 所有I3C设备均支持CCC,可以直接将其传输到特定的I3C从设备,也可以同时传输到所有I3C从设备。 - BCR(Bus Characteristic Register):总线特性寄存器。 - 每个连接到I3C总线的I3C设备都应具有相关的只读总线特性寄存器(BCR),该寄存器描述了I3C兼容设备在动态地址分配和通用命令代码中的作用和功能。 + 每个连接到I3C总线的I3C设备都应具有相关的只读总线特性寄存器(BCR),该寄存器描述了I3C兼容设备在动态地址分配和通用命令代码中的作用和功能。 - DCR(Device Characteristic Register):设备特性寄存器。 - 连接到I3C总线的每个I3C设备都应具有相关的只读设备特性寄存器(DCR),该寄存器描述了用于动态地址分配和通用命令代码的I3C兼容设备类型(例如加速度计、陀螺仪等)。 + 连接到I3C总线的每个I3C设备都应具有相关的只读设备特性寄存器(DCR),该寄存器描述了用于动态地址分配和通用命令代码的I3C兼容设备类型(例如加速度计、陀螺仪等)。 ### 运作机制 @@ -42,14 +42,16 @@ I3C是两线双向串行总线,针对多个传感器从设备进行了优化 I3C模块各分层的作用为: - 接口层:提供打开设备,写入数据,关闭设备的能力。 + - 核心层:主要负责服务绑定、初始化以及释放管理器,并提供添加、删除以及获取控制器的能力。由于框架需要统一管理I3C总线上挂载的所有设备,因此还提供了添加、删除以及获取设备的能力,以及中断回调函数。 + - 适配层:由驱动适配者实现与硬件相关的具体功能,如控制器的初始化等。 在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层,因此这种模式下驱动无需再为每个控制器发布服务。 - **图 1** I3C统一服务模式结构图 +**图 1** I3C统一服务模式结构图 -![image1](figures/统一服务模式结构图.png) +![I3C统一服务模式结构图](figures/统一服务模式结构图.png) ### 约束与限制 @@ -62,6 +64,7 @@ I3C模块当前仅支持轻量和小型系统内核(LiteOS-A) 。 I3C可连接单个或多个I3C、I2C从器件,它主要用于: - 与传感器通信,如陀螺仪、气压计或支持I3C协议的图像传感器等。 + - 通过软件或硬件协议转换,与其他通信接口(如UART串口等)的设备进行通信。 当驱动开发者需要将I3C设备适配到OpenHarmony时,需要进行I3C驱动适配,下文将介绍如何进行I3C驱动适配。 @@ -71,6 +74,7 @@ I3C可连接单个或多个I3C、I2C从器件,它主要用于: 为了保证上层在调用I3C接口时能够正确的操作硬件,核心层在//drivers/hdf_core/framework/support/platform/include/i3c/i3c_core.h中定义了以下钩子函数。驱动适配者需要在适配层实现这些函数的具体功能,并与这些钩子函数挂接,从而完成接口层与核心层的交互。 I3cMethod定义: + ```c struct I3cMethod { int32_t (*sendCccCmd)(struct I3cCntlr *cntlr, struct I3cCccCmd *ccc); @@ -83,39 +87,52 @@ struct I3cMethod { }; ``` -**表1** I3cMethod结构体成员的钩子函数功能说明 -|函数成员|入参|出参|返回值|功能| -|-|-|-|-|-| -|sendCccCmd| **cntlr**:结构体指针,核心层I3C控制器
**ccc**:传入的通用命令代码结构体指针 | **ccc**:传出的通用命令代码结构体指针 | HDF_STATUS相关状态|发送CCC(Common command Code,即通用命令代码)| -|Transfer | **cntlr**:结构体指针,核心层I3C控制器
**msgs**:结构体指针,用户消息
**count**:int16_t,消息数量 | **msgs**:结构体指针,用户消息| HDF_STATUS相关状态 | 使用I3C模式传递用户消息 | +**表 1** I3cMethod结构体成员的钩子函数功能说明 +| 函数成员 | 入参 | 出参 | 返回值 | 功能 | +| - | - | - | - | - | +| sendCccCmd | **cntlr**:结构体指针,核心层I3C控制器
**ccc**:传入的通用命令代码结构体指针 | **ccc**:传出的通用命令代码结构体指针 | HDF_STATUS相关状态|发送CCC(Common command Code,即通用命令代码)| +| Transfer | **cntlr**:结构体指针,核心层I3C控制器
**msgs**:结构体指针,用户消息
**count**:int16_t,消息数量 | **msgs**:结构体指针,用户消息 | HDF_STATUS相关状态 | 使用I3C模式传递用户消息 | |i2cTransfer | **cntlr**:结构体指针,核心层I3C控制器
**msgs**:结构体指针,用户消息
**count**:int16_t,消息数量 | **msgs**:结构体指针,用户消息 | HDF_STATUS相关状态 | 使用I2C模式传递用户消息 | -|setConfig| **cntlr**:结构体指针,核心层I3C控制器
**config**:控制器配置参数| 无 | HDF_STATUS相关状态 | 设置I3C控制器配置参数 | -|getConfig| **cntlr**:结构体指针,核心层I3C控制器| **config**:控制器配置参数 | HDF_STATUS相关状态 | 获取I3C控制器配置参数 | -|requestIbi| **device**:结构体指针,核心层I3C设备| 无 | HDF_STATUS相关状态 | 为I3C设备请求IBI(In-Bind Interrupt,即带内中断) | -|freeIbi| **device**:结构体指针,核心层I3C设备| 无 | HDF_STATUS相关状态 | 释放IBI | +| setConfig | **cntlr**:结构体指针,核心层I3C控制器
**config**:控制器配置参数| 无 | HDF_STATUS相关状态 | 设置I3C控制器配置参数 | +| getConfig | **cntlr**:结构体指针,核心层I3C控制器 | **config**:控制器配置参数 | HDF_STATUS相关状态 | 获取I3C控制器配置参数 | +| requestIbi | **device**:结构体指针,核心层I3C设备 | 无 | HDF_STATUS相关状态 | 为I3C设备请求IBI(In-Bind Interrupt,即带内中断) | +| freeIbi | **device**:结构体指针,核心层I3C设备 | 无 | HDF_STATUS相关状态 | 释放IBI | ### 开发步骤 -I3C模块适配的四个环节是实例化驱动入口、配置属性文件、实例化I3C控制器对象以及注册中断处理子程序。 +I3C模块适配包含以下五个步骤: + +- 实例化驱动入口 -- 实例化驱动入口: - 实例化HdfDriverEntry结构体成员。 + - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 -- 配置属性文件: +- 配置属性文件 - 在device_info.hcs文件中添加deviceNode描述。 + - 【可选】添加i3c_config.hcs器件属性文件。 -- 实例化I3C控制器对象: +- 实例化I3C控制器对象 + - 初始化I3cCntlr成员。 + - 实例化I3cCntlr成员I3cMethod方法集合,其定义和成员函数说明见下文。 - -- 注册中断处理子程序: + +- 注册中断处理子程序 + 为控制器注册中断处理程序,实现设备热接入和IBI(带内中断)功能。 -1. 实例化驱动入口 +- 驱动调试 + + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的测试用例是否成功,数据能否传输等。 +### 开发实例 + +下方将以Hi3516DV300的虚拟驱动//drivers/hdf_core/framework/test/unittest/platform/virtual/i3c_virtual.c为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。 + +1. 实例化驱动入口 驱动入口必须为HdfDriverEntry(在//drivers/hdf_core/framework/include/core/hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 @@ -132,34 +149,35 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 .moduleVersion = 1, .Init = VirtualI3cInit, .Release = VirtualI3cRelease, - .moduleName = "virtual_i3c_driver", // 【必要且与hcs文件中的名字匹配】 + .moduleName = "virtual_i3c_driver", // 【必要且与hcs文件中的名字匹配】 }; - HDF_INIT(g_virtualI3cDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 + HDF_INIT(g_virtualI3cDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 - /* 核心层i3c_core.c管理器服务的驱动入口 */ + // 核心层i3c_core.c管理器服务的驱动入口 struct HdfDriverEntry g_i3cManagerEntry = { .moduleVersion = 1, .Init = I3cManagerInit, .Release = I3cManagerRelease, - .moduleName = "HDF_PLATFORM_I3C_MANAGER", // 这与device_info.hcs文件中device0对应 + .moduleName = "HDF_PLATFORM_I3C_MANAGER", // 这与device_info.hcs文件中device0对应 }; HDF_INIT(g_i3cManagerEntry); ``` 2. 配置属性文件 + 完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在i3c_config.hcs中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值对于驱动适配者的驱动实现以及核心层I3cCntlr相关成员的默认值或限制范围有密切关系。 - 完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在i3c_config.hcs中配置器件属性。 + 统一服务模式的特点是device_info.hcs文件中第一个设备节点必须为I3C管理器,其各项参数必须如表2设置: - deviceNode信息与驱动入口注册相关,器件属性值对于驱动适配者的驱动实现以及核心层I3cCntlr相关成员的默认值或限制范围有密切关系。 - - 统一服务模式的特点是device_info.hcs文件中第一个设备节点必须为I3C管理器,其各项参数必须如下设置: - - |成员名|值| - |-|-| - |moduleName |HDF_PLATFORM_I3C_MANAGER| - |serviceName|无(预留)| - |policy|0| - |cntlrMatchAttr| 无(预留)| + **表 2** device_info.hcs节点参数说明 + + | 成员名 | 值 | + | -------- | -------- | + | policy | 驱动服务发布的策略,I3C管理器具体配置为0,表示驱动不需要发布服务 | + | priority | 驱动启动优先级(0-200),值越大优先级越低。I3C管理器具体配置为52 | + | permission | 驱动创建设备节点权限,I3C管理器具体配置为0664 | + | moduleName | 驱动名称,I3C管理器固定为HDF_PLATFORM_I3C_MANAGER | + | serviceName | 驱动对外发布服务的名称,I3C管理器服务名设置为HDF_PLATFORM_I3C_MANAGER | + | deviceMatchAttr | 驱动私有数据匹配的关键字,I3C管理器没有使用,可忽略 | 从第二个节点开始配置具体I3C控制器信息,此节点并不表示某一路I3C控制器,而是代表一个资源性质设备,用于描述一类I3C控制器的信息。本例只有一个I3C控制器,如有多个控制器,则需要在device_info.hcs文件增加deviceNode信息,以及在i3c_config文件中增加对应的器件属性。 @@ -228,12 +246,12 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 此步骤需要通过实现HdfDriverEntry成员函数(Bind,Init,Release)来完成。 - I3cCntlr成员钩子函数结构体I3cMethod的实例化,I3cLockMethod钩子函数结构体本例未实现,若要实例化,可参考I2C驱动开发,其他成员在Init函数中初始化。 + I3cCntlr成员钩子函数结构体I3cMethod的实例化,I3cLockMethod钩子函数结构体本例未实现,若要实例化,可参考I2C驱动开发。 - 自定义结构体参考 - > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 从驱动的角度看,自定义结构体是参数和数据的载体,而且i3c_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层I3cCntlr对象,例如设备号、总线号等。 + > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 从驱动的角度看,自定义结构体是参数和数据的载体,而且i3c_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层I3cCntlr对象,例如设备号、总线号等。 ```c struct VirtualI3cCntlr { @@ -250,7 +268,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 uint32_t i2cFmPlusRate; }; - /* I3cCntlr是核心层控制器结构体,其中的成员在Init函数中被赋值。 */ + // I3cCntlr是核心层控制器结构体,其中的成员在Init函数中被赋值。 struct I3cCntlr { OsalSpinlock lock; void *owner; @@ -268,121 +286,122 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 **入参:** - HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 + HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 **返回值:** - HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS 定义)。 - - - |状态(值)|问题描述| - |:-|:-:| - |HDF_ERR_INVALID_OBJECT|控制器对象非法| - |HDF_ERR_INVALID_PARAM |参数非法| - |HDF_ERR_MALLOC_FAIL |内存分配失败| - |HDF_ERR_IO |I/O 错误| - |HDF_SUCCESS |传输成功| - |HDF_FAILURE |传输失败| - - **函数说明:** - - 初始化自定义结构体对象,初始化I3cCntlr成员,调用核心层I3cCntlrAdd函数。 - - ```c - static int32_t VirtualI3cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node) - { - int32_t ret; - struct VirtualI3cCntlr *virtual = NULL; // 【必要】自定义结构体对象 - (void)device; - - virtual = (struct VirtualI3cCntlr *)OsalMemCalloc(sizeof(*virtual)); // 【必要】内存分配 - if (virtual == NULL) { - HDF_LOGE("%s: Malloc virtual fail!", __func__); - return HDF_ERR_MALLOC_FAIL; - } - - ret = VirtualI3cReadDrs(virtual, node); // 【必要】将i3c_config文件的默认值填充到结构体中,函数定义见下方 - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret); - goto __ERR__; - } - ... - virtual->regBase = OsalIoRemap(virtual->regBasePhy, virtual->regSize); // 【必要】地址映射 - ret = OsalRegisterIrq(hi35xx->softIrqNum, OSAL_IRQF_TRIGGER_NONE, I3cIbiHandle, "I3C", virtual); //【必要】注册中断程序 - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: register irq failed!", __func__); - return ret; - } - ... - VirtualI3cCntlrInit(virtual); // 【必要】I3C设备的初始化 - virtual->cntlr.priv = (void *)node; // 【必要】存储设备属性 - virtual->cntlr.busId = virtual->busId; // 【必要】初始化I3cCntlr成员 - virtual->cntlr.ops = &g_method; // 【必要】I3cMethod的实例化对象的挂载 - (void)OsalSpinInit(&virtual->spin); - ret = I3cCntlrAdd(&virtual->cntlr); // 【必要且重要】调用此函数将控制器添加至核心,返回成功信号后驱动才完全接入平台核心层。 - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: add i3c controller failed! ret = %d", __func__, ret); - (void)OsalSpinDestroy(&virtual->spin); - goto __ERR__; - } - - return HDF_SUCCESS; - __ERR__: // 若控制器添加失败,需要执行去初始化相关函数。 - if (virtual != NULL) { - OsalMemFree(virtual); - virtual = NULL; - } - - return ret; - } - - static int32_t VirtualI3cInit(struct HdfDeviceObject *device) - { - int32_t ret; - const struct DeviceResourceNode *childNode = NULL; - - if (device == NULL || device->property == NULL) { - HDF_LOGE("%s: device or property is NULL", __func__); - return HDF_ERR_INVALID_OBJECT; - } - - DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { - ret = VirtualI3cParseAndInit(device, childNode); - if (ret != HDF_SUCCESS) { - break; - } - } - - return ret; - } - - static int32_t VirtualI3cReadDrs(struct VirtualI3cCntlr *virtual, const struct DeviceResourceNode *node) - { - struct DeviceResourceIface *drsOps = NULL; - - /* 获取drsOps方法 */ - drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) { - HDF_LOGE("%s: Invalid drs ops fail!", __func__); - return HDF_FAILURE; - } - /* 将配置参数依次读出,并填充至结构体中 */ - if (drsOps->GetUint16(node, "busId", &virtual->busId, 0) != HDF_SUCCESS) { - HDF_LOGE("%s: Read busId fail!", __func__); - return HDF_ERR_IO; - } - if (drsOps->GetUint16(node, "busMode", &virtual->busMode, 0) != HDF_SUCCESS) { - HDF_LOGE("%s: Read busMode fail!", __func__); - return HDF_ERR_IO; - } - if (drsOps->GetUint16(node, "IrqNum", &virtual->IrqNum, 0) != HDF_SUCCESS) { - HDF_LOGE("%s: Read IrqNum fail!", __func__); - return HDF_ERR_IO; - } - ··· - return HDF_SUCCESS; - } - ``` + HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 + + **表 3** HDF_STATUS相关状态说明 + + | 状态(值) | 问题描述 | + | -------- | -------- | + | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | + | HDF_ERR_INVALID_PARAM | 参数非法 | + | HDF_ERR_MALLOC_FAIL | 内存分配失败 | + | HDF_ERR_IO | I/O错误 | + | HDF_SUCCESS | 传输成功 | + | HDF_FAILURE | 传输失败 | + + **函数说明:** + + 初始化自定义结构体对象,初始化I3cCntlr成员,调用核心层I3cCntlrAdd函数。 + + ```c + static int32_t VirtualI3cParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node) + { + int32_t ret; + struct VirtualI3cCntlr *virtual = NULL; // 【必要】自定义结构体对象 + (void)device; + + virtual = (struct VirtualI3cCntlr *)OsalMemCalloc(sizeof(*virtual)); // 【必要】内存分配 + if (virtual == NULL) { + HDF_LOGE("%s: Malloc virtual fail!", __func__); + return HDF_ERR_MALLOC_FAIL; + } + + ret = VirtualI3cReadDrs(virtual, node); // 【必要】将i3c_config文件的默认值填充到结构体中,函数定义见下方 + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: Read drs fail! ret:%d", __func__, ret); + goto __ERR__; + } + ...... + virtual->regBase = OsalIoRemap(virtual->regBasePhy, virtual->regSize); // 【必要】地址映射 + ret = OsalRegisterIrq(hi35xx->softIrqNum, OSAL_IRQF_TRIGGER_NONE, I3cIbiHandle, "I3C", virtual); //【必要】注册中断程序 + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: register irq failed!", __func__); + return ret; + } + ...... + VirtualI3cCntlrInit(virtual); // 【必要】I3C设备的初始化 + virtual->cntlr.priv = (void *)node; // 【必要】存储设备属性 + virtual->cntlr.busId = virtual->busId; // 【必要】初始化I3cCntlr成员 + virtual->cntlr.ops = &g_method; // 【必要】I3cMethod的实例化对象的挂载 + (void)OsalSpinInit(&virtual->spin); + ret = I3cCntlrAdd(&virtual->cntlr); // 【必要且重要】调用此函数将控制器添加至核心,返回成功信号后驱动才完全接入平台核心层。 + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: add i3c controller failed! ret = %d", __func__, ret); + (void)OsalSpinDestroy(&virtual->spin); + goto __ERR__; + } + + return HDF_SUCCESS; + __ERR__: // 若控制器添加失败,需要执行去初始化相关函数。 + if (virtual != NULL) { + OsalMemFree(virtual); + virtual = NULL; + } + + return ret; + } + + static int32_t VirtualI3cInit(struct HdfDeviceObject *device) + { + int32_t ret; + const struct DeviceResourceNode *childNode = NULL; + + if (device == NULL || device->property == NULL) { + HDF_LOGE("%s: device or property is NULL", __func__); + return HDF_ERR_INVALID_OBJECT; + } + + DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { + ret = VirtualI3cParseAndInit(device, childNode); + if (ret != HDF_SUCCESS) { + break; + } + } + + return ret; + } + + static int32_t VirtualI3cReadDrs(struct VirtualI3cCntlr *virtual, const struct DeviceResourceNode *node) + { + struct DeviceResourceIface *drsOps = NULL; + + // 获取drsOps方法 + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) { + HDF_LOGE("%s: Invalid drs ops fail!", __func__); + return HDF_FAILURE; + } + // 将配置参数依次读出,并填充至结构体中 + if (drsOps->GetUint16(node, "busId", &virtual->busId, 0) != HDF_SUCCESS) { + HDF_LOGE("%s: Read busId fail!", __func__); + return HDF_ERR_IO; + } + if (drsOps->GetUint16(node, "busMode", &virtual->busMode, 0) != HDF_SUCCESS) { + HDF_LOGE("%s: Read busMode fail!", __func__); + return HDF_ERR_IO; + } + if (drsOps->GetUint16(node, "IrqNum", &virtual->IrqNum, 0) != HDF_SUCCESS) { + HDF_LOGE("%s: Read IrqNum fail!", __func__); + return HDF_ERR_IO; + } + ...... + return HDF_SUCCESS; + } + ``` - Release函数开发参考 @@ -409,20 +428,20 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 struct I3cCntlr *cntlr = NULL; struct VirtualI3cCntlr *virtual = NULL; struct DeviceResourceIface *drsOps = NULL; - + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); if (drsOps == NULL || drsOps->GetUint32 == NULL) { HDF_LOGE("%s: invalid drs ops fail!", __func__); return; } - + ret = drsOps->GetUint16(node, "busId", (uint16_t *)&busId, 0); if (ret != HDF_SUCCESS) { HDF_LOGE("%s: read busId fail!", __func__); return; } - ... - /* 可以调用I3cCntlrGet函数通过设备的cntlrNum获取I3cCntlr对象,以及调用I3cCntlrRemove函数来释放I3cCntlr对象的内容。 */ + ...... + // 可以调用I3cCntlrGet函数通过设备的cntlrNum获取I3cCntlr对象,以及调用I3cCntlrRemove函数来释放I3cCntlr对象的内容。 cntlr = I3cCntlrGet(busId); if (cntlr != NULL && cntlr->priv == node) { I3cCntlrPut(cntlr); @@ -433,7 +452,7 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 } return; } - + static void VirtualI3cRelease(struct HdfDeviceObject *device) { const struct DeviceResourceNode *childNode = NULL; @@ -444,8 +463,8 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 HDF_LOGE("%s: device or property is NULL", __func__); return; } - ... - /* 遍历、解析i3c_config.hcs中的所有配置节点,并分别进行release操作 */ + ...... + // 遍历、解析i3c_config.hcs中的所有配置节点,并分别进行release操作 DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { VirtualI3cRemoveByNode(childNode); //函数定义如上 } @@ -471,31 +490,31 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 case I3C_RESERVED_ADDR_7H7A: case I3C_RESERVED_ADDR_7H7C: case I3C_RESERVED_ADDR_7H7F: - /* 广播地址单比特错误的所有情形 */ + // 广播地址单比特错误的所有情形 HDF_LOGW("%s: broadcast Address single bit error!", __func__); break; default: HDF_LOGD("%s: Reserved address which is not supported!", __func__); break; } - + return HDF_SUCCESS; } - + static int32_t I3cIbiHandle(uint32_t irq, void *data) { struct VirtualI3cCntlr *virtual = NULL; struct I3cDevice *device = NULL; uint16_t ibiAddr; char *testStr = "Hello I3C!"; - + (void)irq; if (data == NULL) { HDF_LOGW("%s: data is NULL!", __func__); return HDF_ERR_INVALID_PARAM; } virtual = (struct VirtualI3cCntlr *)data; - /* 【必要】获取产生中断的地址,使用CHECK_RESERVED_ADDR宏判断该地址是否为I3C保留地址。 */ + // 【必要】获取产生中断的地址,使用CHECK_RESERVED_ADDR宏判断该地址是否为I3C保留地址。 ibiAddr = VirtualI3cGetIbiAddr(); if (CHECK_RESERVED_ADDR(ibiAddr) == I3C_ADDR_RESERVED) { HDF_LOGD("%s: Calling VirtualI3cResAddrWorker...", __func__); @@ -508,13 +527,17 @@ I3C模块适配的四个环节是实例化驱动入口、配置属性文件、 return HDF_ERR_MALLOC_FAIL; } if (device->ibi->payload > VIRTUAL_I3C_TEST_STR_LEN) { - /* 将字符串"Hello I3C!"放入IBI缓冲区内 */ + // 将字符串"Hello I3C!"放入IBI缓冲区内 *device->ibi->data = *testStr; } - /* 根据产生IBI的I3C设备调用IBI回调函数 */ + // 根据产生IBI的I3C设备调用IBI回调函数 return I3cCntlrIbiCallback(device); } - + return HDF_SUCCESS; } - ``` \ No newline at end of file + ``` + +5. 驱动调试 + + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的测试用例是否成功,数据能否传输等。 diff --git a/zh-cn/device-dev/driver/driver-platform-mipicsi-des.md b/zh-cn/device-dev/driver/driver-platform-mipicsi-des.md index 31aad8035c3f0b6eafdfafbc93a93b5dcb3a61a1..0bb5052f2f9ab7cb7b10a2c51279a4a0680e50d4 100755 --- a/zh-cn/device-dev/driver/driver-platform-mipicsi-des.md +++ b/zh-cn/device-dev/driver/driver-platform-mipicsi-des.md @@ -10,36 +10,36 @@ CSI(Camera Serial Interface)是由MIPI联盟下Camera工作组指定的接 图1显示了简化的CSI接口。D-PHY采用1对源同步的差分时钟和1~4对差分数据线来进行数据传输。数据传输采用DDR方式,即在时钟的上下边沿都有数据传输。 -**图1** CSI发送、接收接口 -![](figures/CSI发送-接收接口.png) +**图 1** CSI发送、接收接口 + +![CSI发送、接收接口](figures/CSI发送-接收接口.png) MIPI CSI标准分为应用层、协议层与物理层,协议层又细分为像素字节转换层、低级协议层、Lane管理层。 - 物理层(PHY Layer) - PHY层指定了传输媒介,在电气层面从串行bit流中捕捉“0”与“1”,同时生成SoT与EoT等信号。 + PHY层指定了传输媒介,在电气层面从串行bit流中捕捉“0”与“1”,同时生成SoT与EoT等信号。 - 协议层(Protocol Layer) - 协议层由三个子层组成,每个子层有不同的职责。CSI-2协议能够在host侧处理器上用一个单独的接口处理多条数据流。协议层规定了多条数据流该如何标记和交织起来,以便每条数据流能够被正确地恢复出来。 - - - 像素字节转换层(Pixel/Byte Packing/Unpacking Layer) + 协议层由三个子层组成,每个子层有不同的职责。CSI-2协议能够在host侧处理器上用一个单独的接口处理多条数据流。协议层规定了多条数据流该如何标记和交织起来,以便每条数据流能够被正确地恢复出来。 - CSI-2规范支持多种不同像素格式的图像应用。在发送方中,本层在发送数据到Low Level Protocol层之前,将来自应用层的像素封包为字节数据。在接收方中,本层在发送数据到应用层之前,将来自Low Level Protocol层的字节数据解包为像素。8位的像素数据在本层中传输时保持不变。 + - 像素字节转换层(Pixel/Byte Packing/Unpacking Layer) - - 低级协议层(Low Level Protocol) + CSI-2规范支持多种不同像素格式的图像应用。在发送方中,本层在发送数据到Low Level Protocol层之前,将来自应用层的像素封包为字节数据。在接收方中,本层在发送数据到应用层之前,将来自Low Level Protocol层的字节数据解包为像素。8位的像素数据在本层中传输时保持不变。 - LLP主要包含了在SoT和EoT事件之间的bit和byte级别的同步方法,以及和下一层传递数据的方法。LLP最小数据粒度是1个字节。LLP也包含了一个字节内的bit值解析,即Endian(大小端里的Endian的意思)的处理。 + - 低级协议层(Low Level Protocol) + LLP主要包含了在SoT和EoT事件之间的bit和byte级别的同步方法,以及和下一层传递数据的方法。LLP最小数据粒度是1个字节。LLP也包含了一个字节内的bit值解析,即Endian(大小端里的Endian的意思)的处理。 - - Lane管理层(Lane Management) + - Lane管理层(Lane Management) - CSI-2的Lane是可扩展的。具体的数据Lane的数量规范并没有给出限制,具体根据应用的带宽需求而定。发送侧分发(distributor功能)来自出口方向数据流的字节到1条或多条Lane上。接收侧则从一条或多条Lane中收集字节并合并(merge功能)到一个数据流上,复原出原始流的字节顺序。对于C-PHY物理层来说,本层专门分发字节对(16 bits)到数据Lane或从数据Lane中收集字节对。基于每Lane的扰码功能是可选特性。 + CSI-2的Lane是可扩展的。具体的数据Lane的数量规范并没有给出限制,具体根据应用的带宽需求而定。发送侧分发(distributor功能)来自出口方向数据流的字节到1条或多条Lane上。接收侧则从一条或多条Lane中收集字节并合并(merge功能)到一个数据流上,复原出原始流的字节顺序。对于C-PHY物理层来说,本层专门分发字节对(16 bits)到数据Lane或从数据Lane中收集字节对。基于每Lane的扰码功能是可选特性。 - 协议层的数据组织形式是包(packet)。接口的发送侧会增加包头(header)和错误校验(error-checking)信息到即将被LLP发送的数据上。接收侧在LLP将包头剥掉,包头会被接收器中对应的逻辑所解析。错误校验信息可以用来做入口数据的完整性检查。 + 协议层的数据组织形式是包(packet)。接口的发送侧会增加包头(header)和错误校验(error-checking)信息到即将被LLP发送的数据上。接收侧在LLP将包头剥掉,包头会被接收器中对应的逻辑所解析。错误校验信息可以用来做入口数据的完整性检查。 - 应用层(Application Layer) - 本层描述了更高层级的应用对于数据中的数据的处理,规范并不涵盖应用层。CSI-2规范只给出了像素值和字节的映射关系。 + 本层描述了更高层级的应用对于数据中的数据的处理,规范并不涵盖应用层。CSI-2规范只给出了像素值和字节的映射关系。 ### 运作机制 @@ -47,9 +47,9 @@ MIPI CSI模块各分层的作用为:接口层提供打开设备、写入数据 ![](../public_sys-resources/icon-note.gif) **说明:**
核心层可以调用接口层的函数,核心层通过钩子函数调用适配层函数,从而适配层可以间接的调用接口层函数,但是不可逆转接口层调用适配层函数。 -**图2**CSI无服务模式结构图 +**图 2** CSI无服务模式结构图 -![image](figures/无服务模式结构图.png "CSI无服务模式结构图") +![CSI无服务模式结构图](figures/无服务模式结构图.png) ### 约束与限制 @@ -65,31 +65,31 @@ MIPI CSI主要用于连接摄像头组件。 MIPI CSI模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/mipi_csi_if.h。 -**表1** ComboDevAttr结构体介绍 +**表 1** ComboDevAttr结构体介绍 -| 名称 | 描述 | +| 名称 | 描述 | | --------- | ----------------------------------------------------- | -| devno | 设备号 | -| inputMode | 输入模式:MIPI/LVDS/SUBSLVDS/HISPI/DC | -| dataRate | Mipi Rx,SLVS输入速率 | -| imgRect | MIPI Rx设备裁剪区域(与原始传感器输入图像大小相对应) | -| MIPIAttr | Mipi设备属性 | -| lvdsAttr | LVDS/SubLVDS/HiSPi设备属性 | +| devno | 设备号 | +| inputMode | 输入模式:MIPI/LVDS/SUBSLVDS/HISPI/DC | +| dataRate | Mipi Rx,SLVS输入速率 | +| imgRect | MIPI Rx设备裁剪区域(与原始传感器输入图像大小相对应) | +| MIPIAttr | Mipi设备属性 | +| lvdsAttr | LVDS/SubLVDS/HiSPi设备属性 | -**表2** ExtDataType结构体介绍 +**表 2** ExtDataType结构体介绍 -| 名称 | 描述 | +| 名称 | 描述 | | --------------- | ------------------------------- | -| devno | 设备号 | -| num | Sensor号 | -| extDataBitWidth | 图片的位深 | -| extDataType | 定义YUV和原始数据格式以及位深度 | +| devno | 设备号 | +| num | Sensor号 | +| extDataBitWidth | 图片的位深 | +| extDataType | 定义YUV和原始数据格式以及位深度 | -**表3** MIPI CSI API接口功能介绍 +**表 3** MIPI CSI API接口功能介绍 @@ -110,17 +110,15 @@ MIPI CSI模块提供的主要接口如表1所示,具体API详见//drivers/hdf_ | int32_t MipiCsiEnableSensorClock(DevHandle handle, uint8_t snsClkSource) | 使能MIPI上的Sensor时钟 | | int32_t MipiCsiDisableSensorClock(DevHandle handle, uint8_t snsClkSource) | 关闭Sensor的时钟 | - ## 开发步骤 #### 使用流程 使用MIPI CSI的一般流程如图3所示。 -**图3** MIPI CSI使用流程图 - +**图 3** MIPI CSI使用流程图 -![](figures/MIPI-CSI使用流程图.png) +![MIPI CSI使用流程图](figures/MIPI-CSI使用流程图.png) #### 获取MIPI CSI控制器操作句柄 @@ -130,427 +128,427 @@ MIPI CSI模块提供的主要接口如表1所示,具体API详见//drivers/hdf_ DevHandle MipiCsiOpen(uint8_t id); ``` -**表4** MipiCsiOpen的参数和返回值描述 +**表 4** MipiCsiOpen的参数和返回值描述 -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ---------- | ----------------------------------------------- | -| id | MIPI CSI通道ID | -| **返回值** | **返回值描述** | -| NULL | 获取失败 | -| 设备句柄 | 获取到指令通道的控制器操作句柄,类型为DevHandle | +| id | uint8_t类型,MIPI CSI通道ID | +| **返回值** | **返回值描述** | +| NULL | 获取失败 | +| 设备句柄 | 获取到指令通道的控制器操作句柄,类型为DevHandle | 假设系统中的MIPI CSI通道为0,获取该通道控制器操作句柄的示例如下: ```c -DevHandle MipiCsiHandle = NULL; /* 设备句柄 */ -id = 0; /* MIPI CSI通道ID */ +DevHandle MipiCsiHandle = NULL; // 设备句柄 +id = 0; // MIPI CSI通道ID -/* 获取控制器操作句柄 */ +// 获取控制器操作句柄 MipiCsiHandle = MipiCsiOpen(id); if (MipiCsiHandle == NULL) { - HDF_LOGE("MipiCsiOpen: failed\n"); - return; + HDF_LOGE("MipiCsiOpen: mipi csi open fail.\n"); + return NULL; } ``` #### 进行MIPI CSI相应配置 -- 写入MIPI CSI配置 +- 写入MIPI CSI配置 ```c int32_t MipiCsiSetComboDevAttr(DevHandle handle, ComboDevAttr *pAttr); ``` - **表5** MipiCsiSetComboDevAttr的参数和返回值描述 + **表 5** MipiCsiSetComboDevAttr的参数和返回值描述 - | 参数 | 参数描述 | + | 参数 | 参数描述 | | ---------- | -------------------------- | - | handle | 控制器操作句柄 | - | pAttr | MIPI CSI相应配置结构体指针 | - | **返回值** | **返回值描述** | - | 0 | 设置成功 | - | 负数 | 设置失败 | + | handle | DevHandle类型,控制器操作句柄 | + | pAttr | 结构体指针,MIPI CSI相应配置 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | 设置成功 | + | 负数 | 设置失败 | ```c int32_t ret; struct ComboDevAttr attr; - /* 当前配置如下 */ + // 当前配置如下 (void)memset_s(&attr, sizeof(ComboDevAttr), 0, sizeof(ComboDevAttr)); - attr.devno = 0; /* 设备0 */ - attr.inputMode = INPUT_MODE_MIPI; /* 输入模式为MIPI */ - attr.dataRate = MIPI_DATA_RATE_X1; /* 每时钟输出1像素 */ - attr.imgRect.x = 0; /* 0: 图像传感器左上位置 */ - attr.imgRect.y = 0; /* 0: 图像传感器右上位置 */ - attr.imgRect.width = 2592; /* 2592: 图像传感器宽度大小 */ - attr.imgRect.height = 1944; /* 1944: 图像传感器高度尺寸 */ - /* 写入配置数据 */ + attr.devno = 0; // 设备0 + attr.inputMode = INPUT_MODE_MIPI; // 输入模式为MIPI + attr.dataRate = MIPI_DATA_RATE_X1; // 每时钟输出1像素 + attr.imgRect.x = 0; // 0: 图像传感器左上位置 + attr.imgRect.y = 0; // 0: 图像传感器右上位置 + attr.imgRect.width = 2592; // 2592: 图像传感器宽度大小 + attr.imgRect.height = 1944; // 1944: 图像传感器高度尺寸 + // 写入配置数据 ret = MipiCsiSetComboDevAttr(MipiCsiHandle, &attr); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiSetComboDevAttr fail! ret=%d\n", __func__, ret); - return -1; + if (ret != HDF_SUCCESS) { + HDF_LOGE("MipiCsiSetComboDevAttr: mipi csi set combo dev attr fail, ret=%d\n", ret); + return ret; } ``` -- 设置YUV和RAW数据格式和位深 +- 设置YUV和RAW数据格式和位深 ```c int32_t MipiCsiSetExtDataType(DevHandle handle, ExtDataType* dataType); ``` - **表6** MipiCsiSetExtDataType的参数和返回值描述 + **表 6** MipiCsiSetExtDataType的参数和返回值描述 - | 参数 | 参数描述 | + | 参数 | 参数描述 | | ---------- | ------------------------------- | - | handle | 控制器操作句柄 | - | dataType | 定义YUV和原始数据格式以及位深度 | - | **返回值** | **返回值描述** | - | 0 | 设置成功 | - | 负数 | 设置失败 | + | handle | DevHandle类型,控制器操作句柄 | + | dataType | 结构体指针,定义YUV和原始数据格式以及位深度 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | 设置成功 | + | 负数 | 设置失败 | ```c int32_t ret; struct ExtDataType dataType; - /* 配置YUV和RAW数据格式和位深参数 */ - dataType.devno = 0; /* 设备0 */ - dataType.num = 0; /* Sensor 0 */ - dataType.extDataBitWidth[0] = 12; /* 位深数组元素0 */ - dataType.extDataBitWidth[1] = 12; /* 位深数组元素1 */ - dataType.extDataBitWidth[2] = 12; /* 位深数组元素2 */ - - dataType.extDataType[0] = 0x39; /* 定义YUV和原始数据格式以及位深度元素0 */ - dataType.extDataType[1] = 0x39; /* 定义YUV和原始数据格式以及位深度元素1 */ - dataType.extDataType[2] = 0x39; /* 定义YUV和原始数据格式以及位深度元素2 */ - /* 设置YUV和RAW数据格式和位深 */ + // 配置YUV和RAW数据格式和位深参数 + dataType.devno = 0; // 设备0 + dataType.num = 0; // Sensor 0 + dataType.extDataBitWidth[0] = 12; // 位深数组元素0 + dataType.extDataBitWidth[1] = 12; // 位深数组元素1 + dataType.extDataBitWidth[2] = 12; // 位深数组元素2 + + dataType.extDataType[0] = 0x39; // 定义YUV和原始数据格式以及位深度元素0 + dataType.extDataType[1] = 0x39; // 定义YUV和原始数据格式以及位深度元素1 + dataType.extDataType[2] = 0x39; // 定义YUV和原始数据格式以及位深度元素2 + // 设置YUV和RAW数据格式和位深 ret = MipiCsiSetExtDataType(MipiCsiHandle, &dataType); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiSetExtDataType fail! ret=%d\n", __func__, ret); - return -1; + if (ret != HDF_SUCCESS) { + HDF_LOGE("MipiCsiSetExtDataType: mipi csi set ext data type fail, ret=%d\n", ret); + return ret; } ``` -- 设置MIPI RX的Lane分布 +- 设置MIPI RX的Lane分布 ```c int32_t MipiCsiSetHsMode(DevHandle handle, LaneDivideMode laneDivideMode); ``` - **表7** MipiCsiSetHsMode的参数和返回值描述 + **表 7** MipiCsiSetHsMode的参数和返回值描述 - | 参数 | 参数描述 | + | 参数 | 参数描述 | | -------------- | -------------- | - | handle | 控制器操作句柄 | - | laneDivideMode | Lane模式参数 | - | **返回值** | **返回值描述** | - | 0 | 设置成功 | - | 负数 | 设置失败 | + | handle | DevHandle类型,控制器操作句柄 | + | laneDivideMode | 结构体类型,Lane模式参数 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | 设置成功 | + | 负数 | 设置失败 | ```c int32_t ret; enum LaneDivideMode mode; - /* Lane模式参数为0 */ + // Lane模式参数为0 mode = LANE_DIVIDE_MODE_0; - /* 设置MIPI RX的 Lane分布 */ + // 设置MIPI RX的 Lane分布 ret = MipiCsiSetHsMode(MipiCsiHandle, mode); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiSetHsMode fail! ret=%d\n", __func__, ret); - return -1; + if (ret != HDF_SUCCESS) { + HDF_LOGE("MipiCsiSetHsMode: mipi csi set hs mode fail, ret=%d\n", ret); + return ret; } ``` -- 设置共模电压模式 +- 设置共模电压模式 ```c int32_t MipiCsiSetPhyCmvmode(DevHandle handle, uint8_t devno, PhyCmvMode cmvMode); ``` - **表8** MipiCsiSetPhyCmvmode的参数和返回值描述 + **表 8** MipiCsiSetPhyCmvmode的参数和返回值描述 - | 参数 | 参数描述 | + | 参数 | 参数描述 | | ---------- | ---------------- | - | handle | 控制器操作句柄 | - | cmvMode | 共模电压模式参数 | - | devno | 设备编号 | - | **返回值** | **返回值描述** | - | 0 | 设置成功 | - | 负数 | 设置失败 | + | handle | DevHandle类型,控制器操作句柄 | + | cmvMode | 结构体类型,共模电压模式参数 | + | devno | uint8_t类型,设备编号 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | 设置成功 | + | 负数 | 设置失败 | ```c int32_t ret; enum PhyCmvMode mode; uint8_t devno; - /* 共模电压模式参数为0 */ + // 共模电压模式参数为0 mode = PHY_CMV_GE1200MV; - /* 设备编号为0 */ + // 设备编号为0 devno = 0; - /* 设置共模电压模式 */ + // 设置共模电压模式 ret = MipiCsiSetPhyCmvmode(MipiCsiHandle, devno, mode); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiSetPhyCmvmode fail! ret=%d\n", __func__, ret); - return -1; + if (ret != HDF_SUCCESS) { + HDF_LOGE("MipiCsiSetPhyCmvmode: mipi csi set phy cmv mode fail, ret=%d\n", ret); + return ret; } ``` #### 复位/撤销复位Sensor -- 复位Sensor +- 复位Sensor ```c int32_t MipiCsiResetSensor(DevHandle handle, uint8_t snsResetSource); ``` - **表9** MipiCsiResetSensor的参数和返回值描述 + **表 9** MipiCsiResetSensor的参数和返回值描述 - | 参数 | 参数描述 | + | 参数 | 参数描述 | | -------------- | ------------------------------------------------ | - | handle | 控制器操作句柄 | - | snsResetSource | 传感器的复位信号线号,在软件中称为传感器的复位源 | - | **返回值** | **返回值描述** | - | 0 | 复位成功 | - | 负数 | 复位失败 | + | handle | DevHandle类型,控制器操作句柄 | + | snsResetSource | uint8_t类型,传感器的复位信号线号,在软件中称为传感器的复位源 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | 复位成功 | + | 负数 | 复位失败 | ```c int32_t ret; uint8_t snsResetSource; - /* 传感器复位信号线号为0 */ + // 传感器复位信号线号为0 snsResetSource = 0; - /* 复位Sensor */ + // 复位Sensor ret = MipiCsiResetSensor(MipiCsiHandle, snsResetSource); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiResetSensor fail! ret=%d\n", __func__, ret); - return -1; + if (ret != HDF_SUCCESS) { + HDF_LOGE("MipiCsiResetSensor: mipi csi reset sensor fail, ret=%d\n", ret); + return ret; } ``` -- 撤销复位Sensor +- 撤销复位Sensor ```c int32_t MipiCsiUnresetSensor(DevHandle handle, uint8_t snsResetSource); ``` - **表10** MipiCsiUnresetSensor的参数和返回值描述 + **表 10** MipiCsiUnresetSensor的参数和返回值描述 - | 参数 | 参数描述 | + | 参数 | 参数描述 | | -------------- | ------------------------------------------------ | - | handle | 控制器操作句柄 | - | snsResetSource | 传感器的复位信号线号,在软件中称为传感器的复位源 | - | **返回值** | **返回值描述** | - | 0 | 撤销复位成功 | - | 负数 | 撤销复位失败 | + | handle | DevHandle类型,控制器操作句柄 | + | snsResetSource | uint8_t类型,传感器的复位信号线号,在软件中称为传感器的复位源 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | 撤销复位成功 | + | 负数 | 撤销复位失败 | ```c int32_t ret; uint8_t snsResetSource; - /* 传感器撤销复位信号线号为0 */ + // 传感器撤销复位信号线号为0 snsResetSource = 0; - /* 撤销复位Sensor */ + // 撤销复位Sensor ret = MipiCsiUnresetSensor(MipiCsiHandle, snsResetSource); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiUnresetSensor fail! ret=%d\n", __func__, ret); - return -1; + if (ret != HDF_SUCCESS) { + HDF_LOGE("MipiCsiUnresetSensor: mipi csi unreset sensor fail, ret=%d\n", ret); + return ret; } ``` #### 复位/撤销复位MIPI RX -- 复位MIPI RX +- 复位MIPI RX ```c int32_t MipiCsiResetRx(DevHandle handle, uint8_t comboDev); ``` - **表11** MipiCsiResetRx的参数和返回值描述 + **表 11** MipiCsiResetRx的参数和返回值描述 - | 参数 | 参数描述 | + | 参数 | 参数描述 | | ---------- | --------------------- | - | handle | 控制器操作句柄 | - | comboDev | MIPI RX或LVDS通路序号 | - | **返回值** | **返回值描述** | - | 0 | 复位成功 | - | 负数 | 复位失败 | + | handle | DevHandle类型,控制器操作句柄 | + | comboDev | uint8_t类型,MIPI RX或LVDS通路序号 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | 复位成功 | + | 负数 | 复位失败 | ```c int32_t ret; uint8_t comboDev; - /* 通路序号为0 */ + // 通路序号为0 comboDev = 0; - /* 复位MIPI RX */ + // 复位MIPI RX ret = MipiCsiResetRx(MipiCsiHandle, comboDev); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiResetRx fail! ret=%d\n", __func__, ret); - return -1; + if (ret != HDF_SUCCESS) { + HDF_LOGE("MipiCsiResetRx: mipi csi reset rx fail, ret=%d\n", ret); + return ret; } ``` -- 撤销复位MIPI RX +- 撤销复位MIPI RX ```c int32_t MipiCsiUnresetRx(DevHandle handle, uint8_t comboDev); ``` - **表12** MipiCsiUnresetRx的参数和返回值描述 + **表 12** MipiCsiUnresetRx的参数和返回值描述 - | 参数 | 参数描述 | + | 参数 | 参数描述 | | ---------- | --------------------- | - | handle | 控制器操作句柄 | - | comboDev | MIPI RX或LVDS通路序号 | - | **返回值** | **返回值描述** | - | 0 | 撤销复位成功 | - | 负数 | 撤销复位失败 | + | handle | DevHandle类型,控制器操作句柄 | + | comboDev | uint8_t类型,MIPI RX或LVDS通路序号 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | 撤销复位成功 | + | 负数 | 撤销复位失败 | ```c int32_t ret; uint8_t comboDev; - /* 通路序号为0 */ + // 通路序号为0 comboDev = 0; - /* 撤销复位MIPI RX */ + // 撤销复位MIPI RX ret = MipiCsiUnresetRx(MipiCsiHandle, comboDev); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiUnresetRx fail! ret=%d\n", __func__, ret); - return -1; + if (ret != HDF_SUCCESS) { + HDF_LOGE("MipiCsiUnresetRx: mipi csi unreset rx fail, ret=%d\n", ret); + return ret; } ``` #### 使能/关闭MIPI的时钟 -- 使能MIPI的时钟 +- 使能MIPI的时钟 ```c int32_t MipiCsiEnableClock(DevHandle handle, uint8_t comboDev); ``` - **表13** MipiCsiEnableClock的参数和返回值描述 + **表 13** MipiCsiEnableClock的参数和返回值描述 - | 参数 | 参数描述 | + | 参数 | 参数描述 | | ---------- | -------------- | - | handle | 控制器操作句柄 | - | comboDev | 通路序号 | + | handle | DevHandle类型,控制器操作句柄 | + | comboDev | uint8_t类型,通路序号 | | **返回值** | **返回值描述** | - | 0 | 使能成功 | - | 负数 | 使能失败 | + | HDF_SUCCESS | 使能成功 | + | 负数 | 使能失败 | ```c int32_t ret; uint8_t comboDev; - /* 通路序号为0 */ + // 通路序号为0 comboDev = 0; - /* 使能MIPI的时钟 */ + // 使能MIPI的时钟 ret = MipiCsiEnableClock(MipiCsiHandle, comboDev); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiEnableClock fail! ret=%d\n", __func__, ret); - return -1; + if (ret != HDF_SUCCESS) { + HDF_LOGE("MipiCsiEnableClock: mipi csi enable clock fail, ret=%d\n", ret); + return ret; } ``` -- 关闭MIPI的时钟 +- 关闭MIPI的时钟 ```c int32_t MipiCsiDisableClock(DevHandle handle, uint8_t comboDev); ``` - **表14** MipiCsiDisableClock的参数和返回值描述 + **表 14** MipiCsiDisableClock的参数和返回值描述 - | 参数 | 参数描述 | + | 参数 | 参数描述 | | ---------- | -------------- | - | handle | 控制器操作句柄 | - | comboDev | 通路序号 | + | handle | DevHandle类型,控制器操作句柄 | + | comboDev | uint8_t类型,通路序号 | | **返回值** | **返回值描述** | - | 0 | 关闭成功 | - | 负数 | 关闭失败 | + | HDF_SUCCESS | 关闭成功 | + | 负数 | 关闭失败 | ```c int32_t ret; uint8_t comboDev; - /* 通路序号为0 */ + // 通路序号为0 comboDev = 0; - /* 关闭MIPI的时钟 */ + // 关闭MIPI的时钟 ret = MipiCsiDisableClock(MipiCsiHandle, comboDev); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiDisableClock fail! ret=%d\n", __func__, ret); - return -1; + if (ret != HDF_SUCCESS) { + HDF_LOGE("MipiCsiDisableClock: mipi csi disable clock fail, ret=%d\n", ret); + return ret; } ``` #### 使能/关闭MIPI上的Sensor时钟 -- 使能MIPI上的Sensor时钟 +- 使能MIPI上的Sensor时钟 ```c int32_t MipiCsiEnableSensorClock(DevHandle handle, uint8_t snsClkSource); ``` - **表15** MipiCsiEnableSensorClock的参数和返回值描述 + **表 15** MipiCsiEnableSensorClock的参数和返回值描述 - | 参数 | 参数描述 | + | 参数 | 参数描述 | | ------------ | ------------------------------------------------ | - | handle | 控制器操作句柄 | - | snsClkSource | 传感器的时钟信号线号,在软件中称为传感器的时钟源 | - | **返回值** | **返回值描述** | - | 0 | 使能成功 | - | 负数 | 使能失败 | + | handle | DevHandle类型,控制器操作句柄 | + | snsClkSource | uint8_t类型,传感器的时钟信号线号,在软件中称为传感器的时钟源 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | 使能成功 | + | 负数 | 使能失败 | ```c int32_t ret; uint8_t snsClkSource; - /* 传感器的时钟信号线号为0 */ + // 传感器的时钟信号线号为0 snsClkSource = 0; - /* 使能MIPI上的Sensor时钟 */ + // 使能MIPI上的Sensor时钟 ret = MipiCsiEnableSensorClock(MipiCsiHandle, snsClkSource); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiEnableSensorClock fail! ret=%d\n", __func__, ret); - return -1; + if (ret != HDF_SUCCESS) { + HDF_LOGE("MipiCsiEnableSensorClock: mipi csi enable sensor clock fail, ret=%d\n", ret); + return ret; } ``` -- 关闭MIPI上的Sensor时钟 +- 关闭MIPI上的Sensor时钟 ```c int32_t MipiCsiDisableSensorClock(DevHandle handle, uint8_t snsClkSource); ``` - **表16** MipiCsiDisableSensorClock的参数和返回值描述 + **表 16** MipiCsiDisableSensorClock的参数和返回值描述 - | 参数 | 参数描述 | + | 参数 | 参数描述 | | ------------ | ------------------------------------------------ | - | handle | 控制器操作句柄 | - | snsClkSource | 传感器的时钟信号线号,在软件中称为传感器的时钟源 | - | **返回值** | **返回值描述** | - | 0 | 关闭成功 | - | 负数 | 关闭失败 | + | handle | DevHandle类型,控制器操作句柄 | + | snsClkSource | uint8_t类型,传感器的时钟信号线号,在软件中称为传感器的时钟源 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | 关闭成功 | + | 负数 | 关闭失败 | ```c int32_t ret; uint8_t snsClkSource; - /* 传感器的时钟信号线号为0 */ + // 传感器的时钟信号线号为0 snsClkSource = 0; - /* 关闭MIPI上的Sensor时钟 */ + // 关闭MIPI上的Sensor时钟 ret = MipiCsiDisableSensorClock(MipiCsiHandle, snsClkSource); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiDisableSensorClock fail! ret=%d\n", __func__, ret); - return -1; + if (ret != HDF_SUCCESS) { + HDF_LOGE("MipiCsiDisableSensorClock: mipi csi disable sensor clock fail, ret=%d\n", ret); + return ret; } ``` @@ -564,16 +562,16 @@ void MipiCsiClose(DevHandle handle); 该函数会释放掉由MipiCsiOpen申请的资源。 -**表17** MipiCsiClose的参数和返回值描述 +**表 17** MipiCsiClose的参数和返回值描述 -| 参数 | 参数描述 | +| 参数 | 参数描述 | | ------------ | ------------------------------------------------ | -| handle | MIPI CSI控制器操作句柄 | +| handle | DevHandle类型,MIPI CSI控制器操作句柄 | ```c -MipiCsiClose(MIPIHandle); /* 释放掉MIPI CSI控制器操作句柄 */ +MipiCsiClose(MIPIHandle); // 释放掉MIPI CSI控制器操作句柄 ``` ## 使用实例 @@ -583,133 +581,257 @@ MipiCsiClose(MIPIHandle); /* 释放掉MIPI CSI控制器操作句柄 */ MIPI CSI完整的使用示例如下所示: ```c -#include "hdf.h" -#include "MIPI_csi_if.h" +#include "hdf_log.h" +#include "mipi_csi_if.h" +#include "securec.h" + +enum InterfaceType { + INTERFACE_MIPI = 0, + INTERFACE_LVDS, + INTERFACE_CMOS, + INTERFACE_BUTT +}; + +static void InitMipiDevAttr(MipiDevAttr *mipiAttr) +{ + MipiDevAttr attr; + if (mipiAttr == NULL) { + return; + } + + HDF_LOGI("InitMipiDevAttr: enter."); + (void)memset_s(&attr, sizeof(MipiDevAttr), 0, sizeof(MipiDevAttr)); + attr.inputDataType = DATA_TYPE_RAW_12BIT; + attr.wdrMode = HI_MIPI_WDR_MODE_NONE; + // laneId: -1 - disable + attr.laneId[0] = 0; // 0 -- laneId 0 + attr.laneId[1] = 1; // 1 -- laneId 1 + attr.laneId[2] = 2; // 2 -- laneId 2 + attr.laneId[3] = 3; // 3 -- laneId 3 + + // Used by the HI_MIPI_WDR_MODE_DT, This is not fully tested! + if (attr.wdrMode == HI_MIPI_WDR_MODE_DT) { + attr.dataType[0] = 0x39; // 0x39 -- data type reserved + attr.dataType[1] = 0x39; // 0x39 -- data type reserved + attr.dataType[2] = 0x39; // 0x39 -- data type reserved + attr.dataType[3] = 0x39; // 0x39 -- data type reserved + } + + *mipiAttr = attr; +} + +static int MipiGetIntputModeType(InputMode inputMode) +{ + switch (inputMode) { + case INPUT_MODE_SUBLVDS: + case INPUT_MODE_LVDS: + case INPUT_MODE_HISPI: + return INTERFACE_LVDS; + case INPUT_MODE_MIPI: + return INTERFACE_MIPI; + case INPUT_MODE_CMOS: + case INPUT_MODE_BT1120: + case INPUT_MODE_BT656: + case INPUT_MODE_BYPASS: + return INTERFACE_CMOS; + default: + break; + } -void PalMipiCsiTestSample(void) + return INTERFACE_BUTT; +} + +static void InitLvdsDevAttr(LvdsDevAttr *lvdsAttr) +{ + int i; + int j; + int k; + LvdsDevAttr attr; + + if (lvdsAttr == NULL) { + return; + } + + (void)memset_s(&attr, sizeof(LvdsDevAttr), 0, sizeof(LvdsDevAttr)); + attr.inputDataType = DATA_TYPE_RAW_12BIT; + attr.wdrMode = HI_WDR_MODE_NONE; + // LVDS synchronization mode. LVDS_SYNC_MODE_SOF, LVDS_SYNC_MODE_SAV + attr.syncMode = LVDS_SYNC_MODE_SOF; + // LVDS Vsync type. LVDS_VSYNC_NORMAL, LVDS_VSYNC_SHARE, LVDS_VSYNC_HCONNECT + attr.vsyncAttr.syncType = LVDS_VSYNC_NORMAL; + // hconnect vsync blanking len, valid when the syncType is LVDS_VSYNC_HCONNECT + // This is not fully tested! + if (attr.vsyncAttr.syncType == LVDS_VSYNC_HCONNECT) { + attr.vsyncAttr.hblank1 = 0; + attr.vsyncAttr.hblank2 = 0; + } + // frame identification code: LVDS_FID_NONE, LVDS_FID_IN_SAV, LVDS_FID_IN_DATA + attr.fidAttr.fidType = LVDS_FID_NONE; + // Sony DOL has the Frame Information Line, in DOL H-Connection mode, should + // configure this flag as false to disable output the Frame Information Line. + // This is not fully tested! + attr.fidAttr.outputFil = 'm'; + // LVDS bit size end mode: LVDS_ENDIAN_LITTLE, LVDS_ENDIAN_BIG + attr.dataEndian = LVDS_ENDIAN_LITTLE; + // sync code endian: little/big, LVDS_ENDIAN_LITTLE, LVDS_ENDIAN_BIG + attr.syncCodeEndian = LVDS_ENDIAN_LITTLE; + // laneId: -1 - disable + attr.laneId[0] = 0; // 0 -- laneId 0 + attr.laneId[1] = 1; // 1 -- laneId 1 + attr.laneId[2] = 2; // 2 -- laneId 2 + attr.laneId[3] = 3; // 3 -- laneId 3 + + /* each vc has 4 params, syncCode[i]: + syncMode is SYNC_MODE_SOF: SOF, EOF, SOL, EOL + syncMode is SYNC_MODE_SAV: invalid sav, invalid eav, valid sav, valid eav + This is not fully tested! */ + for (i = 0; i < LVDS_LANE_NUM; i++) { + for (j = 0; j < WDR_VC_NUM; j++) { + for (k = 0; k < SYNC_CODE_NUM; k++) { + attr.syncCode[i][j][k] = 0; // 0 -- frame0 sof + } + } + } + + *lvdsAttr = attr; +} + +static int32_t PalMipiCsiTestSample(void) { uint8_t id; int32_t ret; uint8_t comboDev; uint8_t snsClkSource; + uint8_t snsResetSource; uint8_t devno; - enum LaneDivideMode mode; - enum PhyCmvMode mode; - struct ComboDevAttr attr; - struct ExtDataType dataType; + LaneDivideMode laneMode; + PhyCmvMode CmvMode; + ComboDevAttr attr; DevHandle MipiCsiHandle = NULL; + enum InterfaceType interType; - /* 控制器ID号 */ + // 控制器ID号 id = 0; - /* 获取控制器操作句柄 */ + // 获取控制器操作句柄 MipiCsiHandle = MipiCsiOpen(id); if (MipiCsiHandle == NULL) { - HDF_LOGE("MipiCsiOpen: failed!\n"); - return; + HDF_LOGE("PalMipiCsiTestSample: mipi csi open fail!\n"); + return HDF_FAILURE; } - - /* Lane模式参数为0 */ - mode = LANE_DIVIDE_MODE_0; - /* 设置MIPI RX的Lane分布 */ - ret = MipiCsiSetHsMode(MipiCsiHandle, mode); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiSetHsMode fail! ret=%d\n", __func__, ret); - return; + + // Lane模式参数为0 + laneMode = LANE_DIVIDE_MODE_0; + // 设置MIPI RX的Lane分布 + ret = MipiCsiSetHsMode(MipiCsiHandle, laneMode); + if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) { + HDF_LOGE("PalMipiCsiTestSample: mipi csi set hs mode fail, ret=%d\n", ret); + return ret; } - /* 通路序号为0 */ + // 通路序号为0 comboDev = 0; - /* 使能MIPI的时钟 */ + // 使能MIPI的时钟 ret = MipiCsiEnableClock(MipiCsiHandle, comboDev); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiEnableClock fail! ret=%d\n", __func__, ret); - return; + if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) { + HDF_LOGE("PalMipiCsiTestSample: mipi csi enable clock fail, ret=%d\n", ret); + return ret; } - /* 复位MIPI RX */ + // 复位MIPI RX ret = MipiCsiResetRx(MipiCsiHandle, comboDev); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiResetRx fail! ret=%d\n", __func__, ret); - return; + if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) { + HDF_LOGE("PalMipiCsiTestSample: mipi csi reset rx fail, ret=%d\n", ret); + return ret; } - /* 传感器的时钟信号线号为0 */ + // 传感器的时钟信号线号为0 snsClkSource = 0; - /* 使能MIPI上的Sensor时钟 */ + // 使能MIPI上的Sensor时钟 ret = MipiCsiEnableSensorClock(MipiCsiHandle, snsClkSource); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiEnableSensorClock fail! ret=%d\n", __func__, ret); - return; + if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) { + HDF_LOGE("PalMipiCsiTestSample: mipi csi enable sensor clock fail, ret=%d\n", ret); + return ret; } - - /* 复位Sensor */ + snsResetSource = 0; + // 复位Sensor ret = MipiCsiResetSensor(MipiCsiHandle, snsResetSource); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiResetSensor fail! ret=%d\n", __func__, ret); - return; + if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) { + HDF_LOGE("PalMipiCsiTestSample: mipi csi reset sensor fail, ret=%d\n", ret); + return ret; } - /* MIPI参数配置如下 */ + // MIPI参数配置如下 (void)memset_s(&attr, sizeof(ComboDevAttr), 0, sizeof(ComboDevAttr)); - attr.devno = 0; /* 设备0 */ - attr.inputMode = INPUT_MODE_MIPI; /* 输入模式为MIPI */ - attr.dataRate = MIPI_DATA_RATE_X1; /* 每时钟输出1像素 */ - attr.imgRect.x = 0; /* 0: 图像传感器左上位置 */ - attr.imgRect.y = 0; /* 0: 图像传感器右上位置 */ - attr.imgRect.width = 2592; /* 2592: 图像传感器宽度大小 */ - attr.imgRect.height = 1944; /* 1944: 图像传感器高度尺寸 */ - /* 写入配置数据 */ + attr.devno = 0; // 设备0 + attr.inputMode = INPUT_MODE_MIPI; // 输入模式为MIPI + attr.dataRate = MIPI_DATA_RATE_X1; // 每时钟输出1像素 + attr.imgRect.x = 0; // 0: 图像传感器左上位置 + attr.imgRect.y = 0; // 0: 图像传感器右上位置 + attr.imgRect.width = 2592; // 2592: 图像传感器宽度大小 + attr.imgRect.height = 1944; // 1944: 图像传感器高度尺寸 + interType = MipiGetIntputModeType(attr.inputMode); + if (interType == INTERFACE_MIPI) { + HDF_LOGI("PalMipiCsiTestSample: call[InitMipiDevAttr]."); + InitMipiDevAttr(&attr.mipiAttr); + } else if (interType == INTERFACE_LVDS) { + HDF_LOGI("PalMipiCsiTestSample: call[InitLvdsDevAttr]."); + InitLvdsDevAttr(&attr.lvdsAttr); + } else { + HDF_LOGE("PalMipiCsiTestSample: interType = %d is error!", attr.inputMode); + } + // 写入配置数据 ret = MipiCsiSetComboDevAttr(MipiCsiHandle, &attr); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiSetComboDevAttr fail! ret=%d\n", __func__, ret); - return; + if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) { + HDF_LOGE("PalMipiCsiTestSample: mipi csi set combo devAttr fail, ret=%d\n", ret); + return ret; } - /* 共模电压模式参数为0 */ - mode = PHY_CMV_GE1200MV; - /* 设备编号为0 */ + // 共模电压模式参数为0 + CmvMode = PHY_CMV_GE1200MV; + // 设备编号为0 devno = 0; - /* 设置共模电压模式 */ - ret = MipiCsiSetPhyCmvmode(MipiCsiHandle, devno, mode); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiSetPhyCmvmode fail! ret=%d\n", __func__, ret); - return; + // 设置共模电压模式 + ret = MipiCsiSetPhyCmvmode(MipiCsiHandle, devno, CmvMode); + if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) { + HDF_LOGE("PalMipiCsiTestSample: mipi csi set phy cmv mode fail, ret=%d\n", ret); + return ret; } - /* 通路序号为0 */ + // 通路序号为0 comboDev = 0; - /* 撤销复位MIPI RX */ + // 撤销复位MIPI RX ret = MipiCsiUnresetRx(MipiCsiHandle, comboDev); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiUnresetRx fail! ret=%d\n", __func__, ret); - return; + if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) { + HDF_LOGE("PalMipiCsiTestSample: mipi csi unreset rx fail, ret=%d\n", ret); + return ret; } - /* 关闭MIPI的时钟 */ + // 关闭MIPI的时钟 ret = MipiCsiDisableClock(MipiCsiHandle, comboDev); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiDisableClock fail! ret=%d\n", __func__, ret); - return; + if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) { + HDF_LOGE("PalMipiCsiTestSample: mipi csi disable clock fail, ret=%d\n", ret); + return ret; } - /* 传感器撤销复位信号线号为0 */ + // 传感器撤销复位信号线号为0 snsResetSource = 0; - /* 撤销复位Sensor */ + // 撤销复位Sensor ret = MipiCsiUnresetSensor(MipiCsiHandle, snsResetSource); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiUnresetSensor fail! ret=%d\n", __func__, ret); - return; + if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) { + HDF_LOGE("PalMipiCsiTestSample: mipi csi unreset sensor fail, ret=%d\n", ret); + return ret; } - /* 关闭MIPI上的Sensor时钟 */ + // 关闭MIPI上的Sensor时钟 ret = MipiCsiDisableSensorClock(MipiCsiHandle, snsClkSource); - if (ret != 0) { - HDF_LOGE("%s: MipiCsiDisableSensorClock fail! ret=%d\n", __func__, ret); - return; + if (ret != HDF_SUCCESS && ret != HDF_ERR_NOT_SUPPORT) { + HDF_LOGE("PalMipiCsiTestSample: mipi csi disable sensor clock fail, ret=%d\n", ret); + return ret; } - - /* 释放MIPI DSI设备句柄 */ - MipiCsiClose(MipiCsiHandle); + HDF_LOGI("PalMipiCsiTestSample: function tests end."); + // 释放MIPI DSI设备句柄 + MipiCsiClose(MipiCsiHandle); + return ret; } ``` diff --git a/zh-cn/device-dev/driver/driver-platform-mipicsi-develop.md b/zh-cn/device-dev/driver/driver-platform-mipicsi-develop.md index fd4c6675b305cb91851a01c5e38fc9618fd9f369..d5f90ae7940f3fd079f2d88f9798ed2b72f196aa 100755 --- a/zh-cn/device-dev/driver/driver-platform-mipicsi-develop.md +++ b/zh-cn/device-dev/driver/driver-platform-mipicsi-develop.md @@ -10,48 +10,51 @@ CSI(Camera Serial Interface)是由MIPI联盟下Camera工作组指定的接 图1显示了简化的CSI接口。D-PHY采用1对源同步的差分时钟和1~4对差分数据线来进行数据传输。数据传输采用DDR方式,即在时钟的上下边沿都有数据传输。 - **图 1** CSI发送、接收接口 -![](figures/CSI发送-接收接口.png) +**图 1** CSI发送、接收接口 + +![CSI发送、接收接口](figures/CSI发送-接收接口.png) MIPI CSI标准分为应用层、协议层与物理层,协议层又细分为像素字节转换层、低级协议层、Lane管理层。 - 物理层(PHY Layer) - PHY层指定了传输媒介,在电气层面从串行bit流中捕捉“0”与“1”,同时生成SoT与EoT等信号。 + PHY层指定了传输媒介,在电气层面从串行bit流中捕捉“0”与“1”,同时生成SoT与EoT等信号。 - 协议层(Protocol Layer) - 协议层由三个子层组成,每个子层有不同的职责。CSI-2协议能够在host侧处理器上用一个单独的接口处理多条数据流。协议层规定了多条数据流该如何标记和交织起来,以便每条数据流能够被正确地恢复出来。 + 协议层由三个子层组成,每个子层有不同的职责。CSI-2协议能够在host侧处理器上用一个单独的接口处理多条数据流。协议层规定了多条数据流该如何标记和交织起来,以便每条数据流能够被正确地恢复出来。 - - 像素字节转换层(Pixel/Byte Packing/Unpacking Layer) + - 像素字节转换层(Pixel/Byte Packing/Unpacking Layer) CSI-2规范支持多种不同像素格式的图像应用。在发送方中,本层在发送数据到Low Level Protocol层之前,将来自应用层的像素封包为字节数据。在接收方中,本层在发送数据到应用层之前,将来自Low Level Protocol层的字节数据解包为像素。8位的像素数据在本层中传输时保持不变。 - - 低级协议层(Low Level Protocol) - + - 低级协议层(Low Level Protocol) LLP主要包含了在SoT和EoT事件之间的bit和byte级别的同步方法,以及和下一层传递数据的方法。LLP最小数据粒度是1个字节。LLP也包含了一个字节内的bit值解析,即Endian(大小端里的Endian的意思)的处理。 - - Lane管理层(Lane Management) + - Lane管理层(Lane Management) CSI-2的Lane是可扩展的。具体的数据Lane的数量规范并没有给出限制,具体根据应用的带宽需求而定。发送侧分发(distributor功能)来自出口方向数据流的字节到1条或多条Lane上。接收侧则从一条或多条Lane中收集字节并合并(merge功能)到一个数据流上,复原出原始流的字节顺序。对于C-PHY物理层来说,本层专门分发字节对(16 bits)到数据Lane或从数据Lane中收集字节对。基于每Lane的扰码功能是可选特性。 - 协议层的数据组织形式是包(packet)。接口的发送侧会增加包头(header)和错误校验(error-checking)信息到即将被LLP发送的数据上。接收侧在LLP将包头剥掉,包头会被接收器中对应的逻辑所解析。错误校验信息可以用来做入口数据的完整性检查。 - 应用层(Application Layer) - 本层描述了更高层级的应用对于数据中的数据的处理,规范并不涵盖应用层。CSI-2规范只给出了像素值和字节的映射关系。 + 本层描述了更高层级的应用对于数据中的数据的处理,规范并不涵盖应用层。CSI-2规范只给出了像素值和字节的映射关系。 ### 运作机制 MIPI CSI模块各分层的作用为: - 接口层提供打开设备、写入数据和关闭设备的接口。 + - 核心层主要提供绑定设备、初始化设备以及释放设备的能力。 + - 适配层实现其它具体的功能。 ![](../public_sys-resources/icon-note.gif) **说明:**
核心层可以调用接口层的函数,核心层通过钩子函数调用适配层函数,从而适配层可以间接的调用接口层函数,但是不可逆转接口层调用适配层函数。 -![image1](figures/无服务模式结构图.png) +**图 2** CSI无服务模式结构图 + +![CSI无服务模式结构图](figures/无服务模式结构图.png) ## 开发指导 @@ -81,306 +84,327 @@ struct MipiCsiCntlrMethod { int32_t (*unresetSensor)(struct MipiCsiCntlr *cntlr, uint8_t snsResetSource); }; ``` -**表1** MipiCsiCntlrMethod成员的钩子函数功能说明 -| 成员函数 | 入参 | 出参 | 返回状态 | 功能 | + +**表 1** MipiCsiCntlrMethod成员的钩子函数功能说明 +| 成员函数 | 入参 | 出参 | 返回状态 | 功能 | | ------------------ | ------------------------------------------------------------ | ---- | ------------------ | -------------------------- | -| setComboDevAttr | **cntlr**:结构体指针,MipiCsi控制器 ;
**pAttr**:结构体指针,MIPI CSI相应配置结构体指针 | 无 | HDF_STATUS相关状态 | 写入MIPI CSI配置 | -| setPhyCmvmode | **cntlr**:结构体指针,MipiCsi控制器 ;
**devno**:uint8_t,设备编号;
**cmvMode**:枚举类型,共模电压模式参数 | 无 | HDF_STATUS相关状态 | 设置共模电压模式 | -| setExtDataType | **cntlr**:结构体指针,MipiCsi控制器 ;
**dataType**:结构体指针,定义YUV和原始数据格式以及位深度 | 无 | HDF_STATUS相关状态 | 设置YUV和RAW数据格式和位深 | -| setHsMode | **cntlr**:结构体指针,MipiCsi控制器 ;
**laneDivideMode**:枚举类型,Lane模式参数 | 无 | HDF_STATUS相关状态 | 设置MIPI RX的Lane分布 | -| enableClock | **cntlr**:结构体指针,MipiCsi控制器 ;
**comboDev**:uint8_t,通路序号 | 无 | HDF_STATUS相关状态 | 使能MIPI的时钟 | -| disableClock | **cntlr**:结构体指针,MipiCsi控制器 ;
**comboDev**:uint8_t,通路序号 | 无 | HDF_STATUS相关状态 | 关闭MIPI的时钟 | -| resetRx | **cntlr**:结构体指针,MipiCsi控制器 ;
**comboDev**:uint8_t,通路序号 | 无 | HDF_STATUS相关状态 | 复位MIPI RX | -| unresetRx | **cntlr**:结构体指针,MipiCsi控制器 ;
**comboDev**:uint8_t,通路序号 | 无 | HDF_STATUS相关状态 | 撤销复位MIPI RX | -| enableSensorClock | **cntlr**:结构体指针,MipiCsi控制器 ;
**snsClkSource**:uint8_t,传感器的时钟信号线号 | 无 | HDF_STATUS相关状态 | 使能MIPI上的Sensor时钟 | -| disableSensorClock | **cntlr**:结构体指针,MipiCsi控制器 ;
**snsClkSource**:uint8_t,传感器的时钟信号线号 | 无 | HDF_STATUS相关状态 | 关闭MIPI上的Sensor时钟 | -| resetSensor | **cntlr**:结构体指针,MipiCsi控制器 ;
**snsClkSource**:uint8_t,传感器的时钟信号线号 | 无 | HDF_STATUS相关状态 | 复位Sensor | -| unresetSensor | **cntlr**:结构体指针,MipiCsi控制器 ;
**snsClkSource**:uint8_t,传感器的时钟信号线号 | 无 | HDF_STATUS相关状态 | 撤销复位Sensor | +| setComboDevAttr | **cntlr**:结构体指针,MipiCsi控制器 ;
**pAttr**:结构体指针,MIPI CSI相应配置结构体指针 | 无 | HDF_STATUS相关状态 | 写入MIPI CSI配置 | +| setPhyCmvmode | **cntlr**:结构体指针,MipiCsi控制器 ;
**devno**:uint8_t类型,设备编号;
**cmvMode**:枚举类型,共模电压模式参数 | 无 | HDF_STATUS相关状态 | 设置共模电压模式 | +| setExtDataType | **cntlr**:结构体指针,MipiCsi控制器 ;
**dataType**:结构体指针,定义YUV和原始数据格式以及位深度 | 无 | HDF_STATUS相关状态 | 设置YUV和RAW数据格式和位深 | +| setHsMode | **cntlr**:结构体指针,MipiCsi控制器 ;
**laneDivideMode**:枚举类型,Lane模式参数 | 无 | HDF_STATUS相关状态 | 设置MIPI RX的Lane分布 | +| enableClock | **cntlr**:结构体指针,MipiCsi控制器 ;
**comboDev**:uint8_t类型,通路序号 | 无 | HDF_STATUS相关状态 | 使能MIPI的时钟 | +| disableClock | **cntlr**:结构体指针,MipiCsi控制器 ;
**comboDev**:uint8_t类型,通路序号 | 无 | HDF_STATUS相关状态 | 关闭MIPI的时钟 | +| resetRx | **cntlr**:结构体指针,MipiCsi控制器 ;
**comboDev**:uint8_t类型,通路序号 | 无 | HDF_STATUS相关状态 | 复位MIPI RX | +| unresetRx | **cntlr**:结构体指针,MipiCsi控制器 ;
**comboDev**:uint8_t类型,通路序号 | 无 | HDF_STATUS相关状态 | 撤销复位MIPI RX | +| enableSensorClock | **cntlr**:结构体指针,MipiCsi控制器 ;
**snsClkSource**:uint8_t类型,传感器的时钟信号线号 | 无 | HDF_STATUS相关状态 | 使能MIPI上的Sensor时钟 | +| disableSensorClock | **cntlr**:结构体指针,MipiCsi控制器 ;
**snsClkSource**:uint8_t类型,传感器的时钟信号线号 | 无 | HDF_STATUS相关状态 | 关闭MIPI上的Sensor时钟 | +| resetSensor | **cntlr**:结构体指针,MipiCsi控制器 ;
**snsClkSource**:uint8_t类型,传感器的时钟信号线号 | 无 | HDF_STATUS相关状态 | 复位Sensor | +| unresetSensor | **cntlr**:结构体指针,MipiCsi控制器 ;
**snsClkSource**:uint8_t类型,传感器的时钟信号线号 | 无 | HDF_STATUS相关状态 | 撤销复位Sensor | ### 开发步骤 -MIPI CSI模块适配的三个必选环节是配置属性文件、实例化驱动入口、以及实例化核心层接口函数。 +MIPI CSI模块适配包含以下四个步骤: -1. 配置属性文件: +1. 实例化驱动入口 - - 在device_info.hcs文件中添加deviceNode描述。 - - 【可选】添加mipicsi_config.hcs器件属性文件。 + - 实例化HdfDriverEntry结构体成员。 -2. 实例化驱动入口: - - - 实例化HdfDriverEntry结构体成员。 - - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 - -3. 实例化MIPI CSI控制器对象: + - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 + +2. 配置属性文件 + + - 在device_info.hcs文件中添加deviceNode描述。 + + - 【可选】添加mipi_csi_config.hcs器件属性文件。 + +3. 实例化MIPI CSI控制器对象 - - 初始化MipiCsiCntlr成员。 - - 实例化MipiCsiCntlr成员MipiCsiCntlrMethod。 - >![](../public_sys-resources/icon-note.gif) **说明:**
- >实例化MipiCsiCntlr成员MipiCsiCntlrMethod,其定义和成员说明见[接口说明](#接口说明)。 + - 初始化MipiCsiCntlr成员。 + + - 实例化MipiCsiCntlr成员MipiCsiCntlrMethod。 + + >![](../public_sys-resources/icon-note.gif) **说明:**
+ >实例化MipiCsiCntlr成员MipiCsiCntlrMethod,其定义和成员说明见[接口说明](#接口说明)。 -4. 驱动调试: +4. 驱动调试 - 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。 + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。 ### 开发实例 -下方将以mipi_rx_hi35xx.c为示例,展示需要厂商提供哪些内容来完整实现设备功能。 +下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/mipi_csi/mipi_csi_hi35xx.c驱动为示例,展示需要厂商提供哪些内容来完整实现设备功能。 + +1. 实例化驱动入口 + + 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HdfDriverEntry结构体的函数指针成员需要被驱动适配者操作函数填充,HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组,方便调用。 + + 一般在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 + + MIPI CSI驱动入口参考 + + ```c + struct HdfDriverEntry g_mipiCsiDriverEntry = { + .moduleVersion = 1, + .Init = Hi35xxMipiCsiInit, // 挂接MIPI CSI模块Init实例化 + .Release = Hi35xxMipiCsiRelease, // 挂接MIPI CSI模块Release实例化 + .moduleName = "HDF_MIPI_RX", // 【必要且与HCS文件中里面的moduleName匹配】 + }; + HDF_INIT(g_mipiCsiDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 + ``` + +2. 配置属性文件 + 一般来说,驱动开发首先需要新增mipi_csi_config.hcs配置文件,在其中配置器件属性,并在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。deviceNode与配置属性的对应关系是依靠deviceMatchAttr字段来完成的。只有当deviceNode下的deviceMatchAttr字段与配置属性文件中的match_attr字段完全相同时,驱动才能正确读取配置数据。器件属性值与核心层MipiCsiCntlr成员的默认值或限制范围有密切关系,deviceNode信息与驱动入口注册相关。 + + >![icon-note.gif](../public_sys-resources/icon-note.gif) **说明:**
+ >本例中MIPI控制器配置属性在源文件中,没有新增配置文件,驱动适配者如有需要,可在device_info.hcs文件的deviceNode增加deviceMatchAttr字段,同时新增mipi_csi_config.hcs文件,并使其match_attr字段与之相同。 + + 无服务模式device_info.hcs文件中设备节点也代表着一个设备对象,如果存在多个设备对象,则按需添加,注意服务名与驱动私有数据匹配的关键字名称必须唯一。其中各项参数如表2所示: + + **表 2** device_info.hcs节点参数说明 + + | 成员名 | 值 | + | -------- | -------- | + | policy | 驱动服务发布的策略,MIPI CSI控制器具体配置为0,表示驱动不需要发布服务 | + | priority | 驱动启动优先级(0-200),值越大优先级越低。MIPI CSI控制器具体配置为160 | + | permission | 驱动创建设备节点权限,MIPI CSI控制器具体配置为0664 | + | moduleName | 驱动名称,MIPI CSI控制器固定为HDF_MIPI_RX | + | serviceName | 驱动对外发布服务的名称,MIPI CSI控制器服务名设置为HDF_MIPI_RX | + | deviceMatchAttr | 驱动私有数据匹配的关键字,MIPI CSI控制器没有使用,可忽略 | + + device_info.hcs配置参考 + + ```c + root { + device_info { + match_attr = "hdf_manager"; + platform :: host { + hostName = "platform_host"; + priority = 50; + device_mipi_csi:: device { + device0 :: deviceNode { + policy = 0; + priority = 160; + permission = 0644; + moduleName = "HDF_MIPI_RX"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。 + serviceName = "HDF_MIPI_RX"; // 【必要且唯一】驱动对外发布服务的名称 + } + } + } + } + } + ``` + +3. 实例化MIPI CSI控制器对象 + + 完成驱动入口注册之后,最后一步就是以核心层MipiCsiCntlr对象的初始化为核心,实现HdfDriverEntry成员函数(Bind,Init,Release)。 + + MipiCsiCntlr对象的初始化包括驱动适配者自定义结构体(用于传递参数和数据)和实例化MipiCsiCntlr成员MipiCsiCntlrMethod(让用户可以通过接口来调用驱动底层函数)。 + + - 自定义结构体参考 + + 从驱动的角度看,自定义结构体是参数和数据的载体,一般来说,config文件中的数值也会用来初始化结构体成员,本例的mipicsi器件属性在源文件中,故基本成员结构与MipiCsiCntlr无太大差异。 + + ```c + typedef struct { + // 数据类型:8/10/12/14/16位 + DataType inputDataType; + // MIPI波分复用模式 + MipiWdrMode wdrMode; + // laneId: -1 - 禁用 + short laneId[MIPI_LANE_NUM]; + + union { + // 用于 HI_MIPI_WDR_MODE_DT + short dataType[WDR_VC_NUM]; + }; + } MipiDevAttr; + + typedef struct { + // 设备号 + uint8_t devno; + // 输入模式: MIPI/LVDS/SUBLVDS/HISPI/DC + InputMode inputMode; + MipiDataRate dataRate; + // MIPI Rx设备裁剪区域(与原始传感器输入图像大小相对应) + ImgRect imgRect; + + union { + MipiDevAttr mipiAttr; + LvdsDevAttr lvdsAttr; + }; + } ComboDevAttr; + + // MipiCsiCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。 + struct MipiCsiCntlr { + // 当驱动程序绑定到HDF框架时,将发送此控制器提供的服务。 + struct IDeviceIoService service; + // 当驱动程序绑定到HDF框架时,将传入设备端指针。 + struct HdfDeviceObject *device; + // 设备号 + unsigned int devNo; + // 控制器提供的所有接口 + struct MipiCsiCntlrMethod *ops; + // 对于控制器调试的所有接口,如果未实现驱动程序,则需要null。 + struct MipiCsiCntlrDebugMethod *debugs; + // 控制器上下文参数变量 + MipiDevCtx ctx; + // 访问控制器上下文参数变量时锁定 + OsalSpinlock ctxLock; + // 操作控制器时锁定方法 + struct OsalMutex lock; + // 匿名数据指针,用于存储csi设备结构。 + void *priv; + }; + ``` + + - MipiCsiCntlr成员钩子函数结构体MipiCsiCntlrMethod的实例化 + + >![](../public_sys-resources/icon-note.gif) **说明:**
+ >其他成员在Init函数中初始化。 + + ```c + static struct MipiCsiCntlrMethod g_method = { + .setComboDevAttr = Hi35xxSetComboDevAttr, + .setPhyCmvmode = Hi35xxSetPhyCmvmode, + .setExtDataType = Hi35xxSetExtDataType, + .setHsMode = Hi35xxSetHsMode, + .enableClock = Hi35xxEnableClock, + .disableClock = Hi35xxDisableClock, + .resetRx = Hi35xxResetRx, + .unresetRx = Hi35xxUnresetRx, + .enableSensorClock = Hi35xxEnableSensorClock, + .disableSensorClock = Hi35xxDisableSensorClock, + .resetSensor = Hi35xxResetSensor, + .unresetSensor = Hi35xxUnresetSensor + }; + ``` + + - Init函数开发参考 + + 入参: + + HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。 + + 返回值: + + HDF_STATUS相关状态 (表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS的定义)。 + + **表 3** HDF_STATUS相关状态说明 + + | 状态(值) | 问题描述 | + | -------- | -------- | + | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | + | HDF_ERR_MALLOC_FAIL | 内存分配失败 | + | HDF_ERR_IO | I/O 错误 | + | HDF_SUCCESS | 初始化成功 | + | HDF_FAILURE | 初始化失败 | + + 函数说明: + + MipiCsiCntlrMethod的实例化对象的挂载,调用MipiCsiRegisterCntlr,以及其他驱动适配者自定义初始化操作。 + + ```c + static int32_t Hi35xxMipiCsiInit(struct HdfDeviceObject *device) + { + int32_t ret; + + HDF_LOGI("%s: enter!", __func__); + g_mipiCsi.priv = NULL; // g_mipiTx是定义的全局变量 + // static struct MipiCsiCntlr g_mipiCsi = { + // .devNo = 0 + // }; + g_mipiCsi.ops = &g_method; // MipiCsiCntlrMethod的实例化对象的挂载 + #ifdef CONFIG_HI_PROC_SHOW_SUPPORT + g_mipiCsi.debugs = &g_debugMethod; + #endif + ret = MipiCsiRegisterCntlr(&g_mipiCsi, device); // 【必要】调用核心层函数和g_mipiTx初始化核心层全局变量 + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: [MipiCsiRegisterCntlr] failed!", __func__); + return ret; + } + + ret = MipiRxDrvInit(); // 【必要】驱动适配者对设备的初始化,形式不限。 + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: [MipiRxDrvInit] failed.", __func__); + return ret; + } + #ifdef MIPICSI_VFS_SUPPORT + ret = MipiCsiDevModuleInit(g_mipiCsi.devNo); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: [MipiCsiDevModuleInit] failed!", __func__); + return ret; + } + #endif + + OsalSpinInit(&g_mipiCsi.ctxLock); + HDF_LOGI("%s: load mipi csi driver success!", __func__); + return ret; + } -1. 一般来说,驱动开发首先需要新增mipicsi_config.hcs配置文件,在其中配置器件属性,并在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。deviceNode与配置属性的对应关系是依靠deviceMatchAttr字段来完成的。只有当deviceNode下的deviceMatchAttr字段与配置属性文件中的match_attr字段完全相同时,驱动才能正确读取配置数据。 + // mipi_csi_core.c核心层 + int32_t MipiCsiRegisterCntlr(struct MipiCsiCntlr *cntlr, struct HdfDeviceObject *device) + { + ...... + // 定义的全局变量:static struct MipiCsiHandle g_mipiCsihandle[MAX_CNTLR_CNT]; + if (g_mipiCsihandle[cntlr->devNo].cntlr == NULL) { + (void)OsalMutexInit(&g_mipiCsihandle[cntlr->devNo].lock); + (void)OsalMutexInit(&(cntlr->lock)); + + g_mipiCsihandle[cntlr->devNo].cntlr = cntlr; // 初始化MipiCsiHandle成员 + g_mipiCsihandle[cntlr->devNo].priv = NULL; + cntlr->device = device; // 使HdfDeviceObject与MipiCsiHandle可以相互转化的前提 + device->service = &(cntlr->service); // 使HdfDeviceObject与MipiCsiHandle可以相互转化的前提 + cntlr->priv = NULL; + HDF_LOGI("%s: success.", __func__); + + return HDF_SUCCESS; + } - 器件属性值与核心层MipiCsiCntlr 成员的默认值或限制范围有密切关系,deviceNode信息与驱动入口注册相关。 + HDF_LOGE("%s: cntlr already exists.", __func__); + return HDF_FAILURE; + } + ``` - >![icon-note.gif](../public_sys-resources/icon-note.gif) **说明:**
- >本例中MIPI控制器配置属性在源文件中,没有新增配置文件,驱动适配者如有需要,可在device_info.hcs文件的deviceNode增加deviceMatchAttr字段,同时新增mipicsi_config.hcs文件,并使其match_attr字段与之相同。 + - Release函数开发参考 - device_info.hcs配置参考 + 入参: - ```c - root { - device_info { - match_attr = "hdf_manager"; - platform :: host { - hostName = "platform_host"; - priority = 50; - device_mipi_csi:: device { - device0 :: deviceNode { - policy = 0; - priority = 160; - permission = 0644; - moduleName = "HDF_MIPI_RX"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。 - serviceName = "HDF_MIPI_RX"; // 【必要且唯一】驱动对外发布服务的名称 - } - } - } + HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。 + + 返回值: + + 无 + + 函数说明: + + 该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源,该函数中需包含释放内存和删除控制器等操作。 + + >![icon-note.gif](../public_sys-resources/icon-note.gif) **说明:**
+ >所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。 + + ```c + static void Hi35xxMipiCsiRelease(struct HdfDeviceObject *device) + { + struct MipiCsiCntlr *cntlr = NULL; + ...... + cntlr = MipiCsiCntlrFromDevice(device); // 这里有HdfDeviceObject到MipiCsiCntlr的强制转化 + // return (device == NULL) ? NULL : (struct MipiCsiCntlr *)device->service; + ...... + + OsalSpinDestroy(&cntlr->ctxLock); + #ifdef MIPICSI_VFS_SUPPORT + MipiCsiDevModuleExit(cntlr->devNo); + #endif + MipiRxDrvExit(); // 【必要】对设备所占资源的释放 + MipiCsiUnregisterCntlr(&g_mipiCsi); // 空函数 + g_mipiCsi.priv = NULL; + + HDF_LOGI("%s: unload mipi csi driver success!", __func__); } - } - ``` - -2. 完成器件属性文件的配置之后,下一步请实例化驱动入口。 - - 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HdfDriverEntry结构体的函数指针成员需要被驱动适配者操作函数填充,HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组,方便调用。 - - 一般在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 - - MIPI CSI驱动入口参考 - - ```c - struct HdfDriverEntry g_mipiCsiDriverEntry = { - .moduleVersion = 1, - .Init = Hi35xxMipiCsiInit, // 见Init开发参考 - .Release = Hi35xxMipiCsiRelease, // 见Release开发参考 - .moduleName = "HDF_MIPI_RX", // 【必要】需要与device_info.hcs 中保持一致 - }; - HDF_INIT(g_mipiCsiDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 - ``` - -3. 完成驱动入口注册之后,最后一步就是以核心层MipiCsiCntlr对象的初始化为核心,实现HdfDriverEntry成员函数(Bind,Init,Release)。 - - MipiCsiCntlr对象的初始化包括驱动适配者自定义结构体(用于传递参数和数据)和实例化MipiCsiCntlr成员MipiCsiCntlrMethod(让用户可以通过接口来调用驱动底层函数)。 - - - 自定义结构体参考 - - 从驱动的角度看,自定义结构体是参数和数据的载体,一般来说,config文件中的数值也会用来初始化结构体成员,本例的mipicsi器件属性在源文件中,故基本成员结构与MipiCsiCntlr无太大差异。 - - ```c - typedef struct { - /** 数据类型:8/10/12/14/16位 */ - DataType inputDataType; - /** MIPI波分复用模式 */ - MipiWdrMode wdrMode; - /** laneId: -1 - 禁用 */ - short laneId[MIPI_LANE_NUM]; - - union { - /** 用于 HI_MIPI_WDR_MODE_DT */ - short dataType[WDR_VC_NUM]; - }; - } MipiDevAttr; - - typedef struct { - /** 设备号 */ - uint8_t devno; - /** 输入模式: MIPI/LVDS/SUBLVDS/HISPI/DC */ - InputMode inputMode; - MipiDataRate dataRate; - /** MIPI Rx设备裁剪区域(与原始传感器输入图像大小相对应) */ - ImgRect imgRect; - - union { - MipiDevAttr mipiAttr; - LvdsDevAttr lvdsAttr; - }; - } ComboDevAttr; - - /* MipiCsiCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。 */ - struct MipiCsiCntlr { - /** 当驱动程序绑定到HDF框架时,将发送此控制器提供的服务。 */ - struct IDeviceIoService service; - /** 当驱动程序绑定到HDF框架时,将传入设备端指针。 */ - struct HdfDeviceObject *device; - /** 设备号 */ - unsigned int devNo; - /** 控制器提供的所有接口 */ - struct MipiCsiCntlrMethod *ops; - /** 对于控制器调试的所有接口,如果未实现驱动程序,则需要null。 */ - struct MipiCsiCntlrDebugMethod *debugs; - /** 控制器上下文参数变量 */ - MipiDevCtx ctx; - /** 访问控制器上下文参数变量时锁定 */ - OsalSpinlock ctxLock; - /** 操作控制器时锁定方法 */ - struct OsalMutex lock; - /** 匿名数据指针,用于存储csi设备结构。 */ - void *priv; - }; - ``` - - - MipiCsiCntlr成员钩子函数结构体MipiCsiCntlrMethod的实例化 - - >![](../public_sys-resources/icon-note.gif) **说明:**
- >其他成员在Init函数中初始化。 - - ```c - static struct MipiCsiCntlrMethod g_method = { - .setComboDevAttr = Hi35xxSetComboDevAttr, - .setPhyCmvmode = Hi35xxSetPhyCmvmode, - .setExtDataType = Hi35xxSetExtDataType, - .setHsMode = Hi35xxSetHsMode, - .enableClock = Hi35xxEnableClock, - .disableClock = Hi35xxDisableClock, - .resetRx = Hi35xxResetRx, - .unresetRx = Hi35xxUnresetRx, - .enableSensorClock = Hi35xxEnableSensorClock, - .disableSensorClock = Hi35xxDisableSensorClock, - .resetSensor = Hi35xxResetSensor, - .unresetSensor = Hi35xxUnresetSensor - }; - ``` - - - Init函数开发参考 - - 入参: - - HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。 - - 返回值: - - HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 - - **表2** HDF_STATUS返回值描述 - - | 状态(值) | 问题描述 | - | :--------------------- | :----------: | - | HDF_ERR_INVALID_OBJECT | 无效对象 | - | HDF_ERR_MALLOC_FAIL | 内存分配失败 | - | HDF_ERR_INVALID_PARAM | 无效参数 | - | HDF_ERR_IO | I/O 错误 | - | HDF_SUCCESS | 执行成功 | - | HDF_FAILURE | 执行失败 | - - 函数说明: - - MipiCsiCntlrMethod的实例化对象的挂载,调用MipiCsiRegisterCntlr,以及其他驱动适配者自定义初始化操作。 - - ```c - static int32_t Hi35xxMipiCsiInit(struct HdfDeviceObject *device) - { - int32_t ret; - - HDF_LOGI("%s: enter!", __func__); - g_mipiCsi.priv = NULL; // g_mipiTx是定义的全局变量 - // static struct MipiCsiCntlr g_mipiCsi = { - // .devNo = 0 - // }; - g_mipiCsi.ops = &g_method; // MipiCsiCntlrMethod的实例化对象的挂载 - #ifdef CONFIG_HI_PROC_SHOW_SUPPORT - g_mipiCsi.debugs = &g_debugMethod; - #endif - ret = MipiCsiRegisterCntlr(&g_mipiCsi, device); // 【必要】调用核心层函数和g_mipiTx初始化核心层全局变量 - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: [MipiCsiRegisterCntlr] failed!", __func__); - return ret; - } - - ret = MipiRxDrvInit(); // 【必要】驱动适配者对设备的初始化,形式不限。 - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: [MipiRxDrvInit] failed.", __func__); - return ret; - } - #ifdef MIPICSI_VFS_SUPPORT - ret = MipiCsiDevModuleInit(g_mipiCsi.devNo); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: [MipiCsiDevModuleInit] failed!", __func__); - return ret; - } - #endif - - OsalSpinInit(&g_mipiCsi.ctxLock); - HDF_LOGI("%s: load mipi csi driver success!", __func__); - - return ret; - } - - /* mipi_csi_core.c核心层 */ - int32_t MipiCsiRegisterCntlr(struct MipiCsiCntlr *cntlr, struct HdfDeviceObject *device) - { - ... - /* 定义的全局变量:static struct MipiCsiHandle g_mipiCsihandle[MAX_CNTLR_CNT]; */ - if (g_mipiCsihandle[cntlr->devNo].cntlr == NULL) { - (void)OsalMutexInit(&g_mipiCsihandle[cntlr->devNo].lock); - (void)OsalMutexInit(&(cntlr->lock)); - - g_mipiCsihandle[cntlr->devNo].cntlr = cntlr; // 初始化MipiCsiHandle成员 - g_mipiCsihandle[cntlr->devNo].priv = NULL; - cntlr->device = device; // 使HdfDeviceObject与MipiCsiHandle可以相互转化的前提 - device->service = &(cntlr->service); // 使HdfDeviceObject与MipiCsiHandle可以相互转化的前提 - cntlr->priv = NULL; - HDF_LOGI("%s: success.", __func__); - - return HDF_SUCCESS; - } - - HDF_LOGE("%s: cntlr already exists.", __func__); - return HDF_FAILURE; - } - ``` - - - Release函数开发参考 - - 入参: - - HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。 - - 返回值: - - 无 - - 函数说明: - - 该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源,该函数中需包含释放内存和删除控制器等操作。 - - >![icon-note.gif](../public_sys-resources/icon-note.gif) **说明:**
- >所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。 - - ```c - static void Hi35xxMipiCsiRelease(struct HdfDeviceObject *device) - { - struct MipiCsiCntlr *cntlr = NULL; - ... - cntlr = MipiCsiCntlrFromDevice(device); // 这里有HdfDeviceObject到MipiCsiCntlr的强制转化 - // return (device == NULL) ? NULL : (struct MipiCsiCntlr *)device->service; - ... - - OsalSpinDestroy(&cntlr->ctxLock); - #ifdef MIPICSI_VFS_SUPPORT - MipiCsiDevModuleExit(cntlr->devNo); - #endif - MipiRxDrvExit(); // 【必要】对设备所占资源的释放 - MipiCsiUnregisterCntlr(&g_mipiCsi); // 空函数 - g_mipiCsi.priv = NULL; - - HDF_LOGI("%s: unload mipi csi driver success!", __func__); - } - ``` + ``` + +4. 驱动调试 + + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈。 diff --git a/zh-cn/device-dev/driver/driver-platform-mipidsi-des.md b/zh-cn/device-dev/driver/driver-platform-mipidsi-des.md index 73b158b58f3380dc511e95cff958e58959b3af01..17d6b5e220b92aec55d60b2b3e91f3b1c4877f94 100644 --- a/zh-cn/device-dev/driver/driver-platform-mipidsi-des.md +++ b/zh-cn/device-dev/driver/driver-platform-mipidsi-des.md @@ -10,41 +10,43 @@ MIPI DSI具备高速模式和低速模式两种工作模式,全部数据通道 图1显示了简化的DSI接口。从概念上看,符合DSI的接口与基于DBI-2和DPI-2标准的接口具有相同的功能。它向外围设备传输像素或命令数据,并且可以从外围设备读取状态或像素信息。主要区别在于,DSI对所有像素数据、命令和事件进行序列化,而在传统接口中,这些像素数据、命令和事件通常需要附加控制信号才能在并行数据总线上传输。 -**图1** DSI发送、接收接口 +**图 1** DSI发送、接收接口 -![image](figures/DSI发送-接收接口.png "DSI发送-接收接口") +![DSI发送、接收接口](figures/DSI发送-接收接口.png) DSI标准对应D-PHY、DSI、DCS规范,可分为四层: - PHY Layer - 定义了传输媒介,输入/输出电路和和时钟和信号机制。PHY层指定传输介质(电导体)、输入/输出电路和从串行比特流中捕获“1”和“0”的时钟机制。这一部分的规范记录了传输介质的特性、信号的电气参数以及时钟与数据通道之间的时序关系。在DSI链路的发送端,并行数据、信号事件和命令按照包组织在协议层转换为包。协议层附加包协议信息和报头,然后通过Lane Management层向PHY发送完整的字节。数据由PHY进行序列化,并通过串行链路发送。DSI链路的接收端执行与发送端相反的操作,将数据包分解为并行的数据、信号事件和命令。如果有多个Lane, Lane管理层将字节分配给单独的物理层,每个Lane一个PHY。 + 定义了传输媒介,输入/输出电路和和时钟和信号机制。PHY层指定传输介质(电导体)、输入/输出电路和从串行比特流中捕获“1”和“0”的时钟机制。这一部分的规范记录了传输介质的特性、信号的电气参数以及时钟与数据通道之间的时序关系。在DSI链路的发送端,并行数据、信号事件和命令按照包组织在协议层转换为包。协议层附加包协议信息和报头,然后通过Lane Management层向PHY发送完整的字节。数据由PHY进行序列化,并通过串行链路发送。DSI链路的接收端执行与发送端相反的操作,将数据包分解为并行的数据、信号事件和命令。如果有多个Lane, Lane管理层将字节分配给单独的物理层,每个Lane一个PHY。 - Lane Management层 - 负责发送和收集数据流到每条Lane。数据Lane的三种操作模式 :espace mode,High-Speed(Burst) mode,Control mode。 + 负责发送和收集数据流到每条Lane。数据Lane的三种操作模式 :espace mode,High-Speed(Burst) mode,Control mode。 - Low Level Protocol层 - 定义了如何组帧和解析以及错误检测等。 + 定义了如何组帧和解析以及错误检测等。 - Application层 - 描述高层编码和解析数据流。这一层描述了数据流中包含的数据的更高级的编码和解释。根据显示子系统架构的不同,它可能由具有指定格式的像素或编码的位流组成,或者由显示模块内的显示控制器解释的命令组成。DSI规范描述了像素值、位流、命令和命令参数到包集合中的字节的映射。 + 描述高层编码和解析数据流。这一层描述了数据流中包含的数据的更高级的编码和解释。根据显示子系统架构的不同,它可能由具有指定格式的像素或编码的位流组成,或者由显示模块内的显示控制器解释的命令组成。DSI规范描述了像素值、位流、命令和命令参数到包集合中的字节的映射。 ### 运作机制 MIPI DSI软件模块各分层的作用为: - 接口层:提供打开设备、写入数据和关闭设备的接口。 + - 核心层:主要提供绑定设备、初始化设备以及释放设备的能力。 + - 适配层:实现其它具体的功能。 ![](../public_sys-resources/icon-note.gif) **说明:**
核心层可以调用接口层的函数,核心层通过钩子函数调用适配层函数,从而适配层可以间接的调用接口层函数,但是不可逆转接口层调用适配层函数。 -**图2** DSI无服务模式结构图 +**图 2** DSI无服务模式结构图 -![image](figures/无服务模式结构图.png "DSI无服务模式结构图") +![DSI无服务模式结构图](figures/无服务模式结构图.png) ### 约束与限制 @@ -62,7 +64,7 @@ MIPI DSI模块提供的主要接口如表1所示,具体API详见//drivers/hdf_ **表1** MIPI DSI API接口功能介绍 -| 功能分类 | 接口名 | +| 功能分类 | 接口名 | | -------- | -------- | | DevHandle MipiDsiOpen(uint8_t id) | 获取MIPI DSI操作句柄 | | void MipiDsiClose(DevHandle handle) | 释放MIPI DSI操作句柄 | @@ -77,25 +79,23 @@ MIPI DSI模块提供的主要接口如表1所示,具体API详见//drivers/hdf_ 使用MIPI DSI的一般流程如下图所示。 -**图3** MIPI DSI使用流程图 - -![image](figures/MIPI-DSI使用流程图.png "MIPI-DSI使用流程图") +**图 3** MIPI DSI使用流程图 +![MIPI DSI使用流程图](figures/MIPI-DSI使用流程图.png) #### 获取MIPI DSI操作句柄 在进行MIPI DSI进行通信前,首先要调用MipiDsiOpen获取操作句柄,该函数会返回指定通道ID的操作句柄。 - ```c DevHandle MipiDsiOpen(uint8_t id); ``` - **表2** MipiDsiOpen的参数和返回值描述 +**表 2** MipiDsiOpen的参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| id | MIPI DSI通道ID | +| id | uint8_t类型,MIPI DSI通道ID | | **返回值** | **返回值描述** | | NULL | 获取失败 | | 设备句柄 | 获取到指令通道的操作句柄, 类型为DevHandle | @@ -104,186 +104,180 @@ DevHandle MipiDsiOpen(uint8_t id); ```c -DevHandle mipiDsiHandle = NULL; /* 设备句柄 */ -chnId = 0; /* MIPI DSI通道ID */ +DevHandle mipiDsiHandle = NULL; // 设备句柄 +chnId = 0; // MIPI DSI通道ID -/* 获取操作句柄 */ +// 获取操作句柄 mipiDsiHandle = MipiDsiOpen(chnId); if (mipiDsiHandle == NULL) { - HDF_LOGE("MipiDsiOpen: failed\n"); - return; + HDF_LOGE("MipiDsiOpen: mipi dsi open fail.\n"); + return NULL; } ``` - #### MIPI DSI相应配置 - 写入MIPI DSI配置 - - ```c - int32_t MipiDsiSetCfg(DevHandle handle, struct MipiCfg *cfg); - ``` - - **表3** MipiDsiSetCfg的参数和返回值描述 - - | **参数** | **参数描述** | - | -------- | -------- | - | handle | 操作句柄 | - | cfg | MIPI DSI相应配置buf 指针 | - | **返回值** | **返回值描述** | - | 0 | 设置成功 | - | 负数 | 设置失败 | - - ```c - int32_t ret; - struct MipiCfg cfg = {0}; - - /* 当前对接的屏幕配置如下 */ - cfg.lane = DSI_4_LANES; - cfg.mode = DSI_CMD_MODE; - cfg.burstMode = VIDEO_NON_BURST_MODE_SYNC_EVENTS; - cfg.format = FORMAT_RGB_24_BIT; - cfg.pixelClk = 174; - cfg.phyDataRate = 384; - cfg.timingInfo.hsaPixels = 50; - cfg.timingInfo.hbpPixels = 55; - cfg.timingInfo.hlinePixels = 1200; - cfg.timingInfo.yResLines = 1800; - cfg.timingInfo.vbpLines = 33; - cfg.timingInfo.vsaLines = 76; - cfg.timingInfo.vfpLines = 120; - cfg.timingInfo.xResPixels = 1342; - /* 写入配置数据 */ - ret = MipiDsiSetCfg(mipiDsiHandle, &cfg); - if (ret != 0) { - HDF_LOGE("%s: SetMipiCfg fail! ret=%d\n", __func__, ret); - return -1; - } - ``` + ```c + int32_t MipiDsiSetCfg(DevHandle handle, struct MipiCfg *cfg); + ``` -- 获取当前MIPI DSI的配置 + **表 3** MipiDsiSetCfg的参数和返回值描述 - ```c - int32_t MipiDsiGetCfg(DevHandle handle, struct MipiCfg *cfg); - ``` + | **参数** | **参数描述** | + | -------- | -------- | + | handle | DevHandle类型,操作句柄 | + | cfg | 结构体指针类型,MIPI DSI相应配置buf 指针 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | 设置MIPI DSI配置成功 | + | 负数 | 设置MIPI DSI配置失败 | + + ```c + int32_t ret; + struct MipiCfg cfg = {0}; - **表4** MipiDsiGetCfg的参数和返回值描述 - - | **参数** | **参数描述** | - | -------- | -------- | - | handle | 操作句柄 | - | cfg | MIPI DSI相应配置buf 指针 | - | **返回值** | **返回值描述** | - | 0 | 获取成功 | - | 负数 | 获取失败 | + // 当前对接的屏幕配置如下 + cfg.lane = DSI_4_LANES; + cfg.mode = DSI_CMD_MODE; + cfg.burstMode = VIDEO_NON_BURST_MODE_SYNC_EVENTS; + cfg.format = FORMAT_RGB_24_BIT; + cfg.pixelClk = 174; + cfg.phyDataRate = 384; + cfg.timingInfo.hsaPixels = 50; + cfg.timingInfo.hbpPixels = 55; + cfg.timingInfo.hlinePixels = 1200; + cfg.timingInfo.yResLines = 1800; + cfg.timingInfo.vbpLines = 33; + cfg.timingInfo.vsaLines = 76; + cfg.timingInfo.vfpLines = 120; + cfg.timingInfo.xResPixels = 1342; + // 写入配置数据 + ret = MipiDsiSetCfg(mipiDsiHandle, &cfg); + if (ret != HDF_SUCCESS) { + HDF_LOGE("MipiDsiSetCfg: set mipi cfg fail, ret:%d\n", ret); + return ret; + } + ``` - - ```c - int32_t ret; - struct MipiCfg cfg; - memset(&cfg, 0, sizeof(struct MipiCfg)); - ret = MipiDsiGetCfg(mipiDsiHandle, &cfg); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: GetMipiCfg fail!\n", __func__); - return HDF_FAILURE; - } - ``` +- 获取当前MIPI DSI的配置 + ```c + int32_t MipiDsiGetCfg(DevHandle handle, struct MipiCfg *cfg); + ``` + + **表 4** MipiDsiGetCfg的参数和返回值描述 + + | **参数** | **参数描述** | + | -------- | -------- | + | handle | DevHandle类型,操作句柄 | + | cfg | 结构体指针,MIPI DSI相应配置buf 指针 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | 获取当前MIPI DSI的配置成功 | + | 负数 | 获取当前MIPI DSI的配置失败 | + + ```c + int32_t ret; + struct MipiCfg cfg; + memset(&cfg, 0, sizeof(struct MipiCfg)); + ret = MipiDsiGetCfg(mipiDsiHandle, &cfg); + if (ret != HDF_SUCCESS) { + HDF_LOGEMipiDsiGetCfg: get mipi cfg fail, ret:%d!\n", ret); + return ret; + } + ``` #### 发送/回读控制指令 - 发送指令 - - ```c - int32_t MipiDsiTx(PalHandle handle, struct DsiCmdDesc *cmd); - ``` - **表5** MipiDsiTx的参数和返回值描述 - - | **参数** | **参数描述** | - | -------- | -------- | - | handle | 操作句柄 | - | cmd | 需要发送的指令数据指针 | - | **返回值** | **返回值描述** | - | 0 | 发送成功 | - | 负数 | 发送失败 | + ```c + int32_t MipiDsiTx(PalHandle handle, struct DsiCmdDesc *cmd); + ``` - - ```c - int32_t ret; - struct DsiCmdDesc *cmd = OsalMemCalloc(sizeof(struct DsiCmdDesc)); - if (cmd == NULL) { - return HDF_FAILURE; - } - cmd->dtype = DTYPE_DCS_WRITE; - cmd->dlen = 1; - cmd->payload = OsalMemCalloc(sizeof(uint8_t)); - if (cmd->payload == NULL) { - HdfFree(cmd); - return HDF_FAILURE; - } - *(cmd->payload) = DTYPE_GEN_LWRITE; - MipiDsiSetLpMode(mipiHandle); - ret = MipiDsiTx(mipiHandle, cmd); - MipiDsiSetHsMode(mipiHandle); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: PalMipiDsiTx fail! ret=%d\n", __func__, ret); - HdfFree(cmd->payload); - HdfFree(cmd); - return HDF_FAILURE; - } - HdfFree(cmd->payload); - HdfFree(cmd); - ``` + **表 5** MipiDsiTx的参数和返回值描述 -- 回读指令 - - ```c - int32_t MipiDsiRx(DevHandle handle, struct DsiCmdDesc *cmd, uint32_t readLen, uint8_t *out); - ``` + | **参数** | **参数描述** | + | -------- | -------- | + | handle | DevHandle类型,操作句柄 | + | cmd | 结构体指针类型,需要发送的指令数据指针 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | 发送成功 | + | 负数 | 发送失败 | + + ```c + int32_t ret; + struct DsiCmdDesc *cmd = OsalMemCalloc(sizeof(struct DsiCmdDesc)); + if (cmd == NULL) { + return HDF_FAILURE; + } + cmd->dtype = DTYPE_DCS_WRITE; + cmd->dlen = 1; + cmd->payload = OsalMemCalloc(sizeof(uint8_t)); + if (cmd->payload == NULL) { + HdfFree(cmd); + return HDF_FAILURE; + } + *(cmd->payload) = DTYPE_GEN_LWRITE; + MipiDsiSetLpMode(mipiHandle); + ret = MipiDsiTx(mipiHandle, cmd); + MipiDsiSetHsMode(mipiHandle); + if (ret != HDF_SUCCESS) { + HDF_LOGE("MipiDsiTx: mipi dsi tx fail, ret:%d\n", ret); + HdfFree(cmd->payload); + HdfFree(cmd); + return ret; + } + HdfFree(cmd->payload); + HdfFree(cmd); + ``` - **表6** MipiDsiRx的参数和返回值描述 +- 回读指令 - | **参数** | **参数描述** | - | -------- | -------- | - | handle | 操作句柄 | - | cmd | 需要回读的指令数据指针 | - | readLen | 期望回读的数据长度 | - | out | 回读的数据buf指针 | - | **返回值** | **返回值描述** | - | 0 | 获取成功 | - | 负数 | 获取失败 | + ```c + int32_t MipiDsiRx(DevHandle handle, struct DsiCmdDesc *cmd, uint32_t readLen, uint8_t *out); + ``` + **表 6** MipiDsiRx的参数和返回值描述 - ```c - int32_t ret; - uint8_t readVal = 0; + | **参数** | **参数描述** | + | -------- | -------- | + | handle | DevHandle类型,操作句柄 | + | cmd | 结构体指针类型,需要回读的指令数据指针 | + | readLen | uint32_t类型,期望回读的数据长度 | + | out | uint8_t类型指针,回读的数据 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | 获取成功 | + | 负数 | 获取失败 | - struct DsiCmdDesc *cmdRead = OsalMemCalloc(sizeof(struct DsiCmdDesc)); - if (cmdRead == NULL) { - return HDF_FAILURE; - } - cmdRead->dtype = DTYPE_DCS_READ; - cmdRead->dlen = 1; - cmdRead->payload = OsalMemCalloc(sizeof(uint8_t)); - if (cmdRead->payload == NULL) { - HdfFree(cmdRead); - return HDF_FAILURE; - } - *(cmdRead->payload) = DDIC_REG_STATUS; - MipiDsiSetLpMode(mipiDsiHandle); - ret = MipiDsiRx(mipiDsiHandle, cmdRead, sizeof(readVal), &readVal); - MipiDsiSetHsMode(mipiDsiHandle); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: MipiDsiRx fail! ret=%d\n", __func__, ret); - HdfFree(cmdRead->payload); - HdfFree(cmdRead); - return HDF_FAILURE; - } - HdfFree(cmdRead->payload); - HdfFree(cmdRead); - ``` + ```c + int32_t ret; + uint8_t readVal = 0; + + struct DsiCmdDesc *cmdRead = OsalMemCalloc(sizeof(struct DsiCmdDesc)); + if (cmdRead == NULL) { + return HDF_FAILURE; + } + cmdRead->dtype = DTYPE_DCS_READ; + cmdRead->dlen = 1; + cmdRead->payload = OsalMemCalloc(sizeof(uint8_t)); + if (cmdRead->payload == NULL) { + HdfFree(cmdRead); + return HDF_FAILURE; + } + *(cmdRead->payload) = DDIC_REG_STATUS; + MipiDsiSetLpMode(mipiDsiHandle); + ret = MipiDsiRx(mipiDsiHandle, cmdRead, sizeof(readVal), &readVal); + MipiDsiSetHsMode(mipiDsiHandle); + if (ret != HDF_SUCCESS) { + HDF_LOGE("MipiDsiRx: mipi dsi rx fail, ret:%d\n", ret); + HdfFree(cmdRead->payload); + HdfFree(cmdRead); + return HDF_FAILURE; + } + HdfFree(cmdRead->payload); + HdfFree(cmdRead); + ``` #### 释放MIPI DSI操作句柄 @@ -296,17 +290,16 @@ void MipiDsiClose(DevHandle handle); 该函数会释放掉由MipiDsiOpen申请的资源。 - **表7** MipiDsiClose的参数和返回值描述 +**表 7** MipiDsiClose的参数和返回值描述 | 参数 | 参数描述 | | -------- | -------- | -| handle | MIPI DSI操作句柄 | +| handle | DevHandle类型,MIPI DSI操作句柄 | ```c -MipiDsiClose(mipiHandle); /* 释放掉MIPI DSI操作句柄 */ +MipiDsiClose(mipiHandle); // 释放掉MIPI DSI操作句柄 ``` - ## 使用实例 本例拟对Hi3516DV300开发板上MIPI DSI设备进行操作。 @@ -314,95 +307,105 @@ MipiDsiClose(mipiHandle); /* 释放掉MIPI DSI操作句柄 */ MIPI DSI完整的使用示例如下所示: ```c -#include "hdf.h" +#include "hdf_log.h" #include "mipi_dsi_if.h" +#include "osal_mem.h" + +#define DTYPE_DCS_WRITE 0x05 +#define DTYPE_DCS_READ 0x06 +#define DTYPE_GEN_LWRITE 0x29 +#define DDIC_REG_STATUS 0x0A -void PalMipiDsiTestSample(void) + +int32_t PalMipiDsiTestSample(void) { uint8_t chnId; int32_t ret; DevHandle mipiDsiHandle = NULL; - /* 设备通道编号 */ + // 设备通道编号 chnId = 0; - /* 获取操作句柄 */ + // 获取操作句柄 mipiDsiHandle = MipiDsiOpen(chnId); if (mipiDsiHandle == NULL) { HDF_LOGE("MipiDsiOpen: failed!\n"); - return; + return HDF_FAILURE; } - /* 配置相应参数 */ + // 配置相应参数 struct MipiCfg cfg = {0}; - cfg.lane = DSI_4_LANES; - cfg.mode = DSI_CMD_MODE; - cfg.burstMode = VIDEO_NON_BURST_MODE_SYNC_EVENTS; + cfg.lane = DSI_2_LANES; + cfg.mode = DSI_VIDEO_MODE; cfg.format = FORMAT_RGB_24_BIT; - cfg.pixelClk = 174; - cfg.phyDataRate = 384; - cfg.timingInfo.hsaPixels = 50; - cfg.timingInfo.hbpPixels = 55; - cfg.timingInfo.hlinePixels = 1200; - cfg.timingInfo.yResLines = 1800; - cfg.timingInfo.vbpLines = 33; - cfg.timingInfo.vsaLines = 76; - cfg.timingInfo.vfpLines = 120; - cfg.timingInfo.xResPixels = 1342; - /* 写入配置数据 */ + cfg.burstMode = VIDEO_BURST_MODE; + cfg.timing.xPixels = 480; // 480: width + cfg.timing.hsaPixels = 10; // 10: horizontal sync porch + cfg.timing.hbpPixels = 20; // 20: horizontal back porch + cfg.timing.hlinePixels = 530; // 530: horizontal total width + cfg.timing.vsaLines = 2; // 2: vertiacl sync width + cfg.timing.vbpLines = 14; // 14: vertiacl back porch + cfg.timing.vfpLines = 16; // 16: vertiacl front porch + cfg.timing.ylines = 960; // 960: height + cfg.timing.edpiCmdSize = 0; // 0 : no care + cfg.pixelClk = 31546; // 31546: pixel clk + cfg.phyDataRate = 379; // 379: mipi clk + // 写入配置数据 ret = MipiDsiSetCfg(mipiDsiHandle, &cfg); if (ret != 0) { - HDF_LOGE("%s: SetMipiCfg fail! ret=%d\n", __func__, ret); - return; + HDF_LOGE("PalMipiDsiTestSample: set mipi dsi cfg fail, ret:%d\n", ret); + return ret; } - /* 发送PANEL初始化指令 */ + // 发送PANEL初始化指令 struct DsiCmdDesc *cmd = OsalMemCalloc(sizeof(struct DsiCmdDesc)); if (cmd == NULL) { - return; + return -1; } - cmd->dtype = DTYPE_DCS_WRITE; - cmd->dlen = 1; + cmd->dataType = DTYPE_DCS_WRITE; + cmd->dataLen = 1; cmd->payload = OsalMemCalloc(sizeof(uint8_t)); if (cmd->payload == NULL) { - HdfFree(cmd); - return; + OsalMemFree(cmd); + return -1; } *(cmd->payload) = DTYPE_GEN_LWRITE; - MipiDsiSetLpMode(mipiHandle); - ret = MipiDsiTx(mipiHandle, cmd); - MipiDsiSetHsMode(mipiHandle); + MipiDsiSetLpMode(mipiDsiHandle); + ret = MipiDsiTx(mipiDsiHandle, cmd); + MipiDsiSetHsMode(mipiDsiHandle); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: MipiDsiTx fail! ret=%d\n", __func__, ret); - HdfFree(cmd->payload); - HdfFree(cmd); - return; + HDF_LOGE("PalMipiDsiTestSample: mipi dsi tx fail, ret:%d\n", ret); + OsalMemFree(cmd->payload); + OsalMemFree(cmd); + return ret; } - HdfFree(cmd->payload); - HdfFree(cmd); - /* 回读panel状态寄存器 */ + OsalMemFree(cmd->payload); + OsalMemFree(cmd); + // 回读panel状态寄存器 uint8_t readVal = 0; struct DsiCmdDesc *cmdRead = OsalMemCalloc(sizeof(struct DsiCmdDesc)); if (cmdRead == NULL) { - return; + return -1; } - cmdRead->dtype = DTYPE_DCS_READ; - cmdRead->dlen = 1; + cmdRead->dataType = DTYPE_DCS_READ; + cmdRead->dataLen = 1; cmdRead->payload = OsalMemCalloc(sizeof(uint8_t)); if (cmdRead->payload == NULL) { - HdfFree(cmdRead); - return; + OsalMemFree(cmdRead); + return -1; } *(cmdRead->payload) = DDIC_REG_STATUS; MipiDsiSetLpMode(mipiDsiHandle); ret = MipiDsiRx(mipiDsiHandle, cmdRead, sizeof(readVal), &readVal); MipiDsiSetHsMode(mipiDsiHandle); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: MipiDsiRx fail! ret=%d\n", __func__, ret); - HdfFree(cmdRead->payload); - HdfFree(cmdRead); - return; + HDF_LOGE("PalMipiDsiTestSample: mipi dsi rx fail, ret:%d\n", ret); + OsalMemFree(cmdRead->payload); + OsalMemFree(cmdRead); + return ret; } - HdfFree(cmdRead->payload); - HdfFree(cmdRead); - /* 释放MIPI DSI设备句柄 */ - MipiDsiClose(handle); + OsalMemFree(cmdRead->payload); + OsalMemFree(cmdRead); + HDF_LOGD("PalMipiDsiTestSample: mipi dsi tests end"); + // 释放MIPI DSI设备句柄 + MipiDsiClose(mipiDsiHandle); + return ret; } ``` diff --git a/zh-cn/device-dev/driver/driver-platform-mipidsi-develop.md b/zh-cn/device-dev/driver/driver-platform-mipidsi-develop.md index a83b5de1313a6454aafbd089ee54b4fc80086d57..7fd7ee7fc14683eb7f34df6a07549b6f96ba18e7 100755 --- a/zh-cn/device-dev/driver/driver-platform-mipidsi-develop.md +++ b/zh-cn/device-dev/driver/driver-platform-mipidsi-develop.md @@ -10,46 +10,47 @@ MIPI DSI具备高速模式和低速模式两种工作模式,全部数据通道 图1显示了简化的DSI接口。从概念上看,符合DSI的接口与基于DBI-2和DPI-2标准的接口具有相同的功能。它向外围设备传输像素或命令数据,并且可以从外围设备读取状态或像素信息。主要区别在于,DSI对所有像素数据、命令和事件进行序列化,而在传统接口中,这些像素数据、命令和事件通常需要附加控制信号才能在并行数据总线上传输。 -**图1** DSI发送、接收接口 +**图 1** DSI发送、接收接口 -![image](figures/DSI发送-接收接口.png "DSI发送-接收接口") +![DSI发送、接收接口](figures/DSI发送-接收接口.png) DSI标准对应D-PHY、DSI、DCS规范,可分为四层: - PHY Layer - 定义了传输媒介,输入/输出电路和和时钟和信号机制。PHY层指定传输介质(电导体)、输入/输出电路和从串行比特流中捕获“1”和“0”的时钟机制。这一部分的规范记录了传输介质的特性、信号的电气参数以及时钟与数据通道之间的时序关系。在DSI链路的发送端,并行数据、信号事件和命令按照包组织在协议层转换为包。协议层附加包协议信息和报头,然后通过Lane Management层向PHY发送完整的字节。数据由PHY进行序列化,并通过串行链路发送。DSI链路的接收端执行与发送端相反的操作,将数据包分解为并行的数据、信号事件和命令。如果有多个Lane, Lane管理层将字节分配给单独的物理层,每个Lane一个PHY。 + 定义了传输媒介,输入/输出电路和和时钟和信号机制。PHY层指定传输介质(电导体)、输入/输出电路和从串行比特流中捕获“1”和“0”的时钟机制。这一部分的规范记录了传输介质的特性、信号的电气参数以及时钟与数据通道之间的时序关系。在DSI链路的发送端,并行数据、信号事件和命令按照包组织在协议层转换为包。协议层附加包协议信息和报头,然后通过Lane Management层向PHY发送完整的字节。数据由PHY进行序列化,并通过串行链路发送。DSI链路的接收端执行与发送端相反的操作,将数据包分解为并行的数据、信号事件和命令。如果有多个Lane, Lane管理层将字节分配给单独的物理层,每个Lane一个PHY。 - Lane Management层 - 负责发送和收集数据流到每条Lane。数据Lane的三种操作模式 :espace mode, High-Speed(Burst) mode, Control mode 。 + 负责发送和收集数据流到每条Lane。数据Lane的三种操作模式 :espace mode, High-Speed(Burst) mode, Control mode 。 - Low Level Protocol层 - 定义了如何组帧和解析以及错误检测等。 + 定义了如何组帧和解析以及错误检测等。 - Application层 - 描述高层编码和解析数据流。这一层描述了数据流中包含的数据的更高级的编码和解释。根据显示子系统架构的不同,它可能由具有指定格式的像素或编码的位流组成,或者由显示模块内的显示控制器解释的命令组成。DSI规范描述了像素值、位流、命令和命令参数到包集合中的字节的映射。 + 描述高层编码和解析数据流。这一层描述了数据流中包含的数据的更高级的编码和解释。根据显示子系统架构的不同,它可能由具有指定格式的像素或编码的位流组成,或者由显示模块内的显示控制器解释的命令组成。DSI规范描述了像素值、位流、命令和命令参数到包集合中的字节的映射。 ### 运作机制 MIPI DSI软件模块各分层的作用为: - 接口层:提供打开设备、写入数据和关闭设备的接口。 + - 核心层:主要提供绑定设备、初始化设备以及释放设备的能力。 + - 适配层:实现其它具体的功能。 ![](../public_sys-resources/icon-note.gif) **说明:**
核心层可以调用接口层的函数,核心层通过钩子函数调用适配层函数,从而适配层可以间接的调用接口层函数,但是不可逆转接口层调用适配层函数。 +**图 2** DSI无服务模式结构图 - **图2** DSI无服务模式结构图 - - ![image](figures/无服务模式结构图.png "DSI无服务模式结构图") +![DSI无服务模式结构图](figures/无服务模式结构图.png) ## 开发指导 - ### 场景介绍 +### 场景介绍 MIPI DSI仅是一个软件层面的概念,主要工作是MIPI DSI资源管理。开发者可以通过使用提供的提供的操作接口,实现DSI资源管理。当驱动开发者需要将MIPI DSI设备适配到OpenHarmony时,需要进行MIPI DSI驱动适配,下文将介绍如何进行MIPI DSI驱动适配。 @@ -73,222 +74,243 @@ struct MipiDsiCntlrMethod { // 核心层结构体的成员函数 }; ``` - **表1** MipiDsiCntlrMethod成员的钩子函数功能说明 +**表 1** MipiDsiCntlrMethod成员的钩子函数功能说明 | 成员函数 | 入参 | 出参 | 返回状态 | 功能 | | -------- | -------- | -------- | -------- | -------- | | setCntlrCfg | cntlr:结构体指针,MipiDsi控制器 | 无 | HDF_STATUS相关状态 | 设置控制器参数 | | setCmd | cntlr:结构体指针,MipiDsi控制器
cmd:结构体指针,指令传入值 | 无 | HDF_STATUS相关状态 | 向显示设备发送指令 | -| getCmd | cntlr:结构体指针,MipiDsi控制器
cmd:传入的命令描述结构体指针
readLen:读取的数据大小 | out:结构体指针,用于存储读取的数据 | HDF_STATUS相关状态 | 通过发送指令读取数据 | +| getCmd | cntlr:结构体指针,MipiDsi控制器
cmd:传入的命令描述结构体指针
readLen:读取的数据大小 | out:uint8_t类型指针,用于存储读取的数据 | HDF_STATUS相关状态 | 通过发送指令读取数据 | | toHs | cntlr:结构体指针,MipiDsi控制器 | 无 | HDF_STATUS相关状态 | 设置为高速模式 | | toLp | cntlr:结构体指针,MipiDsi控制器 | 无 | HDF_STATUS相关状态 | 设置为低电模式 | ### 开发步骤 -MIPI DSI模块适配的三个必选环节是配置属性文件,实例化驱动入口,以及实例化核心层接口函数。 +MIPI DSI模块适配包含以下四个步骤: -1. 配置属性文件 - - 在device_info.hcs文件中添加deviceNode描述。 - - 【可选】添加mipidsi_config.hcs器件属性文件。 +1. 实例化驱动入口 -2. 实例化驱动入口 - - 实例化HdfDriverEntry结构体成员。 - - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 - -3. 实例化MIPIDSI控制器对象 - - 初始化MipiDsiCntlr成员。 - - 实例化MipiDsiCntlr成员MipiDsiCntlrMethod。 - > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 实例化MipiDsiCntlr成员MipiDsiCntlrMethod,其定义和成员说明见[接口说明](#接口说明)。 - -4. 驱动调试 + - 实例化HdfDriverEntry结构体成员。 - 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。 + - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 +2. 配置属性文件 -### 开发实例 + - 在device_info.hcs文件中添加deviceNode描述。 -下方将以mipi_tx_hi35xx.c为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。 + - 【可选】添加mipi_dsi_config.hcs器件属性文件。 -1. 一般来说,驱动开发首先需要mipicsi_config.hcs配置文件,在其中配置器件属性,并在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。deviceNode与配置属性的对应关系是依靠deviceMatchAttr字段来完成的。只有当deviceNode下的deviceMatchAttr字段与配置属性文件中的match_attr字段完全相同时,驱动才能正确读取配置数据。 +3. 实例化MIPI DSI控制器对象 - 器件属性值与核心层MipiDsiCntlr成员的默认值或限制范围有密切关系,deviceNode信息与驱动入口注册相关。 + - 初始化MipiDsiCntlr成员。 - 但本例中MIPI控制器无需配置额外属性,驱动适配者如有需要,则需要在device_info.hcs文件的deviceNode增加deviceMatchAttr信息,以及增加mipidsi_config.hcs文件。 + - 实例化MipiDsiCntlr成员MipiDsiCntlrMethod。 - device_info.hcs 配置参考: - - ```c - root { - device_info { - match_attr = "hdf_manager"; - platform :: host { - hostName = "platform_host"; - priority = 50; - device_mipi_dsi:: device { - device0 :: deviceNode { - policy = 0; - priority = 150; - permission = 0644; - moduleName = "HDF_MIPI_TX"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。 - serviceName = "HDF_MIPI_TX"; // 【必要且唯一】驱动对外发布服务的名称。 - } - } - } - } - } - ``` - -2. 完成器件属性文件的配置之后,下一步请实例化驱动入口。 - - 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HdfDriverEntry结构体的函数指针成员需要被驱动适配者操作函数填充,HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组,方便调用。 - - 一般在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 - - MIPI DSI驱动入口参考: - - ```c - struct HdfDriverEntry g_mipiTxDriverEntry = { - .moduleVersion = 1, - .Init = Hi35xxMipiTxInit, // 见Init开发参考 - .Release = Hi35xxMipiTxRelease, // 见Release开发参考 - .moduleName = "HDF_MIPI_TX", // 【必要】需要与device_info.hcs 中保持一致。 - }; - HDF_INIT(g_mipiTxDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 - ``` + > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 实例化MipiDsiCntlr成员MipiDsiCntlrMethod,其定义和成员说明见[接口说明](#接口说明)。 -3. 完成驱动入口注册之后,下一步就是以核心层MipiDsiCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化MipiDsiCntlr成员MipiDsiCntlrMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。 +4. 驱动调试 - - 自定义结构体参考 + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。 - 从驱动的角度看,自定义结构体是参数和数据的载体,一般来说,config文件中的数值也会用来初始化结构体成员,但本例的mipidsi无器件属性文件,故基本成员结构与MipiDsiCntlr无太大差异。 - - ```c - typedef struct { - unsigned int devno; // 设备号 - short laneId[LANE_MAX_NUM]; // Lane号 - OutPutModeTag outputMode; // 输出模式选择:刷新模式,命令行模式或视频流模式 - VideoModeTag videoMode; // 显示设备的同步模式 - OutputFormatTag outputFormat; // 输出DSI图像数据格式:RGB或YUV - SyncInfoTag syncInfo; // 时序相关的设置 - unsigned int phyDataRate; // 数据速率,单位Mbps - unsigned int pixelClk; // 时钟,单位KHz - } ComboDevCfgTag; - - /* MipiDsiCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。 */ - struct MipiDsiCntlr { - struct IDeviceIoService service; - struct HdfDeviceObject *device; - unsigned int devNo; // 设备号 - struct MipiCfg cfg; - struct MipiDsiCntlrMethod *ops; - struct OsalMutex lock; - void *priv; - }; - ``` - - MipiDsiCntlr成员钩子函数结构体MipiDsiCntlrMethod的实例化,其他成员在Init函数中初始化。 - - - ```c - static struct MipiDsiCntlrMethod g_method = { - .setCntlrCfg = Hi35xxSetCntlrCfg, - .setCmd = Hi35xxSetCmd, - .getCmd = Hi35xxGetCmd, - .toHs = Hi35xxToHs, - .toLp = Hi35xxToLp, - }; - ``` - - Init函数开发参考 +### 开发实例 - 入参: +下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/mipi_dsi/mipi_tx_hi35xx.c驱动为示例,展示需要厂商提供哪些内容来完整实现设备功能。 - HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。 +1. 实例化驱动入口 - 返回值: + 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HdfDriverEntry结构体的函数指针成员需要被驱动适配者操作函数填充,HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组,方便调用。 - HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 + 一般在加载驱动时HDF框架会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 - **表2** HDF_STATUS返回值描述 + MIPI DSI驱动入口参考: + + ```c + struct HdfDriverEntry g_mipiTxDriverEntry = { + .moduleVersion = 1, + .Init = Hi35xxMipiTxInit, // 挂接MIPI DSI模块Init实例化 + .Release = Hi35xxMipiTxRelease, // 挂接MIPI DSI模块Release实例化 + .moduleName = "HDF_MIPI_TX", // 【必要且与HCS文件中里面的moduleName匹配】 + }; + HDF_INIT(g_mipiTxDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 + ``` + +2. 配置属性文件 + 一般来说,驱动开发首先需要mipi_dsi_config.hcs配置文件,在其中配置器件属性,并在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。deviceNode与配置属性的对应关系是依靠deviceMatchAttr字段来完成的。只有当deviceNode下的deviceMatchAttr字段与配置属性文件中的match_attr字段完全相同时,驱动才能正确读取配置数据。器件属性值与核心层MipiDsiCntlr成员的默认值或限制范围有密切关系,deviceNode信息与驱动入口注册相关。但本例中MIPI DSI控制器无需配置额外属性,驱动适配者如有需要,则需要在device_info.hcs文件的deviceNode增加deviceMatchAttr信息,以及增加mipi_dsi_config.hcs文件。 + + 无服务模式device_info.hcs文件中设备节点也代表着一个设备对象,如果存在多个设备对象,则按需添加,注意服务名与驱动私有数据匹配的关键字名称必须唯一。其中各项参数如表2所示: + + **表 2** device_info.hcs节点参数说明 + + | 成员名 | 值 | + | -------- | -------- | + | policy | 驱动服务发布的策略,MIPI DSI控制器具体配置为0,表示驱动不需要发布服务 | + | priority | 驱动启动优先级(0-200),值越大优先级越低。MIPI DSI控制器具体配置为150 | + | permission | 驱动创建设备节点权限,MIPI DSI控制器具体配置为0664 | + | moduleName | 驱动名称,MIPI DSI控制器固定为HDF_MIPI_TX | + | serviceName | 驱动对外发布服务的名称,MIPI DSI控制器服务名设置为HDF_MIPI_TX | + | deviceMatchAttr | 驱动私有数据匹配的关键字,MIPI DSI控制器没有使用,可忽略 | + + device_info.hcs 配置参考: + + ```c + root { + device_info { + match_attr = "hdf_manager"; + platform :: host { + hostName = "platform_host"; + priority = 50; + device_mipi_dsi:: device { + device0 :: deviceNode { + policy = 0; + priority = 150; + permission = 0644; + moduleName = "HDF_MIPI_TX"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。 + serviceName = "HDF_MIPI_TX"; // 【必要且唯一】驱动对外发布服务的名称。 + } + } + } + } + } + ``` + +3. 实例化MIPI DSI控制器对象 + + 完成驱动入口注册之后,下一步就是以核心层MipiDsiCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化MipiDsiCntlr成员MipiDsiCntlrMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。 + + - 自定义结构体参考 + + 从驱动的角度看,自定义结构体是参数和数据的载体,一般来说,config文件中的数值也会用来初始化结构体成员,但本例的MIPI DSI无器件属性文件,故基本成员结构与MipiDsiCntlr无太大差异。 + + ```c + typedef struct { + unsigned int devno; // 设备号 + short laneId[LANE_MAX_NUM]; // Lane号 + OutPutModeTag outputMode; // 输出模式选择:刷新模式,命令行模式或视频流模式 + VideoModeTag videoMode; // 显示设备的同步模式 + OutputFormatTag outputFormat; // 输出DSI图像数据格式:RGB或YUV + SyncInfoTag syncInfo; // 时序相关的设置 + unsigned int phyDataRate; // 数据速率,单位Mbps + unsigned int pixelClk; // 时钟,单位KHz + } ComboDevCfgTag; + + struct MipiDsiCntlr { + struct IDeviceIoService service; + struct HdfDeviceObject *device; + unsigned int devNo; // 设备号 + struct MipiCfg cfg; + struct MipiDsiCntlrMethod *ops; + struct OsalMutex lock; + void *priv; + }; + ``` + + - MipiDsiCntlr成员钩子函数结构体MipiDsiCntlrMethod的实例化。 + + ```c + static struct MipiDsiCntlrMethod g_method = { + .setCntlrCfg = Hi35xxSetCntlrCfg, + .setCmd = Hi35xxSetCmd, + .getCmd = Hi35xxGetCmd, + .toHs = Hi35xxToHs, + .toLp = Hi35xxToLp, + }; + ``` + + - Init函数开发参考 + + 入参: + + HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。 + + 返回值: + + HDF_STATUS相关状态 (表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS的定义)。 + + **表 3** HDF_STATUS相关状态说明 + + | 状态(值) | 问题描述 | + | -------- | -------- | + | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | + | HDF_ERR_MALLOC_FAIL | 内存分配失败 | + | HDF_ERR_IO | I/O 错误 | + | HDF_SUCCESS | 初始化成功 | + | HDF_FAILURE | 初始化失败 | + + 函数说明: + + MipiDsiCntlrMethod的实例化对象的挂载,调用MipiDsiRegisterCntlr,以及其他驱动适配者自定义初始化操作。 + + ```c + static int32_t Hi35xxMipiTxInit(struct HdfDeviceObject *device) + { + int32_t ret; + g_mipiTx.priv = NULL; // g_mipiTx是定义的全局变量 + // static struct MipiDsiCntlr g_mipiTx { + // .devNo=0 + // }; + g_mipiTx.ops = &g_method; // MipiDsiCntlrMethod的实例化对象的挂载 + ret = MipiDsiRegisterCntlr(&g_mipiTx, device); // 【必要】调用核心层函数和g_mipiTx初始化核心层全局变量 + ...... + return MipiTxDrvInit(0); // 【必要】驱动适配者对设备的初始化,形式不限 + } + + // mipi_dsi_core.c核心层 + int32_t MipiDsiRegisterCntlr(struct MipiDsiCntlr *cntlr, struct HdfDeviceObject *device) + { + ...... + // 定义的全局变量:static struct MipiDsiHandle g_mipiDsihandle[MAX_CNTLR_CNT]; + if (g_mipiDsihandle[cntlr->devNo].cntlr == NULL) { + (void)OsalMutexInit(&g_mipiDsihandle[cntlr->devNo].lock); + (void)OsalMutexInit(&(cntlr->lock)); + + g_mipiDsihandle[cntlr->devNo].cntlr = cntlr; // 初始化MipiDsiHandle成员 + g_mipiDsihandle[cntlr->devNo].priv = NULL; + cntlr->device = device; // 使HdfDeviceObject与MipiDsiHandle可以相互转化的前提 + device->service = &(cntlr->service); // 使HdfDeviceObject与MipiDsiHandle可以相互转化的前提 + cntlr->priv = NULL; + ...... + return HDF_SUCCESS; + } + ...... + return HDF_FAILURE; + } + ``` + + - Release函数开发参考 + + 入参: + + HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。 + + 返回值: + + 无。 + + 函数说明: + + 该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源,该函数中需包含释放内存和删除控制器等操作。 + + > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。 - | 状态(值) | 问题描述 | - | -------- | -------- | - | HDF_ERR_INVALID_OBJECT | 无效对象 | - | HDF_ERR_MALLOC_FAIL | 内存分配失败 | - | HDF_ERR_INVALID_PARAM | 无效参数 | - | HDF_ERR_IO | I/O 错误 | - | HDF_SUCCESS | 执行成功 | - | HDF_FAILURE | 执行失败 | + ```c + static void Hi35xxMipiTxRelease(struct HdfDeviceObject *device) + { + struct MipiDsiCntlr *cntlr = NULL; + ...... + cntlr = MipiDsiCntlrFromDevice(device); // 这里有HdfDeviceObject到MipiDsiCntlr的强制转化 + // return (device == NULL) ? NULL : (struct MipiDsiCntlr *)device->service; + ...... + MipiTxDrvExit(); // 【必要】对设备所占资源的释放 + MipiDsiUnregisterCntlr(&g_mipiTx); // 空函数 + g_mipiTx.priv = NULL; + HDF_LOGI("%s: unload mipi_tx driver 1212!", __func__); + } + ``` - 函数说明: - - MipiDsiCntlrMethod的实例化对象的挂载,调用MipiDsiRegisterCntlr,以及其他驱动适配者自定义初始化操作。 +4. 驱动调试 - - ```c - static int32_t Hi35xxMipiTxInit(struct HdfDeviceObject *device) - { - int32_t ret; - g_mipiTx.priv = NULL; // g_mipiTx是定义的全局变量 - // static struct MipiDsiCntlr g_mipiTx { - // .devNo=0 - //}; - g_mipiTx.ops = &g_method; // MipiDsiCntlrMethod的实例化对象的挂载 - ret = MipiDsiRegisterCntlr(&g_mipiTx, device); // 【必要】调用核心层函数和g_mipiTx初始化核心层全局变量 - ... - return MipiTxDrvInit(0); // 【必要】驱动适配者对设备的初始化,形式不限 - } - - /* mipi_dsi_core.c核心层 */ - int32_t MipiDsiRegisterCntlr(struct MipiDsiCntlr *cntlr, struct HdfDeviceObject *device) - { - ... - /* 定义的全局变量:static struct MipiDsiHandle g_mipiDsihandle[MAX_CNTLR_CNT]; */ - if (g_mipiDsihandle[cntlr->devNo].cntlr == NULL) { - (void)OsalMutexInit(&g_mipiDsihandle[cntlr->devNo].lock); - (void)OsalMutexInit(&(cntlr->lock)); - - g_mipiDsihandle[cntlr->devNo].cntlr = cntlr; // 初始化MipiDsiHandle成员 - g_mipiDsihandle[cntlr->devNo].priv = NULL; - cntlr->device = device; // 使HdfDeviceObject与MipiDsiHandle可以相互转化的前提 - device->service = &(cntlr->service); // 使HdfDeviceObject与MipiDsiHandle可以相互转化的前提 - cntlr->priv = NULL; - ... - return HDF_SUCCESS; - } - ... - return HDF_FAILURE; - } - ``` - - Release函数开发参考 - - 入参: - - HdfDeviceObject是整个驱动对外暴露的接口参数,具备HCS配置文件的信息。 - - 返回值: - - 无。 - - 函数说明: - - 该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源,该函数中需包含释放内存和删除控制器等操作。 - - > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。 - - ```c - static void Hi35xxMipiTxRelease(struct HdfDeviceObject *device) - { - struct MipiDsiCntlr *cntlr = NULL; - ... - cntlr = MipiDsiCntlrFromDevice(device); // 这里有HdfDeviceObject到MipiDsiCntlr的强制转化 - // return (device == NULL) ? NULL : (struct MipiDsiCntlr *)device->service; - ... - MipiTxDrvExit(); // 【必要】对设备所占资源的释放 - MipiDsiUnregisterCntlr(&g_mipiTx); // 空函数 - g_mipiTx.priv = NULL; - HDF_LOGI("%s: unload mipi_tx driver 1212!", __func__); - } - ``` + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈。 diff --git a/zh-cn/device-dev/driver/driver-platform-mmc-develop.md b/zh-cn/device-dev/driver/driver-platform-mmc-develop.md index 956b94fdedb0126b0d2261a004a714f1d6c0f9aa..1529e91ded8531b1c5e32d9348b6ed82f7a8da85 100755 --- a/zh-cn/device-dev/driver/driver-platform-mmc-develop.md +++ b/zh-cn/device-dev/driver/driver-platform-mmc-develop.md @@ -14,11 +14,11 @@ MMC、SD、SDIO总线,其总线规范类似,都是从MMC总线规范演化 - SD卡(Secure Digital Memory Card) - SD卡即安全数码卡。它是在MMC的基础上发展而来,SD卡强调数据的安全安全,可以设定存储内容的使用权限,防止数据被他人复制。在数据传输和物理规范上,SD卡(24mm\*32mm\*2.1mm,比MMC卡更厚一点),向前兼容了MMC卡。所有支持SD卡的设备也支持MMC卡。 + SD卡即安全数码卡。它是在MMC的基础上发展而来,SD卡强调数据的安全安全,可以设定存储内容的使用权限,防止数据被他人复制。在数据传输和物理规范上,SD卡(24mm\*32mm\*2.1mm,比MMC卡更厚一点),向前兼容了MMC卡。所有支持SD卡的设备也支持MMC卡。 - SDIO(Secure Digital Input and Output) - 即安全数字输入输出接口。SDIO是在SD规范的标准上定义的一种外设接口,它相较于SD规范增加了低速标准,可以用最小的硬件开销支持低速I/O。SDIO接口兼容以前的SD内存卡,也可以连接SDIO接口的设备。 + 即安全数字输入输出接口。SDIO是在SD规范的标准上定义的一种外设接口,它相较于SD规范增加了低速标准,可以用最小的硬件开销支持低速I/O。SDIO接口兼容以前的SD内存卡,也可以连接SDIO接口的设备。 ### 运作机制 @@ -27,17 +27,20 @@ MMC、SD、SDIO总线,其总线规范类似,都是从MMC总线规范演化 独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为: - 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。 + - device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。 MMC模块各分层作用: - 接口层提供打开MMC设备、检查MMC控制器是否存在设备、关闭MMC设备的接口。 + - 核心层主要提供MMC控制器、移除和管理的能力,还有公共控制器业务。通过钩子函数与适配层交互。 + - 适配层主要是将钩子函数的功能实例化,实现具体的功能。 -**图1** MMC独立服务模式结构图 +**图 1** MMC独立服务模式结构图 -![img1](figures/独立服务模式结构图.png "MMC独立服务模式结构图") +![MMC独立服务模式结构图](figures/独立服务模式结构图.png) ## 开发指导 @@ -71,15 +74,15 @@ struct MmcCntlrOps { }; ``` -**表1** MmcCntlrOps结构体成员的钩子函数功能说明 +**表 1** MmcCntlrOps结构体成员的钩子函数功能说明 | 成员函数 | 入参 | 返回值 | 功能 | | -------- | -------- | -------- | -------- | | doRequest | cntlr:结构体指针,核心层MMC控制器
cmd:结构体指针,传入命令值 | HDF_STATUS相关状态 | request相应处理 | -| setClock | cntlr:结构体指针,核心层MMC控制器
clock:时钟传入值 | HDF_STATUS相关状态 | 设置时钟频率 | +| setClock | cntlr:结构体指针,核心层MMC控制器
clock:uint32_t类型,时钟传入值 | HDF_STATUS相关状态 | 设置时钟频率 | | setPowerMode | cntlr:结构体指针,核心层MMC控制器
mode:枚举值(见MmcPowerMode定义),功耗模式 | HDF_STATUS相关状态 | 设置功耗模式 | -| setBusWidth | cntlr:核心层结构体指针,核心层MMMC控制器
width:枚举值(见MmcBusWidth定义),总线带宽 | HDF_STATUS相关状态 | 设置总线带宽 | -| setBusTiming | cntlr:结构体指针,核心层MMC控制器
timing:枚举值(见MmcBusTiming定义),总线时序 | HDF_STATUS相关状态 | 设置总线时序 | +| setBusWidth | cntlr:核心层结构体指针,核心层MMMC控制器
width:枚举类型(见MmcBusWidth定义),总线带宽 | HDF_STATUS相关状态 | 设置总线带宽 | +| setBusTiming | cntlr:结构体指针,核心层MMC控制器
timing:枚举类型(见MmcBusTiming定义),总线时序 | HDF_STATUS相关状态 | 设置总线时序 | | setSdioIrq | cntlr:结构体指针,核心层MMC控制器
enable:布尔值,控制中断 | HDF_STATUS相关状态 | 使能/去使能SDIO中断 | | hardwareReset | cntlr:结构体指针,核心层MMC控制器 | HDF_STATUS相关状态 | 复位硬件 | | systemInit | cntlr:结构体指针,核心层MMC控制器 | HDF_STATUS相关状态 | 系统初始化 | @@ -95,335 +98,351 @@ struct MmcCntlrOps { MMC模块适配包含以下四个步骤: -- 实例化驱动入口。 -- 配置属性文件。 -- 实例化MMC控制器对象。 -- 驱动调试。 +- 实例化驱动入口 + +- 配置属性文件 + +- 实例化MMC控制器对象 + +- 驱动调试 ### 开发实例 下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/mmc/himci_v200/himci.c驱动为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。 -1. 实例化驱动入口。 - - 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 - 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 - - MMC驱动入口开发参考: - - ```c - struct HdfDriverEntry g_mmcDriverEntry = { - .moduleVersion = 1, - .Bind = HimciMmcBind, // 见Bind参考 - .Init = HimciMmcInit, // 见Init参考 - .Release = HimciMmcRelease, // 见Release参考 - .moduleName = "hi3516_mmc_driver", // 【必要且与HCS文件中里面的moduleName匹配】 - }; - HDF_INIT(g_mmcDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 - ``` - -2. 配置属性文件。 - - 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以三个MMC控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息。器件属性值与核心层MmcCntlr成员的默认值或限制范围有密切关系,需要在mmc_config.hcs中配置器件属性。 - - - device_info.hcs 配置参考: - - 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。 - - ```c - root { - device_info { - match_attr = "hdf_manager"; - platform :: host { - hostName = "platform_host"; - priority = 50; - device_mmc:: device { - device0 :: deviceNode { // 驱动的DeviceNode节点 - policy = 2; // policy字段是驱动服务发布的策略,如果需要面向用户态,则为2 - priority = 10; // 驱动启动优先级 - permission = 0644; // 驱动创建设备节点权限 - moduleName = "hi3516_mmc_driver"; // 【必要】用于指定驱动名称,需要与驱动Entry中的moduleName一致。 - serviceName = "HDF_PLATFORM_MMC_0"; // 【必要】驱动对外发布服务的名称,必须唯一。 - deviceMatchAttr = "hi3516_mmc_emmc"; // 【必要】用于配置控制器私有数据,要与mmc_config.hcs中对应控制器保持一致。 - } - device1 :: deviceNode { - policy = 1; - priority = 20; - permission = 0644; - moduleName = "hi3516_mmc_driver"; - serviceName = "HDF_PLATFORM_MMC_1"; - deviceMatchAttr = "hi3516_mmc_sd"; // SD类型 - } - device2 :: deviceNode { - policy = 1; - priority = 30; - permission = 0644; - moduleName = "hi3516_mmc_driver"; - serviceName = "HDF_PLATFORM_MMC_2"; - deviceMatchAttr = "hi3516_mmc_sdio"; // SDIO类型 - } - ... - } - } - } - } - ``` - - - mmc_config.hcs配置参考: - - 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/mmc/mmc_config.hcs文件配置器件属性,其中配置参数如下: - - ```c - root { - platform { - mmc_config { - template mmc_controller { // 配置模板,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值。 - match_attr = ""; - voltDef = 0; // MMC默认电压,0代表3.3V,1代表1.8V,2代表1.2V - freqMin = 50000; // 【必要】最小频率值 - freqMax = 100000000; // 【必要】最大频率值 - freqDef = 400000; // 【必要】默认频率值 - maxBlkNum = 2048; // 【必要】最大的block号 - maxBlkSize = 512; // 【必要】最大的block个数 - ocrDef = 0x300000; // 【必要】工作电压设置相关 - caps2 = 0; // 【必要】属性寄存器相关,见mmc_caps.h中MmcCaps2定义。 - regSize = 0x118; // 【必要】寄存器位宽 - hostId = 0; // 【必要】主机号 - regBasePhy = 0x10020000; // 【必要】寄存器物理基地址 - irqNum = 63; // 【必要】中断号 - devType = 2; // 【必要】模式选择:EMMC、SD、SDIO、COMBO - caps = 0x0001e045; // 【必要】属性寄存器相关,见mmc_caps.h中MmcCaps定义。 - } - controller_0x10100000 :: mmc_controller { - match_attr = "hi3516_mmc_emmc"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致 - hostId = 0; - regBasePhy = 0x10100000; - irqNum = 96; - devType = 0; // eMMC类型 - caps = 0xd001e045; - caps2 = 0x60; - } - controller_0x100f0000 :: mmc_controller { - match_attr = "hi3516_mmc_sd"; - hostId = 1; - regBasePhy = 0x100f0000; - irqNum = 62; - devType = 1; // SD类型 - caps = 0xd001e005; - } - controller_0x10020000 :: mmc_controller { - match_attr = "hi3516_mmc_sdio"; - hostId = 2; - regBasePhy = 0x10020000; - irqNum = 63; - devType = 2; // SDIO类型 - caps = 0x0001e04d; - } - } - } - } - ``` - - 需要注意的是,新增mmc_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。 - - ```c - #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/mmc/mmc_config.hcs" // 配置文件相对路径 - ``` - -3. 实例化MMC控制器对象。 - - 完成配置属性文件之后,下一步就是以核心层MmcCntlr对象的初始化为核心,包括驱动适配自定义结构体(传递参数和数据),实例化MmcCntlr成员MmcCntlrOps(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。 - - - 驱动适配者自定义结构体参考 - - 从驱动的角度看,自定义结构体是参数和数据的载体,而且mmc_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象。 - - ```c - struct HimciHost { - struct MmcCntlr *mmc; // 【必要】核心层控制对象 - struct MmcCmd *cmd; // 【必要】核心层结构体,传递命令,相关命令见枚举量MmcCmdCode - void *base; // 地址映射需要,寄存器基地址 - enum HimciPowerStatus powerStatus; - uint8_t *alignedBuff; - uint32_t buffLen; - struct scatterlist dmaSg; - struct scatterlist *sg; - uint32_t dmaSgNum; - DMA_ADDR_T dmaPaddr; - uint32_t *dmaVaddr; - uint32_t irqNum; - bool isTuning; - uint32_t id; - struct OsalMutex mutex; - bool waitForEvent; - HIMCI_EVENT himciEvent; - }; - // MmcCntlr是核心层控制器结构体,其中的成员在Bind函数中会被赋值。 - struct MmcCntlr { - struct IDeviceIoService service; - struct HdfDeviceObject *hdfDevObj; - struct PlatformDevice device; - struct OsalMutex mutex; - struct OsalSem released; - uint32_t devType; - struct MmcDevice *curDev; - struct MmcCntlrOps *ops; - struct PlatformQueue *msgQueue; - uint16_t index; - uint16_t voltDef; - uint32_t vddBit; - uint32_t freqMin; - uint32_t freqMax; - uint32_t freqDef; - union MmcOcr ocrDef; - union MmcCaps caps; - union MmcCaps2 caps2; - uint32_t maxBlkNum; - uint32_t maxBlkSize; - uint32_t maxReqSize; - bool devPlugged; - bool detecting; - void *priv; - }; - ``` - - - MmcCntlr成员钩子函数结构体MmcCntlrOps的实例化,其他成员在Bind函数中初始化。 - - ```c - static struct MmcCntlrOps g_himciHostOps = { - .request = HimciDoRequest, - .setClock = HimciSetClock, - .setPowerMode = HimciSetPowerMode, - .setBusWidth = HimciSetBusWidth, - .setBusTiming = HimciSetBusTiming, - .setSdioIrq = HimciSetSdioIrq, - .hardwareReset = HimciHardwareReset, - .systemInit = HimciSystemInit, - .setEnhanceStrobe = HimciSetEnhanceStrobe, - .switchVoltage = HimciSwitchVoltage, - .devReadOnly = HimciDevReadOnly, - .devPlugged = HimciCardPlugged, - .devBusy = HimciDevBusy, - .tune = HimciTune, - .rescanSdioDev = HimciRescanSdioDev, - }; - ``` - - - Bind函数开发参考 - - 入参: - - HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 - - 返回值: - - HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 - - **表2** Bind函数说明 - - | 状态(值) | 问题描述 | - | -------- | -------- | - | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | - | HDF_ERR_MALLOC_FAIL | 内存分配失败 | - | HDF_ERR_INVALID_PARAM | 参数非法 | - | HDF_ERR_IO | I/O 错误 | - | HDF_SUCCESS | 初始化成功 | - | HDF_FAILURE | 初始化失败 | - - 函数说明: - - MmcCntlr、HimciHost、HdfDeviceObject之间互相赋值,方便其他函数可以相互转化,初始化自定义结构体HimciHost对象,初始化MmcCntlr成员,调用核心层MmcCntlrAdd函数,完成MMC控制器的添加。 - - ```c - static int32_t HimciMmcBind(struct HdfDeviceObject *obj) - { - struct MmcCntlr *cntlr = NULL; - struct HimciHost *host = NULL; - int32_t ret; - cntlr = (struct MmcCntlr *)OsalMemCalloc(sizeof(struct MmcCntlr)); - host = (struct HimciHost *)OsalMemCalloc(sizeof(struct HimciHost)); - - host->mmc = cntlr; // 【必要】使HimciHost与MmcCntlr可以相互转化的前提 - cntlr->priv = (void *)host; // 【必要】使HimciHost与MmcCntlr可以相互转化的前提 - cntlr->ops = &g_himciHostOps; // 【必要】MmcCntlrOps的实例化对象的挂载 - cntlr->hdfDevObj = obj; // 【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提 - obj->service = &cntlr->service; // 【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提 - ret = MmcCntlrParse(cntlr, obj); // 【必要】 初始化cntlr,失败就goto _ERR。 - ... - ret = HimciHostParse(host, obj); // 【必要】 初始化host对象的相关属性,失败就goto _ERR。 - ... - ret = HimciHostInit(host, cntlr); // 驱动适配者自定义的初始化,失败就goto _ERR。 - ... - ret = MmcCntlrAdd(cntlr); // 调用核心层函数,失败就goto _ERR。 - ... - (void)MmcCntlrAddDetectMsgToQueue(cntlr); // 将卡检测消息添加到队列中。 - HDF_LOGD("HimciMmcBind: success."); - return HDF_SUCCESS; - ERR: - HimciDeleteHost(host); - HDF_LOGD("HimciMmcBind: fail, err = %d.", ret); - return ret; - } - ``` - - - Init函数开发参考 - - 入参: - - HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 - - 返回值: - - HDF_STATUS相关状态。 - - 函数说明: - - 实现ProcMciInit。 - - ```c - static int32_t HimciMmcInit(struct HdfDeviceObject *obj) - { - static bool procInit = false; - (void)obj; - if (procInit == false) { - if (ProcMciInit() == HDF_SUCCESS) { - procInit = true; - HDF_LOGD("HimciMmcInit: proc init success."); - } - } - HDF_LOGD("HimciMmcInit: success."); - return HDF_SUCCESS; - } - ``` - - - Release函数开发参考 - - 入参: - - HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 - - 返回值: - - 无。 - - 函数说明: - - 释放内存和删除控制器等操作,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 - - > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。 - - ```c - static void HimciMmcRelease(struct HdfDeviceObject *obj) - { - struct MmcCntlr *cntlr = NULL; - ... - cntlr = (struct MmcCntlr *)obj->service; // 这里有HdfDeviceObject到MmcCntlr的强制转化,通过service成员,赋值见Bind函数。 - ... - HimciDeleteHost((struct HimciHost *)cntlr->priv); // 驱动适配者自定义的内存释放函数,这里有MmcCntlr到HimciHost的强制转化。 - } - ``` - -4. 驱动调试。 - - 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。 \ No newline at end of file +1. 实例化驱动入口 + + 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 + + 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 + + MMC驱动入口开发参考: + + ```c + struct HdfDriverEntry g_mmcDriverEntry = { + .moduleVersion = 1, + .Bind = HimciMmcBind, // 见Bind参考 + .Init = HimciMmcInit, // 见Init参考 + .Release = HimciMmcRelease, // 见Release参考 + .moduleName = "hi3516_mmc_driver", // 【必要且与HCS文件中里面的moduleName匹配】 + }; + HDF_INIT(g_mmcDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 + ``` + +2. 配置属性文件 + + 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以三个MMC控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息,以及在mmc_config.hcs文件中增加对应的器件属性。器件属性值与核心层MmcCntlr成员的默认值或限制范围有密切关系,需要在mmc_config.hcs中配置器件属性。 + + 独立服务模式的特点是device_info.hcs文件中设备节点代表着一个设备对象,如果存在多个设备对象,则按需添加,注意服务名与驱动私有数据匹配的关键字名称必须唯一。其中各项参数如表2所示: + + **表 2** device_info.hcs节点参数说明 + + | 成员名 | 值 | + | -------- | -------- | + | policy | 驱动服务发布的策略,MMC控制器具体配置为2,表示驱动对内核态和用户态都发布服务 | + | priority | 驱动启动优先级(0-200),值越大优先级越低。MMC控制器控制器具体配置为10 | + | permission | 驱动创建设备节点权限,MMC控制器控制器具体配置为0664 | + | moduleName | 驱动名称,MMC控制器控制器固定为hi3516_mmc_driver | + | serviceName | 驱动对外发布服务的名称,MMC控制器控制器服务名设置为HDF_PLATFORM_MMC_X,X代表MMC控制器号| + | deviceMatchAttr | 驱动私有数据匹配的关键字,MMC控制器控制器设置为hi3516_mmc_X,X代表控制器类型名 | + + - device_info.hcs 配置参考: + + 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。 + + ```c + root { + device_info { + match_attr = "hdf_manager"; + platform :: host { + hostName = "platform_host"; + priority = 50; + device_mmc:: device { + device0 :: deviceNode { // 驱动的DeviceNode节点 + policy = 2; // policy字段是驱动服务发布的策略,如果需要面向用户态,则为2 + priority = 10; // 驱动启动优先级 + permission = 0644; // 驱动创建设备节点权限 + moduleName = "hi3516_mmc_driver"; // 【必要】用于指定驱动名称,需要与驱动Entry中的moduleName一致。 + serviceName = "HDF_PLATFORM_MMC_0"; // 【必要】驱动对外发布服务的名称,必须唯一。 + deviceMatchAttr = "hi3516_mmc_emmc"; // 【必要】用于配置控制器私有数据,要与mmc_config.hcs中对应控制器保持一致。emmc类型。 + } + device1 :: deviceNode { + policy = 1; + priority = 20; + permission = 0644; + moduleName = "hi3516_mmc_driver"; + serviceName = "HDF_PLATFORM_MMC_1"; + deviceMatchAttr = "hi3516_mmc_sd"; // SD类型 + } + device2 :: deviceNode { + policy = 1; + priority = 30; + permission = 0644; + moduleName = "hi3516_mmc_driver"; + serviceName = "HDF_PLATFORM_MMC_2"; + deviceMatchAttr = "hi3516_mmc_sdio"; // SDIO类型 + } + ...... + } + } + } + } + ``` + + - mmc_config.hcs配置参考: + + 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/mmc/mmc_config.hcs文件配置器件属性,其中配置参数如下: + + ```c + root { + platform { + mmc_config { + template mmc_controller { // 配置模板,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值。 + match_attr = ""; + voltDef = 0; // MMC默认电压,0代表3.3V,1代表1.8V,2代表1.2V + freqMin = 50000; // 【必要】最小频率值 + freqMax = 100000000; // 【必要】最大频率值 + freqDef = 400000; // 【必要】默认频率值 + maxBlkNum = 2048; // 【必要】最大的block号 + maxBlkSize = 512; // 【必要】最大block大小 + ocrDef = 0x300000; // 【必要】工作电压设置相关 + caps2 = 0; // 【必要】属性寄存器相关,见mmc_caps.h中MmcCaps2定义。 + regSize = 0x118; // 【必要】寄存器位宽 + hostId = 0; // 【必要】主机号 + regBasePhy = 0x10020000; // 【必要】寄存器物理基地址 + irqNum = 63; // 【必要】中断号 + devType = 2; // 【必要】模式选择:EMMC、SD、SDIO、COMBO + caps = 0x0001e045; // 【必要】属性寄存器相关,见mmc_caps.h中MmcCaps定义。 + } + controller_0x10100000 :: mmc_controller { + match_attr = "hi3516_mmc_emmc"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致 + hostId = 0; + regBasePhy = 0x10100000; + irqNum = 96; + devType = 0; // eMMC类型 + caps = 0xd001e045; + caps2 = 0x60; + } + controller_0x100f0000 :: mmc_controller { + match_attr = "hi3516_mmc_sd"; + hostId = 1; + regBasePhy = 0x100f0000; + irqNum = 62; + devType = 1; // SD类型 + caps = 0xd001e005; + } + controller_0x10020000 :: mmc_controller { + match_attr = "hi3516_mmc_sdio"; + hostId = 2; + regBasePhy = 0x10020000; + irqNum = 63; + devType = 2; // SDIO类型 + caps = 0x0001e04d; + } + } + } + } + ``` + + 需要注意的是,新增mmc_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。 + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/mmc/mmc_config.hcs" // 配置文件相对路径 + ``` + +3. 实例化MMC控制器对象 + + 完成配置属性文件之后,下一步就是以核心层MmcCntlr对象的初始化为核心,包括驱动适配自定义结构体(传递参数和数据),实例化MmcCntlr成员MmcCntlrOps(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。 + + - 驱动适配者自定义结构体参考 + + 从驱动的角度看,自定义结构体是参数和数据的载体,而且mmc_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象。 + + ```c + struct HimciHost { + struct MmcCntlr *mmc; // 【必要】核心层控制对象 + struct MmcCmd *cmd; // 【必要】核心层结构体,传递命令,相关命令见枚举量MmcCmdCode + void *base; // 地址映射需要,寄存器基地址 + enum HimciPowerStatus powerStatus; + uint8_t *alignedBuff; + uint32_t buffLen; + struct scatterlist dmaSg; + struct scatterlist *sg; + uint32_t dmaSgNum; + DMA_ADDR_T dmaPaddr; + uint32_t *dmaVaddr; + uint32_t irqNum; + bool isTuning; + uint32_t id; + struct OsalMutex mutex; + bool waitForEvent; + HIMCI_EVENT himciEvent; + }; + // MmcCntlr是核心层控制器结构体,其中的成员在Bind函数中会被赋值。 + struct MmcCntlr { + struct IDeviceIoService service; + struct HdfDeviceObject *hdfDevObj; + struct PlatformDevice device; + struct OsalMutex mutex; + struct OsalSem released; + uint32_t devType; + struct MmcDevice *curDev; + struct MmcCntlrOps *ops; + struct PlatformQueue *msgQueue; + uint16_t index; + uint16_t voltDef; + uint32_t vddBit; + uint32_t freqMin; + uint32_t freqMax; + uint32_t freqDef; + union MmcOcr ocrDef; + union MmcCaps caps; + union MmcCaps2 caps2; + uint32_t maxBlkNum; + uint32_t maxBlkSize; + uint32_t maxReqSize; + bool devPlugged; + bool detecting; + void *priv; + }; + ``` + + - MmcCntlr成员钩子函数结构体MmcCntlrOps的实例化。 + + ```c + static struct MmcCntlrOps g_himciHostOps = { + .request = HimciDoRequest, + .setClock = HimciSetClock, + .setPowerMode = HimciSetPowerMode, + .setBusWidth = HimciSetBusWidth, + .setBusTiming = HimciSetBusTiming, + .setSdioIrq = HimciSetSdioIrq, + .hardwareReset = HimciHardwareReset, + .systemInit = HimciSystemInit, + .setEnhanceStrobe = HimciSetEnhanceStrobe, + .switchVoltage = HimciSwitchVoltage, + .devReadOnly = HimciDevReadOnly, + .devPlugged = HimciCardPlugged, + .devBusy = HimciDevBusy, + .tune = HimciTune, + .rescanSdioDev = HimciRescanSdioDev, + }; + ``` + + - Bind函数开发参考 + + 入参: + + HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 + + 返回值: + + HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS的定义)。 + + **表 3** HDF_STATUS相关状态说明 + + | 状态(值) | 问题描述 | + | -------- | -------- | + | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | + | HDF_ERR_MALLOC_FAIL | 内存分配失败 | + | HDF_ERR_INVALID_PARAM | 参数非法 | + | HDF_ERR_IO | I/O 错误 | + | HDF_SUCCESS | 初始化成功 | + | HDF_FAILURE | 初始化失败 | + + 函数说明: + MmcCntlr、HimciHost、HdfDeviceObject之间互相赋值,方便其他函数可以相互转化,初始化自定义结构体HimciHost对象,初始化MmcCntlr成员,调用核心层MmcCntlrAdd函数,完成MMC控制器的添加。 + + ```c + static int32_t HimciMmcBind(struct HdfDeviceObject *obj) + { + struct MmcCntlr *cntlr = NULL; + struct HimciHost *host = NULL; + int32_t ret; + cntlr = (struct MmcCntlr *)OsalMemCalloc(sizeof(struct MmcCntlr)); + host = (struct HimciHost *)OsalMemCalloc(sizeof(struct HimciHost)); + + host->mmc = cntlr; // 【必要】使HimciHost与MmcCntlr可以相互转化的前提 + cntlr->priv = (void *)host; // 【必要】使HimciHost与MmcCntlr可以相互转化的前提 + cntlr->ops = &g_himciHostOps; // 【必要】MmcCntlrOps的实例化对象的挂载 + cntlr->hdfDevObj = obj; // 【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提 + obj->service = &cntlr->service; // 【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提 + ret = MmcCntlrParse(cntlr, obj); // 【必要】 初始化cntlr,失败就goto _ERR。 + ...... + ret = HimciHostParse(host, obj); // 【必要】 初始化host对象的相关属性,失败就goto _ERR。 + ...... + ret = HimciHostInit(host, cntlr); // 驱动适配者自定义的初始化,失败就goto _ERR。 + ...... + ret = MmcCntlrAdd(cntlr); // 调用核心层函数,失败就goto _ERR。 + ...... + (void)MmcCntlrAddDetectMsgToQueue(cntlr); // 将卡检测消息添加到队列中。 + HDF_LOGD("HimciMmcBind: success."); + return HDF_SUCCESS; + ERR: + HimciDeleteHost(host); + HDF_LOGD("HimciMmcBind: fail, err = %d.", ret); + return ret; + } + ``` + + - Init函数开发参考 + + 入参: + + HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 + + 返回值: + + HDF_STATUS相关状态。 + + 函数说明: + + 实现ProcMciInit。 + + ```c + static int32_t HimciMmcInit(struct HdfDeviceObject *obj) + { + static bool procInit = false; + (void)obj; + if (procInit == false) { + if (ProcMciInit() == HDF_SUCCESS) { + procInit = true; + HDF_LOGD("HimciMmcInit: proc init success."); + } + } + HDF_LOGD("HimciMmcInit: success."); + return HDF_SUCCESS; + } + ``` + + - Release函数开发参考 + + 入参: + + HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 + + 返回值: + + 无。 + + 函数说明: + + 释放内存和删除控制器等操作,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 + + > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。 + + ```c + static void HimciMmcRelease(struct HdfDeviceObject *obj) + { + struct MmcCntlr *cntlr = NULL; + ...... + cntlr = (struct MmcCntlr *)obj->service; // 这里有HdfDeviceObject到MmcCntlr的强制转化,通过service成员,赋值见Bind函数。 + ...... + HimciDeleteHost((struct HimciHost *)cntlr->priv); // 驱动适配者自定义的内存释放函数,这里有MmcCntlr到HimciHost的强制转化。 + } + ``` + +4. 驱动调试 + + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据读写成功与否等。 diff --git a/zh-cn/device-dev/driver/driver-platform-pin-des.md b/zh-cn/device-dev/driver/driver-platform-pin-des.md index aaa3ee8e27dddec654de040ada8e910ad73b394a..9339aab419f8ef5f23980081c48ff43e17f0d90d 100644 --- a/zh-cn/device-dev/driver/driver-platform-pin-des.md +++ b/zh-cn/device-dev/driver/driver-platform-pin-des.md @@ -9,8 +9,11 @@ PIN即管脚控制器,用于统一管理各SoC的管脚资源,对外提供 PIN接口定义了操作PIN管脚的通用方法集合,包括: - 获取/释放管脚描述句柄:传入管脚名与链表中每个控制器下管脚名进行匹配,匹配则会获取一个管脚描述句柄,操作完PIN管脚后释放该管脚描述句柄。 + - 设置/获取管脚推拉方式:推拉方式可以是上拉、下拉以及悬空。 + - 设置/获取管脚推拉强度:用户可根据实际设置管脚推拉强度大小。 + - 设置/获取管脚功能:通过管脚功能名设置/获取管脚功能,实现管脚复用。 ### 基本概念 @@ -19,11 +22,11 @@ PIN是一个软件层面的概念,目的是为了统一各SoC的PIN管脚管 - SoC(System on Chip) - 系统级芯片,也有称作片上系统,通常是面向特定用途将微处理器、模拟IP核、数字IP核和存储器集成在单一芯片的标准产品。 + 系统级芯片,也有称作片上系统,通常是面向特定用途将微处理器、模拟IP核、数字IP核和存储器集成在单一芯片的标准产品。 - 管脚复用 - 由于芯片自身的引脚数量有限,无法满足日益增多的外接需求。此时可以通过软件层面的寄存器设置,让引脚工作在不同的状态,从而实现相同引脚完成不同功能的目的。 + 由于芯片自身的引脚数量有限,无法满足日益增多的外接需求。此时可以通过软件层面的寄存器设置,让引脚工作在不同的状态,从而实现相同引脚完成不同功能的目的。 ### 运作机制 @@ -34,11 +37,13 @@ PIN是一个软件层面的概念,目的是为了统一各SoC的PIN管脚管 PIN模块各分层作用: - 接口层提供获取PIN管脚、设置PIN管脚推拉方式、获取PIN管脚推拉方式、设置PIN管脚推拉强度、获取PIN管脚推拉强度、设置PIN管脚功能、获取PIN管脚功能、释放PIN管脚的接口。 + - 核心层主要提供PIN管脚资源匹配,PIN管脚控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。 + - 适配层主要是将钩子函数的功能实例化,实现具体的功能。 **图 1** PIN统一服务模式 -![PIN统一服务模式](figures/统一服务模式结构图.png "统一服务模式") +![PIN统一服务模式](figures/统一服务模式结构图.png) ### 约束与限制 @@ -52,21 +57,21 @@ PIN模块仅是一个软件层面的概念,主要工作是管脚资源管理 ### 接口说明 -PIN模块提供的主要接口如表1所示,更多关于接口的介绍请参考对应的API接口文档。 +PIN模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/pin_if.h。 **表 1** PIN驱动API接口功能介绍 -| **接口名** | **描述** | +| **接口名** | **描述** | | ------------------------------------------------------------ | ---------------- | -| DevHandle PinGet(const char *pinName) | 获取管脚描述句柄 | -| void PinPut(DevHandle handle) | 释放管脚描述句柄 | +| DevHandle PinGet(const char \*pinName) | 获取管脚描述句柄 | +| void PinPut(DevHandle handle) | 释放管脚描述句柄 | | int32_t PinSetPull(DevHandle handle, enum PinPullType pullType) | 设置管脚推拉方式 | -| int32_t PinGetPull(DevHandle handle, enum PinPullType *pullType) | 获取管脚推拉方式 | +| int32_t PinGetPull(DevHandle handle, enum PinPullType \*pullType) | 获取管脚推拉方式 | | int32_t PinSetStrength(DevHandle handle, uint32_t strength) | 设置管脚推拉强度 | -| int32_t PinGetStrength(DevHandle handle, uint32_t *strength) | 获取管脚推拉强度 | -| int32_t PinSetFunc(DevHandle handle, const char *funcName) | 设置管脚功能 | -| int32_t PinGetFunc(DevHandle handle, const char **funcName) | 获取管脚功能 | +| int32_t PinGetStrength(DevHandle handle, uint32_t \*strength) | 获取管脚推拉强度 | +| int32_t PinSetFunc(DevHandle handle, const char \*funcName) | 设置管脚功能 | +| int32_t PinGetFunc(DevHandle handle, const char \**funcName) | 获取管脚功能 | >![](../public_sys-resources/icon-note.gif) **说明:**
>本文涉及PIN的所有接口,支持内核态及用户态使用。 @@ -75,8 +80,8 @@ PIN模块提供的主要接口如表1所示,更多关于接口的介绍请参 使用PIN设备的一般流程如图2所示。 - **图 2** PIN使用流程图 -![](figures/PIN使用流程图.png "PIN使用流程图") +**图 2** PIN使用流程图 +![PIN使用流程图](figures/PIN使用流程图.png) #### 获取PIN管脚描述句柄 @@ -90,12 +95,12 @@ DevHandle PinGet(const char *pinName); -| 参数 | 描述 | +| 参数 | 描述 | | ---------- | ----------------------- | -| pinName | 管脚名 | -| **返回值** | **描述** | -| NULL | 获取PIN管脚描述句柄失败 | -| handle | PIN管脚描述句柄 | +| pinName | 字符指针类型,管脚名 | +| **返回值** | **描述** | +| NULL | 获取PIN管脚描述句柄失败 | +| handle | PIN管脚描述句柄 | 假设PIN需要操作的管脚名为P18,获取其管脚描述句柄的示例如下: @@ -106,7 +111,7 @@ char pinName = "P18"; // PIN管脚名 handle = PinGet(pinName); if (handle == NULL) { HDF_LOGE("PinGet: get handle failed!\n"); - return; + return HDF_FAILURE; } ``` @@ -122,13 +127,13 @@ int32_t PinSetPull(DevHandle handle, enum PinPullType pullType); -| 参数 | 描述 | +| 参数 | 描述 | | ---------- | ----------------------- | -| handle | PIN管脚描述句柄 | -| pullType | PIN管脚推拉方式 | +| handle | DevHandle类型,PIN管脚描述句柄 | +| pullType | 枚举类型,PIN管脚推拉方式 | | **返回值** | **描述** | -| 0 | PIN设置管脚推拉方式成功 | -| 负数 | PIN设置管脚推拉方式失败 | +| HDF_SUCCESS | PIN设置管脚推拉方式成功 | +| 负数 | PIN设置管脚推拉方式失败 | 假设PIN要设置的管脚推拉方式为上拉,其实例如下: @@ -136,7 +141,7 @@ int32_t PinSetPull(DevHandle handle, enum PinPullType pullType); int32_t ret; enum PinPullType pullTypeNum; -/* PIN设置管脚推拉方式 */ +// PIN设置管脚推拉方式 pullTypeNum = 1; ret = PinSetPull(handle, pullTypeNum); if (ret != HDF_SUCCESS) { @@ -157,13 +162,13 @@ int32_t PinGetPull(DevHandle handle, enum PinPullType *pullType); -| 参数 | 描述 | +| 参数 | 描述 | | ---------- | ------------------------- | -| handle | PIN管脚描述句柄 | -| pullType | 接收PIN管脚推拉方式的指针 | +| handle | DevHandle类型,PIN管脚描述句柄 | +| pullType | 枚举类型指针,接收PIN管脚推拉方式 | | **返回值** | **描述** | -| 0 | PIN获取管脚推拉方式成功 | -| 负数 | PIN获取管脚推拉方式失败 | +| HDF_SUCCESS | PIN获取管脚推拉方式成功 | +| 负数 | PIN获取管脚推拉方式失败 | PIN获取管脚推拉方式的实例如下: @@ -171,7 +176,7 @@ PIN获取管脚推拉方式的实例如下: int32_t ret; enum PinPullType pullTypeNum; -/* PIN获取管脚推拉方式 */ +// PIN获取管脚推拉方式 ret = PinGetPull(handle, &pullTypeNum); if (ret != HDF_SUCCESS) { HDF_LOGE("PinGetPull: failed, ret %d\n", ret); @@ -191,20 +196,20 @@ int32_t PinSetStrength(DevHandle handle, uint32_t strength); -| 参数 | 描述 | +| 参数 | 描述 | | ---------- | ----------------------- | -| handle | 管脚描述句柄 | -| strength | PIN管脚推拉强度 | +| handle | DevHandle类型,管脚描述句柄 | +| strength | uint32_t类型,PIN管脚推拉强度 | | **返回值** | **描述** | -| 0 | PIN设置管脚推拉强度成功 | -| 负数 | PIN设置管脚推拉强度失败 | +| HDF_SUCCESS | PIN设置管脚推拉强度成功 | +| 负数 | PIN设置管脚推拉强度失败 | 假设PIN要设置的管脚推拉强度为2,其实例如下: ```c int32_t ret; uint32_t strengthNum; -/* PIN设置管脚推拉强度 */ +// PIN设置管脚推拉强度 strengthNum = 2; ret = PinSetStrength(handle, strengthNum); if (ret != HDF_SUCCESS) { @@ -225,13 +230,13 @@ int32_t PinGetStrength(DevHandle handle, uint32_t *strength); -| 参数 | 描述 | +| 参数 | 描述 | | ---------- | ------------------------- | -| handle | 管脚描述句柄 | -| strength | 接收PIN管脚推拉强度的指针 | +| handle | DevHandle类型,管脚描述句柄 | +| strength | uint32_t类型指针,接收PIN管脚推拉强度 | **返回值** | **描述** | -| 0 | PIN获取管脚推拉强度成功 | -| 负数 | PIN获取管脚推拉强度失败 | +| HDF_SUCCESS | PIN获取管脚推拉强度成功 | +| 负数 | PIN获取管脚推拉强度失败 | PIN获取管脚推拉强度的实例如下: @@ -239,7 +244,7 @@ PIN获取管脚推拉强度的实例如下: int32_t ret; uint32_t strengthNum; -/* PIN获取管脚推拉强度 */ +// PIN获取管脚推拉强度 ret = PinGetStrength(handle, &strengthNum); if (ret != HDF_SUCCESS) { HDF_LOGE("PinGetStrength: failed, ret %d\n", ret); @@ -261,13 +266,13 @@ int32_t PinSetFunc(DevHandle handle, const char *funcName); -| 参数 | 描述 | +| 参数 | 描述 | | ---------- | ------------------- | -| handle | 管脚描述句柄 | -| funcName | PIN管脚功能名 | +| handle | DevHandle类型,管脚描述句柄 | +| funcName | 字符指针类型,PIN管脚功能名 | | **返回值** | **描述** | -| 0 | PIN设置管脚功能成功 | -| 负数 | PIN设置管脚功能失败 | +| HDF_SUCCESS | PIN设置管脚功能成功 | +| 负数 | PIN设置管脚功能失败 | 假设PIN需要设置的管脚功能为LSADC_CH1(ADC通道1),其实例如下: @@ -275,7 +280,7 @@ int32_t PinSetFunc(DevHandle handle, const char *funcName); int32_t ret; char funcName = "LSADC_CH1"; -/* PIN设置管脚功能 */ +// PIN设置管脚功能 ret = PinSetFunc(handle, funcName); if (ret != HDF_SUCCESS) { HDF_LOGE("PinSetFunc: failed, ret %d\n", ret); @@ -295,13 +300,13 @@ int32_t PinGetFunc(DevHandle handle, const char **funcName); -| 参数 | 描述 | +| 参数 | 描述 | | ---------- | --------------------- | -| handle | 管脚描述句柄 | -| funcName | 接收PIN管脚功能名指针 | +| handle | DevHandle类型,管脚描述句柄 | +| funcName | 字符类型双指针,接收PIN管脚功能 | | **返回值** | **描述** | -| 0 | PIN获取管脚功能成功 | -| 负数 | PIN获取管脚功能失败 | +| HDF_SUCCESS | PIN获取管脚功能成功 | +| 负数 | PIN获取管脚功能失败 | PIN获取管脚功能的实例如下: @@ -309,7 +314,7 @@ PIN获取管脚功能的实例如下: int32_t ret; char *funcName = NULL; -/* PIN获取管脚功能 */ +// PIN获取管脚功能 ret = PinGetFunc(handle, &funcName); if (ret != HDF_SUCCESS) { HDF_LOGE("PinGetFunc: failed, ret %d\n", ret); @@ -329,11 +334,11 @@ void PinPut(DevHandle handle); -| 参数 | 描述 | +| 参数 | 描述 | | ---------- | -------------- | -| handle | 管脚描述句柄 | +| handle | DevHandle类型,管脚描述句柄 | | **返回值** | **描述** | -| NA | 无返回值 | +| NA | 无返回值 | PIN销毁管脚描述句柄实例如下: @@ -346,76 +351,84 @@ PinPut(handle); 下面将基于Hi3516DV300开发板展示使用PIN设置管脚相关属性完整操作,步骤主要如下: 1. 传入要设置的管脚名,获取PIN管脚描述句柄。 + 2. 通过PIN管脚描述句柄以及推拉方式pullTypeNum设置管脚推拉方式,如果操作失败则释放PIN管脚描述句柄。 + 3. 通过PIN管脚描述句柄,并用pullTypeNum承接获取的管脚推拉方式,如果操作失败则释放PIN管脚描述句柄。 + 4. 通过PIN管脚描述句柄以及推拉强度strengthNum设置管脚推拉强度,如果操作失败则释放PIN管脚描述句柄。 + 5. 通过PIN管脚描述句柄,并用strengthNum承接获取的管脚推拉强度,如果操作失败则释放PIN管脚描述句柄。 + 5. 通过PIN管脚描述句柄以及管脚功能名funName设置管脚功能,如果操作失败则释放PIN管脚描述句柄。 + 6. 通过PIN管脚描述句柄,并用funName承接获取的管脚功能名,如果操作失败则释放PIN管脚描述句柄。 + 7. 使用完PIN后,不再对管脚进行操作,释放PIN管脚描述句柄。 ```c -#include "hdf_log.h" /* 标准日志打印头文件 */ -#include "pin_if.h" /* PIN标准接口头文件 */ +#include "hdf_log.h" // 标准日志打印头文件 +#include "pin_if.h" // PIN标准接口头文件 int32_t PinTestSample(void) { int32_t ret; uint32_t strengthNum; enum PinPullType pullTypeNum; - char pinName; - char *funName; + char *pinName = NULL; + const char *funcName = NULL; DevHandle handle = NULL; - /* PIN管脚名,要填写实际要设置的管脚名 */ + // PIN管脚名,要填写实际要设置的管脚名 pinName = "P18"; - /* PIN获取管脚描述句柄 */ + // PIN获取管脚描述句柄 handle = PinGet(pinName); if (handle == NULL) { - HDF_LOGE("PinGet: pin get failed!\n"); - return; + HDF_LOGE("PinTestSample: pin get fail!\n"); + return -1; } - /* PIN设置管脚推拉方式为上拉 */ + // PIN设置管脚推拉方式为上拉 pullTypeNum = 1; ret = PinSetPull(handle, pullTypeNum); if (ret != HDF_SUCCESS) { - HDF_LOGE("PinSetPull: failed, ret %d\n", ret); + HDF_LOGE("PinTestSample: pin set pull fail, ret:%d\n", ret); goto ERR; } - /* PIN获取管脚推拉方式 */ + // PIN获取管脚推拉方式 ret = PinGetPull(handle, &pullTypeNum); if (ret != HDF_SUCCESS) { - HDF_LOGE("PinGetPull: failed, ret %d\n", ret); + HDF_LOGE("PinTestSample: pin get pull fail, ret:%d\n", ret); goto ERR; } - /* PIN设置管脚推拉强度为2 */ + // PIN设置管脚推拉强度为2 strengthNum = 2; ret = PinSetStrength(handle, strengthNum); if (ret != HDF_SUCCESS) { - HDF_LOGE("PinSetStrength: failed, ret %d\n", ret); + HDF_LOGE("PinTestSample: pin set strength fail, ret:%d\n", ret); goto ERR; } - /* PIN获取管脚推拉强度 */ + // PIN获取管脚推拉强度 ret = PinGetStrength(handle, &strengthNum); if (ret != HDF_SUCCESS) { - HDF_LOGE("PinGetStrength: failed, ret %d\n", ret); + HDF_LOGE("PinTestSample: pin get strength fail, ret:%d\n", ret); goto ERR; } - /* PIN设置管脚功能为LSADC_CH1 */ - funName = "LSADC_CH1"; - ret = PinSetFunc(handle, funName); + // PIN设置管脚功能为LSADC_CH1 + funcName = "LSADC_CH1"; + ret = PinSetFunc(handle, funcName); if (ret != HDF_SUCCESS) { - HDF_LOGE("PinSetFunc: failed, ret %d\n", ret); + HDF_LOGE("PinTestSample: pin set func fail, ret:%d\n", ret); goto ERR; } - /* PIN获取管脚功能 */ + // PIN获取管脚功能 ret = PinGetFunc(handle, &funcName); if (ret != HDF_SUCCESS) { - HDF_LOGE("PinGetFunc: failed, ret %d\n", ret); + HDF_LOGE("PinTestSample: pin get func fail, ret:%d\n", ret); goto ERR; } + HDF_LOGD("PinTestSample: function tests end.", __func__); ERR: - /* 释放PIN管脚描述句柄 */ + // 释放PIN管脚描述句柄 PinPut(handle); return ret; } diff --git a/zh-cn/device-dev/driver/driver-platform-pin-develop.md b/zh-cn/device-dev/driver/driver-platform-pin-develop.md index 00ae1b6b003d658265815ed4535c47845aa03a67..ba863806cf7d2a00cca4b005eb536911b3f7b841 100755 --- a/zh-cn/device-dev/driver/driver-platform-pin-develop.md +++ b/zh-cn/device-dev/driver/driver-platform-pin-develop.md @@ -12,11 +12,11 @@ PIN是一个软件层面的概念,目的是为了统一对各SoC的PIN管脚 - SoC(System on Chip) - 系统级芯片,又称作片上系统,通常是面向特定用途将微处理器、模拟IP核、数字IP核和存储器集成在单一芯片的标准产品。 + 系统级芯片,又称作片上系统,通常是面向特定用途将微处理器、模拟IP核、数字IP核和存储器集成在单一芯片的标准产品。 - 管脚复用 - 由于芯片自身的引脚数量有限,无法满足日益增多的外接需求。此时可以通过软件层面的寄存器设置,让引脚工作在不同的状态,从而实现相同引脚完成不同功能的目的。 + 由于芯片自身的引脚数量有限,无法满足日益增多的外接需求。此时可以通过软件层面的寄存器设置,让引脚工作在不同的状态,从而实现相同引脚完成不同功能的目的。 ### 运作机制 @@ -27,7 +27,9 @@ PIN是一个软件层面的概念,目的是为了统一对各SoC的PIN管脚 PIN模块各分层作用: - 接口层提供获取PIN管脚、设置PIN管脚推拉方式、获取PIN管脚推拉方式、设置PIN管脚推拉强度、获取PIN管脚推拉强度、设置PIN管脚功能、获取PIN管脚功能、释放PIN管脚的接口。 + - 核心层主要提供PIN管脚资源匹配,PIN管脚控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。 + - 适配层主要是将钩子函数的功能实例化,实现具体的功能。 **图 1** 统一服务模式结构图 @@ -63,9 +65,9 @@ struct PinCntlrMethod { **表 1** PinCntlrMethod成员的钩子函数功能说明 -| 成员函数 | 入参 | 出参 | 返回值 | 功能 | +| 成员函数 | 入参 | 出参 | 返回值 | 功能 | | ------------ | ------------------------------------------- | ------ | ---- | ---- | -| SetPinPull | cntlr:结构体指针,核心层PIN控制器
index:uint32_t类型变量,管脚索引号
pullType:枚举常量,PIN管脚推拉方式 | 无 |HDF_STATUS相关状态|PIN设置管脚推拉方式| +| SetPinPull | cntlr:结构体指针,核心层PIN控制器
index:uint32_t类型变量,管脚索引号
pullType:枚举常量,PIN管脚推拉方式 | 无 |HDF_STATUS相关状态|PIN设置管脚推拉方式 | | GetPinPull | cntlr:结构体指针,核心层PIN控制器
index:uint32_t类型变量,管脚索引号 | pullType:枚举常量指针,传出获取的PIN管脚推拉方式 | HDF_STATUS相关状态 | PIN获取管脚推拉方式 | | SetPinStrength | cntlr:结构体指针,核心层PIN控制器
index:uint32_t类型变量,管脚索引号
strength:uint32_t变量,PIN推拉强度 | 无 | HDF_STATUS相关状态 | PIN设置推拉强度 | | GetPinStrength | cntlr:结构体指针,核心层PIN控制器
index:uint32_t类型变量,管脚索引号 | strength:uint32_t变量指针,传出获取的PIN推拉强度 | HDF_STATUS相关状态 | PIN获取推拉强度 | @@ -76,395 +78,414 @@ struct PinCntlrMethod { PIN模块适配HDF框架包含以下四个步骤: -- 实例化驱动入口。 -- 配置属性文件。 -- 实例化PIN控制器对象。 -- 驱动调试。 +- 实例化驱动入口 + +- 配置属性文件 + +- 实例化PIN控制器对象 + +- 驱动调试 ### 开发实例 下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/pin/pin_hi35xx.c驱动为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。 -1. 实例化驱动入口。 +1. 实例化驱动入口 + + 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 - 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 - 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 + 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 - PIN驱动入口开发参考: + PIN驱动入口开发参考: - ```c - static struct HdfDriverEntry g_hi35xxPinDriverEntry = { - .moduleVersion = 1, - .Bind = Hi35xxPinBind, - .Init = Hi35xxPinInit, - .Release = Hi35xxPinRelease, - .moduleName = "hi35xx_pin_driver", // 【必要且与HCS文件中里面的moduleName匹配】 - }; - HDF_INIT(g_hi35xxPinDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 + ```c + static struct HdfDriverEntry g_hi35xxPinDriverEntry = { + .moduleVersion = 1, + .Bind = Hi35xxPinBind, // PIN不需要实现Bind,本例是一个空实现,驱动适配者可根据自身需要添加相关操作 + .Init = Hi35xxPinInit, // 挂接PIN模块Init实例化 + .Release = Hi35xxPinRelease, // 挂接PIN模块Release实例化 + .moduleName = "hi35xx_pin_driver", // 【必要且与HCS文件中里面的moduleName匹配】 + }; + HDF_INIT(g_hi35xxPinDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 ``` -2. 配置属性文件。 - - 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以两个PIN控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息。器件属性值对于驱动适配者的驱动实现以及核心层PinCntlr相关成员的默认值或限制范围有密切关系,比如控制器号,控制器管脚数量、管脚等。需要在pin_config.hcs中配置器件属性。 - - - device_info.hcs 配置参考: - - 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。 - - ```c - root { - device_info { - platform :: host { - hostName = "platform_host"; - priority = 50; - device_pin :: device { - device0 :: deviceNode { // 用于统一管理PIN并发布服务 - policy = 2; // 2:用户态可见;1:内核态可见;0:不需要发布服务。 - priority = 8; // 启动优先级 - permission = 0644; // 创建设备节点权限 - moduleName = "HDF_PLATFORM_PIN_MANAGER"; - serviceName = "HDF_PLATFORM_PIN_MANAGER"; - } - device1 :: deviceNode { // 为每一个PIN控制器配置一个HDF设备节点,存在多个时必须添加,否则不用。 - policy = 0; - priority = 10; // 驱动启动优先级 - permission = 0644; // 驱动创建设备节点权限 - moduleName = "hi35xx_pin_driver"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。 - deviceMatchAttr = "hisilicon_hi35xx_pin_0"; // 【必要】用于配置控制器私有数据,要与pin_config.hcs中对应控制器保持一致,具体的控制器信息在pin_config.hcs中。 - } - device2 :: deviceNode { - policy = 0; - priority = 10; - permission = 0644; - moduleName = "hi35xx_pin_driver"; - deviceMatchAttr = "hisilicon_hi35xx_pin_1"; - } - ... - } - } - } - } - ``` - - - pin_config.hcs配置参考: - - 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs文件配置器件属性,其中配置参数如下: - - ```c - root { - platform { - pin_config_hi35xx { - template pin_controller { // 【必要】配置模板配,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值。 - number = 0; // 【必要】PIN控制器号 - regStartBasePhy = 0; // 【必要】寄存器物理基地址起始地址 - regSize = 0; // 【必要】寄存器位宽 - pinCount = 0; // 【必要】管脚数量 - match_attr = ""; - template pin_desc { - pinName = ""; // 【必要】管脚名称 - init = 0; // 【必要】寄存器默认值 - F0 = ""; // 【必要】功能0 - F1 = ""; // 功能1 - F2 = ""; // 功能2 - F3 = ""; // 功能3 - F4 = ""; // 功能4 - F5 = ""; // 功能5 - } - } - controller_0 :: pin_controller { - number = 0; - regStartBasePhy = 0x10FF0000; - regSize = 0x48; - pinCount = 18; - match_attr = "hisilicon_hi35xx_pin_0"; - T1 :: pin_desc { - pinName = "T1"; - init = 0x0600; - F0 = "EMMC_CLK"; - F1 = "SFC_CLK"; - F2 = "SFC_BOOT_MODE"; - } - ... // 对应管脚控制器下的每个管脚,按实际添加。 - } - ... // 每个管脚控制器对应一个控制器节点,如存在多个PIN控制器,请依次添加对应的控制器节点。 - } - } - } - ``` - - 需要注意的是,新增pin_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。 - - ```c - #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs" // 配置文件相对路径 - ``` - -3. 实例化PIN控制器对象。 - - 完成配置属性文件之后,下一步就是以核心层PinCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化PinCntlr成员PinCntlrMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。 - - - 驱动适配者自定义结构体参考 - - 从驱动的角度看,自定义结构体是参数和数据的载体,而且pin_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象。 - - 在Hi35xxPinCntlrInit函数中对PinCntlr成员进行初始化操作。 - - ```c - // 驱动适配者自定义管脚描述结构体 - struct Hi35xxPinDesc { - const char *pinName; // 管脚名 - uint32_t init; // 初始化值 - uint32_t index; // 管脚索引 - int32_t pullType; // 管脚推拉方式 - int32_t strength; // 管脚推拉强度 - const char *func[HI35XX_PIN_FUNC_MAX]; // 管脚功能名字符串数组 - }; - - // 驱动适配者自定义结构体 - struct Hi35xxPinCntlr { - struct PinCntlr cntlr; // 是核心层控制对象,具体描述见下面 - struct Hi35xxPinDesc *desc; // 驱动适配者自定义管脚描述结构体指针 - volatile unsigned char *regBase; // 寄存器映射地址 - uint16_t number; // 管脚控制器编号 - uint32_t regStartBasePhy; // 寄存器物理基地址起始地址 - uint32_t regSize; // 寄存器位宽 - uint32_t pinCount; // 管脚数量 - }; - - // PinCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。 - struct PinCntlr { - struct IDeviceIoService service; // 驱动服务 - struct HdfDeviceObject *device; // 驱动设备对象 - struct PinCntlrMethod *method; // 钩子函数 - struct DListHead node; // 链表节点 - OsalSpinlock spin; // 自旋锁 - uint16_t number; // 管脚控制器编号 - uint16_t pinCount; // 管脚数量 - struct PinDesc *pins; // 管脚资源 - void *priv; // 私有数据 - }; - - // PIN管脚控制器初始化 - static int32_t Hi35xxPinCntlrInit(struct HdfDeviceObject *device, struct Hi35xxPinCntlr *hi35xx) - { - struct DeviceResourceIface *drsOps = NULL; - int32_t ret; - // 从hcs文件读取管脚控制器相关属性 - drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) { - HDF_LOGE("%s: invalid drs ops fail!", __func__); - return HDF_FAILURE; - } - ret = drsOps->GetUint16(device->property, "number", &hi35xx->number, 0); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read number failed", __func__); - return ret; - } - - if (hi35xx->number > HI35XX_PIN_MAX_NUMBER) { - HDF_LOGE("%s: invalid number:%u", __func__, hi35xx->number); - return HDF_ERR_INVALID_PARAM; - } - ret = drsOps->GetUint32(device->property, "regStartBasePhy", &hi35xx->regStartBasePhy, 0); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read regStartBasePhy failed", __func__); - return ret; - } - ret = drsOps->GetUint32(device->property, "regSize", &hi35xx->regSize, 0); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read regSize failed", __func__); - return ret; - } - ret = drsOps->GetUint32(device->property, "pinCount", &hi35xx->pinCount, 0); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read pinCount failed", __func__); - return ret; - } - if (hi35xx->pinCount > PIN_MAX_CNT_PER_CNTLR) { - HDF_LOGE("%s: invalid number:%u", __func__, hi35xx->number); - return HDF_ERR_INVALID_PARAM; - } - // 将读取的值赋值给管脚控制器的成员,完成管脚控制器初始化。 - hi35xx->cntlr.pinCount = hi35xx->pinCount; - hi35xx->cntlr.number = hi35xx->number; - hi35xx->regBase = OsalIoRemap(hi35xx->regStartBasePhy, hi35xx->regSize); // 管脚控制器映射 - if (hi35xx->regBase == NULL) { - HDF_LOGE("%s: remap Pin base failed", __func__); - return HDF_ERR_IO; - } - hi35xx->desc = (struct Hi35xxPinDesc *)OsalMemCalloc(sizeof(struct Hi35xxPinDesc) * hi35xx->pinCount); - if (hi35xx->desc == NULL) { - HDF_LOGE("%s: memcalloc hi35xx desc failed", __func__); - return HDF_ERR_MALLOC_FAIL; - } - hi35xx->cntlr.pins = (struct PinDesc *)OsalMemCalloc(sizeof(struct PinDesc) * hi35xx->pinCount); - if (hi35xx->desc == NULL) { - HDF_LOGE("%s: memcalloc hi35xx cntlr pins failed", __func__); - return HDF_ERR_MALLOC_FAIL; - } - return HDF_SUCCESS; - } - ``` - - - PinCntlr成员钩子函数结构体PinCntlrMethod的实例化,其他成员在Init函数中初始化。 - - ```c - static struct PinCntlrMethod g_method = { - .SetPinPull = Hi35xxPinSetPull, // 设置推拉方式 - .GetPinPull = Hi35xxPinGetPull, // 获取推拉方式 - .SetPinStrength = Hi35xxPinSetStrength, // 设置推拉强度 - .GetPinStrength = Hi35xxPinGetStrength, // 获取推拉强度 - .SetPinFunc = Hi35xxPinSetFunc, // 设置管脚功能 - .GetPinFunc = Hi35xxPinGetFunc, // 获取管脚功能 - }; - ``` - - - Init函数开发参考 - - 入参 - - HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 - - 返回值: - - HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 - - | **状态(值)** | **问题描述** | - | ---------------------- | -------------- | - | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | - | HDF_ERR_MALLOC_FAIL | 内存分配失败 | - | HDF_ERR_INVALID_PARAM | 参数非法 | - | HDF_ERR_IO | I/O 错误 | - | HDF_SUCCESS | 初始化成功 | - | HDF_FAILURE | 初始化失败 | - - 函数说明: - - 初始化自定义结构体对象和PinCntlr成员,并通过调用核心层PinCntlrAdd函数挂载PIN控制器。 - - ```c - static int32_t Hi35xxPinReadFunc(struct Hi35xxPinDesc *desc, const struct DeviceResourceNode *node, struct DeviceResourceIface *drsOps) - { - int32_t ret; - uint32_t funcNum = 0; - // 从hcs中读取管脚控制器子节点管脚功能名 - ret = drsOps->GetString(node, "F0", &desc->func[funcNum], "NULL"); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read F0 failed", __func__); - return ret; - } - - funcNum++; - ret = drsOps->GetString(node, "F1", &desc->func[funcNum], "NULL"); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read F1 failed", __func__); - return ret; - } - - funcNum++; - ... - return HDF_SUCCESS; - } - - static int32_t Hi35xxPinParsePinNode(const struct DeviceResourceNode *node, struct Hi35xxPinCntlr *hi35xx, int32_t index) - { - int32_t ret; - struct DeviceResourceIface *drsOps = NULL; - // 从hcs中读取管脚控制器子节点管脚相关属性 - drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetString == NULL) { - HDF_LOGE("%s: invalid drs ops fail!", __func__); - return HDF_FAILURE; - } - ret = drsOps->GetString(node, "pinName", &hi35xx->desc[index].pinName, "NULL"); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read pinName failed", __func__); - return ret; - } - ... - ret = Hi35xxPinReadFunc(&hi35xx->desc[index], node, drsOps); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s:Pin read Func failed", __func__); - return ret; - } - hi35xx->cntlr.pins[index].pinName = hi35xx->desc[index].pinName; - hi35xx->cntlr.pins[index].priv = (void *)node; - ... - return HDF_SUCCESS; - } - - static int32_t Hi35xxPinInit(struct HdfDeviceObject *device) - { - ... - struct Hi35xxPinCntlr *hi35xx = NULL; - ... - ret = Hi35xxPinCntlrInit(device, hi35xx); // 管脚控制器初始化 - ... - DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { // 遍历管脚控制器的每个子节点 - ret = Hi35xxPinParsePinNode(childNode, hi35xx, index); // 解析子节点 - ... - } - - hi35xx->cntlr.method = &g_method; // PinCntlrMethod实例化实例化对象的挂载 - ret = PinCntlrAdd(&hi35xx->cntlr); // 添加控制器 - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: add Pin cntlr: failed", __func__); - ret = HDF_FAILURE; - } - return HDF_SUCCESS; - } - ``` - - - Release函数开发参考 - - 入参: - - HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 - - 返回值: - - 无。 - - 函数说明: - - 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口。当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 - - ```c - static void Hi35xxPinRelease(struct HdfDeviceObject *device) - { - int32_t ret; - uint16_t number; - struct PinCntlr *cntlr = NULL; - struct Hi35xxPinCntlr *hi35xx = NULL; - struct DeviceResourceIface *drsOps = NULL; - - if (device == NULL || device->property == NULL) { - HDF_LOGE("%s: device or property is null", __func__); - return; - } - // 从hcs文件中读取管脚控制器编号 - drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetString == NULL) { - HDF_LOGE("%s: invalid drs ops", __func__); - return; - } - ret = drsOps->GetUint16(device->property, "number", &number, 0); - if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: read cntlr number failed", __func__); - return; - } - - cntlr = PinCntlrGetByNumber(number); // 通过管脚控制器编号获取管脚控制器 - PinCntlrRemove(cntlr); - hi35xx = (struct Hi35xxPinCntlr *)cntlr; - if (hi35xx != NULL) { - if (hi35xx->regBase != NULL) { - OsalIoUnmap((void *)hi35xx->regBase); - } - OsalMemFree(hi35xx); - } - } - ``` - -4. 驱动调试。 - - 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。 \ No newline at end of file +2. 配置属性文件 + + 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以两个PIN控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息,以及在gpio_config.hcs文件中增加对应的器件属性。器件属性值对于驱动适配者的驱动实现以及核心层PinCntlr相关成员的默认值或限制范围有密切关系,比如控制器号,控制器管脚数量、管脚等,需要在pin_config.hcs中配置器件属性。 + + 统一服务模式的特点是device_info.hcs文件中第一个设备节点必须为PIN管理器,其各项参数如表2所示: + + **表 2** device_info.hcs节点参数说明 + + | 成员名 | 值 | + | -------- | -------- | + | policy | 驱动服务发布的策略,PIN管理器具体配置为2,表示驱动对内核态和用户态都发布服务 | + | priority | 驱动启动优先级(0-200),值越大优先级越低。PIN管理器具体配置为8 | + | permission | 驱动创建设备节点权限,PIN管理器具体配置为0664 | + | moduleName | 驱动名称,PIN管理器固定为HDF_PLATFORM_PIN_MANAGER | + | serviceName | 驱动对外发布服务的名称,PIN管理器服务名设置为HDF_PLATFORM_PIN_MANAGER | + | deviceMatchAttr | 驱动私有数据匹配的关键字,PIN管理器没有使用,可忽略 | + + - device_info.hcs 配置参考: + + 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。 + + ```c + root { + device_info { + platform :: host { + hostName = "platform_host"; + priority = 50; + device_pin :: device { + device0 :: deviceNode { // 用于统一管理PIN并发布服务 + policy = 2; // 2:用户态可见;1:内核态可见;0:不需要发布服务。 + priority = 8; // 启动优先级 + permission = 0644; // 创建设备节点权限 + moduleName = "HDF_PLATFORM_PIN_MANAGER"; + serviceName = "HDF_PLATFORM_PIN_MANAGER"; + } + device1 :: deviceNode { // 为每一个PIN控制器配置一个HDF设备节点,存在多个时必须添加,否则不用。 + policy = 0; + priority = 10; // 驱动启动优先级 + permission = 0644; // 驱动创建设备节点权限 + moduleName = "hi35xx_pin_driver"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。 + deviceMatchAttr = "hisilicon_hi35xx_pin_0"; // 【必要】用于配置控制器私有数据,要与pin_config.hcs中对应控制器保持一致,具体的控制器信息在pin_config.hcs中。 + } + device2 :: deviceNode { + policy = 0; + priority = 10; + permission = 0644; + moduleName = "hi35xx_pin_driver"; + deviceMatchAttr = "hisilicon_hi35xx_pin_1"; + } + ...... // 如果存在多个PIN控制器时【必须】添加节点,否则不用 + } + } + } + } + ``` + + - pin_config.hcs配置参考: + + 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs文件配置器件属性,其中配置参数如下: + + ```c + root { + platform { + pin_config_hi35xx { + template pin_controller { // 【必要】配置模板配,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值。 + number = 0; // 【必要】PIN控制器号 + regStartBasePhy = 0; // 【必要】寄存器物理基地址起始地址 + regSize = 0; // 【必要】寄存器位宽 + pinCount = 0; // 【必要】管脚数量 + match_attr = ""; + template pin_desc { + pinName = ""; // 【必要】管脚名称 + init = 0; // 【必要】寄存器默认值 + F0 = ""; // 【必要】功能0 + F1 = ""; // 功能1 + F2 = ""; // 功能2 + F3 = ""; // 功能3 + F4 = ""; // 功能4 + F5 = ""; // 功能5 + } + } + controller_0 :: pin_controller { + number = 0; + regStartBasePhy = 0x10FF0000; + regSize = 0x48; + pinCount = 18; + match_attr = "hisilicon_hi35xx_pin_0"; + T1 :: pin_desc { + pinName = "T1"; + init = 0x0600; + F0 = "EMMC_CLK"; + F1 = "SFC_CLK"; + F2 = "SFC_BOOT_MODE"; + } + ...... // 对应管脚控制器下的每个管脚,按实际添加。 + } + ...... // 每个管脚控制器对应一个控制器节点,如存在多个PIN控制器,请依次添加对应的控制器节点。 + } + } + } + ``` + + 需要注意的是,新增pin_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。 + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pin/pin_config.hcs" // 配置文件相对路径 + ``` + +3. 实例化PIN控制器对象 + + 完成配置属性文件之后,下一步就是以核心层PinCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化PinCntlr成员PinCntlrMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。 + + - 驱动适配者自定义结构体参考 + + 从驱动的角度看,自定义结构体是参数和数据的载体,而且pin_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象。 + + 在Hi35xxPinCntlrInit函数中对PinCntlr成员进行初始化操作。 + + ```c + // 驱动适配者自定义管脚描述结构体 + struct Hi35xxPinDesc { + const char *pinName; // 管脚名 + uint32_t init; // 初始化值 + uint32_t index; // 管脚索引 + int32_t pullType; // 管脚推拉方式 + int32_t strength; // 管脚推拉强度 + const char *func[HI35XX_PIN_FUNC_MAX]; // 管脚功能名字符串数组 + }; + + // 驱动适配者自定义结构体 + struct Hi35xxPinCntlr { + struct PinCntlr cntlr; // 是核心层控制对象,具体描述见下面 + struct Hi35xxPinDesc *desc; // 驱动适配者自定义管脚描述结构体指针 + volatile unsigned char *regBase; // 寄存器映射地址 + uint16_t number; // 管脚控制器编号 + uint32_t regStartBasePhy; // 寄存器物理基地址起始地址 + uint32_t regSize; // 寄存器位宽 + uint32_t pinCount; // 管脚数量 + }; + + // PinCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。 + struct PinCntlr { + struct IDeviceIoService service; // 驱动服务 + struct HdfDeviceObject *device; // 驱动设备对象 + struct PinCntlrMethod *method; // 钩子函数 + struct DListHead node; // 链表节点 + OsalSpinlock spin; // 自旋锁 + uint16_t number; // 管脚控制器编号 + uint16_t pinCount; // 管脚数量 + struct PinDesc *pins; // 管脚资源 + void *priv; // 私有数据 + }; + + // PIN管脚控制器初始化 + static int32_t Hi35xxPinCntlrInit(struct HdfDeviceObject *device, struct Hi35xxPinCntlr *hi35xx) + { + struct DeviceResourceIface *drsOps = NULL; + int32_t ret; + // 从hcs文件读取管脚控制器相关属性 + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetUint16 == NULL) { + HDF_LOGE("%s: invalid drs ops fail!", __func__); + return HDF_FAILURE; + } + ret = drsOps->GetUint16(device->property, "number", &hi35xx->number, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read number failed", __func__); + return ret; + } + + if (hi35xx->number > HI35XX_PIN_MAX_NUMBER) { + HDF_LOGE("%s: invalid number:%u", __func__, hi35xx->number); + return HDF_ERR_INVALID_PARAM; + } + ret = drsOps->GetUint32(device->property, "regStartBasePhy", &hi35xx->regStartBasePhy, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read regStartBasePhy failed", __func__); + return ret; + } + ret = drsOps->GetUint32(device->property, "regSize", &hi35xx->regSize, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read regSize failed", __func__); + return ret; + } + ret = drsOps->GetUint32(device->property, "pinCount", &hi35xx->pinCount, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read pinCount failed", __func__); + return ret; + } + if (hi35xx->pinCount > PIN_MAX_CNT_PER_CNTLR) { + HDF_LOGE("%s: invalid number:%u", __func__, hi35xx->number); + return HDF_ERR_INVALID_PARAM; + } + // 将读取的值赋值给管脚控制器的成员,完成管脚控制器初始化。 + hi35xx->cntlr.pinCount = hi35xx->pinCount; + hi35xx->cntlr.number = hi35xx->number; + hi35xx->regBase = OsalIoRemap(hi35xx->regStartBasePhy, hi35xx->regSize); // 管脚控制器映射 + if (hi35xx->regBase == NULL) { + HDF_LOGE("%s: remap Pin base failed", __func__); + return HDF_ERR_IO; + } + hi35xx->desc = (struct Hi35xxPinDesc *)OsalMemCalloc(sizeof(struct Hi35xxPinDesc) * hi35xx->pinCount); + if (hi35xx->desc == NULL) { + HDF_LOGE("%s: memcalloc hi35xx desc failed", __func__); + return HDF_ERR_MALLOC_FAIL; + } + hi35xx->cntlr.pins = (struct PinDesc *)OsalMemCalloc(sizeof(struct PinDesc) * hi35xx->pinCount); + if (hi35xx->desc == NULL) { + HDF_LOGE("%s: memcalloc hi35xx cntlr pins failed", __func__); + return HDF_ERR_MALLOC_FAIL; + } + return HDF_SUCCESS; + } + ``` + + - PinCntlr成员钩子函数结构体PinCntlrMethod的实例化。 + + ```c + static struct PinCntlrMethod g_method = { + .SetPinPull = Hi35xxPinSetPull, // 设置推拉方式 + .GetPinPull = Hi35xxPinGetPull, // 获取推拉方式 + .SetPinStrength = Hi35xxPinSetStrength, // 设置推拉强度 + .GetPinStrength = Hi35xxPinGetStrength, // 获取推拉强度 + .SetPinFunc = Hi35xxPinSetFunc, // 设置管脚功能 + .GetPinFunc = Hi35xxPinGetFunc, // 获取管脚功能 + }; + ``` + + - Init函数开发参考 + + 入参 + + HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 + + 返回值: + + HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 + + **表 3** HDF_STATUS相关状态说明 + + | 状态(值) | 问题描述 | + | -------- | -------- | + | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | + | HDF_ERR_MALLOC_FAIL | 内存分配失败 | + | HDF_ERR_INVALID_PARAM | 参数非法 | + | HDF_ERR_IO | I/O 错误 | + | HDF_SUCCESS | 初始化成功 | + | HDF_FAILURE | 初始化失败 | + + 函数说明: + + 初始化自定义结构体对象和PinCntlr成员,并通过调用核心层PinCntlrAdd函数挂载PIN控制器。 + + ```c + static int32_t Hi35xxPinReadFunc(struct Hi35xxPinDesc *desc, const struct DeviceResourceNode *node, struct DeviceResourceIface *drsOps) + { + int32_t ret; + uint32_t funcNum = 0; + // 从hcs中读取管脚控制器子节点管脚功能名 + ret = drsOps->GetString(node, "F0", &desc->func[funcNum], "NULL"); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read F0 failed", __func__); + return ret; + } + + funcNum++; + ret = drsOps->GetString(node, "F1", &desc->func[funcNum], "NULL"); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read F1 failed", __func__); + return ret; + } + + funcNum++; + ...... + return HDF_SUCCESS; + } + + static int32_t Hi35xxPinParsePinNode(const struct DeviceResourceNode *node, struct Hi35xxPinCntlr *hi35xx, int32_t index) + { + int32_t ret; + struct DeviceResourceIface *drsOps = NULL; + // 从hcs中读取管脚控制器子节点管脚相关属性 + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetString == NULL) { + HDF_LOGE("%s: invalid drs ops fail!", __func__); + return HDF_FAILURE; + } + ret = drsOps->GetString(node, "pinName", &hi35xx->desc[index].pinName, "NULL"); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read pinName failed", __func__); + return ret; + } + ... + ret = Hi35xxPinReadFunc(&hi35xx->desc[index], node, drsOps); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s:Pin read Func failed", __func__); + return ret; + } + hi35xx->cntlr.pins[index].pinName = hi35xx->desc[index].pinName; + hi35xx->cntlr.pins[index].priv = (void *)node; + ...... + return HDF_SUCCESS; + } + + static int32_t Hi35xxPinInit(struct HdfDeviceObject *device) + { + ...... + struct Hi35xxPinCntlr *hi35xx = NULL; + ...... + ret = Hi35xxPinCntlrInit(device, hi35xx); // 管脚控制器初始化 + ...... + DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { // 遍历管脚控制器的每个子节点 + ret = Hi35xxPinParsePinNode(childNode, hi35xx, index); // 解析子节点 + ...... + } + + hi35xx->cntlr.method = &g_method; // PinCntlrMethod实例化实例化对象的挂载 + ret = PinCntlrAdd(&hi35xx->cntlr); // 添加控制器 + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: add Pin cntlr: failed", __func__); + ret = HDF_FAILURE; + } + return HDF_SUCCESS; + } + ``` + + - Release函数开发参考 + + 入参: + + HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 + + 返回值: + + 无。 + + 函数说明: + + 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口。当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 + + ```c + static void Hi35xxPinRelease(struct HdfDeviceObject *device) + { + int32_t ret; + uint16_t number; + struct PinCntlr *cntlr = NULL; + struct Hi35xxPinCntlr *hi35xx = NULL; + struct DeviceResourceIface *drsOps = NULL; + + if (device == NULL || device->property == NULL) { + HDF_LOGE("%s: device or property is null", __func__); + return; + } + // 从hcs文件中读取管脚控制器编号 + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + if (drsOps == NULL || drsOps->GetUint32 == NULL || drsOps->GetString == NULL) { + HDF_LOGE("%s: invalid drs ops", __func__); + return; + } + ret = drsOps->GetUint16(device->property, "number", &number, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("%s: read cntlr number failed", __func__); + return; + } + + cntlr = PinCntlrGetByNumber(number); // 通过管脚控制器编号获取管脚控制器 + PinCntlrRemove(cntlr); + hi35xx = (struct Hi35xxPinCntlr *)cntlr; + if (hi35xx != NULL) { + if (hi35xx->regBase != NULL) { + OsalIoUnmap((void *)hi35xx->regBase); + } + OsalMemFree(hi35xx); + } + } + ``` + +4. 驱动调试 + + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,PIN管脚设置推拉方式、推拉强度等。 diff --git a/zh-cn/device-dev/driver/driver-platform-pwm-des.md b/zh-cn/device-dev/driver/driver-platform-pwm-des.md index 18dd7fd810d9762fecd60cd9783acbf6e48baca2..73420ff86e0c90b608b8766e85f9b2bd781e70b0 100644 --- a/zh-cn/device-dev/driver/driver-platform-pwm-des.md +++ b/zh-cn/device-dev/driver/driver-platform-pwm-des.md @@ -9,13 +9,16 @@ PWM即脉冲宽度调制(Pulse Width Modulation)的缩写,是一种对模 PWM接口定义了操作PWM设备的通用方法集合,包括: - PWM设备句柄获取和释放 + - PWM周期、占空比、极性的设置 + - PWM使能和关闭 + - PWM配置信息的获取和设置 ### 基本概念 -脉冲是“电脉冲”的简称,指电路中电流或电压短暂起伏的现象,其特点是突变和不连续性。脉冲的种类很多,常见的脉冲波形有:三角脉冲、尖脉冲、矩形脉冲、方形脉冲、梯形脉冲及阶梯脉冲等。脉冲的主要参数包括重复周期T(T=1/F,F为煎复频率)、脉冲幅度U、脉冲前沿上升时间ts、后沿下降时间t、脉冲宽度tk等。 +脉冲是“电脉冲”的简称,指电路中电流或电压短暂起伏的现象,其特点是突变和不连续性。脉冲的种类很多,常见的脉冲波形有:三角脉冲、尖脉冲、矩形脉冲、方形脉冲、梯形脉冲及阶梯脉冲等。脉冲的主要参数包括重复周期T(T=1/F,F为重复频率)、脉冲幅度U、脉冲前沿上升时间ts、后沿下降时间t、脉冲宽度tk等。 ### 运作机制 @@ -24,17 +27,20 @@ PWM接口定义了操作PWM设备的通用方法集合,包括: 独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为: - 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。 + - device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。 PWM模块各分层作用: - 接口层提供打开PWM设备、设置PWM设备周期、设置PWM设备占空时间、设置PWM设备极性、设置PWM设备参数、获取PWM设备参数、使能PWM设备、禁止PWM设备、关闭PWM设备的接口。 + - 核心层主要提供PWM控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。 + - 适配层主要是将钩子函数的功能实例化,实现具体的功能。 -**图1** PWM独立服务模式结构图 +**图 1** PWM独立服务模式结构图 -![image1](figures/独立服务模式结构图.png "PWM独立服务模式结构图") +![PWM独立服务模式结构图](figures/独立服务模式结构图.png) ## 使用指导 @@ -44,31 +50,31 @@ PWM模块各分层作用: ### 接口说明 -PWM模块设备属性如表1所示,PWM模块提供的主要接口如表2所示。 +PWM模块设备属性如表1所示,PWM模块提供的主要接口如表2所示,具体API详见//drivers/hdf_core/framework/include/platform/pwm_if.h。 -**表1** PwmConfig结构体介绍 +**表 1** PwmConfig结构体介绍 | 名称 | 描述 | | -------- | -------- | -| duty | 占空时间,以纳秒为单位。 | -| period | PWM周期,以纳秒为单位。 | -| number | 要生成的方波数:
- 正值:表示将生成指定数量的方波
- 0:表示方波将不断产生 | -| polarity | 极性:正极性/反极性。 | -| status | 状态:启用状态/禁用状态。 | +| duty | uint32_t类型,占空时间,以纳秒为单位。 | +| period | uint32_t类型,PWM周期,以纳秒为单位。 | +| number | uint32_t类型,要生成的方波数:
- 正值:表示将生成指定数量的方波
- 0:表示方波将不断产生 | +| polarity | uint8_t类型,极性:正极性/反极性。 | +| status | uint8_t类型,状态:启用状态/禁用状态。 | -**表2** PWM驱动API接口功能介绍 +**表 2** PWM驱动API接口功能介绍 -| 接口名 | | +| 接口名 | 接口描述| | ------------------------------------------------------------ | ------------------- | -| DevHandle PwmOpen(uint32_t num) | 打开PWM设备 | -| void PwmClose(DevHandle handle) | 关闭PWM设备 | -| int32_t PwmSetPeriod(DevHandle handle, uint32_t period) | 设置PWM设备周期 | -| int32_t PwmSetDuty(DevHandle handle, uint32_t duty) | 设置PWM设备占空时间 | -| int32_t PwmSetPolarity(DevHandle handle, uint8_t polarity) | 设置PWM设备极性 | -| int32_t PwmEnable(DevHandle handle) | 使能PWM设备 | -| int32_t PwmDisable(DevHandle handle) | 禁用PWM设备 | -| int32_t PwmSetConfig(DevHandle handle, struct PwmConfig *config) | 设置PWM设备参数 | -| int32_t PwmGetConfig(DevHandle handle, struct PwmConfig *config) | 获取PWM设备参数 | +| DevHandle PwmOpen(uint32_t num) | 打开PWM设备 | +| void PwmClose(DevHandle handle)| 关闭PWM设备 | +| int32_t PwmSetPeriod(DevHandle handle, uint32_t period) | 设置PWM设备周期 | +| int32_t PwmSetDuty(DevHandle handle, uint32_t duty) | 设置PWM设备占空时间 | +| int32_t PwmSetPolarity(DevHandle handle, uint8_t polarity) | 设置PWM设备极性 | +| int32_t PwmEnable(DevHandle handle) | 使能PWM设备 | +| int32_t PwmDisable(DevHandle handle) | 禁用PWM设备 | +| int32_t PwmSetConfig(DevHandle handle, struct PwmConfig \*config) | 设置PWM设备参数 | +| int32_t PwmGetConfig(DevHandle handle, struct PwmConfig \*config) | 获取PWM设备参数 | > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 本文涉及PWM的所有接口,支持内核态及用户态使用。 @@ -77,9 +83,9 @@ PWM模块设备属性如表1所示,PWM模块提供的主要接口如表2所示 使用PWM的一般流程如下图所示。 -**图2** PWM使用流程图 +**图 2** PWM使用流程图 -![image2](figures/PWM设备使用流程图.png "PWM设备使用流程图") +![PWM使用流程图](figures/PWM设备使用流程图.png) #### 获取PWM设备句柄 @@ -89,14 +95,14 @@ PWM模块设备属性如表1所示,PWM模块提供的主要接口如表2所示 DevHandle PwmOpen(uint32_t num); ``` -**表3** PwmOpen参数和返回值描述 +**表 3** PwmOpen参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| num | PWM设备号 | -| **返回值** | **返回值描述** | -| handle | 打开PWM设备成功,返回PWM设备句柄 | -| NULL | 打开PWM设备失败 | +| num | uint32_t类型,PWM设备号 | +| **返回值** | **返回值描述** | +| handle | 打开PWM设备成功,返回PWM设备句柄 | +| NULL | 打开PWM设备失败 | 假设系统中的PWM设备号为0,获取该PWM设备句柄的示例如下: @@ -105,9 +111,9 @@ uint32_t num = 0; // PWM设备号 DevHandle handle = NULL; handle = PwmOpen(num); // 打开PWM 0设备并获取PWM设备句柄 -if (handle == NULL) { +if (handle == NULL) { HDF_LOGE("PwmOpen: open pwm_%u failed.\n", num); - return; + return HDF_FAILURE; } ``` @@ -119,11 +125,11 @@ if (handle == NULL) { void PwmClose(DevHandle handle); ``` -**表4** PwmClose参数描述 +**表 4** PwmClose参数描述 | **参数** | **参数描述** | | -------- | -------- | -| handle | PWM设备句柄 | +| handle | DevHandle类型,PWM设备句柄 | ```c PwmClose(handle); // 关闭PWM设备销毁PWM设备句柄 @@ -135,14 +141,14 @@ PwmClose(handle); // 关闭PWM设备销毁PWM设备句柄 int32_t PwmEnable(DevHandle handle); ``` -**表5** PwmEnable参数和返回值描述 +**表 5** PwmEnable参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| handle | PWM设备句柄 | +| handle | DevHandle类型,PWM设备句柄 | | **返回值** | **返回值描述** | -| HDF_SUCCESS | 使能成功 | -| 负数 | 使能失败 | +| HDF_SUCCESS | 使能PWM设备成功 | +| 负数 | 使能PWM设备失败 | ```c int32_t ret; @@ -160,14 +166,14 @@ if (ret != HDF_SUCCESS) { int32_t PwmDisable(DevHandle handle); ``` -**表6** PwmDisable参数和返回值描述 +**表 6** PwmDisable参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| handle | PWM设备句柄 | +| handle | DevHandle类型,PWM设备句柄 | | **返回值** | **返回值描述** | -| HDF_SUCCESS | 禁用成功 | -| 负数 | 禁用失败 | +| HDF_SUCCESS | 禁用PWM设备成功 | +| 负数 | 禁用PWM设备失败 | ```c int32_t ret; @@ -185,15 +191,15 @@ if (ret != HDF_SUCCESS) { int32_t PwmSetPeriod(DevHandle handle, uint32_t period); ``` -**表7** PwmSetPeriod参数和返回值描述 +**表 7** PwmSetPeriod参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| handle | PWM设备句柄 | -| period | 要设置的周期,单位为纳秒 | -| **返回值** | **返回值描述** | -| HDF_SUCCESS | 设置成功 | -| 负数 | 设置失败 | +| handle | DevHandle类型,PWM设备句柄 | +| period | uint32_t类型,要设置的周期,单位为纳秒 | +| **返回值** | **返回值描述** | +| HDF_SUCCESS | 设置PWM设备周期成功 | +| 负数 | 设置PWM设备周期失败 | ```c int32_t ret; @@ -211,15 +217,15 @@ if (ret != HDF_SUCCESS) { int32_t PwmSetDuty(DevHandle handle, uint32_t duty); ``` -**表8** PwmSetDuty参数和返回值描述 +**表 8** PwmSetDuty参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| handle | PWM设备句柄 | -| duty | 要设置的占空时间,单位为纳秒 | -| **返回值** | **返回值描述** | -| HDF_SUCCESS | 设置成功 | -| 负数 | 设置失败 | +| handle | DevHandle类型,PWM设备句柄 | +| duty | uint32_t类型,要设置的占空时间,单位为纳秒 | +| **返回值** | **返回值描述** | +| HDF_SUCCESS | 设置PWM设备占空时间成功 | +| 负数 | 设置PWM设备占空时间失败 | ```c @@ -238,15 +244,15 @@ if (ret != HDF_SUCCESS) { int32_t PwmSetPolarity(DevHandle handle, uint8_t polarity); ``` -**表9** PwmSetPolarity参数和返回值描述 +**表 9** PwmSetPolarity参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| handle | PWM设备句柄 | -| polarity | 要设置的极性,正/反 | -| **返回值** | **返回值描述** | -| HDF_SUCCESS | 设置成功 | -| 负数 | 设置失败 | +| handle | DevHandle类型,PWM设备句柄 | +| polarity | uint8_t类型,要设置的极性,正/反 | +| **返回值** | **返回值描述** | +| HDF_SUCCESS | 设置PWM设备极性成功 | +| 负数 | 设置PWM设备极性失败 | ```c @@ -265,15 +271,15 @@ if (ret != HDF_SUCCESS) { int32_t PwmSetConfig(DevHandle handle, struct PwmConfig *config); ``` -**表10** PwmSetConfig参数和返回值描述 +**表 10** PwmSetConfig参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| handle | PWM设备句柄 | -| \*config | 参数指针 | +| handle | DevHandle类型,PWM设备句柄 | +| config | 结构体指针类型,配置参数 | | **返回值** | **返回值描述** | -| HDF_SUCCESS | 设置成功 | -| 负数 | 设置失败 | +| HDF_SUCCESS | 设置PWM设备参数成功 | +| 负数 | 设置PWM设备参数失败 | ```c int32_t ret; @@ -298,15 +304,15 @@ if (ret != HDF_SUCCESS) { int32_t PwmGetConfig(DevHandle handle, struct PwmConfig *config); ``` -**表11** PwmGetConfig参数和返回值描述 +**表 11** PwmGetConfig参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| handle | PWM设备句柄 | -| \*config | 参数指针 | +| handle | DevHandle类型,PWM设备句柄 | +| config | 结构体指针类型,配置参数 | | **返回值** | **返回值描述** | -| HDF_SUCCESS | 获取成功 | -| 负数 | 获取失败 | +| HDF_SUCCESS | 获取PWM设备参数成功 | +| 负数 | 获取PWM设备参数失败 | ```c int32_t ret; @@ -324,13 +330,21 @@ if (ret != HDF_SUCCESS) { 下面将基于Hi3516DV300开发板展示使用PWM完整操作,步骤主要如下: 1. 传入PWM设备号,打开PWM设备并获得PWM设备句柄。 + 2. 通过PWM设备句柄及待设置的周期,设置PWM设备周期。 + 3. 通过PWM设备句柄及待设置的占空时间,设置PWM设备占空时间。 + 4. 通过PWM设备句柄及待设置的极性,设置PWM设备极性。 + 5. 通过PWM设备句柄及待获取的设备参数,获取PWM设备参数。 + 6. 通过PWM设备句柄,使能PWM设备。 + 7. 通过PWM设备句柄及待设置的设备参数,设置PWM设备参数。 + 8. 通过PWM设备句柄,禁用PWM设备。 + 9. 通过PWM设备句柄,关闭PWM设备。 ```c @@ -341,7 +355,8 @@ static int32_t PwmTestSample(void) { int32_t ret; uint32_t num; - uint32_t period + uint32_t period; + uint32_t duty; DevHandle handle = NULL; struct PwmConfig pcfg; @@ -355,52 +370,55 @@ static int32_t PwmTestSample(void) handle = PwmOpen(num); // 获取PWM设备句柄 if (handle == NULL) { - HDF_LOGE("PwmOpen: open pwm_%u failed!\n", num); - return; + HDF_LOGE("PwmTestSample: open pwm_%u fail!\n", num); + return HDF_FAILURE; } - ret = PwmSetPeriod(handle, 50000000); // 设置周期为50000000纳秒 + period = 50000000; // 设置周期为50000000纳秒 + ret = PwmSetPeriod(handle, period); if (ret != HDF_SUCCESS) { - HDF_LOGE("PwmSetPeriod: pwm set period failed, ret %d\n", ret); + HDF_LOGE("PwmTestSample: pwm set period fail, ret:%d\n", ret); goto ERR; } - ret = PwmSetDuty(handle, 25000000); // 设置占空时间为25000000纳秒 + duty = 25000000; // 设置占空时间为25000000纳秒 + ret = PwmSetDuty(handle, duty); if (ret != HDF_SUCCESS) { - HDF_LOGE("PwmSetDuty: pwm set duty failed, ret %d\n", ret); + HDF_LOGE("PwmTestSample: pwm set duty fail, ret:%d\n", ret); goto ERR; } ret = PwmSetPolarity(handle, PWM_INVERTED_POLARITY); // 设置极性为反 if (ret != HDF_SUCCESS) { - HDF_LOGE("PwmSetPolarity: pwm set polarity failed, ret %d\n", ret); + HDF_LOGE("PwmTestSample: pwm set polarity fail, ret:%d\n", ret); goto ERR; } ret = PwmGetConfig(handle, &pcfg); // 获取PWM设备参数 if (ret != HDF_SUCCESS) { - HDF_LOGE("PwmGetConfig: get pwm config failed, ret %d\n", ret); + HDF_LOGE("PwmTestSample: get pwm config fail, ret:%d\n", ret); goto ERR; } ret = PwmEnable(handle); // 启用PWM设备 if (ret != HDF_SUCCESS) { - HDF_LOGE("PwmEnable: enable pwm failed, ret %d\n", ret); + HDF_LOGE("PwmEnable: enable pwm fail, ret:%d\n", ret); goto ERR; } ret = PwmSetConfig(handle, &pcfg); // 设置PWM设备参数 if (ret != HDF_SUCCESS) { - HDF_LOGE("PwmSetConfig: set pwm config failed, ret %d\n", ret); + HDF_LOGE("PwmTestSample: set pwm config fail, ret:%d\n", ret); goto ERR; } ret = PwmDisable(handle); // 禁用PWM设备 if (ret != HDF_SUCCESS) { - HDF_LOGE("PwmDisable: disable pwm failed, ret %d\n", ret); + HDF_LOGE("PwmTestSample: disable pwm fail, ret:%d\n", ret); goto ERR; } + HDF_LOGD("PwmTestSample: all tests end."); ERR: PwmClose(handle); // 销毁PWM设备句柄 return ret; diff --git a/zh-cn/device-dev/driver/driver-platform-pwm-develop.md b/zh-cn/device-dev/driver/driver-platform-pwm-develop.md index 192c93865f7973df001e0f478bded02db9fbb949..119375ff1de1a70f3e7ac49afcf3792609a54717 100755 --- a/zh-cn/device-dev/driver/driver-platform-pwm-develop.md +++ b/zh-cn/device-dev/driver/driver-platform-pwm-develop.md @@ -8,7 +8,7 @@ PWM(Pulse Width Modulation)即脉冲宽度调制,是一种对模拟信号 ### 基本概念 -脉冲是“电脉冲”的简称,指电路中电流或电压短暂起伏的现象,其特点是突变和不连续性。脉冲的种类很多,常见的脉冲波形有:三角脉冲、尖脉冲、矩形脉冲、方形脉冲、梯形脉冲及阶梯脉冲等。脉冲的主要参数包括重复周期T(T=1/F,F为煎复频率)、脉冲幅度U、脉冲前沿上升时间ts、后沿下降时间t、脉冲宽度tk等。 +脉冲是“电脉冲”的简称,指电路中电流或电压短暂起伏的现象,其特点是突变和不连续性。脉冲的种类很多,常见的脉冲波形有:三角脉冲、尖脉冲、矩形脉冲、方形脉冲、梯形脉冲及阶梯脉冲等。脉冲的主要参数包括重复周期T(T=1/F,F为重复频率)、脉冲幅度U、脉冲前沿上升时间ts、后沿下降时间t、脉冲宽度tk等。 ### 运作机制 @@ -17,17 +17,20 @@ PWM(Pulse Width Modulation)即脉冲宽度调制,是一种对模拟信号 独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为: - 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。 + - device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。 PWM模块各分层作用: - 接口层提供打开PWM设备、设置PWM设备周期、设置PWM设备占空时间、设置PWM设备极性、设置PWM设备参数、获取PWM设备参数、使能PWM设备、禁止PWM设备、关闭PWM设备的接口。 + - 核心层主要提供PWM控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。 + - 适配层主要是将钩子函数的功能实例化,实现具体的功能。 -**图1** PWM独立服务模式结构图 +**图 1** PWM独立服务模式结构图 -![image](figures/独立服务模式结构图.png "PWM独立服务模式结构图") +![PWM独立服务模式结构图](figures/独立服务模式结构图.png) ## 开发指导 @@ -49,7 +52,7 @@ struct PwmMethod { }; ``` -**表1** PwmMethod结构体成员的钩子函数功能说明 +**表 1** PwmMethod结构体成员的钩子函数功能说明 | 成员函数 | 入参 | 返回值 | 功能 | | -------- | -------- | -------- | -------- | @@ -61,249 +64,269 @@ struct PwmMethod { PWM模块适配包含以下四个步骤: -- 驱实例化驱动入口。 -- 配置属性文件。 -- 实例化PWM控制器对象。 -- 驱动调试。 +- 驱实例化驱动入口 + +- 配置属性文件 + +- 实例化PWM控制器对象 + +- 驱动调试 ### 开发实例 下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/pwm/pwm_hi35xx.c驱动为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。 -1. 驱实例化驱动入口。 - - 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 - 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 - - PWM驱动入口开发参考: - - ```c - struct HdfDriverEntry g_hdfPwm = { - .moduleVersion = 1, - .moduleName = "HDF_PLATFORM_PWM", // 【必要且与HCS文件中里面的moduleName匹配】 - .Bind = HdfPwmBind, // 见Bind参考 - .Init = HdfPwmInit, // 见Init参考 - .Release = HdfPwmRelease, // 见Release参考 - }; - HDF_INIT(g_hdfPwm); // 调用HDF_INIT将驱动入口注册到HDF框架中 - ``` - -2. 配置属性文件。 - - 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以两个PWM控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息。器件属性值与核心层PwmDev成员的默认值或限制范围有密切关系,比如PWM设备号,需要在pwm_config.hcs文件中增加对应的器件属性。 - - - device_info.hcs 配置参考: - - 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。 - - ```c - root { - device_info { - platform :: host { - hostName = "platform_host"; - priority = 50; - device_pwm :: device { // 为每一个PWM控制器配置一个HDF设备节点 - device0 :: deviceNode { - policy = 1; // 等于1,向内核态发布服务 - priority = 80; // 驱动启动优先级 - permission = 0644; // 驱动创建设备节点权限 - moduleName = "HDF_PLATFORM_PWM"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致 - serviceName = "HDF_PLATFORM_PWM_0"; // 【必要且唯一】驱动对外发布服务的名称 - deviceMatchAttr = "hisilicon_hi35xx_pwm_0"; // 【必要】用于配置控制器私有数据,要与pwm_config.hcs中对应控制器保持一致,具体的控制器信息在pwm_config.hcs中 - } - device1 :: deviceNode { - policy = 1; - priority = 80; - permission = 0644; - moduleName = "HDF_PLATFORM_PWM"; - serviceName = "HDF_PLATFORM_PWM_1"; - deviceMatchAttr = "hisilicon_hi35xx_pwm_1"; - } - ... - } - } - } - } - ``` - - - pwm_config.hcs 配置参考 - - 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pwm/pwm_config.hcs文件配置器件属性,其中配置参数如下: - - ```c - root { - platform { - pwm_config { - template pwm_device { // 【必要】配置模板,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值 - serviceName = ""; - match_attr = ""; - num = 0; // 【必要】设备号 - base = 0x12070000; // 【必要】地址映射需要 - } - device_0x12070000 :: pwm_device { // 存在多个设备时,请逐一添加相关HDF节点和设备节点信息。 - match_attr = "hisilicon_hi35xx_pwm_0"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致 - } - device_0x12070020 :: pwm_device { - match_attr = "hisilicon_hi35xx_pwm_1"; - num = 1; - base = 0x12070020; // 【必要】地址映射需要 - } - } - } - } - ``` - - 需要注意的是,新增pwm_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。 - - ```c - #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pwm/pwm_config.hcs" // 配置文件相对路径 - ``` - -3. 实例化PWM控制器对象。 - - 完成驱动入口注册之后,下一步就是以核心层PwmDev对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化PwmDev成员PwmMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。 - - - 驱动适配者自定义结构体参考。 - - 从驱动的角度看,驱动适配者自定义结构体是参数和数据的载体,而且pwm_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象,例如PWM设备号。 - - ```c - struct HiPwm { - struct PwmDev dev; // 【必要】 核是核心层控制对象 - volatile unsigned char *base; // 【必要】地址映射需要,寄存器基地址 - struct HiPwmRegs *reg; // 设备属性结构体,可自定义。 - bool supportPolarity; // 是否支持极性 - }; - - struct PwmDev { // PwmDev是核心层控制器结构体,其中的成员在Init函数中会被赋值。 - struct IDeviceIoService service; // 驱动服务 - struct HdfDeviceObject *device; // 驱动设备对象 - struct PwmConfig cfg; // 设备属性结构体,相关定义见下。 - struct PwmMethod *method; // 钩子函数 - bool busy; // 是否繁忙 - uint32_t num; // 设备号 - OsalSpinlock lock; // 自旋锁 - void *priv; // 私有数据 - }; - - struct PwmConfig { // PWM设备属性 - uint32_t duty; // 占空时间 nanoseconds - uint32_t period; // pwm 周期 nanoseconds - uint32_t number; // pwm 连续个数 - uint8_t polarity; // Polarity - // ------------------- | -------------- - // PWM_NORMAL_POLARITY | Normal polarity - // PWM_INVERTED_POLARITY | Inverted polarity - // - uint8_t status; // 运行状态 - // ------------------ | ----------------- - // PWM_DISABLE_STATUS | Disabled - // PWM_ENABLE_STATUS | Enabled - }; - ``` - - - PwmDev成员钩子函数结构体PwmMethod的实例化,其他成员在Init函数中初始化。 +1. 驱实例化驱动入口 + + 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 + + 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 + + PWM驱动入口开发参考: + + ```c + struct HdfDriverEntry g_hdfPwm = { + .moduleVersion = 1, + .moduleName = "HDF_PLATFORM_PWM", // 【必要且与HCS文件中里面的moduleName匹配】 + .Bind = HdfPwmBind, // 挂接PWM模块Bind实例化 + .Init = HdfPwmInit, // 挂接PWM模块Init实例化 + .Release = HdfPwmRelease, // 挂接PWM模块Release实例化 + }; + HDF_INIT(g_hdfPwm); // 调用HDF_INIT将驱动入口注册到HDF框架中 + ``` + +2. 配置属性文件 + + 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以两个PWM控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息,以及在pwm_config.hcs文件中增加对应的器件属性。器件属性值与核心层PwmDev成员的默认值或限制范围有密切关系,比如PWM设备号,需要在pwm_config.hcs文件中增加对应的器件属性。 + + 独立服务模式的特点是device_info.hcs文件中设备节点代表着一个设备对象,如果存在多个设备对象,则按需添加,注意服务名与驱动私有数据匹配的关键字名称必须唯一。其中各项参数如表2所示: + + **表 2** device_info.hcs节点参数说明 + + | 成员名 | 值 | + | -------- | -------- | + | policy | 驱动服务发布的策略,PWM控制器具体配置为2,表示驱动对内核态和用户态都发布服务 | + | priority | 驱动启动优先级(0-200),值越大优先级越低。PWM控制器具体配置为80 | + | permission | 驱动创建设备节点权限,PWM控制器具体配置为0664 | + | moduleName | 驱动名称,PWM控制器固定为HDF_PLATFORM_PWM | + | serviceName | 驱动对外发布服务的名称,PWM控制器服务名设置为HDF_PLATFORM_PWM_X,X代表PWM控制器编号 | + | deviceMatchAttr | 驱动私有数据匹配的关键字,PWM控制器设置为hisilicon_hi35xx_pwm_X,X代表PWM控制器编号 | + + + - device_info.hcs 配置参考: + + 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。 + + ```c + root { + device_info { + platform :: host { + hostName = "platform_host"; + priority = 50; + device_pwm :: device { // 为每一个PWM控制器配置一个HDF设备节点 + device0 :: deviceNode { + policy = 2; // policy字段是驱动服务发布的策略,如果需要面向用户态,则为2 + priority = 80; // 驱动启动优先级 + permission = 0644; // 驱动创建设备节点权限 + moduleName = "HDF_PLATFORM_PWM"; // 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致 + serviceName = "HDF_PLATFORM_PWM_0"; // 【必要且唯一】驱动对外发布服务的名称 + deviceMatchAttr = "hisilicon_hi35xx_pwm_0"; // 【必要】用于配置控制器私有数据,要与pwm_config.hcs中对应控制器保持一致,具体的控制器信息在pwm_config.hcs中 + } + device1 :: deviceNode { + policy = 2; + priority = 80; + permission = 0644; + moduleName = "HDF_PLATFORM_PWM"; + serviceName = "HDF_PLATFORM_PWM_1"; + deviceMatchAttr = "hisilicon_hi35xx_pwm_1"; + } + ...... // 如果存在多个PWM设备时【必须】添加节点,否则不用 + } + } + } + } + ``` + + - pwm_config.hcs 配置参考 + + 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pwm/pwm_config.hcs文件配置器件属性,其中配置参数如下: + + ```c + root { + platform { + pwm_config { + template pwm_device { // 【必要】配置模板,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值 + serviceName = ""; + match_attr = ""; + num = 0; // 【必要】设备号 + base = 0x12070000; // 【必要】地址映射需要 + } + device_0x12070000 :: pwm_device { // 存在多个设备时,请逐一添加相关HDF节点和设备节点信息。 + match_attr = "hisilicon_hi35xx_pwm_0"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致 + } + device_0x12070020 :: pwm_device { + match_attr = "hisilicon_hi35xx_pwm_1"; + num = 1; + base = 0x12070020; // 【必要】地址映射需要 + } + ...... // 如果存在多个PWM设备时【必须】添加节点,否则不用 + } + } + } + ``` + + 需要注意的是,新增pwm_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。 + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/pwm/pwm_config.hcs" // 配置文件相对路径 + ``` + +3. 实例化PWM控制器对象 + + 完成驱动入口注册之后,下一步就是以核心层PwmDev对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化PwmDev成员PwmMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。 + + - 驱动适配者自定义结构体参考。 + + 从驱动的角度看,驱动适配者自定义结构体是参数和数据的载体,而且pwm_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象,例如PWM设备号。 + + ```c + struct HiPwm { + struct PwmDev dev; // 【必要】 核是核心层控制对象 + volatile unsigned char *base; // 【必要】地址映射需要,寄存器基地址 + struct HiPwmRegs *reg; // 设备属性结构体,可自定义。 + bool supportPolarity; // 是否支持极性 + }; + + struct PwmDev { // PwmDev是核心层控制器结构体,其中的成员在Init函数中会被赋值。 + struct IDeviceIoService service; // 驱动服务 + struct HdfDeviceObject *device; // 驱动设备对象 + struct PwmConfig cfg; // 设备属性结构体,相关定义见下。 + struct PwmMethod *method; // 钩子函数 + bool busy; // 是否繁忙 + uint32_t num; // 设备号 + OsalSpinlock lock; // 自旋锁 + void *priv; // 私有数据 + }; + + struct PwmConfig { // PWM设备属性 + uint32_t duty; // 占空时间 nanoseconds + uint32_t period; // pwm 周期 nanoseconds + uint32_t number; // pwm 连续个数 + uint8_t polarity; // Polarity + // ------------------- | -------------- + // PWM_NORMAL_POLARITY | Normal polarity + // PWM_INVERTED_POLARITY | Inverted polarity + // + uint8_t status; // 运行状态 + // ------------------ | ----------------- + // PWM_DISABLE_STATUS | Disabled + // PWM_ENABLE_STATUS | Enabled + }; + ``` + + - PwmDev成员钩子函数结构体PwmMethod的实例化,其他成员在Init函数中初始化。 - ```c - struct PwmMethod g_pwmOps = { // pwm_hi35xx.c中的示例:钩子函数实例化 - .setConfig = HiPwmSetConfig, // 配置属性 - }; - ``` - - - Init函数开发参考 - - 入参: - - HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 - - 返回值: - - HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 - - | 状态(值) | 问题描述 | - | -------- | -------- | - | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | - | HDF_ERR_MALLOC_FAIL | 内存分配失败 | - | HDF_ERR_INVALID_PARAM | 参数非法 | - | HDF_ERR_IO | I/O 错误 | - | HDF_SUCCESS | 初始化成功 | - | HDF_FAILURE | 初始化失败 | - - 函数说明: - - 初始化自定义结构体对象,初始化PwmDev成员,调用核心层PwmDeviceAdd函数,完成PWM控制器的添加。 - - ```c - // 此处Bind函数为空函数,可与Init函数结合,也可根据驱动适配者需要实现相关操作。 - static int32_t HdfPwmBind(struct HdfDeviceObject *obj) - { - (void)obj; - return HDF_SUCCESS; - } - - static int32_t HdfPwmInit(struct HdfDeviceObject *obj) - { - int ret; - struct HiPwm *hp = NULL; - ... - hp = (struct HiPwm *)OsalMemCalloc(sizeof(*hp)); - ... - ret = HiPwmProbe(hp, obj); // 【必要】实现见下 - ... - return ret; - } - - static int32_t HiPwmProbe(struct HiPwm *hp, struct HdfDeviceObject *obj) - { - uint32_t tmp; - struct DeviceResourceIface *iface = NULL; - - iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); //初始化自定义结构体HiPwm - ... - - hp->reg = (struct HiPwmRegs *)hp->base; // 初始化自定义结构体HiPwm - hp->supportPolarity = false; // 初始化自定义结构体HiPwm - hp->dev.method = &g_pwmOps; // PwmMethod的实例化对象的挂载 - hp->dev.cfg.duty = PWM_DEFAULT_DUTY_CYCLE; // 初始化PwmDev - hp->dev.cfg.period = PWM_DEFAULT_PERIOD; // 初始化PwmDev - hp->dev.cfg.polarity = PWM_DEFAULT_POLARITY; // 初始化PwmDev - hp->dev.cfg.status = PWM_DISABLE_STATUS; // 初始化PwmDev - hp->dev.cfg.number = 0; // 初始化PwmDev - hp->dev.busy = false; // 初始化PwmDev - if (PwmDeviceAdd(obj, &(hp->dev)) != HDF_SUCCESS) { // 【重要】调用核心层函数,初始化hp->dev的设备和服务。 - OsalIoUnmap((void *)hp->base); - return HDF_FAILURE; - } - return HDF_SUCCESS; - } - ``` - - - Release函数开发参考 - - 入参: - - HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 - - 返回值: - - 无。 - - 函数说明: - - 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 - - ```c - static void HdfPwmRelease(struct HdfDeviceObject *obj) - { - struct HiPwm *hp = NULL; - ... - hp = (struct HiPwm *)obj->service; // 这里有HdfDeviceObject到HiPwm的强制转化 - ... - PwmDeviceRemove(obj, &(hp->dev)); // 【必要】调用核心层函数,释放PwmDev的设备和服务,这里有HiPwm到PwmDev的强制转化。 - HiPwmRemove(hp); // 释放HiPwm - } - ``` - -4. 驱动调试。 - - 【可选】针对新增驱动程序,建议验证驱动基本功能,例如PWM控制状态,中断响应情况等。 \ No newline at end of file + ```c + struct PwmMethod g_pwmOps = { // pwm_hi35xx.c中的示例:钩子函数实例化 + .setConfig = HiPwmSetConfig, // 配置属性 + }; + ``` + + - Init函数开发参考 + + 入参: + + HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 + + 返回值: + + HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 + + **表 3** HDF_STATUS相关状态说明 + + | 状态(值) | 问题描述 | + | -------- | -------- | + | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | + | HDF_ERR_MALLOC_FAIL | 内存分配失败 | + | HDF_ERR_INVALID_PARAM | 参数非法 | + | HDF_ERR_IO | I/O 错误 | + | HDF_SUCCESS | 初始化成功 | + | HDF_FAILURE | 初始化失败 | + + 函数说明: + + 初始化自定义结构体对象,初始化PwmDev成员,调用核心层PwmDeviceAdd函数,完成PWM控制器的添加。 + + ```c + // 此处Bind函数为空函数,可与Init函数结合,也可根据驱动适配者需要实现相关操作。 + static int32_t HdfPwmBind(struct HdfDeviceObject *obj) + { + (void)obj; + return HDF_SUCCESS; + } + + static int32_t HdfPwmInit(struct HdfDeviceObject *obj) + { + int ret; + struct HiPwm *hp = NULL; + ...... + hp = (struct HiPwm *)OsalMemCalloc(sizeof(*hp)); + ...... + ret = HiPwmProbe(hp, obj); // 【必要】实现见下 + ...... + return ret; + + static int32_t HiPwmProbe(struct HiPwm *hp, struct HdfDeviceObject *obj) + { + uint32_t tmp; + struct DeviceResourceIface *iface = NULL; + + iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); // 初始化自定义结构体HiPwm + ...... + + hp->reg = (struct HiPwmRegs *)hp->base; // 初始化自定义结构体HiPwm + hp->supportPolarity = false; // 初始化自定义结构体HiPwm + hp->dev.method = &g_pwmOps; // PwmMethod的实例化对象的挂载 + hp->dev.cfg.duty = PWM_DEFAULT_DUTY_CYCLE; // 初始化PwmDev + hp->dev.cfg.period = PWM_DEFAULT_PERIOD; // 初始化PwmDev + hp->dev.cfg.polarity = PWM_DEFAULT_POLARITY; // 初始化PwmDev + hp->dev.cfg.status = PWM_DISABLE_STATUS; // 初始化PwmDev + hp->dev.cfg.number = 0; // 初始化PwmDev + hp->dev.busy = false; // 初始化PwmDev + if (PwmDeviceAdd(obj, &(hp->dev)) != HDF_SUCCESS) { // 【重要】调用核心层函数,初始化hp->dev的设备和服务。 + OsalIoUnmap((void *)hp->base); + return HDF_FAILURE; + } + return HDF_SUCCESS; + } + ``` + + - Release函数开发参考 + + 入参: + + HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 + + 返回值: + + 无。 + + 函数说明: + + 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 + + ```c + static void HdfPwmRelease(struct HdfDeviceObject *obj) + { + struct HiPwm *hp = NULL; + ...... + hp = (struct HiPwm *)obj->service; // 这里有HdfDeviceObject到HiPwm的强制转化 + ...... + PwmDeviceRemove(obj, &(hp->dev)); // 【必要】调用核心层函数,释放PwmDev的设备和服务,这里有HiPwm到PwmDev的强制转化。 + HiPwmRemove(hp); // 释放HiPwm + } + ``` + +4. 驱动调试 + + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如PWM控制状态等。 diff --git a/zh-cn/device-dev/driver/driver-platform-regulator-des.md b/zh-cn/device-dev/driver/driver-platform-regulator-des.md index 058114fcbb760cfa5ea74bd4d4610893a46dc147..81eb60909ce94a4651ecbf36859cfbb4801e8daf 100755 --- a/zh-cn/device-dev/driver/driver-platform-regulator-des.md +++ b/zh-cn/device-dev/driver/driver-platform-regulator-des.md @@ -1,15 +1,19 @@ # Regulator - ## 概述 ### 功能简介 Regulator模块用于控制系统中某些设备的电压/电流供应。在嵌入式系统(尤其是手机)中,控制耗电量很重要,直接影响到电池的续航时间。所以,如果系统中某一个模块暂时不需要使用,就可以通过Regulator关闭其电源供应;或者降低提供给该模块的电压、电流大小。 + Regulator接口定义了操作Regulator设备的通用方法集合,包括: + - Regulator设备句柄获取和销毁。 + - Regulator设备电压、电流的设置。 + - Regulator设备使能和关闭。 + - Regulator设备电压、电流和状态的获取。 @@ -17,32 +21,35 @@ Regulator接口定义了操作Regulator设备的通用方法集合,包括: - 校准器 - 当输入电压和输出负载发生变化时可以通过软件调整,使其能够提供稳定的输出电压。 + 当输入电压和输出负载发生变化时可以通过软件调整,使其能够提供稳定的输出电压。 - Consumer - 由Regulator供电的设备统称为Consumer, 其可分为静态和动态两类: + 由Regulator供电的设备统称为Consumer, 其可分为静态和动态两类: + + * 静态:不需要改变电压电流,只需要开关电源,通常在bootloader、firmware、kernel board阶段被设置。 - * 静态:不需要改变电压电流,只需要开关电源,通常在bootloader、firmware、kernel board阶段被设置。 - * 动态:根据操作需求改变电压电流。 + * 动态:根据操作需求改变电压电流。 - PMIC(Power Management IC) - 电源管理芯片,内含多个电源甚至其他子系统。 + 电源管理芯片,内含多个电源甚至其他子系统。 ### 运作机制 -在HDF框架中,Regulator模块接口适配模式采用统一服务模式(如图1),这需要一个设备服务来作为Regulator模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如Regulator可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问,实现便捷管理和节约资源的目的。 +在HDF框架中,Regulator模块接口适配模式采用统一服务模式(如图1所示),这需要一个设备服务来作为Regulator模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如Regulator可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。相反,采用统一服务模式可以使用一个设备服务作为管理器,统一处理所有同类型对象的外部访问,实现便捷管理和节约资源的目的。 Regulator模块各分层的作用为: - 接口层:提供打开设备,操作Regulator,关闭设备的能力。 + - 核心层:主要负责服务绑定、初始化以及释放管理器,并提供添加、删除以及获取Regulator设备的能力。 + - 适配层:由驱动适配者实现与硬件相关的具体功能,如设备的初始化等。 **图 1** Regulator统一服务模式结构图 -![image1](figures/统一服务模式结构图.png) +![Regulator统一服务模式结构图](figures/统一服务模式结构图.png) ### 约束与限制 @@ -55,13 +62,14 @@ Regulator模块API当前仅支持内核态调用。 Regulator主要用于: 1. 用于控制系统中某些设备的电压/电流供应。 + 2. 用于稳压电源的管理。 ### 接口说明 Regulator模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/regulator_if.h。 -**表1** Regulator设备API接口说明 +**表 1** Regulator设备API接口说明 | 接口名 | 接口描述 | | --------------------- | ------------------------- | @@ -80,9 +88,9 @@ Regulator模块提供的主要接口如表1所示,具体API详见//drivers/hdf 使用Regulator设备的一般流程如图2所示。 -**图 2** Regulator设备使用流程图 +**图 2** Regulator设备使用流程图 -![](figures/REGULATOR设备使用流程图.png) +![Regulator设备使用流程图](figures/REGULATOR设备使用流程图.png) #### 获取Regulator设备句柄 @@ -92,26 +100,26 @@ Regulator模块提供的主要接口如表1所示,具体API详见//drivers/hdf DevHandle RegulatorOpen(const char *name); ``` -**表2** RegulatorOpen参数和返回值描述 +**表 2** RegulatorOpen参数和返回值描述 | 参数 | 参数描述 | | ---------- | ----------------------------- | -| name | Regulator设备名称 | +| name | 字符指针,Regulator设备名称 | | **返回值** | **返回值描述** | | handle | 获取成功返回Regulator设备句柄 | | NULL | 获取失败 | - - ```c -/* Regulator设备名称 */ +// Regulator设备名称 const char *name = "regulator_virtual_1"; DevHandle handle = NULL; -/* 获取Regulator设备句柄 */ +// 获取Regulator设备句柄 handle = RegulatorOpen(name); -if (handle == NULL) { - /* 错误处理 */ +if (handle == NULL) { + // 错误处理 + HDF_LOGE("RegulatorOpen: open regulator fail.\n"); + return HDF_FAILURE; } ``` @@ -123,14 +131,14 @@ if (handle == NULL) { void RegulatorClose(DevHandle handle); ``` -**表3** RegulatorClose参数描述 +**表 3** RegulatorClose参数描述 | 参数 | 参数描述 | | ------ | ----------------- | -| handle | Regulator设备句柄 | +| handle | DevHandle类型,Regulator设备句柄 | ```c -/* 销毁Regulator设备句柄 */ +// 销毁Regulator设备句柄 RegulatorClose(handle); ``` @@ -142,24 +150,24 @@ RegulatorClose(handle); int32_t RegulatorEnable(DevHandle handle); ``` -**表4** RegulatorEnable参数描述 +**表 4** RegulatorEnable参数描述 | 参数 | 参数描述 | | ---------- | ----------------- | -| handle | Regulator设备句柄 | +| handle | DevHandle类型,Regulator设备句柄 | | **返回值** | **返回值描述** | -| 0 | 使能成功 | -| 负数 | 使能失败 | - - +| HDF_SUCCESS | 使能Regulator设备成功 | +| 负数 | 使能Regulator设备失败 | ```c int32_t ret; -/* 启用Regulator设备 */ +// 启用Regulator设备 ret = RegulatorEnable(handle); -if (ret != 0) { - /* 错误处理 */ +if (ret != HDF_SUCCESS) { + // 错误处理 + HDF_LOGE("RegulatorEnable: enable regulator fail, ret:%d\n", ret); + return ret; } ``` @@ -171,22 +179,24 @@ if (ret != 0) { int32_t RegulatorDisable(DevHandle handle); ``` -**表5** RegulatorDisable参数描述 +**表 5** RegulatorDisable参数描述 | 参数 | 参数描述 | | ---------- | ----------------- | -| handle | Regulator设备句柄 | +| handle | DevHandle类型,Regulator设备句柄 | | **返回值** | **返回值描述** | -| 0 | 禁用成功 | -| 负数 | 禁用失败 | +| HDF_SUCCESS | 禁用Regulator设备成功 | +| 负数 | 禁用Regulator设备失败 | ```c int32_t ret; -/* 禁用Regulator设备 */ +// 禁用Regulator设备 ret = RegulatorDisable(handle); -if (ret != 0) { - /* 错误处理 */ +if (ret != HDF_SUCCESS) { + // 错误处理 + HDF_LOGE("RegulatorDisable: disable regulator fail, ret:%d\n", ret); + return ret; } ``` @@ -198,23 +208,25 @@ if (ret != 0) { int32_t RegulatorForceDisable(DevHandle handle); ``` -**表6** RegulatorForceDisable参数描述 +**表 6** RegulatorForceDisable参数描述 | 参数 | 参数描述 | | ---------- | ----------------- | -| handle | Regulator设备句柄 | +| handle | DevHandle类型,Regulator设备句柄 | | **返回值** | **返回值描述** | -| 0 | 禁用成功 | -| 负数 | 禁用失败 | +| HDF_SUCCESS | 强制禁用Regulator设备成功 | +| 负数 | 强制禁用Regulator设备失败 | ```c int32_t ret; -/* 强制禁用Regulator设备 */ +// 强制禁用Regulator设备 ret = RegulatorForceDisable(handle); -if (ret != 0) { - /* 错误处理 */ +if (ret != HDF_SUCCESS) { + // 错误处理 + HDF_LOGE("RegulatorForceDisable: force disable regulator fail, ret:%d\n", ret); + return ret; } ``` @@ -224,26 +236,28 @@ if (ret != 0) { int32_t RegulatorSetVoltage(DevHandle handle, uint32_t minUv, uint32_t maxUv); ``` -**表7** RegulatorSetVoltage参数描述 +**表 7** RegulatorSetVoltage参数描述 | 参数 | 参数描述 | | ---------- | ----------------- | -| handle | Regulator设备句柄 | -| minUv | 最小电压 | -| maxUv | 最大电压 | +| handle | DevHandle类型,Regulator设备句柄 | +| minUv | uint32_t类型,最小电压 | +| maxUv | uint32_t类型,最大电压 | | **返回值** | **返回值描述** | -| 0 | 设置成功 | -| 负数 | 设置失败 | +| HDF_SUCCESS | 设置Regulator输出电压范围成功 | +| 负数 | 设置Regulator输出电压范围失败 | ```c int32_t ret; int32_t minUv = 0; // 最小电压为0µV int32_t maxUv = 20000; // 最大电压为20000µV -/* 设置Regulator电压输出电压范围 */ +// 设置Regulator电压输出电压范围 ret = RegulatorSetVoltage(handle, minUv, maxUv); -if (ret != 0) { - /* 错误处理 */ +if (ret != HDF_SUCCESS) { + // 错误处理 + HDF_LOGE("RegulatorSetVoltage: regulator set Voltage fail, ret:%d\n", ret); + return ret; } ``` @@ -253,25 +267,27 @@ if (ret != 0) { int32_t RegulatorGetVoltage(DevHandle handle, uint32_t *voltage); ``` -**表8** RegulatorGetVoltage参数描述 +**表 8** RegulatorGetVoltage参数描述 | 参数 | 参数描述 | | ---------- | ----------------- | -| handle | Regulator设备句柄 | -| *voltage | 参数指针 | +| handle | DevHandle类型,Regulator设备句柄 | +| voltage | uint32_t类型指针,待获取的Regulator电压 | | **返回值** | **返回值描述** | -| 0 | 获取成功 | -| 负数 | 获取失败 | +| HDF_SUCCESS | 获取Regulator电压成功 | +| 负数 | 获取Regulator电压失败 | ```c int32_t ret; uint32_t voltage; -/*获取Regulator电压*/ +// 获取Regulator电压 ret = RegulatorGetVoltage(handle, &voltage); -if (ret != 0) { - /*错误处理*/ +if (ret != HDF_SUCCESS) { + // 错误处理 + HDF_LOGE("RegulatorGetVoltage: regulator get Voltage fail, ret:%d\n", ret); + return ret; } ``` @@ -281,26 +297,28 @@ if (ret != 0) { int32_t RegulatorSetCurrent(DevHandle handle, uint32_t minUa, uint32_t maxUa); ``` -**表9** RegulatorSetCurrent参数描述 +**表 9** RegulatorSetCurrent参数描述 | 参数 | 参数描述 | | ---------- | ----------------- | -| handle | Regulator设备句柄 | -| minUa | 最小电流 | -| maxUa | 最大电流 | +| handle | DevHandle类型,Regulator设备句柄 | +| minUa | uint32_t类型,最小电流 | +| maxUa | uint32_t类型,最大电流 | | **返回值** | **返回值描述** | -| 0
| 设置成功 | -| 负数 | 设置失败 | +| HDF_SUCCESS | 设置Regulator输出电流范围成功 | +| 负数 | 设置Regulator输出电流范围失败 | ```c int32_t ret; int32_t minUa = 0; // 最小电流为0μA int32_t maxUa = 200; // 最大电流为200μA -/* 设置Regulator输出电流范围 */ +// 设置Regulator输出电流范围 ret = RegulatorSetCurrent(handle, minUa, maxUa); -if (ret != 0) { - /* 错误处理 */ +if (ret != HDF_SUCCESS) { + // 错误处理 + HDF_LOGE("RegulatorSetCurrent: regulator set current fail, ret:%d\n", ret); + return ret; } ``` @@ -310,24 +328,26 @@ if (ret != 0) { int32_t RegulatorGetCurrent(DevHandle handle, uint32_t *regCurrent); ``` -**表10** RegulatorGetCurrent参数描述 +**表 10** RegulatorGetCurrent参数描述 | 参数 | 参数描述 | | ----------- | ----------------- | -| handle | Regulator设备句柄 | -| *regCurrent | 参数指针 | +| handle | DevHandle类型,Regulator设备句柄 | +| regCurrent | uint32_t类型指针,待获取的Regulator电流 | | **返回值** | **返回值描述** | -| 0 | 获取成功 | +| HDF_SUCCESS | 获取成功 | | 负数 | 获取失败 | ```c int32_t ret; uint32_t regCurrent; -/* 获取Regulator电流 */ +// 获取Regulator电流 ret = RegulatorGetCurrent(handle, ®Current); -if (ret != 0) { - /* 错误处理 */ +if (ret != HDF_SUCCESS) { + // 错误处理 + HDF_LOGE("RegulatorGetCurrent: regulator get current fail, ret:%d\n", ret); + return ret; } ``` @@ -337,29 +357,29 @@ if (ret != 0) { int32_t RegulatorGetStatus(DevHandle handle, uint32_t *status); ``` -**表11** RegulatorGetStatus参数描述 +**表 11** RegulatorGetStatus参数描述 | 参数 | 参数描述 | | ---------- | ----------------- | -| handle | Regulator设备句柄 | -| *status | 参数指针 | +| handle | DevHandle类型,Regulator设备句柄 | +| status | uint32_t类型指针,待获取Regulator状态 | | **返回值** | **返回值描述** | -| 0 | 获取成功 | +| HDF_SUCCESS | 获取成功 | | 负数 | 获取失败 | ```c int32_t ret; uint32_t status; -/* 获取Regulator状态 */ +// 获取Regulator状态 ret = RegulatorGetStatus(handle, &status); -if (ret != 0) { - /* 错误处理 */ +if (ret != HDF_SUCCESS) { + // 错误处理 + HDF_LOGE("RegulatorGetStatus: regulator get status fail, ret:%d\n", ret); + return ret; } ``` - - ## 使用实例 本例拟对Hi3516DV300开发板上Regulator设备进行简单的读取操作。 diff --git a/zh-cn/device-dev/driver/driver-platform-regulator-develop.md b/zh-cn/device-dev/driver/driver-platform-regulator-develop.md index 4db05339c9ca1561690cca164f41cb788c862e7c..346a285a0380c67b7b2a7fdf8b742af90b8433e0 100755 --- a/zh-cn/device-dev/driver/driver-platform-regulator-develop.md +++ b/zh-cn/device-dev/driver/driver-platform-regulator-develop.md @@ -1,6 +1,5 @@ # Regulator - ## 概述 ### 功能简介 @@ -9,19 +8,21 @@ Regulator模块用于控制系统中各类设备的电压/电流供应。在嵌 ### 运作机制 -在HDF框架中,Regulator模块接口适配模式采用统一服务模式(如图1),这需要一个设备服务来作为Regulator模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如Regulator可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。 +在HDF框架中,Regulator模块接口适配模式采用统一服务模式(如图1所示),这需要一个设备服务来作为Regulator模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如Regulator可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。 Regulator模块各分层的作用为: - 接口层:提供打开设备,操作Regulator,关闭设备的能力。 + - 核心层:主要负责服务绑定、初始化以及释放管理器,并提供添加、删除以及获取Regulator设备的能力。 + - 适配层:由驱动适配者实现与硬件相关的具体功能,如设备的初始化等。 在统一模式下,所有的控制器都被核心层统一管理,并由核心层统一发布一个服务供接口层,因此这种模式下驱动无需再为每个控制器发布服务。 -**图 1** 统一服务模式结构图 +**图 1** Regulator统一服务模式结构图 -![image1](figures/统一服务模式结构图.png) +![Regulator统一服务模式结构图](figures/统一服务模式结构图.png) ### 约束与限制 @@ -57,30 +58,33 @@ struct RegulatorMethod { **表 1** RegulatorMethod 结构体成员的钩子函数功能说明 -| 成员函数 | 入参 | 返回值 | 功能 | +| 成员函数 | 入参 | 返回值 | 功能 | | ------------ | ----------------------------------------------------------- | ----------------- | ---------------- | -| open | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 打开设备 | -| close | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 关闭设备 | -| release | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 释放设备句柄 | -| enable | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 使能 | -| disable | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 禁用 | -| forceDisable | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 强制禁用 | -| setVoltage | node:结构体指针,核心层Regulator节点
minUv:uint32_t变量,最小电压
maxUv:uint32_t变量,最大电压 | HDF_STATUS相关状态 | 设置输出电压范围 | -| getVoltage | node:结构体指针,核心层Regulator节点
voltage:uint32_t指针,传出电压值 | HDF_STATUS相关状态 | 获取电压 | -| setCurrent | node:结构体指针,核心层Regulator节点
minUa:uint32_t变量,最小电流
maxUa:uint32_t变量,最大电流 | HDF_STATUS相关状态 | 设置输出电流范围 | -| getCurrent | node:结构体指针,核心层Regulator节点
regCurrent:uint32_t指针,传出电流值 | HDF_STATUS相关状态 | 获取电流 | -| getStatus | node:结构体指针,核心层Regulator节点
status:uint32_t指针,传出状态值 | HDF_STATUS相关状态 | 获取设备状态 | +| open | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 打开设备 | +| close | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 关闭设备 | +| release | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 释放设备句柄 | +| enable | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 使能 | +| disable | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 禁用 | +| forceDisable | node:结构体指针,核心层Regulator节点 | HDF_STATUS相关状态 | 强制禁用 | +| setVoltage | node:结构体指针,核心层Regulator节点
minUv:uint32_t类型,最小电压
maxUv:uint32_t类型,最大电压 | HDF_STATUS相关状态 | 设置输出电压范围 | +| getVoltage | node:结构体指针,核心层Regulator节点
voltage:uint32_t类型指针,传出电压值 | HDF_STATUS相关状态 | 获取电压 | +| setCurrent | node:结构体指针,核心层Regulator节点
minUa:uint32_t类型,最小电流
maxUa:uint32_t类型,最大电流 | HDF_STATUS相关状态 | 设置输出电流范围 | +| getCurrent | node:结构体指针,核心层Regulator节点
regCurrent:uint32_t类型指针,传出电流值 | HDF_STATUS相关状态 | 获取电流 | +| getStatus | node:结构体指针,核心层Regulator节点
status:uint32_t类型指针,传出状态值 | HDF_STATUS相关状态 | 获取设备状态 | ### 开发步骤 Regulator模块适配包含以下四个步骤: -- 实例化驱动入口。 -- 配置属性文件。 -- 实例化核心层接口函数。 -- 驱动调试。 +- 实例化驱动入口 + +- 配置属性文件 + +- 实例化核心层接口函数 -1. 实例化驱动入口: +- 驱动调试 + +1. 实例化驱动入口 驱动开发首先需要实例化驱动入口,驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。 @@ -91,129 +95,128 @@ Regulator模块适配包含以下四个步骤: ```c struct HdfDriverEntry g_regulatorDriverEntry = { .moduleVersion = 1, - .moduleName = "virtual_regulator_driver", // 【必要且与HCS文件中里面的moduleName匹配】 - .Init = VirtualRegulatorInit, - .Release = VirtualRegulatorRelease, + .moduleName = "virtual_regulator_driver", // 【必要且与HCS文件中里面的moduleName匹配】 + .Init = VirtualRegulatorInit, // 见Init参考 + .Release = VirtualRegulatorRelease, // 见Release参考 }; - /* 调用HDF_INIT将驱动入口注册到HDF框架中 */ - HDF_INIT(g_regulatorDriverEntry); + HDF_INIT(g_regulatorDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 ``` -2. 配置属性文件: +2. 配置属性文件 + + 以Hi3516DV300开发板为例,在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。 - 以Hi3516DV300开发板为例,在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。 + deviceNode信息与驱动入口注册相关,器件属性值与核心层RegulatorNode成员的默认值或限制范围有密切关系。 - deviceNode信息与驱动入口注册相关,器件属性值与核心层RegulatorNode成员的默认值或限制范围有密切关系。 + 由于采用了统一服务模式,device_info.hcs文件中第一个设备节点必须为Regulator管理器,其各项参数必须如如表2所示: - 由于采用了统一服务模式,device_info.hcs文件中第一个设备节点必须为Regulator管理器,其各项参数必须如下设置: + **表 2** device_info.hcs节点参数说明 - | 成员名 | 值 | - | --------------- | ------------------------------------------------------------ | - | policy | 具体配置为0,不发布服务 | - | priority | 驱动启动优先级(0-200)。值越大优先级越低,优先级相同则不保证device的加载顺序 | - | permission | 驱动权限 | - | moduleName | 固定为HDF_PLATFORM_REGULATOR_MANAGER | - | serviceName | 固定为HDF_PLATFORM_REGULATOR_MANAGER | - | deviceMatchAttr | 没有使用,可忽略 | + | 成员名 | 值 | + | --------------- | ------------------------------------------------------------ | + | policy | 驱动服务发布的策略,Regulator管理器具体配置为1,表示驱动对内核态发布服务 | + | priority | 驱动启动优先级(0-200)。值越大优先级越低,优先级相同则不保证device的加载顺序,regulator管理器具体配置为50 | + | permission | 驱动创建设备节点权限,Regulator管理器具体配置为0664 | + | moduleName | 驱动名称,Regulator管理器固定为HDF_PLATFORM_REGULATOR_MANAGER | + | serviceName | 驱动对外发布服务的名称,Regulator管理器固定为HDF_PLATFORM_REGULATOR_MANAGER | + | deviceMatchAttr | 驱动私有数据匹配的关键字,Regulator管理器设置为hdf_platform_regulator_manager | - 从第二个节点开始配置具体Regulator控制器信息,此节点并不表示某一路Regulator控制器,而是代表一个资源性质设备,用于描述一类Regulator控制器的信息。本例只有一个Regulator设备,如有多个设备,则需要在device_info.hcs文件增加deviceNode信息,以及在regulator\_config文件中增加对应的器件属性。 + 从第二个节点开始配置具体Regulator控制器信息,此节点并不表示某一路Regulator控制器,而是代表一个资源性质设备,用于描述一类Regulator控制器的信息。本例只有一个Regulator设备,如有多个设备,则需要在device_info.hcs文件增加deviceNode信息,以及在regulator_config.hcs文件中增加对应的器件属性。 - device_info.hcs 配置参考 - ```c - root { - device_info { - platform :: host { - hostName = "platform_host"; - priority = 50; - device_regulator :: device { - device0 :: deviceNode { // 为每一个Regulator控制器配置一个HDF设备节点,存在多个时添加,否则不用。 - policy = 1; // 2:用户态、内核态均可见;1:内核态可见;0:不需要发布服务。 - priority = 50; // 驱动启动优先级 - permission = 0644; // 驱动创建设备节点权限 - /* 【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。 */ - moduleName = "HDF_PLATFORM_REGULATOR_MANAGER"; - serviceName = "HDF_PLATFORM_REGULATOR_MANAGER"; // 【必要且唯一】驱动对外发布服务的名称 - /* 【必要】用于配置控制器私有数据,要与regulator_config.hcs中对应控制器保持一致,具体的控制器信息在regulator_config.hcs中。 */ - deviceMatchAttr = "hdf_platform_regulator_manager"; - } - device1 :: deviceNode { - policy = 0; - priority = 55; - permission = 0644; - moduleName = "linux_regulator_adapter"; - deviceMatchAttr = "linux_regulator_adapter"; - } - } - } - } - } - ``` - - - regulator\_config.hcs配置参考 - - ```c - root { - platform { - regulator_config { - match_attr = "linux_regulator_adapter"; - template regulator_controller { // 【必要】模板配置,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省。 - device_num = 1; - name = ""; - devName = "regulator_adapter_consumer01"; - supplyName = ""; - mode = 1; - minUv = 0; - maxUv = 20000; - minUa = 0; - maxUa = 0; - } - controller_0x130d0000 :: regulator_controller { - device_num = 1; - name = "regulator_adapter_1"; - devName = "regulator_adapter_consumer01"; - supplyName = "virtual-regulator-hdf-adapter"; - mode = 1; - minUv = 1000; - maxUv = 50000; - minUa = 0; - maxUa = 0; - } - /* 每个Regulator控制器对应一个controller节点,如存在多个Regulator控制器,请依次添加对应的controller节点。 */ - controller_0x130d0001 :: regulator_controller { - device_num = 1; - name = "regulator_adapter_2"; - devName = "regulator_adapter_consumer01"; - supplyName = "virtual2-regulator-hdf-adapter"; - mode = 2; - minUv = 0; - maxUv = 0; - minUa = 1000; - maxUa = 50000; - } - } - } - } - ``` - - 需要注意的是,新增regulator_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。 - - 例如:本例中regulator_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/regulator/regulator_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句: - - ```c - #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/regulator/regulator_config.hcs" // 配置文件相对路径 - ``` - -3. 实例化核心层接口函数: + ```c + root { + device_info { + platform :: host { + hostName = "platform_host"; + priority = 50; + device_regulator :: device { + device0 :: deviceNode { // 为每一个Regulator控制器配置一个HDF设备节点,存在多个时添加,否则不用。 + policy = 1; // 2:用户态、内核态均可见;1:内核态可见;0:不需要发布服务。 + priority = 50; // 驱动启动优先级 + permission = 0644; // 驱动创建设备节点权限 + moduleName = "HDF_PLATFORM_REGULATOR_MANAGER"; //【必要】用于指定驱动名称,需要与期望的驱动Entry中的moduleName一致。 + serviceName = "HDF_PLATFORM_REGULATOR_MANAGER"; // 【必要且唯一】驱动对外发布服务的名称 + deviceMatchAttr = "hdf_platform_regulator_manager"; // 【必要】用于配置控制器私有数据,要与regulator_config.hcs中对应控制器保持一致,具体的控制器信息在regulator_config.hcs中。 + } + device1 :: deviceNode { + policy = 0; + priority = 55; + permission = 0644; + moduleName = "linux_regulator_adapter"; + deviceMatchAttr = "linux_regulator_adapter"; + } + } + } + } + } + ``` + + - regulator_config.hcs配置参考 + + ```c + root { + platform { + regulator_config { + match_attr = "linux_regulator_adapter"; + template regulator_controller { // 【必要】模板配置,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省。 + device_num = 1; + name = ""; + devName = "regulator_adapter_consumer01"; + supplyName = ""; + mode = 1; + minUv = 0; // 最小电压 + maxUv = 20000; // 最大电压 + minUa = 0; // 最小电流 + maxUa = 0; // 最大电流 + } + controller_0x130d0000 :: regulator_controller { + device_num = 1; + name = "regulator_adapter_1"; + devName = "regulator_adapter_consumer01"; + supplyName = "virtual-regulator-hdf-adapter"; + mode = 1; + minUv = 1000; + maxUv = 50000; + minUa = 0; + maxUa = 0; + } + // 每个Regulator控制器对应一个controller节点,如存在多个Regulator控制器,请依次添加对应的controller节点。 + controller_0x130d0001 :: regulator_controller { + device_num = 1; + name = "regulator_adapter_2"; + devName = "regulator_adapter_consumer01"; + supplyName = "virtual2-regulator-hdf-adapter"; + mode = 2; + minUv = 0; + maxUv = 0; + minUa = 1000; + maxUa = 50000; + } + } + } + } + ``` + + 需要注意的是,新增regulator_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。 + + 例如:本例中regulator_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/regulator/regulator_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句: + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/regulator/regulator_config.hcs" // 配置文件相对路径 + ``` + +3. 实例化核心层接口函数 完成驱动入口注册之后,下一步就是对核心层RegulatorNode对象的初始化,包括驱动适配者自定义结构体(传递参数和数据),实例化RegulatorNode成员RegulatorMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。 - 自定义结构体参考。 - 从驱动的角度看,RegulatorNode结构体是参数和数据的载体,HDF框架通过DeviceResourceIface将regulator\_config.hcs文件中的数值读入其中。 + 从驱动的角度看,RegulatorNode结构体是参数和数据的载体,HDF框架通过DeviceResourceIface将regulator_config.hcs文件中的数值读入其中。 ```c - /* RegulatorNode是核心层控制器结构体,其中的成员在Init函数中会被赋值。 */ + // RegulatorNode是核心层控制器结构体,其中的成员在Init函数中会被赋值。 struct RegulatorNode { struct RegulatorDesc regulatorInfo; struct DListHead node; @@ -246,45 +249,45 @@ Regulator模块适配包含以下四个步骤: }; ``` - - 实例化RegulatorNode成员RegulatorMethod,其他成员在Init函数中初始化。 + - 实例化RegulatorNode成员RegulatorMethod。 - ```c - /* regulator_virtual.c中的示例:钩子函数的填充 */ - static struct RegulatorMethod g_method = { - .enable = VirtualRegulatorEnable, - .disable = VirtualRegulatorDisable, - .setVoltage = VirtualRegulatorSetVoltage, - .getVoltage = VirtualRegulatorGetVoltage, - .setCurrent = VirtualRegulatorSetCurrent, - .getCurrent = VirtualRegulatorGetCurrent, - .getStatus = VirtualRegulatorGetStatus, - }; - ``` + ```c + // regulator_virtual.c中的示例:钩子函数的填充 + static struct RegulatorMethod g_method = { + .enable = VirtualRegulatorEnable, + .disable = VirtualRegulatorDisable, + .setVoltage = VirtualRegulatorSetVoltage, + .getVoltage = VirtualRegulatorGetVoltage, + .setCurrent = VirtualRegulatorSetCurrent, + .getCurrent = VirtualRegulatorGetCurrent, + .getStatus = VirtualRegulatorGetStatus, + }; + ``` - Init函数开发参考 - 入参: + 入参: - HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 + HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 - 返回值: + 返回值: - HDF\_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf\_core/framework/include/utils/hdf\_base.h中HDF\_STATUS定义)。 + HDF_STATUS相关状态(表4为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 - **表 2** HDF\_STATUS相关状态 - - | 状态(值) | 描述 | - | ---------------------- | -------------- | - | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | - | HDF_ERR_MALLOC_FAIL | 内存分配失败 | - | HDF_ERR_INVALID_PARAM | 参数非法 | - | HDF_ERR_IO | I/O 错误 | - | HDF_SUCCESS | 初始化成功 | - | HDF_FAILURE | 初始化失败 | + **表 3** HDF_STATUS相关状态说明 + + | 状态(值) | 描述 | + | ---------------------- | -------------- | + | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | + | HDF_ERR_MALLOC_FAIL | 内存分配失败 | + | HDF_ERR_INVALID_PARAM | 参数非法 | + | HDF_ERR_IO | I/O 错误 | + | HDF_SUCCESS | 初始化成功 | + | HDF_FAILURE | 初始化失败 | - 函数说明: + 函数说明: - 初始化自定义结构体和RegulatorNode成员,并通过调用核心层RegulatorNodeAdd函数挂载Regulator控制器。 + 初始化自定义结构体和RegulatorNode成员,并通过调用核心层RegulatorNodeAdd函数挂载Regulator控制器。 ```c static int32_t VirtualRegulatorInit(struct HdfDeviceObject *device) @@ -293,10 +296,10 @@ Regulator模块适配包含以下四个步骤: const struct DeviceResourceNode *childNode = NULL; ... DEV_RES_NODE_FOR_EACH_CHILD_NODE(device->property, childNode) { - ret = VirtualRegulatorParseAndInit(device, childNode); // 【必要】实现见下 - ... + ret = VirtualRegulatorParseAndInit(device, childNode); // 【必要】实现见下 + ...... } - ... + ...... } static int32_t VirtualRegulatorParseAndInit(struct HdfDeviceObject *device, const struct DeviceResourceNode *node) @@ -306,39 +309,39 @@ Regulator模块适配包含以下四个步骤: (void)device; regNode = (struct RegulatorNode *)OsalMemCalloc(sizeof(*regNode)); //加载HCS文件 - ... + ...... ret = VirtualRegulatorReadHcs(regNode, node); // 读取HCS文件信息 - ... + ...... regNode->priv = (void *)node; // 实例化节点 regNode->ops = &g_method; // 实例化ops ret = RegulatorNodeAdd(regNode); // 挂载节点 - ... + ...... } ``` - - Release函数开发参考 + - Release函数开发参考 - 入参: + 入参: - HdfDeviceObject是整个驱动对外提供的接口参数,其包含了HCS配置文件中的相关配置信息。 + HdfDeviceObject是整个驱动对外提供的接口参数,其包含了HCS配置文件中的相关配置信息。 - 返回值: + 返回值: - 无。 + 无。 - 函数说明: + 函数说明: - 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 + 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 ```c static void VirtualRegulatorRelease(struct HdfDeviceObject *device) { - ... + ...... RegulatorNodeRemoveAll(); // 【必要】调用核心层函数,释放RegulatorNode的设备和服务 } ``` -4. 驱动调试: +4. 驱动调试 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的测试用例是否成功等。 diff --git a/zh-cn/device-dev/driver/driver-platform-rtc-des.md b/zh-cn/device-dev/driver/driver-platform-rtc-des.md index de7a4a2ce02486f883ca3074cfcfc7738fe286f2..07325ed521523c0e056492389e6ce236a4352421 100644 --- a/zh-cn/device-dev/driver/driver-platform-rtc-des.md +++ b/zh-cn/device-dev/driver/driver-platform-rtc-des.md @@ -10,6 +10,24 @@ RTC(real-time clock)为操作系统中的实时时钟设备,为操作系 在HDF框架中,RTC模块采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,若设备过多会增加内存占用。通常,一个硬件系统中只需要一个RTC设备,因此RTC模块采用独立服务模式较为合适。 +独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为: + +- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。 + +- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。 + +**图 1** RTC独立服务模式结构图 + +![RTC独立服务模式结构图](figures/独立服务模式结构图.png) + +RTC模块各分层作用: + +- 接口层提供打开RTC设备、RTC设备读取时间、RTC设备设置时间、RTC设备读取警报时间、RTC设备设置警报时间、RTC设备定时报警回调函数、RTC设备设置定时报警中断使能去使能、RTC设备设置RTC外频、RTC设备读取RTC外频、复位RTC、设置RTC自定义寄存器配置,读取RTC自定义寄存器配置以及关闭RTC设备的接口。 + +- 核心层主要提供RTC控制器的创建、销毁,通过钩子函数与适配层交互。 + +- 适配层主要是将钩子函数的功能实例化,实现具体的功能。 + ## 使用指导 ### 场景介绍 @@ -20,7 +38,7 @@ RTC主要用于提供实时时间和定时报警功能。 RTC模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/rtc_if.h。 -**表1** RTC设备API接口功能介绍 +**表 1** RTC设备API接口功能介绍 | 接口名 | 接口描述 | | -------- | -------- | @@ -40,40 +58,42 @@ RTC模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/ ### 使用流程 -使用RTC设备的一般流程如下图所示。 +使用RTC设备的一般流程如图2所示。 -**图1** RTC设备使用流程图 +**图 2** RTC设备使用流程图 -![image](figures/RTC设备使用流程图.png "RTC设备使用流程图") +![RTC设备使用流程图](figures/RTC设备使用流程图.png) #### 创建RTC设备句柄 RTC驱动加载成功后,使用驱动框架提供的查询接口并调用RTC设备驱动接口。 > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
-> 当前操作系统仅支持一个RTC设备。 +> 当前操作系统仅支持一个RTC设备。本文涉及的RTC的所有接口,支持内核态及用户态使用。 ```c DevHandle RtcOpen(void); ``` - **表2** RtcOpen参数和返回值描述 +**表 2** RtcOpen参数和返回值描述 | **参数** | **描述** | | -------- | -------- | | void | NA | | **返回值** | **描述** | -| handle | 操作成功返回  指针类型 | -| NULL | 操作失败 | +| handle | 获取对应的RTC设备句柄成功 | +| NULL | 获取对应的RTC设备句柄成功失败 | ```c -DevHandle handle = NULL; +DevHandle handle = NULL; -/* 获取RTC句柄 */ +// 获取RTC句柄 handle = RtcOpen(); -if (handle == NULL) { - /* 错误处理 */ +if (handle == NULL) { + // 错误处理 + HDF_LOGE("open rtc fail!"); + return HDF_FAILURE; } ``` @@ -85,37 +105,45 @@ if (handle == NULL) { int32_t RtcRegisterAlarmCallback(DevHandle handle, enum RtcAlarmIndex alarmIndex, RtcAlarmCallback cb); ``` - **表3** RtcRegisterAlarmCallback参数和返回值描述 +**表 3** RtcRegisterAlarmCallback参数和返回值描述 | **参数** | **描述** | | -------- | -------- | -| handle | RTC设备句柄 | -| alarmIndex | 报警索引 | +| handle | DevHandle类型,RTC设备句柄 | +| alarmIndex | 枚举类型,报警索引 | | cb | 定时报警回调函数 | | **返回值** | **描述** | -| 0 | 操作成功 | +| HDF_SUCCESS | 操作成功 | | 负数 | 操作失败 | - 注册RTC_ALARM_INDEX_A的定时报警处理函数, 示例如下: +注册RTC_ALARM_INDEX_A的定时报警处理函数, 示例如下: ```c -/* 用户注册RTC定时报警回调函数的方法 */ +// 用户注册RTC定时报警回调函数的方法 int32_t RtcAlarmACallback(enum RtcAlarmIndex alarmIndex) { if (alarmIndex == RTC_ALARM_INDEX_A) { - /* 报警A的处理 */ + // 报警A的处理 + HDF_LOGD("RTC Alarm A callback function\n\r"); + return HDF_SUCCESS; } else if (alarmIndex == RTC_ALARM_INDEX_B) { - /* 报警B的处理 */ + // 报警B的处理 + HDF_LOGD("RTC Alarm B callback function\n\r"); + return HDF_SUCCESS; } else { - /* 错误处理 */ + // 错误处理 + HDF_LOGE("RTC Alarm callback function fail!\n"); + return HDF_FAILURE; } - return 0; + return HDF_SUCCESS; } int32_t ret; -/* 注册报警A的定时回调函数 */ +// 注册报警A的定时回调函数 ret = RtcRegisterAlarmCallback(handle, RTC_ALARM_INDEX_A, RtcAlarmACallback); -if (ret != 0) { - /* 错误处理 */ +if (ret != HDF_SUCCESS) { + // 错误处理 + HDF_LOGE("register alarm callback fail, ret:%d", ret); + return ret; } ``` @@ -127,27 +155,29 @@ if (ret != 0) { 系统从RTC读取时间信息,包括年、月、星期、日、时、分、秒、毫秒,则可以通过以下函数完成: ```c - int32_t RtcReadTime(DevHandle handle, struct RtcTime \*time); + int32_t RtcReadTime(DevHandle handle, struct RtcTime *time); ``` - **表4** RtcReadTime参数和返回值描述 + **表 4** RtcReadTime参数和返回值描述 | **参数** | **描述** | | -------- | -------- | - | handle | RTC设备句柄 | - | time | RTC读取时间信息,包括年、月、星期、日、时、分、秒、毫秒 | - | **返回值** | **描述** | - | 0 | 操作成功 | - | 负数 | 操作失败 | + | handle | DevHandle类型,RTC设备句柄 | + | time | 结构体指针类型,RTC读取时间信息,包括年、月、星期、日、时、分、秒、毫秒 | + | **返回值** | **描述** | + | HDF_SUCCESS | 读取RTC时间成功 | + | 负数 | 读取RTC时间失败 | ```c int32_t ret; struct RtcTime tm; - /* 系统从RTC读取时间信息 */ + // 系统从RTC读取时间信息 ret = RtcReadTime(handle, &tm); - if (ret != 0) { - /* 错误处理 */ + if (ret != HDF_SUCCESS) { + // 错误处理 + HDF_LOGE("%s:read time fail, ret:%d", __func__, ret); + return ret; } ``` @@ -156,18 +186,18 @@ if (ret != 0) { 设置RTC时间,则可以通过以下函数完成: ```c - int32_t RtcWriteTime(DevHandle handle, struct RtcTime \*time); + int32_t RtcWriteTime(DevHandle handle, struct RtcTime *time); ``` - **表5** RtcWriteTime参数和返回值描述 + **表 5** RtcWriteTime参数和返回值描述 | **参数** | **描述** | | -------- | -------- | - | handle | RTC设备句柄 | - | time | 写RTC时间信息,包括年、月、星期、日、时、分、秒、毫秒 | + | handle | DevHandle类型,RTC设备句柄 | + | time | 结构体指针类型,写RTC时间信息,包括年、月、星期、日、时、分、秒、毫秒 | | **返回值** | **描述** | - | 0 | 操作成功 | - | 负数 | 操作失败 | + | HDF_SUCCESS | 设置RTC时间成功 | + | 负数 | 设置RTC时间失败 | > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> RTC起始时间为UTC 1970/01/01 Thursday 00:00:00,年的最大取值按照用户器件手册要求计算配置,星期不用配置。 @@ -176,7 +206,7 @@ if (ret != 0) { int32_t ret; struct RtcTime tm; - /* 设置RTC时间为 UTC 2020/01/01 00:59:00 .000 */ + // 设置RTC时间为 UTC 2020/01/01 00:59:00 .000 tm.year = 2020; tm.month = 01; tm.day = 01; @@ -184,10 +214,12 @@ if (ret != 0) { tm.minute = 59; tm.second = 00; tm.millisecond = 0; - /* 写RTC时间信息 */ + // 写RTC时间信息 ret = RtcWriteTime(handle, &tm); - if (ret != 0) { - /* 错误处理 */ + if (ret != HDF_SUCCESS) { + // 错误处理 + HDF_LOGE("write time fail, ret:%d", ret); + return ret; } ``` @@ -196,28 +228,30 @@ if (ret != 0) { 如果需要读取定时报警时间,则可以通过以下函数完成: ```c - int32_t RtcReadAlarm(DevHandle handle, enum RtcAlarmIndex alarmIndex, struct RtcTime \*time); + int32_t RtcReadAlarm(DevHandle handle, enum RtcAlarmIndex alarmIndex, struct RtcTime *time); ``` - **表6** RtcReadAlarm参数和返回值描述 + **表 6** RtcReadAlarm参数和返回值描述 | **参数** | **描述** | | -------- | -------- | - | handle | RTC设备句柄 | - | alarmIndex | 报警索引 | - | time | RTC报警时间信息,包括年、月、星期、日、时、分、秒、毫秒 | + | handle | DevHandle类型,RTC设备句柄 | + | alarmIndex | 枚举类型,报警索引 | + | time | 结构体指针类型,RTC报警时间信息,包括年、月、星期、日、时、分、秒、毫秒 | | **返回值** | **描述** | - | 0 | 操作成功 | - | 负数 | 操作失败 | + | HDF_SUCCESS | 读取RTC报警时间成功 | + | 负数 | 读取RTC报警时间失败 | ```c int32_t ret; struct RtcTime alarmTime; - /* 读RTC_ALARM_INDEX_A索引的RTC定时报警时间信息 */ + // 读RTC_ALARM_INDEX_A索引的RTC定时报警时间信息 ret = RtcReadAlarm(handle, RTC_ALARM_INDEX_A, &alarmTime); - if (ret != 0) { - /* 错误处理 */ + if (ret != HDF_SUCCESS) { + // 错误处理 + HDF_LOGE("read alarm fail, ret:%d", ret); + return ret; } ``` @@ -226,19 +260,19 @@ if (ret != 0) { 根据报警索引设置RTC报警时间,通过以下函数完成: ```c - int32_t RtcWriteAlarm(DevHandle handle, enum RtcAlarmIndex alarmIndex, struct RtcTime \*time); + int32_t RtcWriteAlarm(DevHandle handle, enum RtcAlarmIndex alarmIndex, struct RtcTime *time); ``` - **表7** RtcWriteAlarm参数和返回值描述 + **表 7** RtcWriteAlarm参数和返回值描述 | **参数** | **描述** | | -------- | -------- | - | handle | RTC设备句柄 | - | alarmIndex | 报警索引 | - | time | RTC报警时间信息,包括年、月、星期、日、时、分、秒、毫秒 | + | handle | DevHandle类型,RTC设备句柄 | + | alarmIndex | 枚举类型,报警索引 | + | time | 结构体指针类型,RTC报警时间信息,包括年、月、星期、日、时、分、秒、毫秒 | | **返回值** | **描述** | - | 0 | 操作成功 | - | 负数 | 操作失败 | + | HDF_SUCCESS | 设置RTC报警时间成功 | + | 负数 | 设置RTC报警时间失败 | > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> RTC起始时间为UTC 1970/01/01 Thursday 00:00:00,年的最大取值按照用户器件手册要求计算配置,星期不用配置。 @@ -247,7 +281,7 @@ if (ret != 0) { int32_t ret; struct RtcTime alarmTime; - /* 设置RTC报警时间为2020/01/01 00:59:59 .000 */ + // 设置RTC报警时间为2020/01/01 00:59:59 .000 alarmTime.year = 2020; alarmTime.month = 01; alarmTime.day = 01; @@ -255,10 +289,12 @@ if (ret != 0) { alarmTime.minute = 59; alarmTime.second = 59; alarmTime.millisecond = 0; - /* 设置RTC_ALARM_INDEX_A索引的定时报警时间 */ + // 设置RTC_ALARM_INDEX_A索引的定时报警时间 ret = RtcWriteAlarm(handle, RTC_ALARM_INDEX_A, &alarmTime); - if (ret != 0) { - /* 错误处理 */ + if (ret != HDF_SUCCESS) { + // 错误处理 + HDF_LOGE("write alarm fail, ret:%d", ret); + return ret; } ``` @@ -270,24 +306,26 @@ if (ret != 0) { int32_t RtcAlarmInterruptEnable(DevHandle handle, enum RtcAlarmIndex alarmIndex, uint8_t enable); ``` - **表8** RtcAlarmInterruptEnable参数和返回值描述 + **表 8** RtcAlarmInterruptEnable参数和返回值描述 | **参数** | **描述** | | -------- | -------- | - | handle | RTC设备句柄 | - | alarmIndex | 报警索引 | - | enable | RTC报警中断配置,1:使能,0:去使能 | + | handle | DevHandle类型,RTC设备句柄 | + | alarmIndex | 枚举类型,报警索引 | + | enable | uint8_t类型,RTC报警中断配置,1:使能,0:去使能 | | **返回值** | **描述** | - | 0 | 操作成功 | - | 负数 | 操作失败 | + | HDF_SUCCESS | 设置定时报警中断使能或去使能成功 | + | 负数 | 设置定时报警中断使能或去使能失败 | ```c int32_t ret; - /* 设置RTC报警中断使能 */ + // 设置RTC报警中断使能 ret = RtcAlarmInterruptEnable(handle, RTC_ALARM_INDEX_A, 1); - if (ret != 0) { - /* 错误处理 */ + if (ret != HDF_SUCCESS) { + // 错误处理 + HDF_LOGE("alarm interrupt enable fail, ret:%d", ret); + return ret; } ``` @@ -296,27 +334,28 @@ if (ret != 0) { 读取RTC外接晶体振荡频率,可以通过以下函数完成: ```c - int32_t RtcGetFreq(DevHandle handle, uint32_t \*freq); + int32_t RtcGetFreq(DevHandle handle, uint32_t *freq); ``` - **表9** RtcGetFreq参数和返回值描述 + **表 9** RtcGetFreq参数和返回值描述 | **参数** | **描述** | | -------- | -------- | - | handle | RTC设备句柄 | - | freq | RTC的外接晶体振荡频率,单位(HZ) | + | handle | DevHandle类型,RTC设备句柄 | + | freq | uint32_t类型指针,RTC的外接晶体振荡频率,单位(HZ) | | **返回值** | **描述** | - | 0 | 操作成功 | - | 负数 | 操作失败 | + | HDF_SUCCESS | 读取RTC外频成功 | + | 负数 | 读取RTC外频失败 | ```c int32_t ret; uint32_t freq = 0; - /* 读取RTC外接晶体振荡频率 */ + // 读取RTC外接晶体振荡频率 ret = RtcGetFreq(handle, &freq); - if (ret != 0) { - /* 错误处理 */ + if (ret != HDF_SUCCESS) { + // 错误处理 + HDF_LOGE("get freq fail, ret:%d", ret); } ``` @@ -328,24 +367,26 @@ if (ret != 0) { int32_t RtcSetFreq(DevHandle handle, uint32_t freq); ``` - **表10** RtcSetFreq参数和返回值描述 + **表 10** RtcSetFreq参数和返回值描述 | **参数** | **描述** | | -------- | -------- | - | handle | RTC设备句柄 | - | freq | RTC的外接晶体振荡频率,单位(HZ) | + | handle | DevHandle类型,RTC设备句柄 | + | freq | uint32_t类型,RTC的外接晶体振荡频率,单位(HZ) | | **返回值** | **描述** | - | 0 | 操作成功 | - | 负数 | 操作失败 | + | HDF_SUCCESS | 配置RTC外频成功 | + | 负数 | 配置RTC外频失败 | ```c int32_t ret; - uint32_t freq = 32768; /* 32768 Hz */ + uint32_t freq = 32768; // 32768 Hz - /* 设置RTC外接晶体振荡频率,注意按照器件手册要求配置RTC外频 */ + // 设置RTC外接晶体振荡频率,注意按照器件手册要求配置RTC外频 ret = RtcSetFreq(handle, freq); - if (ret != 0) { - /* 错误处理 */ + if (ret != HDF_SUCCESS) { + // 错误处理 + HDF_LOGE("set freq fail, ret:%d", ret); + return ret; } ``` @@ -357,22 +398,24 @@ if (ret != 0) { int32_t RtcReset(DevHandle handle); ``` - **表11** RtcReset参数和返回值描述 + **表 11** RtcReset参数和返回值描述 | **参数** | **描述** | | -------- | -------- | - | handle | RTC设备句柄 | + | handle | DevHandle类型,RTC设备句柄 | | **返回值** | **描述** | - | 0 | 操作成功 | - | 负数 | 操作失败 | + | HDF_SUCCESS | 复位RTC成功 | + | 负数 | 复位RTC失败 | ```c int32_t ret; - /* 复位RTC,各配置寄存器恢复默认值 */ + // 复位RTC,各配置寄存器恢复默认值 ret = RtcReset(handle); - if (ret != 0) { - /* 错误处理 */ + if (ret != HDF_SUCCESS) { + // 错误处理 + HDF_LOGE("reset fail, ret:%d", ret); + return ret; } ``` @@ -381,29 +424,31 @@ if (ret != 0) { 按照用户定义的寄存器索引,读取对应的寄存器配置,一个索引对应一字节的配置值,通过以下函数完成: ```c - int32_t RtcReadReg(DevHandle handle, uint8_t usrDefIndex, uint8_t \*value); + int32_t RtcReadReg(DevHandle handle, uint8_t usrDefIndex, uint8_t *value); ``` - **表12** RtcReadReg参数和返回值描述 + **表 12** RtcReadReg参数和返回值描述 | **参数** | **描述** | | -------- | -------- | - | handle | RTC设备句柄 | - | usrDefIndex | 用户定义的寄存器对应索引 | - | value | 寄存器值 | - | **返回值** | **描述** | - | 0 | 操作成功 | - | 负数 | 操作失败 | + | handle | DevHandle类型,RTC设备句柄 | + | usrDefIndex | uint8_t类型,用户定义的寄存器对应索引 | + | value | uint8_t类型指针,待读取寄存器值 | + | **返回值** | **描述** | + | HDF_SUCCESS | 读取RTC自定义寄存器配置成功 | + | 负数 | 读取RTC自定义寄存器配置失败 | ```c int32_t ret; - uint8_t usrDefIndex = 0; /* 定义0索引对应用户定义的第一个寄存器*/ + uint8_t usrDefIndex = 0; // 定义0索引对应用户定义的第一个寄存器 uint8_t value = 0; - /* 按照用户定义的寄存器索引,读取对应的寄存器配置,一个索引对应一字节的配置值 */ + // 按照用户定义的寄存器索引,读取对应的寄存器配置,一个索引对应一字节的配置值 ret = RtcReadReg(handle, usrDefIndex, &value); - if (ret != 0) { - /* 错误处理 */ + if (ret != HDF_SUCCESS) { + // 错误处理 + HDF_LOGE("read reg fail, ret:%d", ret); + return ret; } ``` @@ -415,26 +460,28 @@ if (ret != 0) { int32_t RtcWriteReg(DevHandle handle, uint8_t usrDefIndex, uint8_t value); ``` - **表13** RtcWriteReg参数和返回值描述 + **表 13** RtcWriteReg参数和返回值描述 | **参数** | **描述** | | -------- | -------- | - | handle | RTC设备句柄 | - | usrDefIndex | 用户定义的寄存器对应索引 | - | value | 寄存器值 | - | **返回值** | **描述** | - | 0 | 操作成功 | - | 负数 | 操作失败 | + | handle | DevHandle类型,RTC设备句柄 | + | usrDefIndex | uint8_t类型,用户定义的寄存器对应索引 | + | value | uint8_t类型,寄存器值 | + | **返回值** | **描述** | + | HDF_SUCCESS | 设置RTC自定义寄存器配置成功 | + | 负数 | 设置RTC自定义寄存器配置失败 | ```c int32_t ret; - uint8_t usrDefIndex = 0; /* 定义0索引对应用户定义第一个寄存器*/ + uint8_t usrDefIndex = 0; // 定义0索引对应用户定义第一个寄存器 uint8_t value = 0x10; - /* 按照用户的定义的寄存器索引,设置对应的寄存器配置,一个索引对应一字节的配置值 */ + // 按照用户的定义的寄存器索引,设置对应的寄存器配置,一个索引对应一字节的配置值 ret = RtcWriteReg(handle, usrDefIndex, value); - if (ret != 0) { - /* 错误处理 */ + if (ret != HDF_SUCCESS) { + // 错误处理 + HDF_LOGE("write reg fail, ret:%d", ret); + return ret; } ``` @@ -446,14 +493,14 @@ if (ret != 0) { void RtcClose(DevHandle handle); ``` - **表14** RtcClose参数描述 +**表 14** RtcClose参数描述 | **参数** | **描述** | | -------- | -------- | -| handle | RTC设备句柄 | +| handle | DevHandle类型,RTC设备句柄 | ```c -/* 销毁RTC句柄 */ +// 销毁RTC句柄 RtcClose(handle); ``` @@ -471,53 +518,73 @@ RtcClose(handle); 示例如下: -```c +```c有问题 +#include "hdf_log.h" // 标准日志打印头文件 +#include "osal_time.h" // 标准延迟&睡眠接口头文件 #include "rtc_if.h" int32_t RtcAlarmACallback(enum RtcAlarmIndex alarmIndex) { if (alarmIndex == RTC_ALARM_INDEX_A) { - /* 报警A的处理 */ - printf("RTC Alarm A callback function\n\r"); + // 报警A的处理 + HDF_LOGD("RtcAlarmACallback: RTC Alarm A callback function\n\r"); + return HDF_SUCCESS; } else if (alarmIndex == RTC_ALARM_INDEX_B) { - /* 报警B的处理 */ - printf("RTC Alarm B callback function\n\r"); + // 报警B的处理 + HDF_LOGD("RtcAlarmACallback:RTC Alarm B callback function\n\r"); + return HDF_SUCCESS; } else { - /* 错误处理 */ + // 错误处理 + HDF_LOGE("RtcAlarmACallback:RTC Alarm callback function fail!\n\r"); + return HDF_FAILURE; } - return 0; + return HDF_SUCCESS; } -void RtcTestSample(void) +int32_t RtcTestSample(void) { int32_t ret; struct RtcTime tm; struct RtcTime alarmTime; uint32_t freq; + uint8_t usrDefIndex = 0; + uint8_t value = 0; DevHandle handle = NULL; - /* 获取RTC设备句柄 */ + // 获取RTC设备句柄 handle = RtcOpen(); if (handle == NULL) { - /* 错误处理 */ + HDF_LOGE("RtcTestSample:open rtc fail!"); + return HDF_FAILURE; } - /* 注册报警A的定时回调函数 */ + // 注册报警A的定时回调函数 ret = RtcRegisterAlarmCallback(handle, RTC_ALARM_INDEX_A, RtcAlarmACallback); - if (ret != 0) { - /* 错误处理 */ + if (ret != HDF_SUCCESS) { + HDF_LOGE("RtcTestSample:register alarm callback fail, ret:%d", ret); + goto ERR; } - /* 设置RTC外接晶体振荡频率,注意按照器件手册要求配置RTC外频 */ - freq = 32768; /* 32768 Hz */ + // 设置RTC外接晶体振荡频率,注意按照器件手册要求配置RTC外频 + freq = 32768; // 32768 Hz ret = RtcSetFreq(handle, freq); - if (ret != 0) { - /* 错误处理 */ + if (ret != HDF_SUCCESS) { + HDF_LOGE("RtcTestSample:set freq fail, ret:%d", ret); + goto ERR; } - /* 设置RTC报警中断使能 */ + + freq = 0; + ret = RtcGetFreq(handle, &freq); + if (ret != HDF_SUCCESS) { + HDF_LOGE("RtcTestSample:get freq fail, ret:%d", ret); + goto ERR; + } + + // 设置RTC报警中断使能 ret = RtcAlarmInterruptEnable(handle, RTC_ALARM_INDEX_A, 1); - if (ret != 0) { - /* 错误处理 */ + if (ret != HDF_SUCCESS) { + HDF_LOGE("RtcTestSample:alarm interrupt enable fail, ret:%d", ret); + goto ERR; } - /* 设置RTC时间为2020/01/01 00:00:10 .990 */ + // 设置RTC时间为2020/01/01 00:00:10 .990 tm.year = 2020; tm.month = 01; tm.day = 01; @@ -525,12 +592,13 @@ void RtcTestSample(void) tm.minute = 0; tm.second = 10; tm.millisecond = 990; - /* 写RTC时间信息 */ + // 写RTC时间信息 ret = RtcWriteTime(handle, &tm); - if (ret != 0) { - /* 错误处理 */ + if (ret != HDF_SUCCESS) { + HDF_LOGE("RtcTestSample:write time fail, ret:%d", ret); + goto ERR; } - /* 设置RTC报警时间为2020/01/01 00:00:30 .100 */ + // 设置RTC报警时间为2020/01/01 00:00:30 .100 alarmTime.year = 2020; alarmTime.month = 01; alarmTime.day = 01; @@ -538,22 +606,46 @@ void RtcTestSample(void) alarmTime.minute = 0; alarmTime.second = 30; alarmTime.millisecond = 100; - /* 设置RTC_ALARM_INDEX_A索引定时报警时间信息, 定时时间到后会打印"RTC Alarm A callback function" */ + // 设置RTC_ALARM_INDEX_A索引定时报警时间信息, 定时时间到后会打印"RTC Alarm A callback function" ret = RtcWriteAlarm(handle, RTC_ALARM_INDEX_A, &alarmTime); - if (ret != 0) { - /* 错误处理 */ + if (ret != HDF_SUCCESS) { + HDF_LOGE("RtcTestSample:write alarm fail, ret:%d", ret); + goto ERR; } + OsalSleep(5); - /* 读取RTC实时时间 */ + ret = RtcReadAlarm(handle, RTC_ALARM_INDEX_A, &alarmTime); + if (ret != HDF_SUCCESS) { + HDF_LOGE("RtcTestSample: read alarm fail, ret: %d!", ret); + goto ERR; + } + // 读取RTC实时时间 ret = RtcReadTime(handle, &tm); - if (ret != 0) { - /* 错误处理 */ + if (ret != HDF_SUCCESS) { + HDF_LOGE("RtcTestSample:read time fail, ret:%d", ret); + goto ERR; + } + + ret = RtcWriteReg(handle, usrDefIndex, value); + if (ret != HDF_SUCCESS) { + HDF_LOGE("RtcTestSample: write reg fail, ret: %d!", ret); + return ret; + } + + ret = RtcReadReg(handle, usrDefIndex, &value); + if (ret != HDF_SUCCESS) { + HDF_LOGE("RtcTestSample: read reg fail, ret :%d!", ret); + return ret; } - sleep(5) - printf("RTC read time:\n\r"); - printf("year-month-date-weekday hour:minute:second .millisecond %04u-%02u-%02u-%u %02u:%02u:%02u .%03u", + + HDF_LOGD("RtcTestSample: RTC read time:\n\r"); + HDF_LOGD("RtcTestSample: year-month-date-weekday hour:minute:second .millisecond %04u-%02u-%02u-%u %02u:%02u:%02u .%03u", tm.year, tm.month, tm.day, tm.weekday, tm.hour, tm.minute, tm.second, tm.millisecond); - /* 销毁RTC设备句柄 */ + HDF_LOGD("RtcTestSample: all test end."); + +ERR: + // 销毁RTC设备句柄 RtcClose(handle); + return ret; } -``` \ No newline at end of file +``` diff --git a/zh-cn/device-dev/driver/driver-platform-rtc-develop.md b/zh-cn/device-dev/driver/driver-platform-rtc-develop.md index 16eeb076e3ead1bf7096f48b9073db5e0c0d6148..ee4851727e4b8e11f14eacfb972fbbe9697fc8c3 100755 --- a/zh-cn/device-dev/driver/driver-platform-rtc-develop.md +++ b/zh-cn/device-dev/driver/driver-platform-rtc-develop.md @@ -13,11 +13,20 @@ RTC(real-time clock)为操作系统中的实时时钟设备,为操作系 独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为: - 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。 + - device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。 -**图1** RTC独立服务模式结构图 +**图 1** RTC独立服务模式结构图 + +![RTC独立服务模式结构图](figures/独立服务模式结构图.png) + +RTC模块各分层作用: + +- 接口层提供打开RTC设备、RTC设备读取时间、RTC设备设置时间、RTC设备读取警报时间、RTC设备设置警报时间、RTC设备定时报警回调函数、RTC设备设置定时报警中断使能去使能、RTC设备设置RTC外频、RTC设备读取RTC外频、复位RTC、设置RTC自定义寄存器配置,读取RTC自定义寄存器配置以及关闭RTC设备的接口。 -![image](figures/独立服务模式结构图.png "RTC独立服务模式结构图") +- 核心层主要提供RTC控制器的创建、销毁,通过钩子函数与适配层交互。 + +- 适配层主要是将钩子函数的功能实例化,实现具体的功能。 ## 开发指导 @@ -47,7 +56,7 @@ struct RtcMethod { }; ``` - **表1** RtcMethod结构体成员的钩子函数功能说明 +**表 1** RtcMethod结构体成员的钩子函数功能说明 | 函数 | 入参 | 出参 | 返回值 | 功能 | | -------- | -------- | -------- | -------- | -------- | @@ -57,30 +66,37 @@ struct RtcMethod { | WriteAlarm | host:结构体指针,核心层RTC控制器
alarmIndex:枚举值,闹钟报警索引
time:结构体指针,时间传入值 | 无 | HDF_STATUS相关状态 | 写RTC报警时间信息 | | RegisterAlarmCallback | host:结构体指针,核心层RTC控制器
alarmIndex:枚举值,闹钟报警索引
cb:函数指针,回调函数 | 无 | HDF_STATUS相关状态 | 注册报警超时回调函数 | | AlarmInterruptEnable | host:结构体指针,核心层RTC控制器
alarmIndex:枚举值,闹钟报警索引
enable:布尔值,控制报警 | 无 | HDF_STATUS相关状态 | 使能/去使能RTC报警中断 | -| GetFreq | host:结构体指针,核心层RTC控制器 | freq:uint32_t指针,传出的频率值 | HDF_STATUS相关状态 | 读RTC外接晶振频率 | -| SetFreq | host:结构体指针,核心层RTC控制器
freq:uint32_t,频率传入值 | 无 | HDF_STATUS相关状态 | 配置RTC外接晶振频率 | +| GetFreq | host:结构体指针,核心层RTC控制器 | freq:uint32_t类型指针,传出的频率值 | HDF_STATUS相关状态 | 读RTC外接晶振频率 | +| SetFreq | host:结构体指针,核心层RTC控制器
freq:uint32_t类型,频率传入值 | 无 | HDF_STATUS相关状态 | 配置RTC外接晶振频率 | | Reset | host:结构体指针,核心层RTC控制器 | 无 | HDF_STATUS相关状态 | RTC复位 | -| ReadReg | host:结构体指针,核心层RTC控制器
usrDefIndex:结构体,用户自定义寄存器索引 | value:uint8_t指针,传出的寄存器值 | HDF_STATUS相关状态 | 按照用户定义的寄存器索引,读取对应的寄存器配置,一个索引对应一字节的配置值 | -| WriteReg | host:结构体指针,核心层RTC控制器
usrDefIndex:结构体,用户自定义寄存器索引
value:uint8_t,寄存器传入值 | 无 | HDF_STATUS相关状态 | 按照用户定义的寄存器索引,设置对应的寄存器配置,一个索引对应一字节的配置值 | +| ReadReg | host:结构体指针,核心层RTC控制器
usrDefIndex:结构体,用户自定义寄存器索引 | value:uint8_t类型指针,传出的寄存器值 | HDF_STATUS相关状态 | 按照用户定义的寄存器索引,读取对应的寄存器配置,一个索引对应一字节的配置值 | +| WriteReg | host:结构体指针,核心层RTC控制器
usrDefIndex:结构体,用户自定义寄存器索引
value:uint8_t类型,寄存器传入值 | 无 | HDF_STATUS相关状态 | 按照用户定义的寄存器索引,设置对应的寄存器配置,一个索引对应一字节的配置值 | ### 开发步骤 -RTC模块适配HDF框架的三个必选环节是实例化驱动入口,配置属性文件,以及填充核心层接口函数。 +RTC模块适配HDF框架包含以下四个步骤: 1. 实例化驱动入口 - - 实例化HdfDriverEntry结构体成员。 - - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 + + - 实例化HdfDriverEntry结构体成员。 + + - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 2. 配置属性文件 - - 在device_info.hcs文件中添加deviceNode描述。 - - 【可选】添加rtc_config.hcs器件属性文件。 + + - 在device_info.hcs文件中添加deviceNode描述。 + + - 【可选】添加rtc_config.hcs器件属性文件。 3. 实例化RTC控制器对象 - - 初始化RtcHost成员。 - - 实例化RtcHost成员RtcMethod。 - > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 实例化RtcHost成员RtcMethod,其定义和成员说明见[接口说明](#接口说明)。 + + - 初始化RtcHost成员。 + + - 实例化RtcHost成员RtcMethod。 + + > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 实例化RtcHost成员RtcMethod,其定义和成员说明见[接口说明](#接口说明)。 4. 驱动调试 @@ -91,249 +107,265 @@ RTC模块适配HDF框架的三个必选环节是实例化驱动入口,配置 下方将以Hi3516DV300的驱动//device/soc/hisilicon/common/platform/rtc/rtc_hi35xx.c为示例,展示驱动适配者需要提供哪些内容来完整实现设备功能。 -1. 驱动开发首先需要实例化驱动入口。 - - 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。 +1. 实例化驱动入口 - HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 + 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 - 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 + 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 - RTC驱动入口参考: + RTC驱动入口参考: - ```c - struct HdfDriverEntry g_rtcDriverEntry = { - .moduleVersion = 1, - .Bind = HiRtcBind, // 见Bind开发参考 - .Init = HiRtcInit, // 见Init开发参考 - .Release = HiRtcRelease, // 见Release开发参考 - .moduleName = "HDF_PLATFORM_RTC",// 【必要】且与HCS里面的名字匹配 - }; - /* 调用HDF_INIT将驱动入口注册到HDF框架中 */ - HDF_INIT(g_rtcDriverEntry); - ``` + ```c + struct HdfDriverEntry g_rtcDriverEntry = { + .moduleVersion = 1, + .Bind = HiRtcBind, // 挂接RTC模块Bind实例化 + .Init = HiRtcInit, // 挂接RTC模块Init实例化 + .Release = HiRtcRelease, // 挂接RTC模块Release实例化 + .moduleName = "HDF_PLATFORM_RTC", // 【必要且与HCS文件中里面的moduleName匹配】 + }; + HDF_INIT(g_rtcDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 + ``` + +2. 配置属性文件 + + 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode描述。deviceNode信息与驱动入口注册相关。本例只有一个RTC控制器,如有多个器件信息,则需要在device_info.hcs文件增加deviceNode信息,以及在rtc_config.hcs文件中增加对应的器件属性。器件属性值与核心层RtcHost成员的默认值或限制范围有密切关系,比如RTC中断号,需要在rtc_config.hcs文件中增加对应的器件属性。 -2. 完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在rtc_config.hcs中配置器件属性。 + 独立服务模式的特点是device_info.hcs文件中设备节点代表着一个设备对象,如果存在多个设备对象,则按需添加,注意服务名与驱动私有数据匹配的关键字名称必须唯一。其中各项参数如表2所示: - deviceNode信息与驱动入口注册相关,器件属性值与核心层RtcHost成员的默认值或限制范围有密切关系。 + **表 2** device_info.hcs节点参数说明 - 本例只有一个RTC控制器,如有多个器件信息,则需要在device_info.hcs文件增加deviceNode信息,以及在rtc_config文件中增加对应的器件属性。 + | 成员名 | 值 | + | -------- | -------- | + | policy | 驱动服务发布的策略,RTC控制器具体配置为2,表示驱动对内核态和用户态都发布服务 | + | priority | 驱动启动优先级(0-200),值越大优先级越低。RTC控制器具体配置为30 | + | permission | 驱动创建设备节点权限,RTC控制器具体配置为0664 | + | moduleName | 驱动名称,RTC控制器固定为HDF_PLATFORM_RTC | + | serviceName | 驱动对外发布服务的名称,RTC控制器服务名设置为HDF_PLATFORM_RTC | + | deviceMatchAttr | 驱动私有数据匹配的关键字,RTC控制器设置为hisilicon_hi35xx_rtc | - - device_info.hcs配置参考 + - device_info.hcs配置参考 - - ```c - root { - device_info { - platform :: host { - device_rtc :: device { - device0 :: deviceNode { - policy = 1; // 2:用户态可见;1:内核态可见;0:不需要发布服务。 - priority = 30; // 值越小,优先级越高。 - permission = 0644; // 驱动创建设备节点权限 - moduleName = "HDF_PLATFORM_RTC"; // 【必要】用于指定驱动名称,需要与驱动Entry中的moduleName一致。 - serviceName = "HDF_PLATFORM_RTC"; // 【必要】驱动对外发布服务的名称,必须唯一。 - deviceMatchAttr = "hisilicon_hi35xx_rtc"; // 【必要】需要与设备hcs文件中的match_attr匹配。 - } - } - } - } - } - ``` + 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。 + + ```c + root { + device_info { + platform :: host { + device_rtc :: device { + device0 :: deviceNode { // 驱动的DeviceNode节点 + policy = 1; // policy字段是驱动服务发布的策略,如果需要面向用户态,则为2 + priority = 30; // 驱动启动优先级 + permission = 0644; // 驱动创建设备节点权限 + moduleName = "HDF_PLATFORM_RTC"; // 【必要】用于指定驱动名称,需要与驱动Entry中的moduleName一致。 + serviceName = "HDF_PLATFORM_RTC"; // 【必要】驱动对外发布服务的名称,必须唯一。 + deviceMatchAttr = "hisilicon_hi35xx_rtc"; // 【必要】用于配置控制器私有数据,必须和驱动私有数据配置表rtc_config.hcs中的match_attr值保持一致。 + } + } + } + } + } + ``` - - rtc_config.hcs配置参考 + - rtc_config.hcs配置参考 - - ```c - root { - platform { - rtc_config { - controller_0x12080000 { - match_attr = "hisilicon_hi35xx_rtc"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致 - rtcSpiBaseAddr = 0x12080000; // 地址映射相关 - regAddrLength = 0x100; // 地址映射相关 - irq = 37; // 中断号 - supportAnaCtrl = false; - supportLock = false; - anaCtrlAddr = 0xff; - lock0Addr = 0xff; - lock1Addr = 0xff; - lock2Addr = 0xff; - lock3Addr = 0xff; - } - } - } - } - ``` - - 需要注意的是,新增rtc_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。 - - 例如:本例中rtc_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/rtc/rtc_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句: - - ```c - #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/rtc/rtc_config.hcs" // 配置文件相对路径 - ``` - -3. 完成属性文件配置之后,下一步就是以核心层RtcHost对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化RtcHost成员RtcMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。 - - - 自定义结构体参考。 - - 从驱动的角度看,自定义结构体是参数和数据的载体,而且rtc_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员。 - - ```c - struct RtcConfigInfo { - uint32_t spiBaseAddr; // 地址映射相关 - volatile void *remapBaseAddr; // 地址映射相关 - uint16_t regAddrLength; // 地址映射相关 - uint8_t supportAnaCtrl; // 是否支持anactrl - uint8_t supportLock; // 是否支持锁 - uint8_t irq; // 中断号 - uint8_t alarmIndex; // 闹钟索引 - uint8_t anaCtrlAddr; // anactrl地址 - struct RtcLockAddr lockAddr; // 锁地址 - RtcAlarmCallback cb; // 回调函数 - struct OsalMutex mutex; // 互斥锁 - }; - - /* RtcHost是核心层控制器结构体,其中的成员在Init函数中会被赋值。 */ - struct RtcHost { - struct IDeviceIoService service; - struct HdfDeviceObject *device; - struct RtcMethod *method; - void *data; - }; - ``` - - - RtcHost成员钩子函数结构体RtcMethod的实例化,其他成员在Init函数中初始化。 - - ```c - /* rtc_hi35xx.c中的示例:钩子函数的填充 */ - static struct RtcMethod g_method = { - .ReadTime = HiRtcReadTime, - .WriteTime = HiRtcWriteTime, - .ReadAlarm = HiReadAlarm, - .WriteAlarm = HiWriteAlarm, - .RegisterAlarmCallback = HiRegisterAlarmCallback, - .AlarmInterruptEnable = HiAlarmInterruptEnable, - .GetFreq = HiGetFreq, - .SetFreq = HiSetFreq, - .Reset = HiReset, - .ReadReg = HiReadReg, - .WriteReg = HiWriteReg, - }; - ``` - - - Bind函数开发参考 - - 入参: - - HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 - - 返回值: - - HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 - - **表2** HDF_STATUS返回值描述 - - | 状态(值) | 问题描述 | - | -------- | -------- | - | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | - | HDF_ERR_MALLOC_FAIL | 内存分配失败 | - | HDF_ERR_INVALID_PARAM | 参数非法 | - | HDF_ERR_IO | I/O 错误 | - | HDF_SUCCESS | 初始化成功 | - | HDF_FAILURE | 初始化失败 | - - 函数说明: - - 关联HdfDeviceObject对象和RtcHost。 - - ```c - static int32_t HiRtcBind(struct HdfDeviceObject *device) - { - struct RtcHost *host = NULL; - host = RtcHostCreate(device); // 实际是申请内存并挂接device: host->device = device - // 使HdfDeviceObject与RtcHost可以相互转化的前提 - ... - device->service = &host->service; // 使HdfDeviceObject与RtcHost可以相互转化的前提 - // 方便后续通过调用RtcHostFromDevice实现全局性质的host - return HDF_SUCCESS; - } - ``` - - - Init函数开发参考 - - 入参: - - HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 - - 返回值: - - HDF_STATUS相关状态。 - - 函数说明: - - 初始化自定义结构体对象,初始化RtcHost成员。 - - ```c - static int32_t HiRtcInit(struct HdfDeviceObject *device) - { - struct RtcHost *host = NULL; - struct RtcConfigInfo *rtcInfo = NULL; - ... - host = RtcHostFromDevice(device); // 这里是HdfDeviceObject到RtcHost的强制转换 - rtcInfo = OsalMemCalloc(sizeof(*rtcInfo)); - ... - /* HiRtcConfigData会从设备配置树中读取属性填充rtcInfo的supportAnaCtrl、supportLock、spiBaseAddr、regAddrLength、irq, - * 为HiRtcSwInit和HiRtcSwInit提供参数,当函数HiRtcSwInit和HiRtcSwInit内部执行失败后进行内存释放等操作。 - */ - if (HiRtcConfigData(rtcInfo, device->property) != 0) { - ... - } - if (HiRtcSwInit(rtcInfo) != 0) { // 地址映射以及中断注册相关 - ... - } - if (HiRtcHwInit(rtcInfo) != 0) { // 初始化anaCtrl和lockAddr相关内容 - ... - } + 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/rtc/rtc_config.hcs文件配置器件属性,其中配置参数如下: + + ```c + root { + platform { + rtc_config { + controller_0x12080000 { + match_attr = "hisilicon_hi35xx_rtc"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致 + rtcSpiBaseAddr = 0x12080000; // 地址映射相关 + regAddrLength = 0x100; // 地址映射相关 + irq = 37; // 中断号 + supportAnaCtrl = false; + supportLock = false; + anaCtrlAddr = 0xff; + lock0Addr = 0xff; + lock1Addr = 0xff; + lock2Addr = 0xff; + lock3Addr = 0xff; + } + } + } + } + ``` + + 需要注意的是,新增rtc_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。 + + 例如:本例中rtc_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/rtc/rtc_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句: + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/rtc/rtc_config.hcs" // 配置文件相对路径 + ``` + +3. 实例化RTC控制器对象 + + 完成属性文件配置之后,下一步就是以核心层RtcHost对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化RtcHost成员RtcMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。 + + - 自定义结构体参考。 + + 从驱动的角度看,自定义结构体是参数和数据的载体,而且rtc_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员。 + + ```c + struct RtcConfigInfo { + uint32_t spiBaseAddr; // 地址映射相关 + volatile void *remapBaseAddr; // 地址映射相关 + uint16_t regAddrLength; // 地址映射相关 + uint8_t supportAnaCtrl; // 是否支持anactrl + uint8_t supportLock; // 是否支持锁 + uint8_t irq; // 中断号 + uint8_t alarmIndex; // 闹钟索引 + uint8_t anaCtrlAddr; // anactrl地址 + struct RtcLockAddr lockAddr; // 锁地址 + RtcAlarmCallback cb; // 回调函数 + struct OsalMutex mutex; // 互斥锁 + }; - host->method = &g_method; // RtcMethod的实例化对象的挂载 - host->data = rtcInfo; // 使RtcConfigInfo与RtcHost可以相互转化的前提 - HDF_LOGI("Hdf dev service:%s init success!", HdfDeviceGetServiceName(device)); - return HDF_SUCCESS; - } - ``` - - Release函数开发参考 + // RtcHost是核心层控制器结构体,其中的成员在Init函数中会被赋值。 + struct RtcHost { + struct IDeviceIoService service; + struct HdfDeviceObject *device; + struct RtcMethod *method; + void *data; + }; + ``` + + - RtcHost成员钩子函数结构体RtcMethod的实例化。 + + ```c + // rtc_hi35xx.c中的示例:钩子函数的填充 + static struct RtcMethod g_method = { + .ReadTime = HiRtcReadTime, + .WriteTime = HiRtcWriteTime, + .ReadAlarm = HiReadAlarm, + .WriteAlarm = HiWriteAlarm, + .RegisterAlarmCallback = HiRegisterAlarmCallback, + .AlarmInterruptEnable = HiAlarmInterruptEnable, + .GetFreq = HiGetFreq, + .SetFreq = HiSetFreq, + .Reset = HiReset, + .ReadReg = HiReadReg, + .WriteReg = HiWriteReg, + }; + ``` + + - Bind函数开发参考 + + 入参: + + HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 + + 返回值: + + HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 + + **表 3** HDF_STATUS相关状态说明 + + | 状态(值) | 问题描述 | + | -------- | -------- | + | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | + | HDF_ERR_MALLOC_FAIL | 内存分配失败 | + | HDF_ERR_INVALID_PARAM | 参数非法 | + | HDF_ERR_IO | I/O 错误 | + | HDF_SUCCESS | 初始化成功 | + | HDF_FAILURE | 初始化失败 | + + 函数说明: + + 关联HdfDeviceObject对象和RtcHost。 + + ```c + static int32_t HiRtcBind(struct HdfDeviceObject *device) + { + struct RtcHost *host = NULL; + host = RtcHostCreate(device); // 实际是申请内存并挂接device: host->device = device + // 使HdfDeviceObject与RtcHost可以相互转化的前提 + ...... + device->service = &host->service; // 使HdfDeviceObject与RtcHost可以相互转化的前提 + // 方便后续通过调用RtcHostFromDevice实现全局性质的host + return HDF_SUCCESS; + } + ``` + + - Init函数开发参考 + + 入参: + + HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 + + 返回值: + + HDF_STATUS相关状态。 + + 函数说明: + + 初始化自定义结构体对象,初始化RtcHost成员。 + + ```c + static int32_t HiRtcInit(struct HdfDeviceObject *device) + { + struct RtcHost *host = NULL; + struct RtcConfigInfo *rtcInfo = NULL; + ...... + host = RtcHostFromDevice(device); // 这里是HdfDeviceObject到RtcHost的强制转换 + rtcInfo = OsalMemCalloc(sizeof(*rtcInfo)); + ...... + /* HiRtcConfigData会从设备配置树中读取属性填充rtcInfo的supportAnaCtrl、supportLock、spiBaseAddr、regAddrLength、irq, + * 为HiRtcSwInit和HiRtcSwInit提供参数,当函数HiRtcSwInit和HiRtcSwInit内部执行失败后进行内存释放等操作。 + */ + if (HiRtcConfigData(rtcInfo, device->property) != 0) { + ...... + } + if (HiRtcSwInit(rtcInfo) != 0) { // 地址映射以及中断注册相关 + ...... + } + if (HiRtcHwInit(rtcInfo) != 0) { // 初始化anaCtrl和lockAddr相关内容 + ...... + } + + host->method = &g_method; // RtcMethod的实例化对象的挂载 + host->data = rtcInfo; // 使RtcConfigInfo与RtcHost可以相互转化的前提 + HDF_LOGI("Hdf dev service:%s init success!", HdfDeviceGetServiceName(device)); + return HDF_SUCCESS; + } + ``` + - Release函数开发参考 - 入参: + 入参: - HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 + HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 - 返回值: + 返回值: - 无。 + 无。 - 函数说明: + 函数说明: - 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 + 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 - > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 所有强制转换获取相应对象的操作前提是在Init或Bind函数中具备对应赋值的操作。 + > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 所有强制转换获取相应对象的操作前提是在Init或Bind函数中具备对应赋值的操作。 - ```c - static void HiRtcRelease(struct HdfDeviceObject *device) - { - struct RtcHost *host = NULL; - struct RtcConfigInfo *rtcInfo = NULL; - ... - host = RtcHostFromDevice(device); // 这里是HdfDeviceObject到RtcHost的强制转换 - rtcInfo = (struct RtcConfigInfo *)host->data; // 这里是RtcHost到RtcConfigInfo的强制转换 - if (rtcInfo != NULL) { - HiRtcSwExit(rtcInfo); - OsalMemFree(rtcInfo); // 释放RtcConfigInfo - host->data = NULL; - } - RtcHostDestroy(host); // 释放RtcHost - } - ``` + ```c + static void HiRtcRelease(struct HdfDeviceObject *device) + { + struct RtcHost *host = NULL; + struct RtcConfigInfo *rtcInfo = NULL; + ... + host = RtcHostFromDevice(device); // 这里是HdfDeviceObject到RtcHost的强制转换 + rtcInfo = (struct RtcConfigInfo *)host->data; // 这里是RtcHost到RtcConfigInfo的强制转换 + if (rtcInfo != NULL) { + HiRtcSwExit(rtcInfo); + OsalMemFree(rtcInfo); // 释放RtcConfigInfo + host->data = NULL; + } + RtcHostDestroy(host); // 释放RtcHost + } + ``` + +4. 驱动调试 + + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,读取RTC时间、设置RTC时间等。 diff --git a/zh-cn/device-dev/driver/driver-platform-sdio-des.md b/zh-cn/device-dev/driver/driver-platform-sdio-des.md index c43d88eecd9d918c5298ad7ef9317e42d81a8ce2..21747ff03bfaa77fcac49de37bdb254bd8b613a9 100644 --- a/zh-cn/device-dev/driver/driver-platform-sdio-des.md +++ b/zh-cn/device-dev/driver/driver-platform-sdio-des.md @@ -7,26 +7,54 @@ SDIO是安全数字输入输出接口(Secure Digital Input and Output)的缩写,是从SD内存卡接口的基础上演化出来的一种外设接口。SDIO接口兼容以前的SD卡,并且可以连接支持SDIO接口的其他设备。 SDIO接口定义了操作SDIO的通用方法集合,包括: + - 打开/关闭SDIO控制器 + - 独占/释放HOST + - 使能/去使能设备 + - 申请/释放中断 + - 读写、获取/设置公共信息 ### 运作机制 在HDF框架中,SDIO的接口适配模式采用独立服务模式。在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,若设备过多可能增加内存占用。 -SDIO总线有两端,其中一端是主机端(HOST),另一端是设备端(DEVICE)。所有的通信都是由HOST端发出命令开始的,在DEVICE端只要能解析HOST的命令,就可以同HOST进行通信了。SDIO的HOST可以连接多个DEVICE,如下图所示: +独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为: + +- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。 + +- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。 + +SDIO模块各分层作用: + +- 接口层提供打开SDIO设备、设置块的大小、读取数据、写数据、设置公共信息、获取公共信息、刷新数据、独占HOST、释放Host、使能SDIO功能设备、去使能SDIO功能设备、申请中断、释放中断关闭SDIO设备的接口。 + +- 核心层主要提供SDIO控制器的添加、移除及管理的能力,通过钩子函数与适配层交互。 + +- 适配层主要是将钩子函数的功能实例化,实现具体的功能。 + +**图 1** SDIO独立服务模式结构图 + +![SDIO独立服务模式结构图](figures/独立服务模式结构图.png) + +SDIO总线有两端,其中一端是主机端(HOST),另一端是设备端(DEVICE)。所有的通信都是由HOST端发出命令开始的,在DEVICE端只要能解析HOST的命令,就可以同HOST进行通信了。SDIO的HOST可以连接多个DEVICE,如图2所示: + - CLK信号:HOST给DEVICE的时钟信号。 + - VDD信号:电源信号。 + - VSS信号:Ground信号。 + - D0-3信号:4条数据线,其中,DAT1信号线复用为中断线,在1BIT模式下DAT0用来传输数据,在4BIT模式下DAT0-DAT3用来传输数据。 + - CMD信号:用于HOST发送命令和DEVICE回复响应。 -**图1** SDIO的HOST-DEVICE连接示意图 +**图 2** SDIO的HOST-DEVICE连接示意图 -![image](figures/SDIO的HOST-DEVICE连接示意图.png "SDIO的HOST-DEVICE连接示意图") +![SDIO的HOST-DEVICE连接示意图](figures/SDIO的HOST-DEVICE连接示意图.png) ### 约束与限制 @@ -42,9 +70,9 @@ SDIO的应用比较广泛,目前,有许多手机都支持SDIO功能,并且 SDIO模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/sdio_if.h。 -**表1** SDIO驱动API接口功能介绍 +**表 1** SDIO驱动API接口功能介绍 -| 接口名 | 接口描述 | +| 接口名 | 接口描述 | | -------- | -------- | | DevHandle SdioOpen(int16_t mmcBusNum, struct SdioFunctionConfig \*config) | 打开指定总线号的SDIO控制器 | | void SdioClose(DevHandle handle) | 关闭SDIO控制器 | @@ -67,11 +95,11 @@ SDIO模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core ### 使用流程 -使用SDIO的一般流程如下图所示。 +使用SDIO的一般流程如图3所示。 - **图2** SDIO使用流程图 +**图 3** SDIO使用流程图 - ![image](figures/SDIO使用流程图.png "SDIO使用流程图") +![SDIO使用流程图](figures/SDIO使用流程图.png) #### 打开SDIO控制器 @@ -81,17 +109,17 @@ SDIO模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core DevHandle SdioOpen(int16_t mmcBusNum, struct SdioFunctionConfig *config); ``` - **表2** SdioOpen函数的参数和返回值描述 +**表 2** SdioOpen函数的参数和返回值描述 | 参数 | 参数描述 | | -------- | -------- | -| mmcBusNum | 总线号 | -| config | SDIO功能配置信息 | +| mmcBusNum | int16_t类型,总线号 | +| config | 结构体指针,SDIO功能配置信息 | | **返回值** | **返回值描述** | | NULL | 获取SDIO控制器的设备句柄失败 | | 设备句柄 | SDIO控制器的设备句柄 | - 打开SDIO控制器的示例如下: +打开SDIO控制器的示例如下: ```c DevHandle handle = NULL; @@ -99,10 +127,11 @@ struct SdioFunctionConfig config; config.funcNr = 1; config.vendorId = 0x123; config.deviceId = 0x456; -/* 打开总线号为1的SDIO控制器 */ +// 打开总线号为1的SDIO控制器 handle = SdioOpen(1, &config); if (handle == NULL) { - HDF_LOGE("SdioOpen: failed!\n"); + HDF_LOGE("SdioOpen: open sdio fail!\n"); + return NULL; } ``` @@ -114,16 +143,16 @@ if (handle == NULL) { void SdioClaimHost(DevHandle handle); ``` - **表3** SdioClaimHost函数的参数描述 +**表 3** SdioClaimHost函数的参数描述 | 参数 | 参数描述 | | -------- | -------- | -| handle | SDIO控制器的设备句柄 | +| handle | DevHandle类型,SDIO控制器的设备句柄 | 独占HOST示例如下: ```c -SdioClaimHost(handle); /* 独占HOST */ +SdioClaimHost(handle); // 独占HOST ``` #### 使能SDIO设备 @@ -134,62 +163,64 @@ SdioClaimHost(handle); /* 独占HOST */ int32_t SdioEnableFunc(DevHandle handle); ``` - **表4** SdioEnableFunc函数的参数和返回值描述 +**表 4** SdioEnableFunc函数的参数和返回值描述 | 参数 | 参数描述 | | -------- | -------- | -| handle | SDIO控制器的设备句柄 | +| handle | DevHandle类型,SDIO控制器的设备句柄 | | **返回值** | **返回值描述** | -| 0 | SDIO使能成功 | +| HDF_SUCCESS | SDIO使能成功 | | 负数 | SDIO使能失败 | 使能SDIO设备的示例如下: ```c int32_t ret; -/* 使能SDIO设备 */ +// 使能SDIO设备 ret = SdioEnableFunc(handle); -if (ret != 0) { - HDF_LOGE("SdioEnableFunc: failed, ret %d\n", ret); +if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioEnableFunc: sdio enable func fail, ret:%d\n", ret); + return ret; } ``` #### 注册SDIO中断 -在通信之前,还需要注册SDIO中断,注册SDIO中断函数如下图所示: +在通信之前,还需要注册SDIO中断,注册SDIO中断函数如下所示: ```c int32_t SdioClaimIrq(DevHandle handle, SdioIrqHandler *handler); ``` - **表5** SdioClaimIrq函数的参数和返回值描述 +**表 5** SdioClaimIrq函数的参数和返回值描述 | 参数 | 参数描述 | | -------- | -------- | -| handle | SDIO控制器的设备句柄 | -| handler | 中断服务函数指针 | +| handle | DevHandle类型,SDIO控制器的设备句柄 | +| handler | 函数指针,中断服务函数 | | **返回值** | **返回值描述** | -| 0 | 注册中断成功 | -| 负数 | 注册中断失败 | +| HDF_SUCCESS | 注册SDIO中断成功 | +| 负数 | 注册SDIO中断失败 | 注册SDIO中的示例如下: ```c -/* 中断服务函数需要根据各自平台的情况去实现 */ +// 中断服务函数需要根据各自平台的情况去实现 static void SdioIrqFunc(void *data) { if (data == NULL) { HDF_LOGE("SdioIrqFunc: data is NULL.\n"); return; } - /* 需要开发者自行添加具体实现 */ + // 需要开发者自行添加具体实现 } int32_t ret; -/* 注册SDIO中断 */ +// 注册SDIO中断 ret = SdioClaimIrq(handle, SdioIrqFunc); -if (ret != 0) { - HDF_LOGE("SdioClaimIrq: failed, ret %d\n", ret); +if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioClaimIrq: sdio claim irq fail, ret:%d\n", ret); + return ret; } ``` @@ -197,201 +228,207 @@ if (ret != 0) { - 向SDIO设备增量写入指定长度的数据 - 对应的接口函数如下所示: - - ```c - int32_t SdioWriteBytes(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size); - ``` - - **表6** SdioWriteBytes函数的参数和返回值描述 - - | 参数 | 参数描述 | - | -------- | -------- | - | handle | SDIO控制器的设备句柄 | - | data | 待写入数据的指针 | - | addr | 待写入数据的起始地址 | - | size | 待写入数据的长度 | - | **返回值** | **返回值描述** | - | 0 | SDIO写数据成功 | - | 负数 | SDIO写数据失败 | - - 向SDIO设备增量写入指定长度的数据的示例如下: - - ```c - int32_t ret; - uint8_t wbuff[] = {1,2,3,4,5}; - uint32_t addr = 0x100 + 0x09; - /* 向SDIO设备起始地址0x109,增量写入5个字节的数据 */ - ret = SdioWriteBytes(handle, wbuff, addr, sizeof(wbuff) / sizeof(wbuff[0])); - if (ret != 0) { - HDF_LOGE("SdioWriteBytes: failed, ret %d\n", ret); - } - ``` + 对应的接口函数如下所示: + + ```c + int32_t SdioWriteBytes(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size); + ``` + + **表 6** SdioWriteBytes函数的参数和返回值描述 + + | 参数 | 参数描述 | + | -------- | -------- | + | handle | DevHandle类型,SDIO控制器的设备句柄 | + | data | uint8_t类型指针,待写入数据 | + | addr | uint32_t类型,待写入数据的起始地址 | + | size | uint32_t类型,待写入数据的长度 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | SDIO写数据成功 | + | 负数 | SDIO写数据失败 | + + 向SDIO设备增量写入指定长度的数据的示例如下: + + ```c + int32_t ret; + uint8_t wbuff[] = {1,2,3,4,5}; + uint32_t addr = 0x100 + 0x09; + // 向SDIO设备起始地址0x109,增量写入5个字节的数据 + ret = SdioWriteBytes(handle, wbuff, addr, sizeof(wbuff) / sizeof(wbuff[0])); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioWriteBytes: sdio write bytes fail, ret:%d\n", ret); + return ret; + } + ``` - 从SDIO设备增量读取指定长度的数据 - 对应的接口函数如下所示: - - ```c - int32_t SdioReadBytes(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size); - ``` - - **表7** SdioReadBytes函数的参数和返回值描述 - - | 参数 | 参数描述 | - | -------- | -------- | - | handle | SDIO控制器的设备句柄 | - | data | 接收读取数据的指针 | - | addr | 待读取数据的起始地址 | - | size | 待读取数据的长度 | - | **返回值** | **返回值描述** | - | 0 | SDIO读数据成功 | - | 负数 | SDIO读数据失败 | - - 从SDIO设备增量读取指定长度的数据的示例如下: - - ```c - int32_t ret; - uint8_t rbuff[5] = {0}; - uint32_t addr = 0x100 + 0x09; - /* 从SDIO设备起始地址0x109,增量读取5个字节的数据 */ - ret = SdioReadBytes(handle, rbuff, addr, 5); - if (ret != 0) { - HDF_LOGE("SdioReadBytes: failed, ret %d\n", ret); - } - ``` + 对应的接口函数如下所示: + + ```c + int32_t SdioReadBytes(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size); + ``` + + **表 7** SdioReadBytes函数的参数和返回值描述 + + | 参数 | 参数描述 | + | -------- | -------- | + | handle | DevHandle类型,SDIO控制器的设备句柄 | + | data | uint8_t类型指针,接收读取数据 | + | addr | uint32_t类型,待读取数据的起始地址 | + | size | uint32_t类型,待读取数据的长度 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | SDIO读数据成功 | + | 负数 | SDIO读数据失败 | + + 从SDIO设备增量读取指定长度的数据的示例如下: + + ```c + int32_t ret; + uint8_t rbuff[5] = {0}; + uint32_t addr = 0x100 + 0x09; + // 从SDIO设备起始地址0x109,增量读取5个字节的数据 + ret = SdioReadBytes(handle, rbuff, addr, 5); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioReadBytes: sdio read bytes fail, ret:%d\n", ret); + return ret; + } + ``` - 向SDIO设备的固定地址写入指定长度的数据 - 对应的接口函数如下所示: + 对应的接口函数如下所示: - ```c - int32_t SdioWriteBytesToFixedAddr(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen); - ``` + ```c + int32_t SdioWriteBytesToFixedAddr(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen); + ``` - **表8** SdioWriteBytesToFixedAddr函数的参数和返回值描述 + **表 8** SdioWriteBytesToFixedAddr函数的参数和返回值描述 - | 参数 | 参数描述 | - | -------- | -------- | - | handle | SDIO控制器的设备句柄 | - | data | 待写入数据的指针 | - | addr | 待写入数据的固定地址 | - | size | 待写入数据的长度 | - | scatterLen | 集散表的长度。如果该字段不为0,则data为集散表类型。 | - | **返回值** | **返回值描述** | - | 0 | SDIO写数据成功 | - | 负数 | SDIO写数据失败 | - - 向SDIO设备的固定地址写入指定长度的数据的示例如下: - - ```c - int32_t ret; - uint8_t wbuff[] = {1,2,3,4,5}; - uint32_t addr = 0x100 + 0x09; - /* 向SDIO设备固定地址0x109写入5个字节的数据 */ - ret = SdioWriteBytesToFixedAddr(handle, wbuff, addr, sizeof(wbuff) / sizeof(wbuff[0]), 0); - if (ret != 0) { - HDF_LOGE("SdioWriteBytesToFixedAddr: failed, ret %d\n", ret); - } - ``` + | 参数 | 参数描述 | + | -------- | -------- | + | handle | DevHandle类型,SDIO控制器的设备句柄 | + | data | uint8_t类型指针,待写入数据 | + | addr | uint32_t类型,待写入数据的固定地址 | + | size | uint32_t类型,待写入数据的长度 | + | scatterLen | uint32_t类型,集散表的长度。如果该字段不为0,则data为集散表类型。 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | SDIO写数据成功 | + | 负数 | SDIO写数据失败 | + + 向SDIO设备的固定地址写入指定长度的数据的示例如下: + + ```c + int32_t ret; + uint8_t wbuff[] = {1, 2, 3, 4, 5}; + uint32_t addr = 0x100 + 0x09; + // 向SDIO设备固定地址0x109写入5个字节的数据 + ret = SdioWriteBytesToFixedAddr(handle, wbuff, addr, sizeof(wbuff) / sizeof(wbuff[0]), 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioWriteBytesToFixedAddr: sdio write bytes to fixed addr fail, ret:%d\n", ret); + return ret; + } + ``` - 从SDIO设备的固定地址读取指定长度的数据 - 对应的接口函数如下所示: + 对应的接口函数如下所示: - ```c - int32_t SdioReadBytesFromFixedAddr(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen); - ``` + ```c + int32_t SdioReadBytesFromFixedAddr(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen); + ``` - **表9** SdioReadBytesFromFixedAddr函数的参数和返回值描述 + **表 9** SdioReadBytesFromFixedAddr函数的参数和返回值描述 - | 参数 | 参数描述 | - | -------- | -------- | - | handle | SDIO控制器的设备句柄 | - | data | 接收读取数据的指针 | - | addr | 待读取数据的起始地址 | - | size | 待读取数据的长度 | - | scatterLen | 集散表的长度。如果该字段不为0,则data为集散表类型。 | - | **返回值** | **返回值描述** | - | 0 | SDIO读数据成功 | - | 负数 | SDIO读数据失败 | - - 从SDIO设备的固定地址读取指定长度的数据的示例如下: - - ```c - int32_t ret; - uint8_t rbuff[5] = {0}; - uint32_t addr = 0x100 + 0x09; - /* 从SDIO设备固定地址0x109中读取5个字节的数据 */ - ret = SdioReadBytesFromFixedAddr(handle, rbuff, addr, 5, 0); - if (ret != 0) { - HDF_LOGE("SdioReadBytesFromFixedAddr: failed, ret %d\n", ret); - } - ``` + | 参数 | 参数描述 | + | -------- | -------- | + | handle | DevHandle类型,SDIO控制器的设备句柄 | + | data | uint8_t类型指针,接收读取数据 | + | addr | uint32_t类型,待读取数据的起始地址 | + | size | uint32_t类型,待读取数据的长度 | + | scatterLen | uint32_t类型,集散表的长度。如果该字段不为0,则data为集散表类型。 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | SDIO读数据成功 | + | 负数 | SDIO读数据失败 | + + 从SDIO设备的固定地址读取指定长度的数据的示例如下: + + ```c + int32_t ret; + uint8_t rbuff[5] = {0}; + uint32_t addr = 0x100 + 0x09; + // 从SDIO设备固定地址0x109中读取5个字节的数据 + ret = SdioReadBytesFromFixedAddr(handle, rbuff, addr, 5, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioReadBytesFromFixedAddr: sdio read bytes from fixed addr fail, ret:%d\n", ret); + return ret; + } + ``` - 向SDIO function 0的指定地址空间写入指定长度的数据 - 当前只支持写入一个字节的数据,对应的接口函数如下所示: - - ```c - int32_t SdioWriteBytesToFunc0(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size); - ``` - - **表10** SdioWriteBytesToFunc0函数的参数和返回值描述 - - | 参数 | 参数描述 | - | -------- | -------- | - | handle | SDIO控制器的设备句柄 | - | data | 待写入数据的指针 | - | addr | 待写入数据的起始地址 | - | size | 待写入数据的长度 | - | **返回值** | **返回值描述** | - | 0 | SDIO写数据成功 | - | 负数 | SDIO写数据失败 | - - 向SDIO function 0的指定地址空间写入指定长度的数据的示例如下: - - ```c - int32_t ret; - uint8_t wbuff = 1; - /* 向SDIO function 0地址0x2中写入1字节的数据 */ - ret = SdioWriteBytesToFunc0(handle, &wbuff, 0x2, 1); - if (ret != 0) { - HDF_LOGE("SdioWriteBytesToFunc0: failed, ret %d\n", ret); - } - ``` + 当前只支持写入一个字节的数据,对应的接口函数如下所示: + + ```c + int32_t SdioWriteBytesToFunc0(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size); + ``` + + **表 10** SdioWriteBytesToFunc0函数的参数和返回值描述 + + | 参数 | 参数描述 | + | -------- | -------- | + | handle | DevHandle类型,SDIO控制器的设备句柄 | + | data | uint8_t类型指针,待写入数据 | + | addr | uint32_t类型,待写入数据的起始地址 | + | size | uint32_t类型,待写入数据的长度 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | SDIO写数据成功 | + | 负数 | SDIO写数据失败 | + + 向SDIO function 0的指定地址空间写入指定长度的数据的示例如下: + + ```c + int32_t ret; + uint8_t wbuff = 1; + // 向SDIO function 0地址0x2中写入1字节的数据 + ret = SdioWriteBytesToFunc0(handle, &wbuff, 0x2, 1); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioWriteBytesToFunc0: sdio write bytes to func0 fail, ret:%d\n", ret); + return ret; + } + ``` - 从SDIO function 0的指定地址空间读取指定长度的数据 - 当前只支持读取一个字节的数据,对应的接口函数如下所示: + 当前只支持读取一个字节的数据,对应的接口函数如下所示: - ```c - int32_t SdioReadBytesFromFunc0(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size); - ``` + ```c + int32_t SdioReadBytesFromFunc0(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size); + ``` - **表11** SdioReadBytesFromFunc0函数的参数和返回值描述 + **表 11** SdioReadBytesFromFunc0函数的参数和返回值描述 - | 参数 | 参数描述 | - | -------- | -------- | - | handle | SDIO控制器的设备句柄 | - | data | 接收读取数据的指针 | - | addr | 待读取数据的起始地址 | - | size | 待读取数据的长度 | - | **返回值** | **返回值描述** | - | 0 | SDIO读数据成功 | - | 负数 | SDIO读数据失败 | + | 参数 | 参数描述 | + | -------- | -------- | + | handle | DevHandle类型,SDIO控制器的设备句柄 | + | data | uint8_t类型指针,接收读取数据 | + | addr | uint32_t类型,待读取数据的起始地址 | + | size | uint32_t类型,待读取数据的长度 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | SDIO读数据成功 | + | 负数 | SDIO读数据失败 | - 从SDIO function 0的指定地址空间读取指定长度的数据的示例如下: + 从SDIO function 0的指定地址空间读取指定长度的数据的示例如下: - ```c - int32_t ret; - uint8_t rbuff; - /* 从SDIO function 0设备地址0x2中读取1字节的数据 */ - ret = SdioReadBytesFromFunc0(handle, &rbuff, 0x2, 1); - if (ret != 0) { - HDF_LOGE("SdioReadBytesFromFunc0: failed, ret %d\n", ret); - } - ``` + ```c + int32_t ret; + uint8_t rbuff; + /* 从SDIO function 0设备地址0x2中读取1字节的数据 */ + ret = SdioReadBytesFromFunc0(handle, &rbuff, 0x2, 1); + if (ret != 0) { + HDF_LOGE("SdioReadBytesFromFunc0: sdio read bytes from func0 fail, ret:%d\n", ret); + return ret; + } + ``` #### 释放SDIO中断 @@ -399,23 +436,24 @@ if (ret != 0) { int32_t SdioReleaseIrq(DevHandle handle); - **表12** SdioReleaseIrq函数的参数和返回值描述 +**表 12** SdioReleaseIrq函数的参数和返回值描述 | 参数 | 参数描述 | | -------- | -------- | -| handle | SDIO控制器的设备句柄 | +| handle | DevHandle类型,SDIO控制器的设备句柄 | | **返回值** | **返回值描述** | -| 0 | 释放SDIO中断成功 | +| HDF_SUCCESS | 释放SDIO中断成功 | | 负数 | 释放SDIO中断失败 | 释放SDIO中断的示例如下: ```c int32_t ret; -/* 释放SDIO中断 */ +// 释放SDIO中断 ret = SdioReleaseIrq(handle); -if (ret != 0) { - HDF_LOGE("SdioReleaseIrq: failed, ret %d\n", ret); +if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioReleaseIrq: sdio release irq fail, ret:%d\n", ret); + return ret; } ``` @@ -425,23 +463,24 @@ if (ret != 0) { int32_t SdioDisableFunc(DevHandle handle); - **表13** SdioDisableFunc函数的参数和返回值描述 +**表 13** SdioDisableFunc函数的参数和返回值描述 | 参数 | 参数描述 | | -------- | -------- | -| handle | SDIO控制器的设备句柄 | +| handle | DevHandle类型,SDIO控制器的设备句柄 | | **返回值** | **返回值描述** | -| 0 | 去使能SDIO设备成功 | +| HDF_SUCCESS | 去使能SDIO设备成功 | | 负数 | 去使能SDIO设备失败 | 去使能SDIO设备的示例如下: ```c int32_t ret; -/* 去使能SDIO设备 */ +// 去使能SDIO设备 ret = SdioDisableFunc(handle); -if (ret != 0) { - HDF_LOGE("SdioDisableFunc: failed, ret %d\n", ret); +if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioDisableFunc: sdio disable func fail, ret:%d\n", ret); + return ret; } ``` @@ -453,16 +492,16 @@ if (ret != 0) { void SdioReleaseHost(DevHandle handle); ``` - **表14** SdioReleaseHost函数的参数描述 +**表 14** SdioReleaseHost函数的参数描述 | 参数 | 参数描述 | | -------- | -------- | -| handle | SDIO控制器的设备句柄 | +| handle | DevHandle类型,SDIO控制器的设备句柄 | 释放HOST的示例如下: ```c -SdioReleaseHost(handle); /* 释放HOST */ +SdioReleaseHost(handle); // 释放HOST ``` #### 关闭SDIO控制器 @@ -475,16 +514,16 @@ void SdioClose(DevHandle handle); 该函数会释放掉申请的资源。 - **表15** SdioClose函数的参数描述 +**表 15** SdioClose函数的参数描述 | 参数 | 参数描述 | | -------- | -------- | -| handle | SDIO控制器的设备句柄 | +| handle | DevHandle类型,SDIO控制器的设备句柄 | 关闭SDIO控制器的示例如下: ```c -SdioClose(handle); /* 关闭SDIO控制器 */ +SdioClose(handle); // 关闭SDIO控制器 ``` ### 使用实例 diff --git a/zh-cn/device-dev/driver/driver-platform-sdio-develop.md b/zh-cn/device-dev/driver/driver-platform-sdio-develop.md index b5d7e6205a959a27f5be0d71202e9435a4604043..bde6b9371d4e1ed14ba2878c45beec52d8db725f 100755 --- a/zh-cn/device-dev/driver/driver-platform-sdio-develop.md +++ b/zh-cn/device-dev/driver/driver-platform-sdio-develop.md @@ -13,11 +13,20 @@ SDIO(Secure Digital Input and Output)由SD卡发展而来,与SD卡统称 独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为: - 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。 + - device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。 -**图1** SDIO独立服务模式结构图 +SDIO模块各分层作用: + +- 接口层提供打开SDIO设备、设置块的大小、读取数据、写数据、设置公共信息、获取公共信息、刷新数据、独占HOST、释放Host、使能SDIO功能设备、去使能SDIO功能设备、申请中断、释放中断关闭SDIO设备的接口。 + +- 核心层主要提供SDIO控制器的添加、移除及管理的能力,通过钩子函数与适配层交互。 + +- 适配层主要是将钩子函数的功能实例化,实现具体的功能。 -![image](figures/独立服务模式结构图.png "SDIO独立服务模式结构图") +**图 1** SDIO独立服务模式结构图 + +![SDIO独立服务模式结构图](figures/独立服务模式结构图.png) ### 约束与限制 @@ -36,7 +45,6 @@ SDIO的应用比较广泛,目前,有许多手机都支持SDIO功能,并且 SdioDeviceOps定义: ```c -/* 函数模板 */ struct SdioDeviceOps { int32_t (*incrAddrReadBytes)(struct SdioDevice *dev, uint8_t *data, uint32_t addr, uint32_t size); int32_t (*incrAddrWriteBytes)(struct SdioDevice *dev, uint8_t *data, uint32_t addr, uint32_t size); @@ -58,19 +66,19 @@ struct SdioDeviceOps { }; ``` - **表1** SdioDeviceOps结构体成员的钩子函数功能说明 +**表 1** SdioDeviceOps结构体成员的钩子函数功能说明 | 函数 | 入参 | 出参 | 返回值 | 功能 | | -------- | -------- | -------- | -------- | -------- | -| incrAddrReadBytes | dev:结构体指针,SDIO设备控制器
addr:uint32_t,地址值
size:uint32_t,大小 | data:uint8_t指针,传出值 | HDF_STATUS相关状态 | 从指定的SDIO地址增量读取给定长度的数据 | -| incrAddrWriteBytes | dev:结构体指针,SDIO设备控制器
data:uint8_t指针,传入值
addr:uint32_t,地址值
size:uint32_t,大小 | 无 | HDF_STATUS相关状态 | 将给定长度的数据增量写入指定的SDIO地址 | -| fixedAddrReadBytes | dev:结构体指针,SDIO设备控制器
addr:uint32_t,地址值
size:uint32_t,大小
scatterLen:uint32_t,数据长度 | data:uint8_t指针,传出值 | HDF_STATUS相关状态 | 从固定SDIO地址读取给定长度的数据。 | -| fixedAddrWriteBytes | dev:结构体指针,SDIO设备控制器
data:uint8_t指针,传入值
addr:uint32_t,地址值
size:uint32_t,大小
scatterLen:uint32_t,数据长度 | 无 | HDF_STATUS相关状态 | 将给定长度的数据写入固定SDIO地址 | -| func0ReadBytes | dev:结构体指针,SDIO设备控制器
addr:uint32_t,地址值
size:uint32_t,大小 | data:uint8_t指针,传出值 | HDF_STATUS相关状态 | 从SDIO函数0的地址空间读取给定长度的数据。 | -| func0WriteBytes | dev:结构体指针,SDIO设备控制器
data:uint8_t指针,传入值
addr:uint32_t,地址值
size:uint32_t,大小 | 无 | HDF_STATUS相关状态 | 将给定长度的数据写入SDIO函数0的地址空间。 | -| setBlockSize | dev:结构体指针,SDIO设备控制器
blockSize:uint32_t,Block大小 | 无 | HDF_STATUS相关状态 | 设置block大小 | -| getCommonInfo | dev:联合体指针,SDIO设备控制器
infoType:uint32_t,info类型 | info:结构体指针,传出SdioFuncInfo信息 | HDF_STATUS相关状态 | 获取CommonInfo,说明见下 | -| setCommonInfo | dev:结构体指针,SDIO设备控制器
info:联合体指针,SdioFuncInfo信息传入
infoType:uint32_t,info类型 | 无 | HDF_STATUS相关状态 | 设置CommonInfo,说明见下 | +| incrAddrReadBytes | dev:结构体指针,SDIO设备控制器
addr:uint32_t类型,地址值
size:uint32_t类型,大小 | data:uint8_t类型指针,传出值 | HDF_STATUS相关状态 | 从指定的SDIO地址增量读取给定长度的数据 | +| incrAddrWriteBytes | dev:结构体指针,SDIO设备控制器
data:uint8_t类型指针,传入值
addr:uint32_t类型,地址值
size:uint32_t类型,大小 | 无 | HDF_STATUS相关状态 | 将给定长度的数据增量写入指定的SDIO地址 | +| fixedAddrReadBytes | dev:结构体指针,SDIO设备控制器
addr:uint32_t类型,地址值
size:uint32_t类型,大小
scatterLen:uint32_t类型,数据长度 | data:uint8_t类型指针,传出值 | HDF_STATUS相关状态 | 从固定SDIO地址读取给定长度的数据。 | +| fixedAddrWriteBytes | dev:结构体指针,SDIO设备控制器
data:uint8_t类型指针,传入值
addr:uint32_t类型,地址值
size:uint32_t类型,大小
scatterLen:uint32_t类型,数据长度 | 无 | HDF_STATUS相关状态 | 将给定长度的数据写入固定SDIO地址 | +| func0ReadBytes | dev:结构体指针,SDIO设备控制器
addr:uint32_t类型,地址值
size:uint32_t类型,大小 | data:uint8_t类型指针,传出值 | HDF_STATUS相关状态 | 从SDIO函数0的地址空间读取给定长度的数据。 | +| func0WriteBytes | dev:结构体指针,SDIO设备控制器
data:uint8_t类型指针,传入值
addr:uint32_t类型,地址值
size:uint32_t类型,大小 | 无 | HDF_STATUS相关状态 | 将给定长度的数据写入SDIO函数0的地址空间。 | +| setBlockSize | dev:结构体指针,SDIO设备控制器
blockSize:uint32_t类型,Block大小 | 无 | HDF_STATUS相关状态 | 设置block大小 | +| getCommonInfo | dev:联合体指针,SDIO设备控制器
infoType:uint32_t类型,info类型 | info:结构体指针,传出SdioFuncInfo信息 | HDF_STATUS相关状态 | 获取CommonInfo,说明见下 | +| setCommonInfo | dev:结构体指针,SDIO设备控制器
info:联合体指针,SdioFuncInfo信息传入
infoType:uint32_t类型,info类型 | 无 | HDF_STATUS相关状态 | 设置CommonInfo,说明见下 | | flushData | dev:结构体指针,SDIO设备控制器 | 无 | HDF_STATUS相关状态 | 当SDIO需要重新初始化或发生意外错误时调用的函数 | | enableFunc | dev:结构体指针,SDIO设备控制器 | 无 | HDF_STATUS相关状态 | 使能SDIO设备 | | disableFunc | dev:结构体指针,SDIO设备控制器 | 无 | HDF_STATUS相关状态 | 去使能SDIO设备 | @@ -87,267 +95,287 @@ struct SdioDeviceOps { ### 开发步骤 -SDIO模块适配HDF框架的三个必选环节是实例化驱动入口,配置属性文件,以及实例化SDIO控制器对象。 +SDIO模块适配包含以下四个步骤: 1. 实例化驱动入口 - - 实例化HdfDriverEntry结构体成员。 - - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 + + - 实例化HdfDriverEntry结构体成员。 + + - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 2. 配置属性文件 - - 在device_info.hcs文件中添加deviceNode描述。 - - 【可选】添加sdio_config.hcs器件属性文件。 + + - 在device_info.hcs文件中添加deviceNode描述。 + + - 【可选】添加sdio_config.hcs器件属性文件。 3. 实例化SDIO控制器对象 - - 初始化SdioDevice成员。 - - 实例化SdioDevice成员SdioDeviceOps。 - > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 实例化SdioDevice成员SdioDeviceOps,其定义和成员说明见[接口说明](#接口说明)。 + + - 初始化SdioDevice成员。 + + - 实例化SdioDevice成员SdioDeviceOps。 + + > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 实例化SdioDevice成员SdioDeviceOps,其定义和成员说明见[接口说明](#接口说明)。 4. 驱动调试 - 【可选】针对新增驱动程序,建议验证驱动基本功能,例如SDIO控制状态,中断响应情况等。 + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如SDIO控制状态,中断响应情况,读写数据是否成功等。 + ### 开发实例 -下方将以sdio_adapter.c为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。 - -1. 驱动开发首先需要实例化驱动入口。 - - 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。 - - HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 - - 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 - - SDIO 驱动入口参考: - - ```c - struct HdfDriverEntry g_sdioDriverEntry = { - .moduleVersion = 1, - .Bind = Hi35xxLinuxSdioBind, // 见Bind开发参考 - .Init = Hi35xxLinuxSdioInit, // 见Init开发参考 - .Release = Hi35xxLinuxSdioRelease, // 见Release开发参考 - .moduleName = "HDF_PLATFORM_SDIO", // 【必要且与HCS文件中里面的moduleName匹配】 - }; - /* 调用HDF_INIT将驱动入口注册到HDF框架中 */ - HDF_INIT(g_sdioDriverEntry); - ``` - -2. 完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在sdio_config.hcs中配置器件属性。 - - deviceNode信息与驱动入口注册相关,器件属性值与核心层SdioDevice成员的默认值或限制范围有密切关系。 - - 本例只有一个SDIO控制器,如有多个器件信息,则需要在device_info.hcs文件增加deviceNode信息,以及在sdio_config文件中增加对应的器件属性。 - - - device_info.hcs 配置参考: - - ```c - root { - device_info { - match_attr = "hdf_manager"; - platform :: host { - hostName = "platform_host"; - priority = 50; - device_sdio :: device { - device0 :: deviceNode { - policy = 1; - priority = 70; - permission = 0644; - moduleName = "HDF_PLATFORM_SDIO"; // 【必要】用于指定驱动名称,需要与驱动Entry中的moduleName一致。 - serviceName = "HDF_PLATFORM_MMC_2"; // 【必要】驱动对外发布服务的名称,必须唯一。 - deviceMatchAttr = "hisilicon_hi35xx_sdio_0"; // 【必要】用于配置控制器私有数据,要与sdio_config.hcs中对应控制器保持一致。 - } - } - } - } - } - ``` - - - sdio_config.hcs 配置参考: - - - ```c - root { - platform { - sdio_config { - template sdio_controller { - match_attr = ""; - hostId = 2; // 【必要】模式固定为2,在mmc_config.hcs有介绍。 - devType = 2; // 【必要】模式固定为2,在mmc_config.hcs有介绍。 - } - controller_0x2dd1 :: sdio_controller { - match_attr = "hisilicon_hi35xx_sdio_0"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致。 - } - } - } - ``` - - 需要注意的是,新增sdio_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。 - - 例如:本例中sdio_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/sdio/sdio_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句: - - ```c - #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/sdio/sdio_config.hcs" // 配置文件相对路径 - ``` - -3. 完成属性文件配置之后,下一步就是以核心层SdioDevice对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化SdioDevice成员SdioDeviceOps(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。 - - - 自定义结构体参考: - - 从驱动的角度看,自定义结构体是参数和数据的载体,而且sdio_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象。 - - ```c - typedef struct { - uint32_t maxBlockNum; // 单个request最大的block个数 - uint32_t maxBlockSize; // 单个block最大的字节数1~2048 - uint32_t maxRequestSize; // 单个request最大的字节数1~2048 - uint32_t enTimeout; // 最大超时时间,单位毫秒,且不能超过一秒。 - uint32_t funcNum; // 函数编号1~7 - uint32_t irqCap; // 中断能力 - void *data; // 私有数据 - } SdioFuncInfo; - - /* SdioDevice是核心层控制器结构体,其中的成员在Bind函数中会被赋值。 */ - struct SdioDevice { - struct SdDevice sd; - struct SdioDeviceOps *sdioOps; - struct SdioRegister sdioReg; - uint32_t functions; - struct SdioFunction *sdioFunc[SDIO_MAX_FUNCTION_NUMBER]; - struct SdioFunction *curFunction; - struct OsalThread thread; // 中断线程 - struct OsalSem sem; - bool irqPending; - bool threadRunning; - }; - ``` - - - SdioDevice成员钩子函数结构体SdioDeviceOps的实例化,其他成员在Init函数中初始化。 - - ```c - static struct SdioDeviceOps g_sdioDeviceOps = { - .incrAddrReadBytes = Hi35xxLinuxSdioIncrAddrReadBytes, - .incrAddrWriteBytes = Hi35xxLinuxSdioIncrAddrWriteBytes, - .fixedAddrReadBytes = Hi35xxLinuxSdioFixedAddrReadBytes, - .fixedAddrWriteBytes = Hi35xxLinuxSdioFixedAddrWriteBytes, - .func0ReadBytes = Hi35xxLinuxSdioFunc0ReadBytes, - .func0WriteBytes = Hi35xxLinuxSdioFunc0WriteBytes, - .setBlockSize = Hi35xxLinuxSdioSetBlockSize, - .getCommonInfo = Hi35xxLinuxSdioGetCommonInfo, - .setCommonInfo = Hi35xxLinuxSdioSetCommonInfo, - .flushData = Hi35xxLinuxSdioFlushData, - .enableFunc = Hi35xxLinuxSdioEnableFunc, - .disableFunc = Hi35xxLinuxSdioDisableFunc, - .claimIrq = Hi35xxLinuxSdioClaimIrq, - .releaseIrq = Hi35xxLinuxSdioReleaseIrq, - .findFunc = Hi35xxLinuxSdioFindFunc, - .claimHost = Hi35xxLinuxSdioClaimHost, - .releaseHost = Hi35xxLinuxSdioReleaseHost, - }; - ``` - - - Bind函数开发参考 - - 入参: - - HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 - - 返回值: - - HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS 定义)。 - - **表2** Bind函数入参及返回值 - - | 状态(值) | 问题描述 | - | -------- | -------- | - | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | - | HDF_ERR_MALLOC_FAIL | 内存分配失败 | - | HDF_ERR_INVALID_PARAM | 参数非法 | - | HDF_ERR_IO | I/O 错误 | - | HDF_SUCCESS | 初始化成功 | - | HDF_FAILURE | 初始化失败 | - - 函数说明: - - 初始化自定义结构体对象,初始化SdioCntlr成员,调用核心层SdioCntlrAdd函数,以及其他驱动适配者自定义初始化操作。 +下方将以//drivers/hdf_core/adapter/khdf/linux/model/storage/sdio_adapter.c为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。 - - ```c - static int32_t Hi35xxLinuxSdioBind(struct HdfDeviceObject *obj) - { - struct MmcCntlr *cntlr = NULL; - int32_t ret; - ... - cntlr = (struct MmcCntlr *)OsalMemCalloc(sizeof(struct MmcCntlr));// 分配内存 - ... - cntlr->ops = &g_sdioCntlrOps; //【必要】struct MmcCntlrOps g_sdioCntlrOps={ - // .rescanSdioDev = Hi35xxLinuxSdioRescan,}; - cntlr->hdfDevObj = obj; //【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提 - obj->service = &cntlr->service; //【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提 - ret = Hi35xxLinuxSdioCntlrParse(cntlr, obj); //【必要】初始化cntlr的index、devType,失败则goto _ERR。 - ... - ret = MmcCntlrAdd(cntlr); //【必要】调用核心层mmc_core.c的函数,失败则goto _ERR。 - ... - ret = MmcCntlrAllocDev(cntlr, (enum MmcDevType)cntlr->devType); //【必要】调用核心层mmc_core.c的函数,失败则goto _ERR。 - ... - - MmcDeviceAddOps(cntlr->curDev, &g_sdioDeviceOps); //【必要】调用核心层mmc_core.c的函数,钩子函数挂载。 - HDF_LOGD("Hi35xxLinuxSdioBind: Success!"); - return HDF_SUCCESS; - - _ERR: - Hi35xxLinuxSdioDeleteCntlr(cntlr); - HDF_LOGE("Hi35xxLinuxSdioBind: Fail!"); - return HDF_FAILURE; - } - ``` - - - Init函数开发参考 - - 入参: - - HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 - - 返回值: - - HDF_STATUS相关状态。 - - 函数说明: - - 无操作,可根据驱动适配者需要添加。 +1. 实例化驱动入口 + + 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 + + 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 + + SDIO 驱动入口参考: + + ```c + struct HdfDriverEntry g_sdioDriverEntry = { + .moduleVersion = 1, + .Bind = Hi35xxLinuxSdioBind, // 见Bind开发参考 + .Init = Hi35xxLinuxSdioInit, // 见Init开发参考 + .Release = Hi35xxLinuxSdioRelease, // 见Release开发参考 + .moduleName = "HDF_PLATFORM_SDIO", // 【必要且与HCS文件中里面的moduleName匹配】 + }; + HDF_INIT(g_sdioDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 + ``` + +2. 配置属性文件 + + 完成驱动入口注册之后,下一步请在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在sdio_config.hcs中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值与核心层SdioDevice成员的默认值或限制范围有密切关系。本例只有一个SDIO控制器,如有多个器件信息,则需要在device_info.hcs文件增加deviceNode信息,以及在sdio_config文件中增加对应的器件属性。 + + 独立服务模式的特点是device_info.hcs文件中设备节点代表着一个设备对象,如果存在多个设备对象,则按需添加,注意服务名与驱动私有数据匹配的关键字名称必须唯一。其中各项参数如表2所示: + + **表 2** device_info.hcs节点参数说明 + + | 成员名 | 值 | + | -------- | -------- | + | policy | 驱动服务发布的策略,SDIO设备控制器具体配置为1,表示驱动对内核态发布服务 | + | priority | 驱动启动优先级(0-200),值越大优先级越低。SDIO设备控制器具体配置为30 | + | permission | 驱动创建设备节点权限,SDIO设备控制器具体配置为0664 | + | moduleName | 驱动名称,SDIO设备控制器固定为hi3516_mmc_driver | + | serviceName | 驱动对外发布服务的名称,SDIO设备控制器服务名设置为HDF_PLATFORM_MMC_2| + | deviceMatchAttr | 驱动私有数据匹配的关键字,SDIO设备控制器设置为hi3516_mmc_sdio| + + - device_info.hcs 配置参考: + + ```c + root { + device_info { + match_attr = "hdf_manager"; + platform :: host { + hostName = "platform_host"; + priority = 50; + device_sdio :: device { + device0 :: deviceNode { + policy = 1; + priority = 70; + permission = 0644; + moduleName = "HDF_PLATFORM_SDIO"; // 【必要】用于指定驱动名称,需要与驱动Entry中的moduleName一致。 + serviceName = "HDF_PLATFORM_MMC_2"; // 【必要】驱动对外发布服务的名称,必须唯一。 + deviceMatchAttr = "hisilicon_hi35xx_sdio_0"; // 【必要】用于配置控制器私有数据,要与sdio_config.hcs中对应控制器保持一致。 + } + } + } + } + } + ``` + + - sdio_config.hcs 配置参考: + + ```c + root { + platform { + sdio_config { + template sdio_controller { + match_attr = ""; + hostId = 2; // 【必要】模式固定为2,在mmc_config.hcs有介绍。 + devType = 2; // 【必要】模式固定为2,在mmc_config.hcs有介绍。 + } + controller_0x2dd1 :: sdio_controller { + match_attr = "hisilicon_hi35xx_sdio_0"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致。 + } + } + } + ``` + + 需要注意的是,新增sdio_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。 + + 例如:本例中sdio_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/sdio/sdio_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句: + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/sdio/sdio_config.hcs" // 配置文件相对路径 + ``` + +3. 实例化SDIO设备控制器对象 + + 完成属性文件配置之后,下一步就是以核心层SdioDevice对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化SdioDevice成员SdioDeviceOps(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。 + + - 自定义结构体参考: + + 从驱动的角度看,自定义结构体是参数和数据的载体,而且sdio_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象。 + + ```c + typedef struct { + uint32_t maxBlockNum; // 单个request最大的block个数 + uint32_t maxBlockSize; // 单个block最大的字节数1~2048 + uint32_t maxRequestSize; // 单个request最大的字节数1~2048 + uint32_t enTimeout; // 最大超时时间,单位毫秒,且不能超过一秒。 + uint32_t funcNum; // 函数编号1~7 + uint32_t irqCap; // 中断能力 + void *data; // 私有数据 + } SdioFuncInfo; + + // SdioDevice是核心层控制器结构体,其中的成员在Bind函数中会被赋值。 + struct SdioDevice { + struct SdDevice sd; + struct SdioDeviceOps *sdioOps; + struct SdioRegister sdioReg; + uint32_t functions; + struct SdioFunction *sdioFunc[SDIO_MAX_FUNCTION_NUMBER]; + struct SdioFunction *curFunction; + struct OsalThread thread; // 中断线程 + struct OsalSem sem; + bool irqPending; + bool threadRunning; + }; + ``` + + - SdioDevice成员钩子函数结构体SdioDeviceOps的实例化。 + + ```c + static struct SdioDeviceOps g_sdioDeviceOps = { + .incrAddrReadBytes = Hi35xxLinuxSdioIncrAddrReadBytes, + .incrAddrWriteBytes = Hi35xxLinuxSdioIncrAddrWriteBytes, + .fixedAddrReadBytes = Hi35xxLinuxSdioFixedAddrReadBytes, + .fixedAddrWriteBytes = Hi35xxLinuxSdioFixedAddrWriteBytes, + .func0ReadBytes = Hi35xxLinuxSdioFunc0ReadBytes, + .func0WriteBytes = Hi35xxLinuxSdioFunc0WriteBytes, + .setBlockSize = Hi35xxLinuxSdioSetBlockSize, + .getCommonInfo = Hi35xxLinuxSdioGetCommonInfo, + .setCommonInfo = Hi35xxLinuxSdioSetCommonInfo, + .flushData = Hi35xxLinuxSdioFlushData, + .enableFunc = Hi35xxLinuxSdioEnableFunc, + .disableFunc = Hi35xxLinuxSdioDisableFunc, + .claimIrq = Hi35xxLinuxSdioClaimIrq, + .releaseIrq = Hi35xxLinuxSdioReleaseIrq, + .findFunc = Hi35xxLinuxSdioFindFunc, + .claimHost = Hi35xxLinuxSdioClaimHost, + .releaseHost = Hi35xxLinuxSdioReleaseHost, + }; + ``` + + - Bind函数开发参考 + + 入参: + + HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 + + 返回值: + + HDF_STATUS相关状态 (表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS的定义)。 + + **表 3** HDF_STATUS相关状态说明 + + | 状态(值) | 问题描述 | + | -------- | -------- | + | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | + | HDF_ERR_MALLOC_FAIL | 内存分配失败 | + | HDF_ERR_IO | I/O 错误 | + | HDF_SUCCESS | 初始化成功 | + | HDF_FAILURE | 初始化失败 | + + 函数说明: + + 初始化自定义结构体对象,初始化SdioCntlr成员,调用核心层SdioCntlrAdd函数,以及其他驱动适配者自定义初始化操作。 - ```c - static int32_t Hi35xxLinuxSdioInit(struct HdfDeviceObject *obj) - { - (void)obj; // 无操作,可根据驱动适配者的需要进行添加 - HDF_LOGD("Hi35xxLinuxSdioInit: Success!"); - return HDF_SUCCESS; - } - ``` - - Release函数开发参考 + ```c + static int32_t Hi35xxLinuxSdioBind(struct HdfDeviceObject *obj) + { + struct MmcCntlr *cntlr = NULL; + int32_t ret; + ...... + cntlr = (struct MmcCntlr *)OsalMemCalloc(sizeof(struct MmcCntlr));// 分配内存 + ...... + cntlr->ops = &g_sdioCntlrOps; // 【必要】struct MmcCntlrOps g_sdioCntlrOps={ + // .rescanSdioDev = Hi35xxLinuxSdioRescan,}; + cntlr->hdfDevObj = obj; // 【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提 + obj->service = &cntlr->service; // 【必要】使HdfDeviceObject与MmcCntlr可以相互转化的前提 + ret = Hi35xxLinuxSdioCntlrParse(cntlr, obj); // 【必要】初始化cntlr的index、devType,失败则goto _ERR。 + ...... + ret = MmcCntlrAdd(cntlr); // 【必要】调用核心层mmc_core.c的函数,失败则goto _ERR。 + ...... + ret = MmcCntlrAllocDev(cntlr, (enum MmcDevType)cntlr->devType); // 【必要】调用核心层mmc_core.c的函数,失败则goto _ERR。 + ...... + + MmcDeviceAddOps(cntlr->curDev, &g_sdioDeviceOps); // 【必要】调用核心层mmc_core.c的函数,钩子函数挂载。 + HDF_LOGD("Hi35xxLinuxSdioBind: Success!"); + return HDF_SUCCESS; + + _ERR: + Hi35xxLinuxSdioDeleteCntlr(cntlr); + HDF_LOGE("Hi35xxLinuxSdioBind: Fail!"); + return HDF_FAILURE; + } + ``` + + - Init函数开发参考 + + 入参: + + HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 + + 返回值: + + HDF_STATUS相关状态。 - 入参: + 函数说明: - HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 + 无操作,可根据驱动适配者需要添加。 - 返回值: + ```c + static int32_t Hi35xxLinuxSdioInit(struct HdfDeviceObject *obj) + { + (void)obj; // 无操作,可根据驱动适配者的需要进行添加 + HDF_LOGD("Hi35xxLinuxSdioInit: Success!"); + return HDF_SUCCESS; + } + ``` - 无。 + - Release函数开发参考 - 函数说明: + 入参: - 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 + HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 - > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 所有强制转换获取相应对象的操作前提是在Bind函数中具备对应赋值的操作。 + 返回值: + + 无。 + + 函数说明: + + 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 + + > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 所有强制转换获取相应对象的操作前提是在Bind函数中具备对应赋值的操作。 - ```c - static void Hi35xxLinuxSdioRelease(struct HdfDeviceObject *obj) - { - if (obj == NULL) { - return; - } - Hi35xxLinuxSdioDeleteCntlr((struct MmcCntlr *)obj->service); // 【必要】自定义的内存释放函数,这里有HdfDeviceObject到MmcCntlr的强制转换 - } - ``` \ No newline at end of file + ```c + static void Hi35xxLinuxSdioRelease(struct HdfDeviceObject *obj) + { + if (obj == NULL) { + return; + } + Hi35xxLinuxSdioDeleteCntlr((struct MmcCntlr *)obj->service); // 【必要】自定义的内存释放函数,这里有HdfDeviceObject到MmcCntlr的强制转换 + } + ``` + +4. 驱动调试 + + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如SDIO控制状态,中断响应情况,读写数据是否成功等。 diff --git a/zh-cn/device-dev/driver/driver-platform-spi-des.md b/zh-cn/device-dev/driver/driver-platform-spi-des.md index 0f5f1f7432a2b3e7d4ad4ab6963ee4bc1a845ee2..9c1c3946213b2334c4095e1a099b627c4ee7d667 100644 --- a/zh-cn/device-dev/driver/driver-platform-spi-des.md +++ b/zh-cn/device-dev/driver/driver-platform-spi-des.md @@ -7,37 +7,70 @@ SPI指串行外设接口(Serial Peripheral Interface),是一种高速的,全双工,同步的通信总线。SPI是由Motorola公司开发,用于在主设备和从设备之间进行通信。 SPI接口定义了操作SPI设备的通用方法集合,包括: - - SPI设备句柄获取和释放。 - - SPI读写:从SPI设备读取或写入指定长度数据。 - - SPI自定义传输:通过消息传输结构体执行任意读写组合过程。 - - SPI设备配置:获取和设置SPI设备属性。 + +- SPI设备句柄获取和释放。 + +- SPI读写:从SPI设备读取或写入指定长度数据。 + +- SPI自定义传输:通过消息传输结构体执行任意读写组合过程。 + +- SPI设备配置:获取和设置SPI设备属性。 ### 运作机制 在HDF框架中,SPI的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,若设备过多可能增加内存占用。 +独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为: + +- 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。 + +- device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。 + +**图 1** SPI独立服务模式结构图 + +![SPI独立服务模式结构图](figures/独立服务模式结构图.png) + +SPI模块各分层作用: + +- 接口层提供打开SPI设备、SPI写数据、SPI读数据、SPI传输、配置SPI设备属性、获取SPI设备属性、关闭SPI设备的接口。 + +- 核心层主要提供SPI控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。 + +- 适配层主要是将钩子函数的功能实例化,实现具体的功能。 + SPI以主从方式工作,通常有一个主设备和一个或者多个从设备。主设备和从设备之间一般用4根线相连,它们分别是: - - SCLK:时钟信号,由主设备产生; - - MOSI:主设备数据输出,从设备数据输入; - - MISO:主设备数据输入,从设备数据输出; - - CS:片选,从设备使能信号,由主设备控制。 -一个主设备和两个从设备的连接示意图如下所示,Device A和Device B共享主设备的SCLK、MISO和MOSI三根引脚,Device A的片选CS0连接主设备的CS0,Device B的片选CS1连接主设备的CS1。 +- SCLK:时钟信号,由主设备产生; + +- MOSI:主设备数据输出,从设备数据输入; + +- MISO:主设备数据输入,从设备数据输出; -**图1** SPI主从设备连接示意图 +- CS:片选,从设备使能信号,由主设备控制。 -![image](figures/SPI主从设备连接示意图.png "SPI主从设备连接示意图") +一个主设备和两个从设备的连接示意图如图2所示,Device A和Device B共享主设备的SCLK、MISO和MOSI三根引脚,Device A的片选CS0连接主设备的CS0,Device B的片选CS1连接主设备的CS1。 + +**图 2** SPI主从设备连接示意图 + +![SPI主从设备连接示意图](figures/SPI主从设备连接示意图.png) - SPI通信通常由主设备发起,通过以下步骤完成一次通信: - 1. 通过CS选中要通信的从设备,在任意时刻,一个主设备上最多只能有一个从设备被选中。 - 2. 通过SCLK给选中的从设备提供时钟信号。 - 3. 基于SCLK时钟信号,主设备数据通过MOSI发送给从设备,同时通过MISO接收从设备发送的数据,完成通信。 + + 1. 通过CS选中要通信的从设备,在任意时刻,一个主设备上最多只能有一个从设备被选中。 + + 2. 通过SCLK给选中的从设备提供时钟信号。 + + 3. 基于SCLK时钟信号,主设备数据通过MOSI发送给从设备,同时通过MISO接收从设备发送的数据,完成通信。 - 根据SCLK时钟信号的CPOL(Clock Polarity,时钟极性)和CPHA(Clock Phase,时钟相位)的不同组合,SPI有以下四种工作模式: - - CPOL=0,CPHA=0 时钟信号idle状态为低电平,第一个时钟边沿采样数据。 - - CPOL=0,CPHA=1 时钟信号idle状态为低电平,第二个时钟边沿采样数据。 - - CPOL=1,CPHA=0 时钟信号idle状态为高电平,第一个时钟边沿采样数据。 - - CPOL=1,CPHA=1 时钟信号idle状态为高电平,第二个时钟边沿采样数据。 + + - CPOL=0,CPHA=0 时钟信号idle状态为低电平,第一个时钟边沿采样数据。 + + - CPOL=0,CPHA=1 时钟信号idle状态为低电平,第二个时钟边沿采样数据。 + + - CPOL=1,CPHA=0 时钟信号idle状态为高电平,第一个时钟边沿采样数据。 + + - CPOL=1,CPHA=1 时钟信号idle状态为高电平,第二个时钟边沿采样数据。 ### 约束与限制 @@ -53,7 +86,7 @@ SPI通常用于与闪存、实时时钟、传感器以及模数/数模转换器 SPI模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/spi_if.h。 -**表1** SPI驱动API接口功能介绍 +**表 1** SPI驱动API接口功能介绍 | 接口名 | 接口描述 | | -------- | -------- | @@ -69,9 +102,9 @@ SPI模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/ 使用SPI的一般流程如下图所示。 - **图2** SPI使用流程图 +**图 3** SPI使用流程图 - ![image](figures/SPI使用流程图.png "SPI使用流程图") +![SPI使用流程图](figures/SPI使用流程图.png) #### 获取SPI设备句柄 @@ -81,28 +114,28 @@ SPI模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/ DevHandle SpiOpen(const struct SpiDevInfo *info); ``` - **表2** SpiOpen参数和返回值描述 +**表 2** SpiOpen参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| info | SPI设备描述符 | +| info | 结构体类型,SPI设备描述符 | | **返回值** | **返回值描述** | | NULL | 获取SPI设备句柄失败 | -| 设备句柄 | 对应的SPI设备句柄 | +| 设备句柄 | 获取对应的SPI设备句柄成功 | 假设系统中的SPI设备总线号为0,片选号为0,获取该SPI设备句柄的示例如下: ```c -struct SpiDevInfo spiDevinfo; /* SPI设备描述符 */ -DevHandle spiHandle = NULL; /* SPI设备句柄 */ -spiDevinfo.busNum = 0; /* SPI设备总线号 */ -spiDevinfo.csNum = 0; /* SPI设备片选号 */ +struct SpiDevInfo spiDevinfo; // SPI设备描述符 +DevHandle spiHandle = NULL; // SPI设备句柄 +spiDevinfo.busNum = 0; // SPI设备总线号 +spiDevinfo.csNum = 0; // SPI设备片选号 -/* 获取SPI设备句柄 */ +// 获取SPI设备句柄 spiHandle = SpiOpen(&spiDevinfo); if (spiHandle == NULL) { - HDF_LOGE("SpiOpen: failed\n"); - return; + HDF_LOGE("SpiOpen: spi open fail!\n"); + return HDF_FAILURE; } ``` @@ -114,22 +147,23 @@ if (spiHandle == NULL) { int32_t SpiGetCfg(DevHandle handle, struct SpiCfg *cfg); ``` - **表3** SpiGetCfg参数和返回值描述 +**表 3** SpiGetCfg参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| handle | SPI设备句柄 | -| cfg | SPI设备配置参数 | +| handle | DevHandle类型,SPI设备句柄 | +| cfg | 结构体指针类型,SPI设备配置参数 | | **返回值** | **返回值描述** | -| 0 | 获取配置成功 | -| 负数 | 获取配置失败 | +| HDF_SUCCESS | 获取设备属性成功 | +| 负数 | 获取设备属性失败 | ```c int32_t ret; -struct SpiCfg cfg = {0}; /* SPI配置信息*/ -ret = SpiGetCfg(spiHandle, &cfg); /* 获取SPI设备属性 */ -if (ret != 0) { +struct SpiCfg cfg = {0}; // SPI配置信息 +ret = SpiGetCfg(spiHandle, &cfg); // 获取SPI设备属性 +if (ret != HDF_SUCCESS) { HDF_LOGE("SpiGetCfg: failed, ret %d\n", ret); + return ret; } ``` @@ -141,26 +175,27 @@ if (ret != 0) { int32_t SpiSetCfg(DevHandle handle, struct SpiCfg *cfg); ``` - **表4** SpiSetCfg参数和返回值描述 +**表 4** SpiSetCfg参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| handle | SPI设备句柄 | -| cfg | SPI设备配置参数 | +| handle | DevHandle类型,SPI设备句柄 | +| cfg | 结构体指针类型,SPI设备配置参数 | | **返回值** | **返回值描述** | -| 0 | 配置成功 | -| 负数 | 配置失败 | +| HDF_SUCCESS | 配置设备属性成功 | +| 负数 | 配置设备属性失败 | ```c int32_t ret; -struct SpiCfg cfg = {0}; /* SPI配置信息*/ -cfg.mode = SPI_MODE_LOOP; /* 以回环模式进行通信 */ -cfg.transferMode = PAL_SPI_POLLING_TRANSFER; /* 以轮询的方式进行通信 */ -cfg.maxSpeedHz = 115200; /* 最大传输频率 */ -cfg.bitsPerWord = 8; /* 读写位宽为8比特 */ -ret = SpiSetCfg(spiHandle, &cfg); /* 配置SPI设备属性 */ -if (ret != 0) { +struct SpiCfg cfg = {0}; // SPI配置信息 +cfg.mode = SPI_MODE_LOOP; // 以回环模式进行通信 +cfg.transferMode = PAL_SPI_POLLING_TRANSFER; // 以轮询的方式进行通信 +cfg.maxSpeedHz = 115200; // 最大传输频率 +cfg.bitsPerWord = 8; // 读写位宽为8比特 +ret = SpiSetCfg(spiHandle, &cfg); // 配置SPI设备属性 +if (ret != HDF_SUCCESS) { HDF_LOGE("SpiSetCfg: failed, ret %d\n", ret); + return ret; } ``` @@ -168,98 +203,101 @@ if (ret != 0) { - 向SPI设备写入指定长度的数据 - 如果只向SPI设备写一次数据,则可以通过以下函数完成: - - ```c - int32_t SpiWrite(DevHandle handle, uint8_t *buf, uint32_t len); - ``` - - **表5** SpiWrite参数和返回值描述 - - | **参数** | **参数描述** | - | -------- | -------- | - | handle | SPI设备句柄 | - | buf | 待写入数据的指针 | - | len | 待写入的数据长度 | - | **返回值** | **返回值描述** | - | 0 | 写入成功 | - | 负数 | 写入失败 | - - ```c - int32_t ret; - uint8_t wbuff[4] = {0x12, 0x34, 0x56, 0x78}; - /* 向SPI设备写入指定长度的数据 */ - ret = SpiWrite(spiHandle, wbuff, 4); - if (ret != 0) { - HDF_LOGE("SpiWrite: failed, ret %d\n", ret); - } - ``` + 如果只向SPI设备写一次数据,则可以通过以下函数完成: + + ```c + int32_t SpiWrite(DevHandle handle, uint8_t *buf, uint32_t len); + ``` + + **表 5** SpiWrite参数和返回值描述 + + | **参数** | **参数描述** | + | -------- | -------- | + | handle | DevHandle类型,SPI设备句柄 | + | buf | uint8_t类型指针,待写入数据 | + | len | uint32_t类型,待写入的数据长度 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | 写入成功 | + | 负数 | 写入失败 | + + ```c + int32_t ret; + uint8_t wbuff[4] = {0x12, 0x34, 0x56, 0x78}; + // 向SPI设备写入指定长度的数据 + ret = SpiWrite(spiHandle, wbuff, 4); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SpiWrite: failed, ret %d\n", ret); + return ret; + } + ``` - 从SPI设备读取指定长度的数据 - 如果只读取一次数据,则可以通过以下函数完成: - - ```c - int32_t SpiRead(DevHandle handle, uint8_t *buf, uint32_t len); - ``` - - **表6** SpiRead参数和返回值描述 - - | **参数** | **参数描述** | - | -------- | -------- | - | handle | SPI设备句柄 | - | buf | 待读取数据的指针 | - | len | 待读取的数据长度 | - | **返回值** | **返回值描述** | - | 0 | 读取成功 | - | 负数 | 读取失败 | - - ```c - int32_t ret; - uint8_t rbuff[4] = {0}; - /* 从SPI设备读取指定长度的数据 */ - ret = SpiRead(spiHandle, rbuff, 4); - if (ret != 0) { - HDF_LOGE("SpiRead: failed, ret %d\n", ret); - } - ``` + 如果只读取一次数据,则可以通过以下函数完成: + + ```c + int32_t SpiRead(DevHandle handle, uint8_t *buf, uint32_t len); + ``` + + **表 6** SpiRead参数和返回值描述 + + | **参数** | **参数描述** | + | -------- | -------- | + | handle | DevHandle类型,SPI设备句柄 | + | buf | uint8_t类型指针,待读取数据 | + | len | uint32_t类型,待读取的数据长度 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | 读取成功 | + | 负数 | 读取失败 | + + ```c + int32_t ret; + uint8_t rbuff[4] = {0}; + // 从SPI设备读取指定长度的数据 + ret = SpiRead(spiHandle, rbuff, 4); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SpiRead: failed, ret %d\n", ret); + return ret; + } + ``` - 自定义传输 - 如果需要发起一次自定义传输,则可以通过以下函数完成: - - ```c - int32_t SpiTransfer(DevHandle handle, struct SpiMsg *msgs, uint32_t count); - ``` - - **表7** SpiTransfer参数和返回值描述 - - | **参数** | **参数描述** | - | -------- | -------- | - | handle | SPI设备句柄 | - | msgs | 待传输数据的数组 | - | count | msgs数组长度 | - | **返回值** | **返回值描述** | - | 0 | 执行成功 | - | 负数 | 执行失败 | - - ```c - int32_t ret; - uint8_t wbuff[1] = {0x12}; - uint8_t rbuff[1] = {0}; - struct SpiMsg msg; /* 自定义传输的消息 */ - msg.wbuf = wbuff; /* 写入的数据 */ - msg.rbuf = rbuff; /* 读取的数据 */ - msg.len = 1; /* 读取、写入数据的长度都是1 */ - msg.csChange = 1; /* 进行下一次传输前关闭片选 */ - msg.delayUs = 0; /* 进行下一次传输前不进行延时 */ - msg.speed = 115200; /* 本次传输的速度 */ - /* 进行一次自定义传输,传输的msg个数为1 */ - ret = SpiTransfer(spiHandle, &msg, 1); - if (ret != 0) { - HDF_LOGE("SpiTransfer: failed, ret %d\n", ret); - } - ``` + 如果需要发起一次自定义传输,则可以通过以下函数完成: + + ```c + int32_t SpiTransfer(DevHandle handle, struct SpiMsg *msgs, uint32_t count); + ``` + + **表 7** SpiTransfer参数和返回值描述 + + | **参数** | **参数描述** | + | -------- | -------- | + | handle | DevHandle类型,SPI设备句柄 | + | msgs | 结构体指针,待传输数据的数组 | + | count | uint32_t类型,msgs数组长度 | + | **返回值** | **返回值描述** | + | HDF_SUCCESS | 传输执行成功 | + | 负数 | 传输执行失败 | + + ```c + int32_t ret; + uint8_t wbuff[1] = {0x12}; + uint8_t rbuff[1] = {0}; + struct SpiMsg msg; // 自定义传输的消息 + msg.wbuf = wbuff; // 写入的数据 + msg.rbuf = rbuff; // 读取的数据 + msg.len = 1; // 读取、写入数据的长度都是1 + msg.csChange = 1; // 进行下一次传输前关闭片选 + msg.delayUs = 0; // 进行下一次传输前不进行延时 + msg.speed = 115200; // 本次传输的速度 + // 进行一次自定义传输,传输的msg个数为1 + ret = SpiTransfer(spiHandle, &msg, 1); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SpiTransfer: failed, ret %d\n", ret); + return ret; + } + ``` #### 销毁SPI设备句柄 @@ -271,14 +309,14 @@ void SpiClose(DevHandle handle); 该函数会释放掉申请的资源。 - **表8** SpiClose参数描述 +**表 8** SpiClose参数描述 | **参数** | **参数描述** | | -------- | -------- | -| handle | SPI设备句柄 | +| handle | DevHandle类型,SPI设备句柄 | ```c -SpiClose(spiHandle); /* 销毁SPI设备句柄 */ +SpiClose(spiHandle); // 销毁SPI设备句柄 ``` ### 使用实例 @@ -294,61 +332,62 @@ SPI设备完整的使用示例如下所示,首先获取SPI设备句柄,然 void SpiTestSample(void) { int32_t ret; - struct SpiCfg cfg; /* SPI配置信息 */ - struct SpiDevInfo spiDevinfo; /* SPI设备描述符 */ - DevHandle spiHandle = NULL; /* SPI设备句柄 */ - struct SpiMsg msg; /* 自定义传输的消息 */ + struct SpiCfg cfg; // SPI配置信息 + struct SpiDevInfo spiDevinfo; // SPI设备描述符 + DevHandle spiHandle = NULL; // SPI设备句柄 + struct SpiMsg msg; // 自定义传输的消息 uint8_t rbuff[4] = { 0 }; uint8_t wbuff[4] = { 0x12, 0x34, 0x56, 0x78 }; uint8_t wbuff2[4] = { 0xa1, 0xb2, 0xc3, 0xd4 }; - spiDevinfo.busNum = 0; /* SPI设备总线号 */ - spiDevinfo.csNum = 0; /* SPI设备片选号 */ - spiHandle = SpiOpen(&spiDevinfo); /* 根据spiDevinfo获取SPI设备句柄 */ + spiDevinfo.busNum = 0; // SPI设备总线号 + spiDevinfo.csNum = 0; // SPI设备片选号 + spiHandle = SpiOpen(&spiDevinfo); // 根据spiDevinfo获取SPI设备句柄 if (spiHandle == NULL) { - HDF_LOGE("SpiOpen: failed\n"); + HDF_LOGE("SpiTestSample: spi open fail!\n"); return; } - /* 获取SPI设备属性 */ + // 获取SPI设备属性 ret = SpiGetCfg(spiHandle, &cfg); - if (ret != 0) { - HDF_LOGE("SpiGetCfg: failed, ret %d\n", ret); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SpiTestSample: spi get cfg fail, ret:%d!\n", ret); goto err; } - cfg.maxSpeedHz = 115200; /* 将最大时钟频率改为115200 */ - cfg.bitsPerWord = 8; /* 传输位宽改为8比特 */ - /* 配置SPI设备属性 */ + cfg.maxSpeedHz = 115200; // 将最大时钟频率改为115200 + cfg.bitsPerWord = 8; // 传输位宽改为8比特 + // 配置SPI设备属性 ret = SpiSetCfg(spiHandle, &cfg); - if (ret != 0) { - HDF_LOGE("SpiSetCfg: failed, ret %d\n", ret); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SpiTestSample: spi set cfg fail, ret:%d!\n", ret); goto err; } /* 向SPI设备写入指定长度的数据 */ ret = SpiWrite(spiHandle, wbuff, 4); - if (ret != 0) { - HDF_LOGE("SpiWrite: failed, ret %d\n", ret); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SpiTestSample: spi write fail, ret:%d!\n", ret); goto err; } /* 从SPI设备读取指定长度的数据 */ ret = SpiRead(spiHandle, rbuff, 4); - if (ret != 0) { - HDF_LOGE("SpiRead: failed, ret %d\n", ret); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SpiTestSample: spi read fail, ret:%d!\n", ret); goto err; } - msg.wbuf = wbuff2; /* 写入的数据 */ - msg.rbuf = rbuff; /* 读取的数据 */ - msg.len = 4; /* 读取写入数据的长度为4 */ - msg.csChange = 1; /* 进行下一次传输前关闭片选 */ - msg.delayUs = 0; /* 进行下一次传输前不进行延时 */ - msg.speed = 115200; /* 本次传输的速度 */ - /* 进行一次自定义传输,传输的msg个数为1 */ + msg.wbuf = wbuff2; // 写入的数据 + msg.rbuf = rbuff; // 读取的数据 + msg.len = 4; // 读取写入数据的长度为4 + msg.keepCs = 0; // 当前传输完成后是否保持CS活动,1表述保持,0表示关闭CS + msg.delayUs = 0; // 进行下一次传输前不进行延时 + msg.speed = 115200; // 本次传输的速度 + // 进行一次自定义传输,传输的msg个数为1 ret = SpiTransfer(spiHandle, &msg, 1); - if (ret != 0) { - HDF_LOGE("SpiTransfer: failed, ret %d\n", ret); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SpiTestSample: spi transfer fail, ret:%d!\n", ret); goto err; } + HDF_LOGD("SpiTestSample: function tests end!"); err: - /* 销毁SPI设备句柄 */ + // 销毁SPI设备句柄 SpiClose(spiHandle); } ``` diff --git a/zh-cn/device-dev/driver/driver-platform-spi-develop.md b/zh-cn/device-dev/driver/driver-platform-spi-develop.md index 906e067e5db54c62fc2e59d7e5629d7c6b6cc1ee..d8f0496e9b199e9c8d9ed0b6f958623197a498e5 100755 --- a/zh-cn/device-dev/driver/driver-platform-spi-develop.md +++ b/zh-cn/device-dev/driver/driver-platform-spi-develop.md @@ -8,39 +8,59 @@ SPI即串行外设接口(Serial Peripheral Interface),是一种高速的 ### 运作机制 -在HDF框架中,SPI的接口适配模式采用独立服务模式(如图1),在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,若设备过多可能增加内存占用。 +在HDF框架中,SPI的接口适配模式采用独立服务模式(如图1所示),在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,若设备过多可能增加内存占用。 独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为: - 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。 + - device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。 -**图1** SPI独立服务模式结构图 +**图 1** SPI独立服务模式结构图 + +![SPI独立服务模式结构图](figures/独立服务模式结构图.png) + +SPI模块各分层作用: + +- 接口层提供打开SPI设备、SPI写数据、SPI读数据、SPI传输、配置SPI设备属性、获取SPI设备属性、关闭SPI设备的接口。 + +- 核心层主要提供SPI控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。 -![image](figures/独立服务模式结构图.png "RTC独立服务模式结构图") +- 适配层主要是将钩子函数的功能实例化,实现具体的功能。 SPI以主从方式工作,通常有一个主设备和一个或者多个从设备。主设备和从设备之间一般用4根线相连,它们分别是: - - SCLK:时钟信号,由主设备产生; - - MOSI:主设备数据输出,从设备数据输入; - - MISO:主设备数据输入,从设备数据输出; - - CS:片选,从设备使能信号,由主设备控制。 -一个主设备和两个从设备的连接示意图如下所示,Device A和Device B共享主设备的SCLK、MISO和MOSI三根引脚,Device A的片选CS0连接主设备的CS0,Device B的片选CS1连接主设备的CS1。 +- SCLK:时钟信号,由主设备产生; - **图2** SPI主从设备连接示意图 +- MOSI:主设备数据输出,从设备数据输入; + +- MISO:主设备数据输入,从设备数据输出; + +- CS:片选,从设备使能信号,由主设备控制。 - ![image](figures/SPI主从设备连接示意图.png "SPI主从设备连接示意图") +一个主设备和两个从设备的连接示意图如图2所示,Device A和Device B共享主设备的SCLK、MISO和MOSI三根引脚,Device A的片选CS0连接主设备的CS0,Device B的片选CS1连接主设备的CS1。 + +**图 2** SPI主从设备连接示意图 + +![SPI主从设备连接示意图](figures/SPI主从设备连接示意图.png) - SPI通信通常由主设备发起,通过以下步骤完成一次通信: - 1. 通过CS选中要通信的从设备,在任意时刻,一个主设备上最多只能有一个从设备被选中。 - 2. 通过SCLK给选中的从设备提供时钟信号。 - 3. 基于SCLK时钟信号,主设备数据通过MOSI发送给从设备,同时通过MISO接收从设备发送的数据,完成通信。 + + 1. 通过CS选中要通信的从设备,在任意时刻,一个主设备上最多只能有一个从设备被选中。 + + 2. 通过SCLK给选中的从设备提供时钟信号。 + + 3. 基于SCLK时钟信号,主设备数据通过MOSI发送给从设备,同时通过MISO接收从设备发送的数据,完成通信。 - 根据SCLK时钟信号的CPOL(Clock Polarity,时钟极性)和CPHA(Clock Phase,时钟相位)的不同组合,SPI有以下四种工作模式: - - CPOL=0,CPHA=0 时钟信号idle状态为低电平,第一个时钟边沿采样数据。 - - CPOL=0,CPHA=1 时钟信号idle状态为低电平,第二个时钟边沿采样数据。 - - CPOL=1,CPHA=0 时钟信号idle状态为高电平,第一个时钟边沿采样数据。 - - CPOL=1,CPHA=1 时钟信号idle状态为高电平,第二个时钟边沿采样数据。 + + - CPOL=0,CPHA=0 时钟信号idle状态为低电平,第一个时钟边沿采样数据。 + + - CPOL=0,CPHA=1 时钟信号idle状态为低电平,第二个时钟边沿采样数据。 + + - CPOL=1,CPHA=0 时钟信号idle状态为高电平,第一个时钟边沿采样数据。 + + - CPOL=1,CPHA=1 时钟信号idle状态为高电平,第二个时钟边沿采样数据。 ## 开发指导 @@ -64,11 +84,11 @@ struct SpiCntlrMethod { }; ``` - **表1** SpiCntlrMethod结构体成员的钩子函数功能说明 +**表 1** SpiCntlrMethod结构体成员的钩子函数功能说明 | 成员函数 | 入参 | 返回值 | 功能 | | -------- | -------- | -------- | -------- | -| Transfer | cntlr:结构体指针,核心层SPI控制器。
msg:结构体指针,Spi消息。
count:uint32_t,消息个数。 | HDF_STATUS相关状态 | 传输消息 | +| Transfer | cntlr:结构体指针,核心层SPI控制器。
msg:结构体指针,Spi消息。
count:uint32_t类型,消息个数。 | HDF_STATUS相关状态 | 传输消息 | | SetCfg | cntlr:结构体指针,核心层SPI控制器。
cfg:结构体指针,Spi属性。 | HDF_STATUS相关状态 | 设置控制器属性 | | GetCfg | cntlr:结构体指针,核心层SPI控制器。
cfg:结构体指针,Spi属性。 | HDF_STATUS相关状态 | 获取控制器属性 | | Open | cntlr:结构体指针,核心层SPI控制器。 | HDF_STATUS相关状态 | 打开SPI | @@ -77,21 +97,28 @@ struct SpiCntlrMethod { ### 开发步骤 -SPI模块适配HDF框架的三个必选环节是实例化驱动入口,配置属性文件,以及实例化核心层接口函数。 +SPI模块适配包含以下四个步骤: 1. 实例化驱动入口 - - 实例化HdfDriverEntry结构体成员。 - - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 + + - 实例化HdfDriverEntry结构体成员。 + + - 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。 2. 配置属性文件 - - 在device_info.hcs文件中添加deviceNode描述。 - - 【可选】添加spi_config.hcs器件属性文件。 + + - 在device_info.hcs文件中添加deviceNode描述。 + + - 【可选】添加spi_config.hcs器件属性文件。 3. 实例化SPI控制器对象 - - 初始化SpiCntlr成员。 - - 实例化SpiCntlr成员SpiCntlrMethod。 - > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 实例化SpiCntlr成员SpiCntlrMethod,其定义和成员说明见[接口说明](#接口说明)。 + + - 初始化SpiCntlr成员。 + + - 实例化SpiCntlr成员SpiCntlrMethod。 + + > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 实例化SpiCntlr成员SpiCntlrMethod,其定义和成员说明见[接口说明](#接口说明)。 4. 驱动调试 @@ -101,322 +128,334 @@ SPI模块适配HDF框架的三个必选环节是实例化驱动入口,配置 下方将以//device/soc/hisilicon/common/platform/spi/spi_hi35xx.c为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。 -1. 首先需要实例化驱动入口。 - - 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。 - - HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 - - 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 - - SPI驱动入口参考: - - ```c - struct HdfDriverEntry g_hdfSpiDevice = { - .moduleVersion = 1, - .moduleName = "HDF_PLATFORM_SPI", //【必要且与HCS文件中里面的moduleName匹配】 - .Bind = HdfSpiDeviceBind, //见Bind开发参考 - .Init = HdfSpiDeviceInit, //见Init开发参考 - .Release = HdfSpiDeviceRelease, //见Release开发参考 - }; - /* 调用HDF_INIT将驱动入口注册到HDF框架中 */ - HDF_INIT(g_hdfSpiDevice); - ``` - -2. 完成驱动入口注册之后,在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode信息,并在spi_config.hcs中配置器件属性。 - - deviceNode信息与驱动入口注册相关,器件属性值与核心层SpiCntlr成员的默认值或限制范围有密切关系。 - - 本例只有一个SPI控制器,如有多个器件信息,则需要在device_info.hcs文件增加deviceNode信息,以及在spi_config文件中增加对应的器件属性。 - - - device_info.hcs配置参考 - - ```c - root { - device_info { - match_attr = "hdf_manager"; - platform :: host { - hostName = "platform_host"; - priority = 50; - device_spi :: device { //为每一个SPI控制器配置一个HDF设备节点 - device0 :: deviceNode { - policy = 2; - priority = 60; - permission = 0644; - moduleName = "HDF_PLATFORM_SPI"; - serviceName = "HDF_PLATFORM_SPI_0"; - deviceMatchAttr = "hisilicon_hi35xx_spi_0"; - } - device1 :: deviceNode { - policy = 2; - priority = 60; - permission = 0644; - moduleName = "HDF_PLATFORM_SPI"; // 【必要】用于指定驱动名称,该字段的值必须和驱动入口结构的moduleName值一致。 - serviceName = "HDF_PLATFORM_SPI_1"; // 【必要且唯一】驱动对外发布服务的名称。 - deviceMatchAttr = "hisilicon_hi35xx_spi_1"; // 需要与spi_config.hcs配置文件中的match_attr匹配。 - } - ... - } - } - } - } - ``` - - - spi_config.hcs配置参考 - - - ```c - root { - platform { - spi_config { // 每一个SPI控制器配置私有数据 - template spi_controller { // 模板公共参数,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省。 - serviceName = ""; - match_attr = ""; - transferMode = 0; // 数据传输模式:中断传输(0)、流控传输(1)、DMA传输(2) - busNum = 0; // 总线号 - clkRate = 100000000; - bitsPerWord = 8; // 传输位宽 - mode = 19; // SPI 数据的输入输出模式 - maxSpeedHz = 0; // 最大时钟频率 - minSpeedHz = 0; // 最小时钟频率 - speed = 2000000; // 当前消息传输速度 - fifoSize = 256; // FIFO大小 - numCs = 1; // 片选号 - regBase = 0x120c0000; // 地址映射需要 - irqNum = 100; // 中断号 - REG_CRG_SPI = 0x120100e4; // CRG_REG_BASE(0x12010000) + 0x0e4 - CRG_SPI_CKEN = 0; - CRG_SPI_RST = 0; - REG_MISC_CTRL_SPI = 0x12030024; // MISC_REG_BASE(0x12030000) + 0x24 - MISC_CTRL_SPI_CS = 0; - MISC_CTRL_SPI_CS_SHIFT = 0; - } - controller_0x120c0000 :: spi_controller { - busNum = 0; // 【必要】总线号 - CRG_SPI_CKEN = 0x10000; // (0x1 << 16) 0:close clk, 1:open clk - CRG_SPI_RST = 0x1; // (0x1 << 0) 0:cancel reset, 1:reset - match_attr = "hisilicon_hi35xx_spi_0"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致 - } - controller_0x120c1000 :: spi_controller { - busNum = 1; - CRG_SPI_CKEN = 0x20000; // (0x1 << 17) 0:close clk, 1:open clk - CRG_SPI_RST = 0x2; // (0x1 << 1) 0:cancel reset, 1:reset - match_attr = "hisilicon_hi35xx_spi_1"; - regBase = 0x120c1000; // 【必要】地址映射需要 - irqNum = 101; // 【必要】中断号 - } - ... - /* 【可选】可新增,但需要在device_info.hcs添加对应的节点。 */ - } - } - } - ``` - - 需要注意的是,新增spi_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。 - - 例如:本例中spi_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/spi/spi_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句: - - ```c - #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/spi/spi_config.hcs" // 配置文件相对路径 - ``` - -3. 完成属性文件配置之后,下一步就是以核心层SpiCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化SpiCntlr成员SpiCntlrMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。 - - - 自定义结构体参考 - - 从驱动的角度看,自定义结构体是参数和数据的载体,而且spi_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象,例如设备号、总线号等。 - - - ```c - struct Pl022 { //对应于spi_config.hcs中的参数 - struct SpiCntlr *cntlr; - struct DListHead deviceList; - struct OsalSem sem; - volatile unsigned char *phyBase; - volatile unsigned char *regBase; - uint32_t irqNum; - uint32_t busNum; - uint32_t numCs; - uint32_t curCs; - uint32_t speed; - uint32_t fifoSize; - uint32_t clkRate; - uint32_t maxSpeedHz; - uint32_t minSpeedHz; - uint32_t regCrg; - uint32_t clkEnBit; - uint32_t clkRstBit; - uint32_t regMiscCtrl; - uint32_t miscCtrlCsShift; - uint32_t miscCtrlCs; - uint16_t mode; - uint8_t bitsPerWord; - uint8_t transferMode; - }; - - /* SpiCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。 */ - struct SpiCntlr { - struct IDeviceIoService service; - struct HdfDeviceObject *device; - uint32_t busNum; - uint32_t numCs; - uint32_t curCs; - struct OsalMutex lock; - struct SpiCntlrMethod *method; - struct DListHead list; - void *priv; - }; - ``` - - - SpiCntlr成员钩子函数结构体SpiCntlrMethod的实例化,其他成员在Init函数中初始化。 - - - ```c - /* spi_hi35xx.c中的示例:钩子函数的实例化 */ - struct SpiCntlrMethod g_method = { - .Transfer = Pl022Transfer, - .SetCfg = Pl022SetCfg, - .GetCfg = Pl022GetCfg, - .Open = Pl022Open, - .Close = Pl022Close, - }; - ``` - - - Bind函数参考 +1. 实例化驱动入口 - 入参: + 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 - HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 + 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 - 返回值: + SPI驱动入口参考: - HDF_STATUS相关状态。 + ```c + struct HdfDriverEntry g_hdfSpiDevice = { + .moduleVersion = 1, + .moduleName = "HDF_PLATFORM_SPI", //【必要且与HCS文件中里面的moduleName匹配】 + .Bind = HdfSpiDeviceBind, // 挂接SPI模块Bind实例化 + .Init = HdfSpiDeviceInit, // 挂接SPI模块Init实例化 + .Release = HdfSpiDeviceRelease, // 挂接SPI模块Release实例化 + }; + HDF_INIT(g_hdfSpiDevice); // 调用HDF_INIT将驱动入口注册到HDF框架中 + ``` - 函数说明: +2. 配置属性文件 - 将SpiCntlr对象同HdfDeviceObject进行了关联。 + 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode描述。deviceNode信息与驱动入口注册相关。 本例只有一个SPI控制器,如有多个器件信息,则需要在device_info.hcs文件增加deviceNode信息,以及在spi_config.hcs文件中增加对应的器件属性。器件属性值与核心层WatchdogCntlr成员的默认值或限制范围有密切关系,比如busNum设备号,需要在watchdog_config.hcs文件中增加对应的器件属性。 + + 独立服务模式的特点是device_info.hcs文件中设备节点代表着一个设备对象,如果存在多个设备对象,则按需添加,注意服务名与驱动私有数据匹配的关键字名称必须唯一。其中各项参数如表2所示: + + **表 2** device_info.hcs节点参数说明 + + | 成员名 | 值 | + | -------- | -------- | + | policy | 驱动服务发布的策略,SPI控制器具体配置为2,表示驱动对内核态和用户态都发布服务 | + | priority | 驱动启动优先级(0-200),值越大优先级越低。SPI控制器具体配置为60 | + | permission | 驱动创建设备节点权限,SPI控制器具体配置为0664 | + | moduleName | 驱动名称,SPI控制器固定为HDF_PLATFORM_SPI | + | serviceName | 驱动对外发布服务的名称,SPI控制器服务名设置为HDF_PLATFORM_SPI_X,X代表SPI控制器编号| + | deviceMatchAttr | 驱动私有数据匹配的关键字,SPI控制器设置为hisilicon_hi35xx_spi_X,X代表SPI控制器编号 | + + - device_info.hcs配置参考 + + ```c + root { + device_info { + match_attr = "hdf_manager"; + platform :: host { + hostName = "platform_host"; + priority = 50; + device_spi :: device { // 为每一个SPI控制器配置一个HDF设备节点 + device0 :: deviceNode { + policy = 2; + priority = 60; + permission = 0644; + moduleName = "HDF_PLATFORM_SPI"; + serviceName = "HDF_PLATFORM_SPI_0"; + deviceMatchAttr = "hisilicon_hi35xx_spi_0"; + } + device1 :: deviceNode { + policy = 2; // 驱动服务发布的策略,policy大于等于1(用户态可见为2,仅内核态可见为1)。 + priority = 60; // 驱动启动优先级 + permission = 0644; // 驱动创建设备节点权限 + moduleName = "HDF_PLATFORM_SPI"; // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致。 + serviceName = "HDF_PLATFORM_SPI_1"; // 驱动对外发布服务的名称,必须唯一,必须要按照HDF_PLATFORM_SPI_1的格式,X为SPI控制器编号。 + deviceMatchAttr = "hisilicon_hi35xx_spi_1"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值一致。 + } + ...... // 如果存在多个SPI设备时【必须】添加节点,否则不用 + } + } + } + } + ``` + + - spi_config.hcs配置参考 + + + ```c + root { + platform { + spi_config { // 每一个SPI控制器配置私有数据 + template spi_controller { // 模板公共参数,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省。 + serviceName = ""; + match_attr = ""; + transferMode = 0; // 数据传输模式:中断传输(0)、流控传输(1)、DMA传输(2) + busNum = 0; // 总线号 + clkRate = 100000000; + bitsPerWord = 8; // 传输位宽 + mode = 19; // SPI 数据的输入输出模式 + maxSpeedHz = 0; // 最大时钟频率 + minSpeedHz = 0; // 最小时钟频率 + speed = 2000000; // 当前消息传输速度 + fifoSize = 256; // FIFO大小 + numCs = 1; // 片选号 + regBase = 0x120c0000; // 地址映射需要 + irqNum = 100; // 中断号 + REG_CRG_SPI = 0x120100e4; // CRG_REG_BASE(0x12010000) + 0x0e4 + CRG_SPI_CKEN = 0; + CRG_SPI_RST = 0; + REG_MISC_CTRL_SPI = 0x12030024; // MISC_REG_BASE(0x12030000) + 0x24 + MISC_CTRL_SPI_CS = 0; + MISC_CTRL_SPI_CS_SHIFT = 0; + } + controller_0x120c0000 :: spi_controller { + busNum = 0; // 【必要】总线号 + CRG_SPI_CKEN = 0x10000; // (0x1 << 16) 0:close clk, 1:open clk + CRG_SPI_RST = 0x1; // (0x1 << 0) 0:cancel reset, 1:reset + match_attr = "hisilicon_hi35xx_spi_0"; // 【必要】需要和device_info.hcs中的deviceMatchAttr值一致 + } + controller_0x120c1000 :: spi_controller { + busNum = 1; + CRG_SPI_CKEN = 0x20000; // (0x1 << 17) 0:close clk, 1:open clk + CRG_SPI_RST = 0x2; // (0x1 << 1) 0:cancel reset, 1:reset + match_attr = "hisilicon_hi35xx_spi_1"; + regBase = 0x120c1000; // 【必要】地址映射需要 + irqNum = 101; // 【必要】中断号 + } + ...... // 如果存在多个SPI设备时【必须】添加节点,否则不用 + } + } + } + ``` + + 需要注意的是,新增spi_config.hcs配置文件后,必须在hdf.hcs文件中将其包含,否则配置文件无法生效。 + + 例如:本例中spi_config.hcs所在路径为device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/spi/spi_config.hcs,则必须在产品对应的hdf.hcs中添加如下语句: + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/spi/spi_config.hcs" // 配置文件相对路径 + ``` + +3. 实例化SPI控制器对象 + + 完成属性文件配置之后,下一步就是以核心层SpiCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化SpiCntlr成员SpiCntlrMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。 + + - 自定义结构体参考 + + 从驱动的角度看,自定义结构体是参数和数据的载体,而且spi_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象,例如设备号、总线号等。 - ```c - static int32_t HdfSpiDeviceBind(struct HdfDeviceObject *device) - { - ... - return (SpiCntlrCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS; - } - - struct SpiCntlr *SpiCntlrCreate(struct HdfDeviceObject *device) - { - struct SpiCntlr *cntlr = NULL; // 创建核心层SpiCntlr对象 - ... - cntlr = (struct SpiCntlr *)OsalMemCalloc(sizeof(*cntlr)); // 分配内存 - ... - cntlr->device = device; // 使HdfDeviceObject与SpiCntlr可以相互转化的前提 - device->service = &(cntlr->service); // 使HdfDeviceObject与SpiCntlr可以相互转化的前提 - (void)OsalMutexInit(&cntlr->lock); // 锁初始化 - DListHeadInit(&cntlr->list); // 添加对应的节点 - cntlr->priv = NULL; - return cntlr; - } - ``` - - - Init函数开发参考 - - 入参: - - HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 - - 返回值: - - HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 - - **表2** HDF_STATUS返回值描述 - - | 状态(值) | 描述 | - | -------- | -------- | - | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | - | HDF_ERR_MALLOC_FAIL | 内存分配失败 | - | HDF_ERR_INVALID_PARAM | 参数非法 | - | HDF_ERR_IO | I/O 错误 | - | HDF_SUCCESS | 初始化成功 | - | HDF_FAILURE | 初始化失败 | - - 函数说明: - - 初始化自定义结构体对象,初始化SpiCntlr成员。 + ```c + // 对应于spi_config.hcs中的参数 + struct Pl022 { + struct SpiCntlr *cntlr; + struct DListHead deviceList; + struct OsalSem sem; + volatile unsigned char *phyBase; + volatile unsigned char *regBase; + uint32_t irqNum; + uint32_t busNum; + uint32_t numCs; + uint32_t curCs; + uint32_t speed; + uint32_t fifoSize; + uint32_t clkRate; + uint32_t maxSpeedHz; + uint32_t minSpeedHz; + uint32_t regCrg; + uint32_t clkEnBit; + uint32_t clkRstBit; + uint32_t regMiscCtrl; + uint32_t miscCtrlCsShift; + uint32_t miscCtrlCs; + uint16_t mode; + uint8_t bitsPerWord; + uint8_t transferMode; + }; + + // SpiCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。 + struct SpiCntlr { + struct IDeviceIoService service; + struct HdfDeviceObject *device; + uint32_t busNum; + uint32_t numCs; + uint32_t curCs; + struct OsalMutex lock; + struct SpiCntlrMethod *method; + struct DListHead list; + void *priv; + }; + ``` + + - SpiCntlr成员钩子函数结构体SpiCntlrMethod的实例化。 + + ```c + // spi_hi35xx.c中的示例:钩子函数的实例化 + struct SpiCntlrMethod g_method = { + .Transfer = Pl022Transfer, + .SetCfg = Pl022SetCfg, + .GetCfg = Pl022GetCfg, + .Open = Pl022Open, + .Close = Pl022Close, + }; + ``` + + - Bind函数参考 + + 入参: + + HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 + + 返回值: + + HDF_STATUS相关状态。 + + 函数说明: + + 将SpiCntlr对象同HdfDeviceObject进行了关联。 + + ```c + static int32_t HdfSpiDeviceBind(struct HdfDeviceObject *device) + { + ...... + return (SpiCntlrCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS; + } + + struct SpiCntlr *SpiCntlrCreate(struct HdfDeviceObject *device) + { + struct SpiCntlr *cntlr = NULL; // 创建核心层SpiCntlr对象 + ...... + cntlr = (struct SpiCntlr *)OsalMemCalloc(sizeof(*cntlr)); // 分配内存 + ...... + cntlr->device = device; // 使HdfDeviceObject与SpiCntlr可以相互转化的前提 + device->service = &(cntlr->service); // 使HdfDeviceObject与SpiCntlr可以相互转化的前提 + (void)OsalMutexInit(&cntlr->lock); // 锁初始化 + DListHeadInit(&cntlr->list); // 添加对应的节点 + cntlr->priv = NULL; + return cntlr; + } + ``` + + - Init函数开发参考 + + 入参: + + HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 + + 返回值: + + HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS定义)。 + + **表 3** HDF_STATUS相关状态说明 + + | 状态(值) | 描述 | + | -------- | -------- | + | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | + | HDF_ERR_MALLOC_FAIL | 内存分配失败 | + | HDF_ERR_INVALID_PARAM | 参数非法 | + | HDF_ERR_IO | I/O 错误 | + | HDF_SUCCESS | 初始化成功 | + | HDF_FAILURE | 初始化失败 | + + 函数说明: + + 初始化自定义结构体对象,初始化SpiCntlr成员。 + + ```c + static int32_t HdfSpiDeviceInit(struct HdfDeviceObject *device) + { + int32_t ret; + struct SpiCntlr *cntlr = NULL; + ...... + cntlr = SpiCntlrFromDevice(device); // 这里有HdfDeviceObject到SpiCntlr的强制转换,通过service成员,赋值见Bind函数。 + // return (device == NULL) ? NULL : (struct SpiCntlr *)device->service; + ...... + ret = Pl022Init(cntlr, device); // 【必要】实例化驱动适配者自定义操作对象,示例见下。 + ...... + ret = Pl022Probe(cntlr->priv); + ...... + return ret; + } + + static int32_t Pl022Init(struct SpiCntlr *cntlr, const struct HdfDeviceObject *device) + { + int32_t ret; + struct Pl022 *pl022 = NULL; + ...... + pl022 = (struct Pl022 *)OsalMemCalloc(sizeof(*pl022)); // 申请内存 + ...... + ret = SpiGetBaseCfgFromHcs(pl022, device->property); // 初始化busNum、numCs、speed、fifoSize、clkRate、mode、bitsPerWord、transferMode参数值。 + ...... + ret = SpiGetRegCfgFromHcs(pl022, device->property); // 初始化regBase、phyBase、irqNum、regCrg、clkEnBit、clkRstBit、regMiscCtrl、regMiscCtrl、 miscCtrlCs、miscCtrlCsShift参数值。 + ...... + // 计算最大、最小速度对应的频率。 + pl022->maxSpeedHz = (pl022->clkRate) / ((SCR_MIN + 1) * CPSDVSR_MIN); + pl022->minSpeedHz = (pl022->clkRate) / ((SCR_MAX + 1) * CPSDVSR_MAX); + DListHeadInit(&pl022->deviceList); // 初始化DList链表 + pl022->cntlr = cntlr; // 使Pl022与SpiCntlr可以相互转化的前提 + cntlr->priv = pl022; // 使Pl022与SpiCntlr可以相互转化的前提 + cntlr->busNum = pl022->busNum; // 给SpiCntlr的busNum赋值 + cntlr->method = &g_method; // SpiCntlrMethod的实例化对象的挂载 + ...... + ret = Pl022CreatAndInitDevice(pl022); + if (ret != 0) { + Pl022Release(pl022); // 初始化失败则释放Pl022对象 + return ret; + } + return 0; + } + ``` + + - Release函数开发参考 + + 入参: + + HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 + + 返回值: + + 无。 + + 函数说明: + + 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 + + > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。 + + ```c + static void HdfSpiDeviceRelease(struct HdfDeviceObject *device) + { + struct SpiCntlr *cntlr = NULL; + ...... + cntlr = SpiCntlrFromDevice(device); // 这里有HdfDeviceObject到SpiCntlr的强制转换,通过service成员,赋值见Bind函数 + // return (device==NULL) ?NULL:(struct SpiCntlr *)device->service; + ...... + if (cntlr->priv != NULL) { + Pl022Remove((struct Pl022 *)cntlr->priv); // 这里有SpiCntlr到Pl022的强制转换 + } + SpiCntlrDestroy(cntlr); // 释放Pl022对象 + } + ``` - - ```c - static int32_t HdfSpiDeviceInit(struct HdfDeviceObject *device) - { - int32_t ret; - struct SpiCntlr *cntlr = NULL; - ... - cntlr = SpiCntlrFromDevice(device); // 这里有HdfDeviceObject到SpiCntlr的强制转换,通过service成员,赋值见Bind函数。 - // return (device == NULL) ? NULL : (struct SpiCntlr *)device->service; - ... - ret = Pl022Init(cntlr, device); // 【必要】实例化驱动适配者自定义操作对象,示例见下。 - ... - ret = Pl022Probe(cntlr->priv); - ... - return ret; - } - - static int32_t Pl022Init(struct SpiCntlr *cntlr, const struct HdfDeviceObject *device) - { - int32_t ret; - struct Pl022 *pl022 = NULL; - ... - pl022 = (struct Pl022 *)OsalMemCalloc(sizeof(*pl022)); // 申请内存 - ... - ret = SpiGetBaseCfgFromHcs(pl022, device->property); // 初始化busNum、numCs、speed、fifoSize、clkRate、mode、bitsPerWord、transferMode参数值。 - ... - ret = SpiGetRegCfgFromHcs(pl022, device->property); // 初始化regBase、phyBase、irqNum、regCrg、clkEnBit、clkRstBit、regMiscCtrl、regMiscCtrl、 miscCtrlCs、miscCtrlCsShift参数值。 - ... - // 计算最大、最小速度对应的频率。 - pl022->maxSpeedHz = (pl022->clkRate) / ((SCR_MIN + 1) * CPSDVSR_MIN); - pl022->minSpeedHz = (pl022->clkRate) / ((SCR_MAX + 1) * CPSDVSR_MAX); - DListHeadInit(&pl022->deviceList); // 初始化DList链表 - pl022->cntlr = cntlr; // 使Pl022与SpiCntlr可以相互转化的前提 - cntlr->priv = pl022; // 使Pl022与SpiCntlr可以相互转化的前提 - cntlr->busNum = pl022->busNum; // 给SpiCntlr的busNum赋值 - cntlr->method = &g_method; // SpiCntlrMethod的实例化对象的挂载 - ... - ret = Pl022CreatAndInitDevice(pl022); - if (ret != 0) { - Pl022Release(pl022); // 初始化失败则释放Pl022对象 - return ret; - } - return 0; - } - ``` - - Release函数开发参考 - - 入参: - - HdfDeviceObject是整个驱动对外提供的接口参数,具备HCS配置文件的信息。 - - 返回值: - - 无。 - - 函数说明: - - 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。 - - > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。 - - ```c - static void HdfSpiDeviceRelease(struct HdfDeviceObject *device) - { - struct SpiCntlr *cntlr = NULL; - ... - cntlr = SpiCntlrFromDevice(device); // 这里有HdfDeviceObject到SpiCntlr的强制转换,通过service成员,赋值见Bind函数 - // return (device==NULL) ?NULL:(struct SpiCntlr *)device->service; - ... - if (cntlr->priv != NULL) { - Pl022Remove((struct Pl022 *)cntlr->priv); // 这里有SpiCntlr到Pl022的强制转换 - } - SpiCntlrDestroy(cntlr); // 释放Pl022对象 - } - ``` +4. 驱动调试 + + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,SPI获取设备属性、SPI设置设备属性、SPI传输等。 diff --git a/zh-cn/device-dev/driver/driver-platform-uart-des.md b/zh-cn/device-dev/driver/driver-platform-uart-des.md index 500ab657eac4f5a9392d64771cc3eb01f9bed8b3..b35f2eadd1f8cd9dc6456db5444969c9a6392611 100644 --- a/zh-cn/device-dev/driver/driver-platform-uart-des.md +++ b/zh-cn/device-dev/driver/driver-platform-uart-des.md @@ -8,37 +8,43 @@ UART指异步收发传输器(Universal Asynchronous Receiver/Transmitter), 两个UART设备的连接示意图如下,UART与其他模块一般用2线(图1)或4线(图2)相连,它们分别是: - - TX:发送数据端,和对端的RX相连。 - - RX:接收数据端,和对端的TX相连。 - - RTS:发送请求信号,用于指示本设备是否准备好,可接受数据,和对端CTS相连。 - - CTS:允许发送信号,用于判断是否可以向对端发送数据,和对端RTS相连。 +- TX:发送数据端,和对端的RX相连。 -**图1** 2线UART设备连接示意图 +- RX:接收数据端,和对端的TX相连。 -![image1](figures/2线UART设备连接示意图.png "2线UART设备连接示意图") +- RTS:发送请求信号,用于指示本设备是否准备好,可接受数据,和对端CTS相连。 -**图2** 4线UART设备连接示意图 +- CTS:允许发送信号,用于判断是否可以向对端发送数据,和对端RTS相连。 -![image2](figures/4线UART设备连接示意图.png "4线UART设备连接示意图") +**图 1** 2线UART设备连接示意图 + +![2线UART设备连接示意图](figures/2线UART设备连接示意图.png) + +**图 2** 4线UART设备连接示意图 + +![4线UART设备连接示意图](figures/4线UART设备连接示意图.png) UART通信之前,收发双方需要约定好一些参数:波特率、数据格式(起始位、数据位、校验位、停止位)等。通信过程中,UART通过TX发送给对端数据,通过RX接收对端发送的数据。当UART接收缓存达到预定的门限值时,RTS变为不可发送数据,对端的CTS检测到不可发送数据,则停止发送数据。 UART接口定义了操作UART端口的通用方法集合,包括: - 打开/关闭UART设备 + - 读写数据 + - 设置/获取UART设备波特率 + - 设置/获取UART设备属性 ### 基本概念 - 异步通信 - 异步通信中,数据通常以字符或者字节为单位组成字符帧传送。字符帧由发送端逐帧发送,通过传输线被接收设备逐帧接收。发送端和接收端可以由各自的时钟来控制数据的发送和接收,这两个时钟源彼此独立,互不同步。异步通信以一个字符为传输单位,通信中两个字符间的时间间隔是不固定的,然而在同一个字符中的两个相邻位代码间的时间间隔是固定的。 + 异步通信中,数据通常以字符或者字节为单位组成字符帧传送。字符帧由发送端逐帧发送,通过传输线被接收设备逐帧接收。发送端和接收端可以由各自的时钟来控制数据的发送和接收,这两个时钟源彼此独立,互不同步。异步通信以一个字符为传输单位,通信中两个字符间的时间间隔是不固定的,然而在同一个字符中的两个相邻位代码间的时间间隔是固定的。 - 全双工传输(Full Duplex) - 此通信模式允许数据在两个方向上同时传输,它在能力上相当于两个单工通信方式的结合。全双工可以同时进行信号的双向传输。 + 此通信模式允许数据在两个方向上同时传输,它在能力上相当于两个单工通信方式的结合。全双工可以同时进行信号的双向传输。 ### 运作机制 @@ -47,17 +53,24 @@ UART接口定义了操作UART端口的通用方法集合,包括: 独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为: - 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。 + - device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。 UART模块各分层作用: - 接口层提供打开UART设备、UART设备读取指定长度数据、UART设备写入指定长度数据、设置UART设备波特率、获取设UART设备波特率、设置UART设备属性、获取UART设备波特率、设置UART设备传输模式、关闭UART设备的接口。 -- 核心层主要提供看UART控制器的创建、移除以及管理的能力,通过钩子函数与适配层交互。 + +- 核心层主要提供UART控制器的创建、移除以及管理的能力,通过钩子函数与适配层交互。 + - 适配层主要是将钩子函数的功能实例化,实现具体的功能。 -**图3** UART独立服务模式结构图 +**图 3** UART独立服务模式结构图 + +![UART独立服务模式结构图](figures/独立服务模式结构图.png) + +### 约束与限制 -![image3](figures/独立服务模式结构图.png "UART独立服务模式结构图") +UART模块UartSetTransMode接口设置传输模式在Linux中不支持,仅为空实现。 ## 使用指导 @@ -67,18 +80,18 @@ UART模块应用比较广泛,主要用于实现设备之间的低速串行通 ### 接口说明 -**表1** UART驱动API接口功能介绍 +**表 1** UART驱动API接口功能介绍 | 接口名 | 接口描述 | | -------- | -------- | | DevHandle UartOpen(uint32_t port) | UART获取设备句柄 | | void UartClose(DevHandle handle) | UART释放设备句柄 | -| int32_t UartRead(DevHandle handle, uint8_t *data, uint32_t size) | 从UART设备中读取指定长度的数据 | -| int32_t UartWrite(DevHandle handle, uint8_t *data, uint32_t size) | 向UART设备中写入指定长度的数据 | -| int32_t UartGetBaud(DevHandle handle, uint32_t *baudRate) | UART获取波特率 | +| int32_t UartRead(DevHandle handle, uint8_t \*data, uint32_t size) | 从UART设备中读取指定长度的数据 | +| int32_t UartWrite(DevHandle handle, uint8_t \*data, uint32_t size) | 向UART设备中写入指定长度的数据 | +| int32_t UartGetBaud(DevHandle handle, uint32_t \*baudRate) | UART获取波特率 | | int32_t UartSetBaud(DevHandle handle, uint32_t baudRate) | UART设置波特率 | -| int32_t UartGetAttribute(DevHandle handle, struct UartAttribute *attribute) | UART获取设备属性 | -| int32_t UartSetAttribute(DevHandle handle, struct UartAttribute *attribute) | UART设置设备属性 | +| int32_t UartGetAttribute(DevHandle handle, struct UartAttribute \*attribute) | UART获取设备属性 | +| int32_t UartSetAttribute(DevHandle handle, struct UartAttribute \*attribute) | UART设置设备属性 | | int32_t UartSetTransMode(DevHandle handle, enum UartTransMode mode) | UART设置传输模式 | > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
@@ -88,9 +101,9 @@ UART模块应用比较广泛,主要用于实现设备之间的低速串行通 使用UART的一般流程如下图所示。 -**图3** UART使用流程图 +**图 4** UART使用流程图 -![image3](figures/UART使用流程图.png "UART使用流程图") +![UART使用流程图](figures/UART使用流程图.png) #### 获取UART设备句柄 @@ -101,11 +114,11 @@ UART模块应用比较广泛,主要用于实现设备之间的低速串行通 DevHandle UartOpen(uint32_t port); ``` -**表2** UartOpen参数和返回值描述 +**表 2** UartOpen参数和返回值描述 | 参数 | 参数描述 | | -------- | -------- | -| port | UART设备号 | +| port | uint32_t类型,UART设备号 | | **返回值** | **返回值描述** | | NULL | 获取UART设备句柄失败 | | 设备句柄 | UART设备句柄 | @@ -131,12 +144,12 @@ if (handle == NULL) { int32_t UartSetBaud(DevHandle handle, uint32_t baudRate); ``` -**表3** UartSetBaud参数和返回值描述 +**表 3** UartSetBaud参数和返回值描述 | 参数 | 参数描述 | | -------- | -------- | -| handle | UART设备句柄 | -| baudRate | 待设置的波特率值 | +| handle | DevHandle类型,UART设备句柄 | +| baudRate | uint32_t类型,待设置的波特率值 | | **返回值** | **返回值描述** | | HDF_SUCCESS | UART设置波特率成功 | | 负数 | UART设置波特率失败 | @@ -161,12 +174,12 @@ if (ret != HDF_SUCCESS) { int32_t UartGetBaud(DevHandle handle, uint32_t *baudRate); ``` -**表4** UartGetBaud参数和返回值描述 +**表 4** UartGetBaud参数和返回值描述 | 参数 | 参数描述 | | -------- | -------- | -| handle | UART设备句柄 | -| baudRate | 接收波特率值的指针 | +| handle | DevHandle类型,UART设备句柄 | +| baudRate | uint32_t类型指针,用于接收波特率的值 | | **返回值** | **返回值描述** | | HDF_SUCCESS | UART获取波特率成功 | | 负数 | UART获取波特率失败 | @@ -192,12 +205,12 @@ if (ret != HDF_SUCCESS) { int32_t UartSetAttribute(DevHandle handle, struct UartAttribute *attribute); ``` -**表5** UartSetAttribute参数和返回值描述 +**表 5** UartSetAttribute参数和返回值描述 | 参数 | 参数描述 | | -------- | -------- | -| handle | UART设备句柄 | -| attribute | 待设置的设备属性 | +| handle | DevHandle类型,UART设备句柄 | +| attribute | 结构体指针,待设置的设备属性 | | **返回值** | **返回值描述** | | HDF_SUCCESS | UART设置设备属性成功 | | 负数 | UART设置设备属性失败 | @@ -231,12 +244,12 @@ turn ret; int32_t UartGetAttribute(DevHandle handle, struct UartAttribute *attribute); ``` -**表6** UartGetAttribute参数和返回值描述 +**表 6** UartGetAttribute参数和返回值描述 | 参数 | 参数描述 | | -------- | -------- | -| handle | UART设备句柄 | -| attribute | 接收UART设备属性的指针 | +| handle | DevHandle类型,UART设备句柄 | +| attribute | 结构体指针,接收UART设备属性的指针 | | **返回值** | **返回值描述** | | HDF_SUCCESS | UART获取设备属性成功 | | 负数 | UART获取设备属性失败 | @@ -262,12 +275,12 @@ if (ret != HDF_SUCCESS) { int32_t UartSetTransMode(DevHandle handle, enum UartTransMode mode); ``` -**表7** UartSetTransMode参数和返回值描述 +**表 7** UartSetTransMode参数和返回值描述 | 参数 | 参数描述 | | -------- | -------- | -| handle | UART设备句柄 | -| mode | 待设置的传输模式, | +| handle | DevHandle类型,UART设备句柄 | +| mode | 枚举类型,待设置的传输模式 | | **返回值** | **返回值描述** | | HDF_SUCCESS | UART设置传输模式成功 | | 负数 | UART设置传输模式失败 | @@ -292,13 +305,13 @@ if (ret != HDF_SUCCESS) { int32_t UartWrite(DevHandle handle, uint8_t *data, uint32_t size); ``` -**表8** UartWrite参数和返回值描述 +**表 8** UartWrite参数和返回值描述 | 参数 | 参数描述 | | -------- | -------- | -| handle | UART设备句柄 | -| data | 待写入数据的指针 | -| size | 待写入数据的长度 | +| handle | DevHandle类型,UART设备句柄 | +| data | uint8_t类型指针,待写入数据的 | +| size | uint32_t类型,待写入数据的长度 | | **返回值** | **返回值描述** | | HDF_SUCCESS | UART写数据成功 | | 负数 | UART写数据失败 | @@ -324,13 +337,13 @@ if (ret != HDF_SUCCESS) { int32_t UartRead(DevHandle handle, uint8_t *data, uint32_t size); ``` -**表9** UartRead参数和返回值描述 +**表 9** UartRead参数和返回值描述 | 参数 | 参数描述 | | -------- | -------- | -| handle | UART设备句柄 | -| data | 接收读取数据的指针 | -| size | 待读取数据的长度 | +| handle | DevHandle类型,UART设备句柄 | +| data | uint8_t类型指针,接收读取数据 | +| size | uint32_t类型,待读取数据的长度 | | **返回值** | **返回值描述** | | 非负数 | UART读取到的数据长度 | | 负数 | UART读取数据失败 | @@ -362,7 +375,7 @@ void UartClose(DevHandle handle); 该函数会释放申请的资源。 -**表10** UartClose参数和返回值描述 +**表 10** UartClose参数和返回值描述 | 参数 | 参数描述 | | -------- | -------- | @@ -379,20 +392,28 @@ UartClose(handle); // 销毁UART设备句柄 下面将基于Hi3516DV300开发板展示使用UART完整操作,步骤主要如下: 1. 传入UART端口号num,打开端口号对应的UART设备并获得UART设备句柄。 + 2. 通过UART设备句柄及设置的波特率,设置UART设备的波特率。 + 3. 通过UART设备句柄及待获取的波特率,获取UART设备的波特率。 + 4. 通过UART设备句柄及待设置的设备属性,设置UART设备的设备属性。 + 5. 通过UART设备句柄及待获取的设备属性,获取UART设备的设备属性。 + 6. 通过UART设备句柄及待设置的传输模式,设置UART设备的传输模式。 + 7. 通过UART设备句柄及待传输的数据及大小,传输指定长度的数据。 + 8. 通过UART设备句柄及待接收的数据及大小,接收指定长度的数据。 + 9. 通过UART设备句柄,关闭UART设备。 ```c #include "hdf_log.h" #include "uart_if.h" -void UartTestSample(void) +static int32_t UartTestSample(void) { int32_t ret; uint32_t port; @@ -415,7 +436,7 @@ void UartTestSample(void) handle = UartOpen(port); // 获取UART设备句柄 if (handle == NULL) { HDF_LOGE("UartOpen: open uart_%u failed!\n", port); - return; + return HDF_FAILURE; } ret = UartSetBaud(handle, 9600); // 设置UART波特率为9600 @@ -459,8 +480,9 @@ void UartTestSample(void) HDF_LOGE("UartRead: read data failed, ret %d\n", ret); goto ERR; } + HDF_LOGI("%s: function tests end, %d", __func__, ret); ERR: UartClose(handle); // 销毁UART设备句柄 - return ret; + return ret; } ``` diff --git a/zh-cn/device-dev/driver/driver-platform-uart-develop.md b/zh-cn/device-dev/driver/driver-platform-uart-develop.md index cff8e0e2b1c72bd1b9ad3e2ed2750e8a83ed89d7..f9a94e1430ed3800572accc26e0fd78b1ab2107e 100755 --- a/zh-cn/device-dev/driver/driver-platform-uart-develop.md +++ b/zh-cn/device-dev/driver/driver-platform-uart-develop.md @@ -8,18 +8,21 @@ UART指异步收发传输器(Universal Asynchronous Receiver/Transmitter), 两个UART设备的连接示意图如下,UART与其他模块一般用2线(图1)或4线(图2)相连,它们分别是: - - TX:发送数据端,和对端的RX相连。 - - RX:接收数据端,和对端的TX相连。 - - RTS:发送请求信号,用于指示本设备是否准备好,可接受数据,和对端CTS相连。 - - CTS:允许发送信号,用于判断是否可以向对端发送数据,和对端RTS相连。 +- TX:发送数据端,和对端的RX相连。 -**图1** 2线UART设备连接示意图 +- RX:接收数据端,和对端的TX相连。 -![image1](figures/2线UART设备连接示意图.png "2线UART设备连接示意图") +- RTS:发送请求信号,用于指示本设备是否准备好,可接受数据,和对端CTS相连。 -**图2** 4线UART设备连接示意图 +- CTS:允许发送信号,用于判断是否可以向对端发送数据,和对端RTS相连。 -![image2](figures/4线UART设备连接示意图.png "4线UART设备连接示意图") +**图 1** 2线UART设备连接示意图 + +![2线UART设备连接示意图](figures/2线UART设备连接示意图.png) + +**图 2** 4线UART设备连接示意图 + +![4线UART设备连接示意图](figures/4线UART设备连接示意图.png) UART通信之前,收发双方需要约定好一些参数:波特率、数据格式(起始位、数据位、校验位、停止位)等。通信过程中,UART通过TX发送给对端数据,通过RX接收对端发送的数据。当UART接收缓存达到预定的门限值时,RTS变为不可发送数据,对端的CTS检测到不可发送数据,则停止发送数据。 @@ -27,11 +30,11 @@ UART通信之前,收发双方需要约定好一些参数:波特率、数据 - 异步通信 - 异步通信中,数据通常以字符或者字节为单位组成字符帧传送。字符帧由发送端逐帧发送,通过传输线被接收设备逐帧接收。发送端和接收端可以由各自的时钟来控制数据的发送和接收,这两个时钟源彼此独立,互不同步。异步通信以一个字符为传输单位,通信中两个字符间的时间间隔是不固定的,然而在同一个字符中的两个相邻位代码间的时间间隔是固定的。 + 异步通信中,数据通常以字符或者字节为单位组成字符帧传送。字符帧由发送端逐帧发送,通过传输线被接收设备逐帧接收。发送端和接收端可以由各自的时钟来控制数据的发送和接收,这两个时钟源彼此独立,互不同步。异步通信以一个字符为传输单位,通信中两个字符间的时间间隔是不固定的,然而在同一个字符中的两个相邻位代码间的时间间隔是固定的。 - 全双工传输(Full Duplex) - 此通信模式允许数据在两个方向上同时传输,它在能力上相当于两个单工通信方式的结合。全双工可以同时进行信号的双向传输。 + 此通信模式允许数据在两个方向上同时传输,它在能力上相当于两个单工通信方式的结合。全双工可以同时进行信号的双向传输。 ### 运作机制 @@ -40,17 +43,20 @@ UART通信之前,收发双方需要约定好一些参数:波特率、数据 独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为: - 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。 + - device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。 UART模块各分层作用: - 接口层提供打开UART设备、UART设备读取指定长度数据、UART设备写入指定长度数据、设置UART设备波特率、获取设UART设备波特率、设置UART设备属性、获取UART设备波特率、设置UART设备传输模式、关闭UART设备的接口。 -- 核心层主要提供看UART控制器的创建、移除以及管理的能力,通过钩子函数与适配层交互。 + +- 核心层主要提供UART控制器的创建、移除以及管理的能力,通过钩子函数与适配层交互。 + - 适配层主要是将钩子函数的功能实例化,实现具体的功能。 -**图3** UART独立服务模式结构图 +**图 3** UART独立服务模式结构图 -![image3](figures/独立服务模式结构图.png "UART独立服务模式结构图") +![UART独立服务模式结构图](figures/独立服务模式结构图.png) ## 开发指导 @@ -79,7 +85,7 @@ struct UartHostMethod { }; ``` -**表1** UartHostMethod结构体成员的回调函数功能说明 +**表 1** UartHostMethod结构体成员的回调函数功能说明 | 函数 | 入参 | 出参 | 返回值 | 功能 | | -------- | -------- | -------- | -------- | -------- | @@ -92,358 +98,375 @@ struct UartHostMethod { | GetAttribute | host:结构体指针,核心层UART控制器 | attribute:结构体指针,传出的属性值(见uart_if.h中UartAttribute定义) | HDF_STATUS相关状态 | 获取设备uart相关属性 | | SetAttribute | host:结构体指针,核心层UART控制器
attribute:结构体指针,属性传入值 | 无 | HDF_STATUS相关状态 | 设置设备UART相关属性 | | SetTransMode | host:结构体指针,核心层UART控制器
mode:枚举值(见uart_if.h中UartTransMode定义),传输模式 | 无 | HDF_STATUS相关状态 | 设置传输模式 | -| PollEvent | host:结构体指针,核心层UART控制器
filep:void类型指针file
table:void类型指针table | 无 | HDF_STATUS相关状态 | poll轮询机制 | +| PollEvent | host:结构体指针,核心层UART控制器
filep:void类型指针filep
table:void类型指针table | 无 | HDF_STATUS相关状态 | poll轮询机制 | ### 开发步骤 UART模块适配HDF框架包含以下四个步骤: -- 实例化驱动入口。 -- 配置属性文件。 -- 实例化UART控制器对象。 -- 驱动调试。 +- 实例化驱动入口 + +- 配置属性文件 + +- 实例化UART控制器对象 + +- 驱动调试 ### 开发实例 下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/uart/uart_hi35xx.c驱动为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。 -1. 实例化驱动入口。 - - 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 - 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 - - UART驱动入口开发参考: - - ```c - struct HdfDriverEntry g_hdfUartDevice = { - .moduleVersion = 1, - .moduleName = "HDF_PLATFORM_UART", // 【必要且与HCS文件中里面的moduleName匹配】 - .Bind = HdfUartDeviceBind, // 见Bind参考 - .Init = HdfUartDeviceInit, // 见Init参考 - .Release = HdfUartDeviceRelease, // 见Release参考 - }; - HDF_INIT(g_hdfUartDevice); //调用HDF_INIT将驱动入口注册到HDF框架中 - ``` - -2. 配置属性文件。 - - 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以两个UART控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息。器件属性值与核心层UartHost成员的默认值或限制范围有密切关系,比如UART设备端口号,需要在uart_config.hcs文件中增加对应的器件属性。 - - - device_info.hcs 配置参考: - - 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。 - - ```c - root { - device_info { - match_attr = "hdf_manager"; - platform :: host { - hostName = "platform_host"; - priority = 50; - device_uart :: device { - device0 :: deviceNode { - policy = 1; // 驱动服务发布的策略,policy大于等于1(用户态可见为2,仅内核态可见为1)。 - priority = 40; // 驱动启动优先级 - permission = 0644; // 驱动创建设备节点权限 - moduleName = "HDF_PLATFORM_UART"; // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致。 - serviceName = "HDF_PLATFORM_UART_0"; // 驱动对外发布服务的名称,必须唯一,必须要按照HDF_PLATFORM_UART_X的格式,X为UART控制器编号。 - deviceMatchAttr = "hisilicon_hi35xx_uart_0"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值一致。 - } - device1 :: deviceNode { - policy = 2; - permission = 0644; - priority = 40; - moduleName = "HDF_PLATFORM_UART"; - serviceName = "HDF_PLATFORM_UART_1"; - deviceMatchAttr = "hisilicon_hi35xx_uart_1"; - } - ... - } - } - } - } - ``` - - - uart_config.hcs 配置参考: - - 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/uart/uart_config.hcs文件配置器件属性,其中配置参数如下: - - ```c - root { - platform { - template uart_controller { // 配置模板,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值 - match_attr = ""; - num = 0; // 【必要】端口号 - baudrate = 115200; // 【必要】波特率,数值可按需填写 - fifoRxEn = 1; // 【必要】使能接收FIFO - fifoTxEn = 1; // 【必要】使能发送FIFO - flags = 4; // 【必要】标志信号 - regPbase = 0x120a0000; // 【必要】地址映射需要 - interrupt = 38; // 【必要】中断号 - iomemCount = 0x48; // 【必要】地址映射需要 - } - controller_0x120a0000 :: uart_controller { - match_attr = "hisilicon_hi35xx_uart_0"; // 【必要】必须和device_info.hcs中对应的设备的deviceMatchAttr值一致 - } - controller_0x120a1000 :: uart_controller { - num = 1; - baudrate = 9600; - regPbase = 0x120a1000; - interrupt = 39; - match_attr = "hisilicon_hi35xx_uart_1"; - } - ... // 如果存在多个UART设备时【必须】添加节点,否则不用 - } - } - ``` - - 需要注意的是,新增uart_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。 - - ```c - #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/uart/uart_config.hcs" // 配置文件相对路径 - ``` - -3. 实例化UART控制器对象。 - - 完成属性文件配置之后,下一步就是以核心层UartHost对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化UartHost成员UartHostMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。 - - - 驱动适配者自定义结构体参考 - - 从驱动的角度看,驱动适配者自定义结构体是参数和数据的载体,而且uart_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象,例如端口号。 - - ```c - struct UartPl011Port { // 驱动适配者自定义管脚描述结构体 - int32_t enable; - unsigned long physBase; // 物理地址 - uint32_t irqNum; // 中断号 - uint32_t defaultBaudrate; // 默认波特率 - uint32_t flags; // 标志信号,下面三个宏与之相关 - #define PL011_FLG_IRQ_REQUESTED (1 << 0) - #define PL011_FLG_DMA_RX_REQUESTED (1 << 1) - #define PL011_FLG_DMA_TX_REQUESTED (1 << 2) - struct UartDmaTransfer *rxUdt; // DMA传输相关 - struct UartDriverData *udd; - }; - struct UartDriverData { // 数据传输相关的结构体 - uint32_t num; // 端口号 - uint32_t baudrate; // 波特率(可设置) - struct UartAttribute attr; // 数据位、停止位等传输属性相关 - struct UartTransfer *rxTransfer; // 缓冲区相关,可理解为FIFO结构 - wait_queue_head_t wait; // 条件变量相关的排队等待信号 - int32_t count; // 数据数量 - int32_t state; // UART控制器状态 - #define UART_STATE_NOT_OPENED 0 - #define UART_STATE_OPENING 1 - #define UART_STATE_USEABLE 2 - #define UART_STATE_SUSPENDED 3 - uint32_t flags; // 状态标志 - #define UART_FLG_DMA_RX (1 << 0) - #define UART_FLG_DMA_TX (1 << 1) - #define UART_FLG_RD_BLOCK (1 << 2) - RecvNotify recv; // 函数指针类型,指向串口数据接收函数 - struct UartOps *ops; // 自定义函数指针结构体 - void *private; // 私有数据 - }; - - // UartHost是核心层控制器结构体,其中的成员在Init函数中会被赋值。 - struct UartHost { - struct IDeviceIoService service; // 驱动服务 - struct HdfDeviceObject *device; // 驱动设备对象 - uint32_t num; // 端口号 - OsalAtomic atom; // 原子量 - void *priv; // 私有数据 - struct UartHostMethod *method; // 回调函数 - }; - ``` - - - UartHost成员回调函数结构体UartHostMethod的实例化,其中的成员在Init函数中初始化。 - - ```c - // uart_hi35xx.c 中的示例:钩子函数的实例化 - struct UartHostMethod g_uartHostMethod = { - .Init = Hi35xxInit, // 初始化 - .Deinit = Hi35xxDeinit, // 去初始化 - .Read = Hi35xxRead, // 接收数据 - .Write = Hi35xxWrite, // 发送数据 - .SetBaud = Hi35xxSetBaud, // 设置波特率 - .GetBaud = Hi35xxGetBaud, // 获取波特率 - .SetAttribute = Hi35xxSetAttribute, // 设置设备属性 - .GetAttribute = Hi35xxGetAttribute, // 获取设备属性 - .SetTransMode = Hi35xxSetTransMode, // 设置传输模式 - .pollEvent = Hi35xxPollEvent, // 轮询 - }; - ``` - - - Bind函数开发参考 - - 入参: - - HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 - - 返回值: - - HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可参见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS中HDF_STATUS定义)。 - - **表2** HDF_STATUS返回值说明 - - | 状态(值) | 问题描述 | - | -------- | -------- | - | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | - | HDF_ERR_MALLOC_FAIL | 内存分配失败 | - | HDF_ERR_INVALID_PARAM | 参数非法 | - | HDF_ERR_IO | I/O 错误 | - | HDF_SUCCESS | 初始化成功 | - | HDF_FAILURE | 初始化失败 | - - 函数说明: - - 初始化自定义结构体对象,初始化UartHost成员。 - - ```c - //uart_hi35xx.c - static int32_t HdfUartDeviceBind(struct HdfDeviceObject *device) - { - ... - return (UartHostCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS; // 【必须】调用核心层函数UartHostCreate - } - - // uart_core.c核心层UartHostCreate函数说明 - struct UartHost *UartHostCreate(struct HdfDeviceObject *device) - { - struct UartHost *host = NULL; // 新建UartHost - ... - host = (struct UartHost *)OsalMemCalloc(sizeof(*host)); // 分配内存 - ... - host->device = device; // 【必要】使HdfDeviceObject与UartHost可以相互转化的前提 - device->service = &(host->service); // 【必要】使HdfDeviceObject与UartHost可以相互转化的前提 - host->device->service->Dispatch = UartIoDispatch; // 为service成员的Dispatch方法赋值 - OsalAtomicSet(&host->atom, 0); // 原子量初始化或者原子量设置 - host->priv = NULL; - host->method = NULL; - return host; - } - ``` - - - Init函数开发参考 - - 入参: - - HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 - - 返回值: - - HDF_STATUS相关状态。 - - 函数说明: - - 初始化自定义结构体对象,初始化UartHost成员,调用核心层UartAddDev函数,完成UART控制器的添加,接入VFS。 +1. 实例化驱动入口 + + 驱动入口必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 + + 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 + + UART驱动入口开发参考: + + ```c + struct HdfDriverEntry g_hdfUartDevice = { + .moduleVersion = 1, + .moduleName = "HDF_PLATFORM_UART", // 【必要且与HCS文件中里面的moduleName匹配】 + .Bind = HdfUartDeviceBind, // 挂接UART模块Bind实例化 + .Init = HdfUartDeviceInit, // 挂接UART模块Init实例化 + .Release = HdfUartDeviceRelease, // 挂接UART模块Release实例化 + }; + HDF_INIT(g_hdfUartDevice); // 调用HDF_INIT将驱动入口注册到HDF框架中 + ``` + +2. 配置属性文件 + + 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode信息,deviceNode信息与驱动入口注册相关。本例以两个UART控制器为例,如有多个器件信息,则需要在device_info.hcs文件增加对应的deviceNode信息,以及在uart_config.hcs文件中增加对应的器件属性。器件属性值与核心层UartHost成员的默认值或限制范围有密切关系,比如UART设备端口号,需要在uart_config.hcs文件中增加对应的器件属性。 + + 独立服务模式的特点是device_info.hcs文件中设备节点代表着一个设备对象,如果存在多个设备对象,则按需添加,注意服务名与驱动私有数据匹配的关键字名称必须唯一。其中各项参数如表2所示: + + **表 2** device_info.hcs节点参数说明 + + | 成员名 | 值 | + | -------- | -------- | + | policy | 驱动服务发布的策略,UART控制器具体配置为2,表示驱动对内核态和用户态都发布服务 | + | priority | 驱动启动优先级(0-200),值越大优先级越低。UART控制器具体配置为40 | + | permission | 驱动创建设备节点权限,UART控制器具体配置为0664 | + | moduleName | 驱动名称,UART控制器固定为HDF_PLATFORM_UART | + | serviceName | 驱动对外发布服务的名称,UART控制器服务名设置为HDF_PLATFORM_UART_X,X代表UART控制器编号 | + | deviceMatchAttr | 驱动私有数据匹配的关键字,UART控制器设置为hisilicon_hi35xx_uart_X ,X代表UART控制器编号 | + + - device_info.hcs 配置参考: + + 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。 + + ```c + root { + device_info { + match_attr = "hdf_manager"; + platform :: host { + hostName = "platform_host"; + priority = 50; + device_uart :: device { + device0 :: deviceNode { + policy = 1; // 驱动服务发布的策略,policy大于等于1(用户态可见为2,仅内核态可见为1)。 + priority = 40; // 驱动启动优先级 + permission = 0644; // 驱动创建设备节点权限 + moduleName = "HDF_PLATFORM_UART"; // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致。 + serviceName = "HDF_PLATFORM_UART_0"; // 驱动对外发布服务的名称,必须唯一,必须要按照HDF_PLATFORM_UART_X的格式,X为UART控制器编号。 + deviceMatchAttr = "hisilicon_hi35xx_uart_0"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值一致。 + } + device1 :: deviceNode { + policy = 2; + permission = 0644; + priority = 40; + moduleName = "HDF_PLATFORM_UART"; + serviceName = "HDF_PLATFORM_UART_1"; + deviceMatchAttr = "hisilicon_hi35xx_uart_1"; + } + ...... // 如果存在多个UART设备时【必须】添加节点,否则不用 + } + } + } + } + ``` + + - uart_config.hcs 配置参考: + + 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/uart/uart_config.hcs文件配置器件属性,其中配置参数如下: + + ```c + root { + platform { + template uart_controller { // 配置模板,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值 + match_attr = ""; + num = 0; // 【必要】端口号 + baudrate = 115200; // 【必要】波特率,数值可按需填写 + fifoRxEn = 1; // 【必要】使能接收FIFO + fifoTxEn = 1; // 【必要】使能发送FIFO + flags = 4; // 【必要】标志信号 + regPbase = 0x120a0000; // 【必要】地址映射需要 + interrupt = 38; // 【必要】中断号 + iomemCount = 0x48; // 【必要】地址映射需要 + } + controller_0x120a0000 :: uart_controller { + match_attr = "hisilicon_hi35xx_uart_0"; // 【必要】必须和device_info.hcs中对应的设备的deviceMatchAttr值一致 + } + controller_0x120a1000 :: uart_controller { + num = 1; + baudrate = 9600; + regPbase = 0x120a1000; + interrupt = 39; + match_attr = "hisilicon_hi35xx_uart_1"; + } + ...... // 如果存在多个UART设备时【必须】添加节点,否则不用 + } + } + ``` + + 需要注意的是,新增uart_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。 + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/uart/uart_config.hcs" // 配置文件相对路径 + ``` + +3. 实例化UART控制器对象 + + 完成属性文件配置之后,下一步就是以核心层UartHost对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化UartHost成员UartHostMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind、Init、Release)。 + + - 驱动适配者自定义结构体参考 + + 从驱动的角度看,驱动适配者自定义结构体是参数和数据的载体,而且uart_config.hcs文件中的数值会被HDF读入并通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象,例如端口号。 + + ```c + struct UartPl011Port { // 驱动适配者自定义管脚描述结构体 + int32_t enable; + unsigned long physBase; // 物理地址 + uint32_t irqNum; // 中断号 + uint32_t defaultBaudrate; // 默认波特率 + uint32_t flags; // 标志信号,下面三个宏与之相关 + #define PL011_FLG_IRQ_REQUESTED (1 << 0) + #define PL011_FLG_DMA_RX_REQUESTED (1 << 1) + #define PL011_FLG_DMA_TX_REQUESTED (1 << 2) + struct UartDmaTransfer *rxUdt; // DMA传输相关 + struct UartDriverData *udd; + }; + struct UartDriverData { // 数据传输相关的结构体 + uint32_t num; // 端口号 + uint32_t baudrate; // 波特率(可设置) + struct UartAttribute attr; // 数据位、停止位等传输属性相关 + struct UartTransfer *rxTransfer; // 缓冲区相关,可理解为FIFO结构 + wait_queue_head_t wait; // 条件变量相关的排队等待信号 + int32_t count; // 数据数量 + int32_t state; // UART控制器状态 + #define UART_STATE_NOT_OPENED 0 + #define UART_STATE_OPENING 1 + #define UART_STATE_USEABLE 2 + #define UART_STATE_SUSPENDED 3 + uint32_t flags; // 状态标志 + #define UART_FLG_DMA_RX (1 << 0) + #define UART_FLG_DMA_TX (1 << 1) + #define UART_FLG_RD_BLOCK (1 << 2) + RecvNotify recv; // 函数指针类型,指向串口数据接收函数 + struct UartOps *ops; // 自定义函数指针结构体 + void *private; // 私有数据 + }; + + // UartHost是核心层控制器结构体,其中的成员在Init函数中会被赋值。 + struct UartHost { + struct IDeviceIoService service; // 驱动服务 + struct HdfDeviceObject *device; // 驱动设备对象 + uint32_t num; // 端口号 + OsalAtomic atom; // 原子量 + void *priv; // 私有数据 + struct UartHostMethod *method; // 回调函数 + }; + ``` + + - UartHost成员回调函数结构体UartHostMethod的实例化。 + + ```c + // uart_hi35xx.c 中的示例:钩子函数的实例化 + struct UartHostMethod g_uartHostMethod = { + .Init = Hi35xxInit, // 初始化 + .Deinit = Hi35xxDeinit, // 去初始化 + .Read = Hi35xxRead, // 接收数据 + .Write = Hi35xxWrite, // 发送数据 + .SetBaud = Hi35xxSetBaud, // 设置波特率 + .GetBaud = Hi35xxGetBaud, // 获取波特率 + .SetAttribute = Hi35xxSetAttribute, // 设置设备属性 + .GetAttribute = Hi35xxGetAttribute, // 获取设备属性 + .SetTransMode = Hi35xxSetTransMode, // 设置传输模式 + .pollEvent = Hi35xxPollEvent, // 轮询 + }; + ``` + + - Bind函数开发参考 + + 入参: + + HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 + + 返回值: + + HDF_STATUS相关状态(表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS中HDF_STATUS定义)。 + + **表 3** HDF_STATUS相关状态说明 + + | 状态(值) | 问题描述 | + | -------- | -------- | + | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | + | HDF_ERR_MALLOC_FAIL | 内存分配失败 | + | HDF_ERR_INVALID_PARAM | 参数非法 | + | HDF_ERR_IO | I/O 错误 | + | HDF_SUCCESS | 初始化成功 | + | HDF_FAILURE | 初始化失败 | + + 函数说明: + + 初始化自定义结构体对象,初始化UartHost成员。 + + ```c + //uart_hi35xx.c + static int32_t HdfUartDeviceBind(struct HdfDeviceObject *device) + { + ...... + return (UartHostCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS; // 【必须】调用核心层函数UartHostCreate + } + + // uart_core.c核心层UartHostCreate函数说明 + struct UartHost *UartHostCreate(struct HdfDeviceObject *device) + { + struct UartHost *host = NULL; // 新建UartHost + ...... + host = (struct UartHost *)OsalMemCalloc(sizeof(*host)); // 分配内存 + ...... + host->device = device; // 【必要】使HdfDeviceObject与UartHost可以相互转化的前提 + device->service = &(host->service); // 【必要】使HdfDeviceObject与UartHost可以相互转化的前提 + host->device->service->Dispatch = UartIoDispatch; // 为service成员的Dispatch方法赋值 + OsalAtomicSet(&host->atom, 0); // 原子量初始化或者原子量设置 + host->priv = NULL; + host->method = NULL; + return host; + } + ``` + + - Init函数开发参考 + + 入参: + + HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 + + 返回值: + + HDF_STATUS相关状态。 + + 函数说明: + + 初始化自定义结构体对象,初始化UartHost成员,调用核心层UartAddDev函数,完成UART控制器的添加,接入VFS。 - ```c - int32_t HdfUartDeviceInit(struct HdfDeviceObject *device) - { - int32_t ret; - struct UartHost *host = NULL; - HDF_LOGI("%s: entry", __func__); - ... - host = UartHostFromDevice(device); // 通过service成员后强制转为UartHost,赋值是在Bind函数中 - ... - ret = Hi35xxAttach(host, device); // 完成UartHost对象的初始化,见下 - ... - host->method = &g_uartHostMethod; // UartHostMethod的实例化对象的挂载 - return ret; - } - // 完成UartHost对象的初始化。 - static int32_t Hi35xxAttach(struct UartHost *host, struct HdfDeviceObject *device) - { - int32_t ret; - struct UartDriverData *udd = NULL; // udd和port对象是驱动适配者自定义的结构体对象,可根据需要实现相关功能 - struct UartPl011Port *port = NULL; - ... - // 【必要】步骤【1】~【7】主要实现对udd对象的实例化赋值,然后赋值给核心层UartHost对象。 - udd = (struct UartDriverData *)OsalMemCalloc(sizeof(*udd)); // 【1】 - ... - port = (struct UartPl011Port *)OsalMemCalloc(sizeof(struct UartPl011Port)); // 【2】 - ... - udd->ops = Pl011GetOps(); // 【3】设备开启、关闭、属性设置、发送操作等函数挂载。 - udd->recv = PL011UartRecvNotify; // 【4】数据接收通知函数(条件锁机制)挂载 - udd->count = 0; // 【5】 - port->udd = udd; // 【6】使UartPl011Port与UartDriverData可以相互转化的前提 - ret = UartGetConfigFromHcs(port, device->property); // 将HdfDeviceObject的属性传递给驱动适配者自定义结构体,用于相关操作,示例代码见下 - ... - udd->private = port; // 【7】 - host->priv = udd; // 【必要】使UartHost与UartDriverData可以相互转化的前提 - host->num = udd->num; // 【必要】UART设备号 - UartAddDev(host); // 【必要】核心层uart_dev.c中的函数,作用:注册一个字符设备节点到vfs,这样从用户态可以通过这个虚拟文件节点访问UART - return HDF_SUCCESS; - } - - static int32_t UartGetConfigFromHcs(struct UartPl011Port *port, const struct DeviceResourceNode *node) - { - uint32_t tmp, regPbase, iomemCount; - struct UartDriverData *udd = port->udd; - struct DeviceResourceIface *iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); - ... - // 通过请求参数提取相应的值,并赋值给驱动适配者自定义的结构体。 - if (iface->GetUint32(node, "num", &udd->num, 0) != HDF_SUCCESS) { - HDF_LOGE("%s: read busNum fail", __func__); - return HDF_FAILURE; - } - ... - return 0; - } - ``` - - - Release函数开发参考 - - 入参: - - HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 - - 返回值: - - 无。 - - 函数说明: - - 该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源,该函数中需包含释放内存和删除控制器等操作。 - - > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
- > 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。 - - ```c - void HdfUartDeviceRelease(struct HdfDeviceObject *device) - { - struct UartHost *host = NULL; - ... - host = UartHostFromDevice(device); // 这里有HdfDeviceObject到UartHost的强制转化,通过service成员,赋值见Bind函数。 - ... - if (host->priv != NULL) { - Hi35xxDetach(host); // 驱动适配自定义的内存释放函数,见下。 - } - UartHostDestroy(host); // 调用核心层函数释放host - } - - static void Hi35xxDetach(struct UartHost *host) - { - struct UartDriverData *udd = NULL; - struct UartPl011Port *port = NULL; - ... - udd = host->priv; // 这里有UartHost到UartDriverData的转化 - ... - UartRemoveDev(host); // VFS注销 - port = udd->private; // 这里有UartDriverData到UartPl011Port的转化 - if (port != NULL) { - if (port->physBase != 0) { - OsalIoUnmap((void *)port->physBase); // 地址反映射 - } - OsalMemFree(port); - udd->private = NULL; - } - OsalMemFree(udd); // 释放UartDriverData - host->priv = NULL; - } - ``` - -4. 驱动调试。 - - 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。 \ No newline at end of file + ```c + int32_t HdfUartDeviceInit(struct HdfDeviceObject *device) + { + int32_t ret; + struct UartHost *host = NULL; + HDF_LOGI("%s: entry", __func__); + ...... + host = UartHostFromDevice(device); // 通过service成员后强制转为UartHost,赋值是在Bind函数中 + ...... + ret = Hi35xxAttach(host, device); // 完成UartHost对象的初始化,见下 + ...... + host->method = &g_uartHostMethod; // UartHostMethod的实例化对象的挂载 + return ret; + } + // 完成UartHost对象的初始化。 + static int32_t Hi35xxAttach(struct UartHost *host, struct HdfDeviceObject *device) + { + int32_t ret; + struct UartDriverData *udd = NULL; // udd和port对象是驱动适配者自定义的结构体对象,可根据需要实现相关功能 + struct UartPl011Port *port = NULL; + ...... + // 【必要】步骤【1】~【7】主要实现对udd对象的实例化赋值,然后赋值给核心层UartHost对象。 + udd = (struct UartDriverData *)OsalMemCalloc(sizeof(*udd)); // 【1】 + ...... + port = (struct UartPl011Port *)OsalMemCalloc(sizeof(struct UartPl011Port)); // 【2】 + ...... + udd->ops = Pl011GetOps(); // 【3】设备开启、关闭、属性设置、发送操作等函数挂载。 + udd->recv = PL011UartRecvNotify; // 【4】数据接收通知函数(条件锁机制)挂载 + udd->count = 0; // 【5】 + port->udd = udd; // 【6】使UartPl011Port与UartDriverData可以相互转化的前提 + ret = UartGetConfigFromHcs(port, device->property); // 将HdfDeviceObject的属性传递给驱动适配者自定义结构体,用于相关操作,示例代码见下 + ...... + udd->private = port; // 【7】 + host->priv = udd; // 【必要】使UartHost与UartDriverData可以相互转化的前提 + host->num = udd->num; // 【必要】UART设备号 + UartAddDev(host); // 【必要】核心层uart_dev.c中的函数,作用:注册一个字符设备节点到vfs,这样从用户态可以通过这个虚拟文件节点访问UART + return HDF_SUCCESS; + } + + static int32_t UartGetConfigFromHcs(struct UartPl011Port *port, const struct DeviceResourceNode *node) + { + uint32_t tmp, regPbase, iomemCount; + struct UartDriverData *udd = port->udd; + struct DeviceResourceIface *iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + ...... + // 通过请求参数提取相应的值,并赋值给驱动适配者自定义的结构体。 + if (iface->GetUint32(node, "num", &udd->num, 0) != HDF_SUCCESS) { + HDF_LOGE("%s: read busNum fail", __func__); + return HDF_FAILURE; + } + ...... + return 0; + } + ``` + + - Release函数开发参考 + + 入参: + + HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 + + 返回值: + + 无。 + + 函数说明: + + 该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源,该函数中需包含释放内存和删除控制器等操作。 + + > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
+ > 所有强制转换获取相应对象的操作前提是在Init函数中具备对应赋值的操作。 + + ```c + void HdfUartDeviceRelease(struct HdfDeviceObject *device) + { + struct UartHost *host = NULL; + ... + host = UartHostFromDevice(device); // 这里有HdfDeviceObject到UartHost的强制转化,通过service成员,赋值见Bind函数。 + ... + if (host->priv != NULL) { + Hi35xxDetach(host); // 驱动适配自定义的内存释放函数,见下。 + } + UartHostDestroy(host); // 调用核心层函数释放host + } + + static void Hi35xxDetach(struct UartHost *host) + { + struct UartDriverData *udd = NULL; + struct UartPl011Port *port = NULL; + ... + udd = host->priv; // 这里有UartHost到UartDriverData的转化 + ... + UartRemoveDev(host); // VFS注销 + port = udd->private; // 这里有UartDriverData到UartPl011Port的转化 + if (port != NULL) { + if (port->physBase != 0) { + OsalIoUnmap((void *)port->physBase); // 地址反映射 + } + OsalMemFree(port); + udd->private = NULL; + } + OsalMemFree(udd); // 释放UartDriverData + host->priv = NULL; + } + ``` + +4. 驱动调试 + + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。 diff --git a/zh-cn/device-dev/driver/driver-platform-watchdog-des.md b/zh-cn/device-dev/driver/driver-platform-watchdog-des.md index 09a16a81fb094c6d65f7a4f1dbae1104f879480b..3a11cb12663bde5ccac8a5657be581c36cfa2b35 100644 --- a/zh-cn/device-dev/driver/driver-platform-watchdog-des.md +++ b/zh-cn/device-dev/driver/driver-platform-watchdog-des.md @@ -9,9 +9,13 @@ Watchdog接口定义了看门狗操作的通用方法集合,包括: - 打开/关闭看门狗设备 + - 启动/停止看门狗设备 + - 设置/获取看门狗设备超时时间 + - 获取看门狗设备状态 + - 喂狗 ### 基本概念 @@ -25,17 +29,20 @@ Watchdog接口定义了看门狗操作的通用方法集合,包括: 独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为: - 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。 + - device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。 Watchdog模块各分层作用: - 接口层提供打开看门狗设备、获取看门狗设备状态、启动看门狗设备、设置看门狗设备超时时间、获取看门狗设备超时时间、喂狗、停止看门狗设备超时时间、关闭看门狗设备的接口。 + - 核心层主要提供看门狗控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。 + - 适配层主要是将钩子函数的功能实例化,实现具体的功能。 **图 1** Watchdog独立服务模式结构图 -![image1](figures/独立服务模式结构图.png "Watchdog独立服务模式结构图") +![Watchdog独立服务模式结构图](figures/独立服务模式结构图.png) ## 使用指导 @@ -45,19 +52,19 @@ Watchdog模块各分层作用: ### 接口说明 -Watchdog模块提供的主要接口如表1所示。 +Watchdog模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/watchdog_if.h。 -**表1** 看门狗API接口功能介绍 +**表 1** 看门狗API接口功能介绍 | 接口名 | 描述 | | -------- | -------- | -| int32_t WatchdogOpen(int16_t wdtId, DevHandle *handle) | 打开看门狗 | +| int32_t WatchdogOpen(int16_t wdtId, DevHandle \*handle) | 打开看门狗 | | void WatchdogClose(DevHandle handle) | 关闭看门狗 | | int32_t WatchdogStart(DevHandle handle) | 启动看门狗 | | int32_t WatchdogStop(DevHandle handle) | 停止看门狗 | | int32_t WatchdogSetTimeout(DevHandle handle, uint32_t seconds) | 设置看门狗超时时间 | -| int32_t WatchdogGetTimeout(DevHandle handle, uint32_t *seconds) | 获取看门狗超时时间 | -| int32_t WatchdogGetStatus(DevHandle handle, int32_t *status) | 获取看门狗状态 | +| int32_t WatchdogGetTimeout(DevHandle handle, uint32_t \*seconds) | 获取看门狗超时时间 | +| int32_t WatchdogGetStatus(DevHandle handle, int32_t \*status) | 获取看门狗状态 | | int32_t WatchdogFeed(DevHandle handle) | 清除看门狗定时器(喂狗) | > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
@@ -67,9 +74,9 @@ Watchdog模块提供的主要接口如表1所示。 使用看门狗的一般流程如下图所示。 -**图2** 看门狗使用流程图 +**图 2** 看门狗使用流程图 -![image2](figures/看门狗使用流程图.png "看门狗使用流程图") +![看门狗使用流程图](figures/看门狗使用流程图.png) #### 打开看门狗设备 @@ -79,12 +86,12 @@ Watchdog模块提供的主要接口如表1所示。 DevHandle WatchdogOpen(int16_t wdtId, DevHandle *handle); ``` -**表2** WatchdogOpen参数和返回值描述 +**表 2** WatchdogOpen参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| wdtId | 看门狗设备号 | -| handle | 看门狗设备句柄 | +| wdtId | int16_t类型,看门狗设备号 | +| handle | DevHandle类型,看门狗设备句柄 | | **返回值** | **返回值描述** | | HDF_SUCCESS | 打开看门狗设备成功 | | 负数 | 打开看门狗设备失败 | @@ -107,12 +114,12 @@ if (ret != HDF_SUCCESS) { int32_t WatchdogGetStatus(DevHandle handle, int32_t *status); ``` -**表3** WatchdogGetStatus参数和返回值描述 +**表 3** WatchdogGetStatus参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| handle | 看门狗设备句柄 | -| status | 获取到的看门狗状态 | +| handle | DevHandle类型,看门狗设备句柄 | +| status | int32_t类型指针,获取到的看门狗状态 | | **返回值** | **返回值描述** | | HDF_SUCCESS | 获取看门狗状态成功 | | 负数 | 获取看门狗状态失败 | @@ -130,17 +137,16 @@ if (ret != HDF_SUCCESS) { #### 设置超时时间 - ```c int32_t WatchdogSetTimeout(DevHandle *handle, uint32_t seconds); ``` -**表4** WatchdogSetTimeout参数和返回值描述 +**表 4** WatchdogSetTimeout参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| handle | 看门狗设备句柄 | -| seconds | 超时时间,单位为秒 | +| handle | DevHandle类型,看门狗设备句柄 | +| seconds | uint32_t类型,超时时间,单位为秒 | | **返回值** | **返回值描述** | | HDF_SUCCESS | 设置成功 | | 负数 | 设置失败 | @@ -161,12 +167,12 @@ if (ret != HDF_SUCCESS) { int32_t WatchdogGetTimeout(DevHandle *handle, uint32_t *seconds); ``` -**表5** WatchdogGetTimeout参数和返回值描述 +**表 5** WatchdogGetTimeout参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| handle | 看门狗设备句柄 | -| seconds | 获取的看门狗超时时间 | +| handle | DevHandle类型,看门狗设备句柄 | +| seconds | uint32_t类型指针,获取的看门狗超时时间 | | **返回值** | **返回值描述** | | HDF_SUCCESS | 获取看门狗超时时间成功 | | 负数 | 获取看门狗超时时间失败 | @@ -188,11 +194,11 @@ int32_t WatchdogGetTimeout(DevHandle *handle, uint32_t *seconds); int32_t WatchdogStart(DevHandle handle); ``` -**表6** WatchdogStart参数和返回值描述 +**表 6** WatchdogStart参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| handle | 看门狗设备句柄 | +| handle | DevHandle类型,看门狗设备句柄 | | **返回值** | **返回值描述** | | HDF_SUCCESS | 启动看门狗成功 | | 负数 | 启动看门狗失败 | @@ -213,11 +219,11 @@ if (ret != HDF_SUCCESS) { int32_t WatchdogFeed(DevHandle handle); ``` -**表7** WatchdogFeed参数和返回值描述 +**表 7** WatchdogFeed参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| handle | 看门狗设备句柄 | +| handle | DevHandle类型,看门狗设备句柄 | | **返回值** | **返回值描述** | | HDF_SUCCESS | 喂狗成功 | | 负数 | 喂狗失败 | @@ -238,11 +244,11 @@ if (ret != HDF_SUCCESS) { int32_t WatchdogStop(DevHandle handle); ``` -**表8** WatchdogStop参数和返回值描述 +**表 8** WatchdogStop参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| handle | 看门狗设备句柄 | +| handle | DevHandle类型,看门狗设备句柄 | | **返回值** | **返回值描述** | | HDF_SUCCESS | 停止看门狗成功 | | 负数 | 停止看门狗失败 | @@ -265,11 +271,11 @@ if (ret != HDF_SUCCESS) { void WatchdogClose(DevHandle handle); ``` -**表9** WatchdogClose参数和返回值描述 +**表 9** WatchdogClose参数和返回值描述 | **参数** | **参数描述** | | -------- | -------- | -| handle | 看门狗设备句柄 | +| handle | DevHandle类型,看门狗设备句柄 | ```c WatchdogClose(handle); // 关闭看门狗 @@ -280,17 +286,23 @@ WatchdogClose(handle); // 关闭看门狗 下面将基于Hi3516DV300开发板展示使用Watchdog完整操作,步骤主要如下: 1. 传入看门狗ID号,及空的描述句柄,打开看门狗设备并获得看门狗设备句柄。 + 2. 通过看门狗设备句柄及超时时间,设置看门狗设备超时时间。 + 3. 通过看门狗设备句柄及待获取超时时间,获取看门狗设备超时时间。 + 4. 通过看门狗设备句柄启动看门狗设备。 + 5. 通过看门狗设备句柄喂狗。 + 6. 通过看门狗设备句柄停止看门狗设备。 + 7. 通过看门狗设备句柄关闭看门狗设备。 ```c -#include "watchdog_if.h" /* watchdog标准接口头文件 */ -#include "hdf_log.h" /* 标准日志打印头文件 */ -#include "osal_time.h" /* 标准延迟&睡眠接口头文件 */ +#include "watchdog_if.h" // watchdog标准接口头文件 +#include "hdf_log.h" // 标准日志打印头文件 +#include "osal_time.h" // 标准延迟&睡眠接口头文件 #define WATCHDOG_TEST_TIMEOUT 2 #define WATCHDOG_TEST_FEED_TIME 6 @@ -304,91 +316,92 @@ static int32_t TestCaseWatchdog(void) uint32_t timeout; DevHandle *handle = NULL; - /* 打开0号看门狗设备 */ + // 打开0号看门狗设备 ret = WatchdogOpen(wdtId, handle); if (ret != HDF_SUCCESS) { - HDF_LOGE("WatchdogOpen: open watchdog_%hd failed, ret:%d\n", wdtId, ret); + HDF_LOGE("TestCaseWatchdog: open watchdog_%hd failed, ret:%d\n", wdtId, ret); return ret; } - /* 设置超时时间 */ + // 设置超时时间 ret = WatchdogSetTimeout(handle, WATCHDOG_TEST_TIMEOUT); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: set timeout fail! ret:%d\n", __func__, ret); + HDF_LOGE("TestCaseWatchdog: set timeout fail! ret:%d\n", ret); WatchdogClose(handle); return ret; } - /* 获取超时时间 */ + // 获取超时时间 ret = WatchdogGetTimeout(handle, &timeout); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: get timeout fail! ret:%d\n", __func__, ret); + HDF_LOGE("TestCaseWatchdog: get timeout fail! ret:%d\n", ret); WatchdogClose(handle); return ret; } - /* 比较设置与获取的超时时间是否一致*/ + // 比较设置与获取的超时时间是否一致 if (timeout != WATCHDOG_TEST_TIMEOUT) { - HDF_LOGE("%s: set:%u, but get:%u", __func__, WATCHDOG_TEST_TIMEOUT, timeout); + HDF_LOGE("TestCaseWatchdog: set:%u, but get:%u", WATCHDOG_TEST_TIMEOUT, timeout); WatchdogClose(handle); return HDF_FAILURE; } - HDF_LOGI("%s: read timeout back:%u\n", __func__, timeout); + HDF_LOGI("TestCaseWatchdog: read timeout back:%u\n", timeout); - /* 启动看门狗,开始计时 */ + // 启动看门狗,开始计时 ret = WatchdogStart(handle); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: start fail! ret:%d\n", __func__, ret); + HDF_LOGE("TestCaseWatchdog: start fail! ret:%d\n", ret); WatchdogClose(handle); return ret; } - /* 获取看门狗状态,是否启动*/ + // 获取看门狗状态,是否启动 status = WATCHDOG_STOP; ret = WatchdogGetStatus(handle, &status); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: get status fail! ret:%d", __func__, ret); + HDF_LOGE("TestCaseWatchdog: get status fail! ret:%d", ret); WatchdogClose(handle); return ret; } if (status != WATCHDOG_START) { - HDF_LOGE("%s: status is:%d after start", __func__, status); + HDF_LOGE("TestCaseWatchdog: status is:%d after start", status); WatchdogClose(handle); return HDF_FAILURE; } - /* 每隔1S喂狗一次 */ + // 每隔1S喂狗一次 for (i = 0; i < WATCHDOG_TEST_FEED_TIME; i++) { - HDF_LOGI("%s: feeding watchdog %d times... \n", __func__, i); + HDF_LOGI("TestCaseWatchdog: feeding watchdog %d times... \n", i); ret = WatchdogFeed(handle); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: feed dog fail! ret:%d\n", __func__, ret); + HDF_LOGE("TestCaseWatchdog: feed dog fail! ret:%d\n", ret); WatchdogClose(handle); return ret; } OsalSleep(1); } - /* 由于喂狗间隔小于超时时间,系统不会发生复位,此日志可以正常打印 */ - HDF_LOGI("%s: no reset ... feeding test OK!!!\n", __func__); + // 由于喂狗间隔小于超时时间,系统不会发生复位,此日志可以正常打印 + HDF_LOGI("TestCaseWatchdog: no reset ... feeding test OK!!!\n"); ret = WatchdogStop(handle); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: stop fail! ret:%d", __func__, ret); + HDF_LOGE("TestCaseWatchdog: stop fail! ret:%d", ret); WatchdogClose(handle); return ret; } - /* 获取看门狗状态,是否停止*/ + // 获取看门狗状态,是否停止 status = WATCHDOG_START; ret = WatchdogGetStatus(handle, &status); if (ret != HDF_SUCCESS) { - HDF_LOGE("%s: get status fail! ret:%d", __func__, ret); + HDF_LOGE("TestCaseWatchdog: get status fail! ret:%d", ret); WatchdogClose(handle); return ret; } if (status != WATCHDOG_STOP) { - HDF_LOGE("%s: status is:%d after stop", __func__, status); + HDF_LOGE("TestCaseWatchdog: status is:%d after stop", status); WatchdogClose(handle); return HDF_FAILURE; } WatchdogClose(handle); + HDF_LOGD("TestCaseWatchdog: function tests end."); return HDF_SUCCESS; } -``` \ No newline at end of file +``` diff --git a/zh-cn/device-dev/driver/driver-platform-watchdog-develop.md b/zh-cn/device-dev/driver/driver-platform-watchdog-develop.md index 95f0816254de45d7855ebd9f7e7fab10eaa80aad..68553fbe7a6f15af7f8638bb98749602cd757c85 100755 --- a/zh-cn/device-dev/driver/driver-platform-watchdog-develop.md +++ b/zh-cn/device-dev/driver/driver-platform-watchdog-develop.md @@ -17,17 +17,20 @@ 独立服务模式下,核心层不会统一发布一个服务供上层使用,因此这种模式下驱动要为每个控制器发布一个服务,具体表现为: - 驱动适配者需要实现HdfDriverEntry的Bind钩子函数以绑定服务。 + - device_info.hcs文件中deviceNode的policy字段为1或2,不能为0。 Watchdog模块各分层作用: - 接口层提供打开看门狗设备、获取看门狗设备状态、启动看门狗设备、设置看门狗设备超时时间、获取看门狗设备超时时间、喂狗、停止看门狗设备超时时间、关闭看门狗设备的接口。 + - 核心层主要提供看门狗控制器的添加、移除以及管理的能力,通过钩子函数与适配层交互。 + - 适配层主要是将钩子函数的功能实例化,实现具体的功能。 **图 1** Watchdog独立服务模式结构图 -![image](figures/独立服务模式结构图.png "Watchdog独立服务模式结构图") +![Watchdog独立服务模式结构图](figures/独立服务模式结构图.png) ## 开发指导 @@ -54,15 +57,15 @@ struct WatchdogMethod { }; ``` -**表1** WatchdogMethod成员的钩子函数功能说明 +**表 1** WatchdogMethod成员的钩子函数功能说明 | 成员函数 | 入参 | 出参 | 返回值 | 功能 | | -------- | -------- | -------- | -------- | -------- | | getStatus | wdt:结构体指针,核心层Watchdog控制器 | status:int32_t类型指针,表示获取的看门狗的状态(打开或关闭) | HDF_STATUS相关状态 | 获取看门狗状态 | -| setTimeout | wdt:结构体指针,核心层Watchdog控制器;seconds:设置的看门狗超时时间 | 无 | HDF_STATUS相关状态 | 设置看门狗超时时间,单位秒,需要保证看门狗实际运行的时间符合该值 | -| getTimeout | wdt:结构体指针,核心层Watchdog控制器 | seconds:uint32_t类型指针,表示获取的超时时间 | HDF_STATUS相关状态 | 获取看门狗超时时间 | -| start | wdt:结构体指针,核心层Watchdog控制器 | 无 | HDF_STATUS相关状态 | 启动看门狗 | -| stop | wdt:结构体指针,核心层Watchdog控制器 | 无 | HDF_STATUS相关状态 | 停止看门狗 | +| setTimeout | wdt:结构体指针,核心层Watchdog控制器
seconds:uint32_t类型,设置的看门狗超时时间 | 无 | HDF_STATUS相关状态 | 设置看门狗超时时间,单位秒,需要保证看门狗实际运行的时间符合该值 | +| getTimeout | wdt:结构体指针,核心层Watchdog控制器 | seconds:uint32_t类型指针,表示获取的超时时间 | HDF_STATUS相关状态 | 获取看门狗超时时间 | +| start | wdt:结构体指针,核心层Watchdog控制器 | 无 | HDF_STATUS相关状态 | 启动看门狗 | +| stop | wdt:结构体指针,核心层Watchdog控制器 | 无 | HDF_STATUS相关状态 | 停止看门狗 | | feed | wdt:结构体指针,核心层Watchdog控制器 | 无 | HDF_STATUS相关状态 | 喂狗 | | getPriv | wdt:结构体指针,核心层Watchdog控制器 | 无 | HDF_STATUS相关状态 | 获取看门狗驱动的私有数据 | | releasePriv | wdt:结构体指针,核心层Watchdog控制器 | 无 | HDF_STATUS相关状态 | 释放看门狗驱动的私有数据 | @@ -71,18 +74,22 @@ struct WatchdogMethod { Watchdog模块适配包含以下四个步骤: -- 实例化驱动入口。 -- 配置属性文件。 -- 实例化Watchdog控制器对象。 -- 驱动调试。 +- 实例化驱动入口 + +- 配置属性文件 + +- 实例化Watchdog控制器对象 + +- 驱动调试 ### 开发实例 下方将基于Hi3516DV300开发板以//device_soc_hisilicon/common/platform/watchdog/watchdog_hi35xx.c驱动为示例,展示需要驱动适配者提供哪些内容来完整实现设备功能。 -1. 实例化驱动入口。 +1. 实例化驱动入口 驱动入口必须为HdfDriverEntry(在 hdf_device_desc.h 中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。 + 一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。 Watchdog驱动入口开发参考: @@ -90,199 +97,212 @@ Watchdog模块适配包含以下四个步骤: ```c struct HdfDriverEntry g_watchdogDriverEntry = { .moduleVersion = 1, - .Bind = Hi35xxWatchdogBind, // 见Bind参考 - .Init = Hi35xxWatchdogInit, // 见Init参考 - .Release = Hi35xxWatchdogRelease, // 见Release参考 + .Bind = Hi35xxWatchdogBind, // 挂接Watchdog模块Bind实例化 + .Init = Hi35xxWatchdogInit, // 挂接Watchdog模块Init实例化,本例是一个空实现,驱动适配者可根据自身需要添加相关操作 + .Release = Hi35xxWatchdogRelease, // 挂接Watchdog模块Release实例化 .moduleName = "HDF_PLATFORM_WATCHDOG", // 【必要且与HCS文件中里面的moduleName匹配】 }; HDF_INIT(g_watchdogDriverEntry); // 调用HDF_INIT将驱动入口注册到HDF框架中 ``` -2. 配置属性文件。 - - 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode描述。deviceNode信息与驱动入口注册相关。本例以一个Watchdog控制器为例,如有多个器件信息,则需要在device_info文件增加对应的deviceNode描述。器件属性值与核心层WatchdogCntlr成员的默认值或限制范围有密切关系,比如Watchdog设备号,需要在watchdog_config.hcs文件中增加对应的器件属性。 - - - device_info.hcs 配置参考: - - 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。 - - ```c - root { - device_info { - match_attr = "hdf_manager"; - device_watchdog :: device { // 设备节点 - device0 :: deviceNode { // 驱动的DeviceNode节点 - policy = 2; // policy字段是驱动服务发布的策略,如果需要面向用户态,则为2 - priority = 20; // 驱动启动优先级 - permission = 0644; // 驱动创建设备节点权限 - moduleName = "HDF_PLATFORM_WATCHDOG"; // 【必要】用于指定驱动名称,该字段的值必须和驱动入口结构的moduleName值一致 - serviceName = "HDF_PLATFORM_WATCHDOG_0"; // 【必要】驱动对外发布服务的名称,必须唯一。 - deviceMatchAttr = "hisilicon_hi35xx_watchdog_0"; // 【必要】用于配置控制器私有数据,必须和驱动私有数据配置表watchdog_config.hcs中的match_attr值保持一致。 - } - } - } - } - ``` - - - watchdog_config.hcs 配置参考: - - 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/watchdog/watchdog_config.hcs文件配置器件属性,其中配置参数如下: - - ```c - root { - platform { - template watchdog_controller { // 【必要】配置模板,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值 - id = 0; // watchdog ID号 - match_attr = ""; - regBase = 0x12050000; // 【必要】地址映射需要,物理基地址 - regStep = 0x1000; // 【必要】地址映射需要,寄存器偏移步进 - } - controller_0x12050000 :: watchdog_controller { // 【必要】是作为设备驱动私有数据匹配的关键字 - match_attr = "hisilicon_hi35xx_watchdog_0"; // 【必要】必须和device_info.hcs中的deviceMatchAttr值一致 - } - // 如果存在多个watchdog设备时【必须】添加节点,否则不用 - ... - } - } - ``` - - 需要注意的是,新增watchdog_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。 - - ```c - #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/watchdog/watchdog_config.hcs" // 配置文件相对路径 - ``` - -3. 实例化Watchdog控制器对象。 - - 完成驱动入口注册之后,下一步就是以核心层WatchdogCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化WatchdogCntlr成员WatchdogMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。 - - - 驱动适配者自定义结构体参考。 - - 从驱动的角度看,驱动适配者自定义结构体是参数和数据的载体,而且watchdog_config.hcs文件中的数值会被HDF读入通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层WatchdogCntlr对象,例如watchdog设备ID号。 - - ```c - struct Hi35xxWatchdog { - struct WatchdogCntlr wdt; // 【必要】是核心层控制对象,具体描述见下面 - OsalSpinlock lock; // 【必要】驱动适配者需要基于此锁变量对watchdog设备实现对应的加锁解锁 - volatile unsigned char *regBase; // 【必要】地址映射需要,寄存器基地址 - uint32_t phyBase; // 【必要】地址映射需要,物理基址 - uint32_t regStep; // 【必要】地址映射需要,寄存器偏移步进 - }; - - struct WatchdogCntlr { // WatchdogCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。 - struct IDeviceIoService service; // 驱动服务 - struct HdfDeviceObject *device; // 驱动设备对象 - OsalSpinlock lock; // 自旋锁 - struct WatchdogMethod *ops; // 钩子函数 - int16_t wdtId; // watchdog设备ID号 - void *priv; // 私有数据 - }; - ``` - - - WatchdogCntlr成员钩子函数结构体WatchdogMethod的实例化,其他成员在Init和Bind函数中初始化。 - - ```c - static struct WatchdogMethod g_method = { // 钩子函数实例化 - .getStatus = Hi35xxWatchdogGetStatus, // 获取看门狗状态 - .start = Hi35xxWatchdogStart, // 启动看门狗 - .stop = Hi35xxWatchdogStop, // 停止看门狗 - .setTimeout = Hi35xxWatchdogSetTimeout, // 设置看门狗超时时间 - .getTimeout = Hi35xxWatchdogGetTimeout, // 获取看门狗超时时间 - .feed = Hi35xxWatchdogFeed, // 喂狗 - }; - ``` - - - Init函数和Bind函数开发参考: - - 入参: - - HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 - - 返回值: - - HDF_STATUS相关状态 (下表为部分展示,如需使用其他状态,可见//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS的定义)。 - - **表2** Init函数和Bind函数返回值和描述 - - | 状态(值) | 问题描述 | - | -------- | -------- | - | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | - | HDF_ERR_MALLOC_FAIL | 内存分配失败 | - | HDF_ERR_IO | I/O 错误 | - | HDF_SUCCESS | 初始化成功 | - | HDF_FAILURE | 初始化失败 | - - 函数说明: - - 初始化自定义结构体对象,初始化WatchdogCntlr成员,调用核心层WatchdogCntlrAdd函数,完成看门狗控制器的添加。 - - ```c - // 一般而言,Init函数需要根据入参(HdfDeviceObject对象)的属性值初始化Hi35xxWatchdog结构体的成员, - // 但watchdog_hi35xx.c示例中是在bind函数中实现的 - static int32_t Hi35xxWatchdogInit(struct HdfDeviceObject *device) - { - (void)device; - return HDF_SUCCESS; - } - - static int32_t Hi35xxWatchdogBind(struct HdfDeviceObject *device) - { - int32_t ret; - struct Hi35xxWatchdog *hwdt = NULL; - ... - hwdt = (struct Hi35xxWatchdog *)OsalMemCalloc(sizeof(*hwdt)); //Hi35xxWatchdog 结构体指针的内存申请 - ... - hwdt->regBase = OsalIoRemap(hwdt->phyBase, hwdt->regStep); //地址映射 - ... - hwdt->wdt.priv = (void *)device->property; // 【必要】此处是将设备属性的内容赋值给priv成员,但后续没有调用 priv 成员, - // 如果需要用到priv成员,需要额外实例化WatchdogMethod的getPriv和releasePriv成员函数 - hwdt->wdt.ops = &g_method; // 【必要】WatchdogMethod实例化对象的挂载 - hwdt->wdt.device = device; // 【必要】这是为了方便HdfDeviceObject与WatchdogcCntlr相互转化 - ret = WatchdogCntlrAdd(&hwdt->wdt); // 【必要】调用此函数初始化核心层结构体,返回成功信号后驱动才完全接入平台核心层 - if (ret != HDF_SUCCESS) { // 不成功的话,需要去除映射并释放Init函数申请的资源 - OsalIoUnmap((void *)hwdt->regBase); - OsalMemFree(hwdt); - return ret; - } - return HDF_SUCCESS; - } - ``` - - - Release函数开发参考: - - 入参: - - HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 - - 返回值: - - 无。 - - 函数说明: - - 该函数需要在驱动入口结构体中赋值给Release,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。该函数中需包含释放内存和删除控制器等操作。 - - ```c - static void Hi35xxWatchdogRelease(struct HdfDeviceObject *device) - { - struct WatchdogCntlr *wdt = NULL; - struct Hi35xxWatchdog *hwdt = NULL; - ... - wdt = WatchdogCntlrFromDevice(device); // 【必要】通过device获取WatchdogCntlr - ... - if (wdt == NULL) { - return; - } - WatchdogCntlrRemove(wdt); // 【必要】调用WatchdogCntlrRemove函数来释放WatchdogCntlr对象的内容 - hwdt = (struct Hi35xxWatchdog *)wdt; // 这里将WatchdogCntlr转化为Hi35xxWatchdog - if (hwdt->regBase != NULL) { // 【必要】解除地址映射 - OsalIoUnmap((void *)hwdt->regBase); - hwdt->regBase = NULL; - } - OsalMemFree(hwdt); // 【必要】释放驱动适配者自定义对象占用的内存 - } - ``` - -4. 驱动调试。 - - 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。 \ No newline at end of file +2. 配置属性文件 + + 完成驱动入口注册之后,需要在device_info.hcs文件中添加deviceNode描述。deviceNode信息与驱动入口注册相关。本例以一个Watchdog控制器为例,如有多个器件信息,则需要在device_info文件增加对应的deviceNode描述,以及在watchdog_config.hcs文件中增加对应的器件属性。器件属性值与核心层WatchdogCntlr成员的默认值或限制范围有密切关系,比如Watchdog设备号,需要在watchdog_config.hcs文件中增加对应的器件属性。 + + 独立服务模式的特点是device_info.hcs文件中设备节点代表着一个设备对象,如果存在多个设备对象,则按需添加,注意服务名与驱动私有数据匹配的关键字名称必须唯一。其中各项参数如表2所示: + + **表 2** device_info.hcs节点参数说明 + + | 成员名 | 值 | + | -------- | -------- | + | policy | 驱动服务发布的策略,Watchdog控制器具体配置为2,表示驱动对内核态和用户态都发布服务 | + | priority | 驱动启动优先级(0-200),值越大优先级越低。Watchdog控制器具体配置为20 | + | permission | 驱动创建设备节点权限,Watchdog控制器具体配置为0664 | + | moduleName | 驱动名称,Watchdog控制器固定为HDF_PLATFORM_WATCHDOG | + | serviceName | 驱动对外发布服务的名称,Watchdog控制器服务名设置为HDF_PLATFORM_WATCHDOG_X,X代表Watchdog控制器编号| + | deviceMatchAttr | 驱动私有数据匹配的关键字,Watchdog控制器设置为hisilicon_hi35xx_watchdog_X,X代表Watchdog控制器编号 | + + - device_info.hcs 配置参考: + + 在//vendor/hisilicon/hispark_taurus/hdf_config/device_info/device_info.hcs文件中添加deviceNode描述。 + + ```c + root { + device_info { + match_attr = "hdf_manager"; + device_watchdog :: device { + device0 :: deviceNode { // 驱动的DeviceNode节点 + policy = 2; // policy字段是驱动服务发布的策略,如果需要面向用户态,则为2 + priority = 20; // 驱动启动优先级 + permission = 0644; // 驱动创建设备节点权限 + moduleName = "HDF_PLATFORM_WATCHDOG"; // 【必要】用于指定驱动名称,该字段的值必须和驱动入口结构的moduleName值一致 + serviceName = "HDF_PLATFORM_WATCHDOG_0"; // 【必要】驱动对外发布服务的名称,必须唯一。 + deviceMatchAttr = "hisilicon_hi35xx_watchdog_0"; // 【必要】用于配置控制器私有数据,必须和驱动私有数据配置表watchdog_config.hcs中的match_attr值保持一致。 + } + ...... // 如果存在多个watchdog设备时【必须】添加节点,否则不用 + } + } + } + ``` + + - watchdog_config.hcs 配置参考: + + 在//device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/watchdog/watchdog_config.hcs文件配置器件属性,其中配置参数如下: + + ```c + root { + platform { + template watchdog_controller { // 【必要】配置模板,如果下面节点使用时继承该模板,则节点中未声明的字段会使用该模板中的默认值 + id = 0; // watchdog ID号 + match_attr = ""; + regBase = 0x12050000; // 【必要】地址映射需要,物理基地址 + regStep = 0x1000; // 【必要】地址映射需要,寄存器偏移步进 + } + controller_0x12050000 :: watchdog_controller { // 【必要】是作为设备驱动私有数据匹配的关键字 + match_attr = "hisilicon_hi35xx_watchdog_0"; // 【必要】必须和device_info.hcs中的deviceMatchAttr值一致 + } + ...... // 如果存在多个watchdog设备时【必须】添加节点,否则不用 + } + } + ``` + + 需要注意的是,新增watchdog_config.hcs配置文件后,必须在产品对应的hdf.hcs文件中将其包含如下语句所示,否则配置文件无法生效。 + + ```c + #include "../../../../device/soc/hisilicon/hi3516dv300/sdk_liteos/hdf_config/watchdog/watchdog_config.hcs" // 配置文件相对路径 + ``` + +3. 实例化Watchdog控制器对象 + + 完成驱动入口注册之后,下一步就是以核心层WatchdogCntlr对象的初始化为核心,包括驱动适配者自定义结构体(传递参数和数据),实例化WatchdogCntlr成员WatchdogMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)。 + + - 驱动适配者自定义结构体参考。 + + 从驱动的角度看,驱动适配者自定义结构体是参数和数据的载体,而且watchdog_config.hcs文件中的数值会被HDF读入通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层WatchdogCntlr对象,例如watchdog设备ID号。 + + ```c + struct Hi35xxWatchdog { + struct WatchdogCntlr wdt; // 【必要】是核心层控制对象,具体描述见下面 + OsalSpinlock lock; // 【必要】驱动适配者需要基于此锁变量对watchdog设备实现对应的加锁解锁 + volatile unsigned char *regBase; // 【必要】地址映射需要,寄存器基地址 + uint32_t phyBase; // 【必要】地址映射需要,物理基址 + uint32_t regStep; // 【必要】地址映射需要,寄存器偏移步进 + }; + + struct WatchdogCntlr { // WatchdogCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值。 + struct IDeviceIoService service; // 驱动服务 + struct HdfDeviceObject *device; // 驱动设备对象 + OsalSpinlock lock; // 自旋锁 + struct WatchdogMethod *ops; // 钩子函数 + int16_t wdtId; // watchdog设备ID号 + void *priv; // 私有数据 + }; + ``` + + - WatchdogCntlr成员钩子函数结构体WatchdogMethod的实例化。 + + ```c + static struct WatchdogMethod g_method = { // 钩子函数实例化 + .getStatus = Hi35xxWatchdogGetStatus, // 获取看门狗状态 + .start = Hi35xxWatchdogStart, // 启动看门狗 + .stop = Hi35xxWatchdogStop, // 停止看门狗 + .setTimeout = Hi35xxWatchdogSetTimeout, // 设置看门狗超时时间 + .getTimeout = Hi35xxWatchdogGetTimeout, // 获取看门狗超时时间 + .feed = Hi35xxWatchdogFeed, // 喂狗 + }; + ``` + + - Init函数和Bind函数开发参考: + + 入参: + + HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 + + 返回值: + + HDF_STATUS相关状态 (表3为部分展示,如需使用其他状态,可参考//drivers/hdf_core/framework/include/utils/hdf_base.h中HDF_STATUS的定义)。 + + **表 3** HDF_STATUS相关状态说明 + + | 状态(值) | 问题描述 | + | -------- | -------- | + | HDF_ERR_INVALID_OBJECT | 控制器对象非法 | + | HDF_ERR_MALLOC_FAIL | 内存分配失败 | + | HDF_ERR_IO | I/O 错误 | + | HDF_SUCCESS | 初始化成功 | + | HDF_FAILURE | 初始化失败 | + + 函数说明: + + 初始化自定义结构体对象,初始化WatchdogCntlr成员,调用核心层WatchdogCntlrAdd函数,完成看门狗控制器的添加。 + + ```c + // 一般而言,Init函数需要根据入参(HdfDeviceObject对象)的属性值初始化Hi35xxWatchdog结构体的成员, + // 但watchdog_hi35xx.c示例中是在bind函数中实现的 + static int32_t Hi35xxWatchdogInit(struct HdfDeviceObject *device) + { + (void)device; + return HDF_SUCCESS; + } + + static int32_t Hi35xxWatchdogBind(struct HdfDeviceObject *device) + { + int32_t ret; + struct Hi35xxWatchdog *hwdt = NULL; + ...... + hwdt = (struct Hi35xxWatchdog *)OsalMemCalloc(sizeof(*hwdt)); // Hi35xxWatchdog 结构体指针的内存申请 + ...... + hwdt->regBase = OsalIoRemap(hwdt->phyBase, hwdt->regStep); // 地址映射 + ...... + hwdt->wdt.priv = (void *)device->property; // 【必要】此处是将设备属性的内容赋值给priv成员,但后续没有调用 priv 成员, + // 如果需要用到priv成员,需要额外实例化WatchdogMethod的getPriv和releasePriv成员函数 + hwdt->wdt.ops = &g_method; // 【必要】WatchdogMethod实例化对象的挂载 + hwdt->wdt.device = device; // 【必要】这是为了方便HdfDeviceObject与WatchdogcCntlr相互转化 + ret = WatchdogCntlrAdd(&hwdt->wdt); // 【必要】调用此函数初始化核心层结构体,返回成功信号后驱动才完全接入平台核心层 + if (ret != HDF_SUCCESS) { // 不成功的话,需要去除映射并释放Init函数申请的资源 + OsalIoUnmap((void *)hwdt->regBase); + OsalMemFree(hwdt); + return ret; + } + return HDF_SUCCESS; + } + ``` + + - Release函数开发参考: + + 入参: + + HdfDeviceObject:HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口。 + + 返回值: + + 无。 + + 函数说明: + + 该函数需要在驱动入口结构体中赋值给Release,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。该函数中需包含释放内存和删除控制器等操作。 + + ```c + static void Hi35xxWatchdogRelease(struct HdfDeviceObject *device) + { + struct WatchdogCntlr *wdt = NULL; + struct Hi35xxWatchdog *hwdt = NULL; + ...... + wdt = WatchdogCntlrFromDevice(device); // 【必要】通过device获取WatchdogCntlr + ...... + if (wdt == NULL) { + return; + } + WatchdogCntlrRemove(wdt); // 【必要】调用WatchdogCntlrRemove函数来释放WatchdogCntlr对象的内容 + hwdt = (struct Hi35xxWatchdog *)wdt; // 这里将WatchdogCntlr转化为Hi35xxWatchdog + if (hwdt->regBase != NULL) { // 【必要】解除地址映射 + OsalIoUnmap((void *)hwdt->regBase); + hwdt->regBase = NULL; + } + OsalMemFree(hwdt); // 【必要】释放驱动适配者自定义对象占用的内存 + } + ``` + +4. 驱动调试 + + 【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,获取看门狗状态、喂狗等。