Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenHarmony
Docs
提交
bc3eb53a
D
Docs
项目概览
OpenHarmony
/
Docs
1 年多 前同步成功
通知
159
Star
292
Fork
28
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
Docs
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
bc3eb53a
编写于
8月 23, 2021
作者:
T
tianyangpeng
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
by typ
Signed-off-by:
N
tianyangpeng
<
tianyangpeng@huawei.com
>
上级
cb021254
变更
18
隐藏空白更改
内联
并排
Showing
18 changed file
with
3431 addition
and
4 deletion
+3431
-4
zh-cn/device-dev/driver/Readme-CN.md
zh-cn/device-dev/driver/Readme-CN.md
+14
-2
zh-cn/device-dev/driver/driver-peripherals.md
zh-cn/device-dev/driver/driver-peripherals.md
+1
-1
zh-cn/device-dev/driver/driver-platform-adc-develop.md
zh-cn/device-dev/driver/driver-platform-adc-develop.md
+317
-0
zh-cn/device-dev/driver/driver-platform-develop.md
zh-cn/device-dev/driver/driver-platform-develop.md
+12
-0
zh-cn/device-dev/driver/driver-platform-gpio-develop.md
zh-cn/device-dev/driver/driver-platform-gpio-develop.md
+285
-0
zh-cn/device-dev/driver/driver-platform-i2c-develop.md
zh-cn/device-dev/driver/driver-platform-i2c-develop.md
+309
-0
zh-cn/device-dev/driver/driver-platform-mipidsi-develop.md
zh-cn/device-dev/driver/driver-platform-mipidsi-develop.md
+235
-0
zh-cn/device-dev/driver/driver-platform-mmc-develop.md
zh-cn/device-dev/driver/driver-platform-mmc-develop.md
+379
-0
zh-cn/device-dev/driver/driver-platform-pwm-develop.md
zh-cn/device-dev/driver/driver-platform-pwm-develop.md
+275
-0
zh-cn/device-dev/driver/driver-platform-rtc-develop.md
zh-cn/device-dev/driver/driver-platform-rtc-develop.md
+294
-0
zh-cn/device-dev/driver/driver-platform-sdio-develop.md
zh-cn/device-dev/driver/driver-platform-sdio-develop.md
+309
-0
zh-cn/device-dev/driver/driver-platform-spi-develop.md
zh-cn/device-dev/driver/driver-platform-spi-develop.md
+355
-0
zh-cn/device-dev/driver/driver-platform-uart-develop.md
zh-cn/device-dev/driver/driver-platform-uart-develop.md
+391
-0
zh-cn/device-dev/driver/driver-platform-watchdog-develop.md
zh-cn/device-dev/driver/driver-platform-watchdog-develop.md
+254
-0
zh-cn/device-dev/driver/driver-platform.md
zh-cn/device-dev/driver/driver-platform.md
+1
-1
zh-cn/device-dev/driver/figure/无服务模式.png
zh-cn/device-dev/driver/figure/无服务模式.png
+0
-0
zh-cn/device-dev/driver/figure/独立服务模式.png
zh-cn/device-dev/driver/figure/独立服务模式.png
+0
-0
zh-cn/device-dev/driver/figure/统一服务模式.png
zh-cn/device-dev/driver/figure/统一服务模式.png
+0
-0
未找到文件。
zh-cn/device-dev/driver/Readme-CN.md
浏览文件 @
bc3eb53a
...
...
@@ -7,7 +7,19 @@
-
[
驱动消息机制管理
](
driver-hdf-news.md
)
-
[
配置管理
](
driver-hdf-manage.md
)
-
[
HDF开发实例
](
driver-hdf-sample.md
)
-
[
平台驱动
](
driver-platform.md
)
-
[
平台驱动开发
](
driver-platform-develop.md
)
-
[
GPIO
](
driver-platform-gpio-develop.md
)
-
[
I2C
](
driver-platform-i2c-develop.md
)
-
[
RTC
](
driver-platform-rtc-develop.md
)
-
[
SDIO
](
driver-platform-sdio-develop.md
)
-
[
SPI
](
driver-platform-spi-develop.md
)
-
[
UART
](
driver-platform-uart-develop.md
)
-
[
WATCHDOG
](
driver-platform-watchdog-develop.md
)
-
[
MIPI_DSI
](
driver-platform-mipidsi-develop.md
)
-
[
MMC
](
driver-platform-mmc-develop.md
)
-
[
PWM
](
driver-platform-pwm-develop.md
)
-
[
ADC
](
driver-platform-adc-develop.md
)
-
[
平台驱动使用
](
driver-platform.md
)
-
[
GPIO
](
driver-platform-gpio-des.md
)
-
[
I2C
](
driver-platform-i2c-des.md
)
-
[
RTC
](
driver-platform-rtc-des.md
)
...
...
@@ -16,7 +28,7 @@
-
[
UART
](
driver-platform-uart-des.md
)
-
[
WATCHDOG
](
driver-platform-watchdog-des.md
)
-
[
MIPI DSI
](
driver-platform-mipidsi-des.md
)
-
[
外设
](
driver-peripherals.md
)
-
[
外设
驱动开发
](
driver-peripherals.md
)
-
[
LCD
](
driver-peripherals-lcd-des.md
)
-
[
TOUCHSCREEN
](
driver-peripherals-touch-des.md
)
-
[
SENSOR
](
driver-peripherals-sensor-des.md
)
...
...
zh-cn/device-dev/driver/driver-peripherals.md
浏览文件 @
bc3eb53a
# 外设<a name="ZH-CN_TOPIC_0000001157319411"></a>
# 外设
驱动开发
<a name="ZH-CN_TOPIC_0000001157319411"></a>
-
**[LCD](driver-peripherals-lcd-des.md)**
...
...
zh-cn/device-dev/driver/driver-platform-adc-develop.md
0 → 100755
浏览文件 @
bc3eb53a
# ADC
-
[
概述
](
#1
)
-
[
开发步骤
](
#2
)
-
[
开发实例
](
#3
)
## 概述 <a name="1"></a>
ADC(Analog to Digital Converter),即模拟-数字转换器,是一种将模拟信号转换成对应数字信号的设备,在HDF框架中,ADC模块接口适配模式采用统一服务模式,这需要一个设备服务来作为ADC模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如ADC可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。
![
image1
](
figure/统一服务模式.png
)
## 开发步骤 <a name="2"></a>
ADC模块适配的三个环节是配置属性文件,实例化驱动入口,以及实例化核心层接口函数。
1.
**实例化驱动入口:**
-
实例化HdfDriverEntry结构体成员。
-
调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
2.
**配置属性文件:**
-
在device_info.hcs文件中添加deviceNode描述。
-
【可选】添加adc_config.hcs器件属性文件。
3.
**实例化ADC控制器对象:**
-
初始化AdcDevice成员。
-
实例化AdcDevice成员AdcMethod,其定义和成员说明见下
4.
**驱动调试:**
-
【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,信号采集的成功与否等。
> ![](../public_sys-resources/icon-note.gif) **说明:**
> AdcMethod定义
>
> ```c
> struct AdcMethod {
> int32_t (*read)(struct AdcDevice *device, uint32_t channel, uint32_t *val);
> int32_t (*start)(struct AdcDevice *device);
> int32_t (*stop)(struct AdcDevice *device);
> };
> ```
>
> 表1 AdcMethod结构体成员的回调函数功能说明
>
> |函数成员|入参|出参|返回值|功能|
> |-|-|-|-|-|
> |read|**device**: 结构体指针,核心层ADC控制器;<br />**channel**:uint32_t,传入的通道号;|**val**:uint32_t指针,要传出的信号数据;|HDF_STATUS相关状态|读取ADC采样的信号数据|
> |stop |**device**: 结构体指针,核心层ADC控制器;|无|HDF_STATUS相关状态|关闭ADC设备|
> |start |**device**: 结构体指针,核心层ADC控制器;|无|HDF_STATUS相关状态|开启ADC设备|
## 开发实例 <a name="3"></a>
接下来以 adc_hi35xx.c 为示例, 展示需要厂商提供哪些内容来完整实现设备功能
1.
驱动开发首先需要实例化驱动入口,驱动入口必须为HdfDriverEntry(在 hdf_device_desc.h 中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
-
ADC驱动入口参考
> ADC模块这种类型的控制器会出现很多个设备挂接的情况,因而在HDF框架中首先会为这类型的设备创建一个管理器对象,并同时对外发布一个管理器服务来统一处理外部访问。这样,用户需要打开某个设备时,会先获取到管理器服务,然后管理器服务根据用户指定参数查找到指定设备。
>
> ADC管理器服务的驱动由核心层实现,**厂商不需要关注这部分内容的实现,这个但在实现Init函数的时候需要调用核心层的AdcDeviceAdd函数,它会实现相应功能。**
```
c
static
struct
HdfDriverEntry
g_hi35xxAdcDriverEntry
=
{
.
moduleVersion
=
1
,
.
Init
=
Hi35xxAdcInit
,
.
Release
=
Hi35xxAdcRelease
,
.
moduleName
=
"hi35xx_adc_driver"
,
//【必要且与 HCS 里面的名字匹配】
};
HDF_INIT
(
g_hi35xxAdcDriverEntry
);
//调用HDF_INIT将驱动入口注册到HDF框架中
//核心层adc_core.c管理器服务的驱动入口
struct
HdfDriverEntry
g_adcManagerEntry
=
{
.
moduleVersion
=
1
,
.
Init
=
AdcManagerInit
,
.
Release
=
AdcManagerRelease
,
.
moduleName
=
"HDF_PLATFORM_ADC_MANAGER"
,
//这与device_info文件中device0对应
};
HDF_INIT
(
g_adcManagerEntry
);
```
2.
完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,并在adc_config.hcs中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值对于厂商驱动的实现以及核心层AdcDevice相关成员的默认值或限制范围有密切关系。
**统一服务模式**
的特点是device_info文件中第一个设备节点必须为ADC管理器,其各项参数必须如下设置:
|成员名|值|
|-|-|
|moduleName | 固定为 HDF_PLATFORM_ADC_MANAGER|
|serviceName| 无|
|policy| 具体配置为0,不发布服务|
|deviceMatchAttr| 没有使用,可忽略|
**从第二个节点开始配置具体ADC控制器信息**
,此节点并不表示某一路ADC控制器,而是代表一个资源性质设备,用于描述一类ADC控制器的信息。
**本例只有一个ADC设备,如有多个设备,则需要在device_info文件增加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 配置参考
```
c
root
{
platform
{
adc_config_hi35xx
{
match_attr
=
"hisilicon_hi35xx_adc"
;
template
adc_device
{
regBasePhy
=
0x120e0000
;
//寄存器物理基地址
regSize
=
0x34
;
//寄存器位宽
deviceNum
=
0
;
//设备号
validChannel
=
0x1
;
//有效通道
dataWidth
=
10
;
//信号接收的数据位宽
scanMode
=
1
;
//扫描模式
delta
=
0
;
//delta参数
deglitch
=
0
;
glitchSample
=
5000
;
rate
=
20000
;
}
device_0
::
adc_device
{
deviceNum
=
0
;
validChannel
=
0x2
;
}
}
}
}
```
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/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函数
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
;
}
```
-
**Release 函数参考**
> **入参:**
> HdfDeviceObject 是整个驱动对外暴露的接口参数,具备 HCS 配置文件的信息
>
> **返回值:**
> 无
>
> **函数说明:**
> 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给 Release 接口, 当HDF框架调用Init函数初始化驱动失败时,可以调用 Release 释放驱动资源。所有强制转换获取相应对象的操作**前提**是在Init函数中具备对应赋值的操作。
```
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操作
OsalIoUnmap
((
void
*
)
hi35xx
->
regBase
);
OsalMemFree
(
hi35xx
);
}
return
;
}
```
\ No newline at end of file
zh-cn/device-dev/driver/driver-platform-develop.md
0 → 100755
浏览文件 @
bc3eb53a
# 平台驱动开发
-
**[GPIO](driver-platform-gpio-develop.md)**
-
**[I2C](driver-platform-i2c-develop.md)**
-
**[RTC](driver-platform-rtc-develop.md)**
-
**[SDIO](driver-platform-sdio-develop.md)**
-
**[SPI](driver-platform-spi-develop.md)**
-
**[UART](driver-platform-uart-develop.md)**
-
**[WATCHDOG](driver-platform-watchdog-develop.md)**
-
**[MIPI_DSI](driver-platform-mipidsi-develop.md)**
-
**[MMC](driver-platform-mmc-develop.md)**
-
**[PWM](driver-platform-pwm-develop.md)**
-
**[ADC](driver-platform-adc-develop.md)**
zh-cn/device-dev/driver/driver-platform-gpio-develop.md
0 → 100755
浏览文件 @
bc3eb53a
# GPIO
-
[
概述
](
#1
)
-
[
开发步骤
](
#2
)
-
[
开发实例
](
#3
)
## 概述 <a name="1"></a>
GPIO(General-purpose input/output)即通用型输入输出,在HDF框架中,
GPIO的接口适配模式采用无服务模式,用于不需要在用户态提供API的设备类型,或者没有用户态和内核区分的OS系统,其关联方式是DevHandle直接指向设备对象内核态地址(DevHandle是一个void类型指针)。
图 1 无服务模式结构图
![
image1
](
figure/无服务模式.png
)
## 开发步骤 <a name="2"></a>
GPIO模块适配的三个环节是配置属性文件,实例化驱动入口,以及实例化核心层接口函数。GPIO控制器分组管理所有管脚,相关参数会在属性文件中有所体现;驱动入口和接口函数的实例化环节是厂商驱动接入HDF的核心环节。
1.
**实例化驱动入口:**
-
实例化HdfDriverEntry结构体成员。
-
调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
2.
**配置属性文件:**
-
在device_info.hcs文件中添加deviceNode描述。
-
【可选】添加gpio_config.hcs器件属性文件。
3.
**实例化GPIO控制器对象:**
-
初始化GpioCntlr成员。
-
实例化GpioCntlr成员GpioMethod,其定义和成员
**说明**
见下
4.
**驱动调试:**
-
【可选】针对新增驱动程序,建议验证驱动基本功能,例如GPIO控制状态,中断响应情况等。
> ![](../public_sys-resources/icon-note.gif) **说明:**
>
> GpioMethod定义
>
> ```c
> struct GpioMethod {
> int32_t (*request)(struct GpioCntlr *cntlr, uint16_t local);// 【可选】
> int32_t (*release)(struct GpioCntlr *cntlr, uint16_t local);// 【可选】
> int32_t (*write)(struct GpioCntlr *cntlr, uint16_t local, uint16_t val);
> int32_t (*read)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *val);
> 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 (*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结构体成员的回调函数功能说明
>
> |函数成员|入参|出参|返回值|功能|
> |-|-|-|-|-|
> |write |**cntlr**:结构体指针,核心层GPIO控制器;<br />**local**:uint16_t,GPIO端口标识号 ;<br />**val**:uint16_t,电平传入值; |无| HDF_STATUS相关状态 | GPIO引脚写入电平值 |
> |read |**cntlr**:结构体指针,核心层GPIO控制器;<br />**local**:uint16_t,GPIO端口标识; |**val**:uint16_t 指针,<br />用于传出电平值 ;| HDF_STATUS相关状态 | GPIO引脚读取电平值 |
> |setDir |**cntlr**:结构体指针,核心层GPIO控制器;<br />**local**:uint16_t,GPIO端口标识号 ;<br />**dir**:uint16_t,管脚方向传入值; |无| HDF_STATUS相关状态 | 设置GPIO引脚输入/输出方向 |
> |getDir |**cntlr**:结构体指针,核心层GPIO控制器;<br />**local**:uint16_t,GPIO端口标识号 ; |**dir**:uint16_t 指针,<br />用于传出管脚方向值 ;| HDF_STATUS相关状态 | 读GPIO引脚输入/输出方向 |
> |setIrq |**cntlr**:结构体指针,核心层GPIO控制器;<br />**local**:uint16_t,GPIO端口标识号;<br />**mode**:uint16_t,表示触发模式(边沿或电平);<br />**func**:函数指针,中断服务程序 ;<br />**arg**:void指针,中断服务程序入参;|无| HDF_STATUS相关状态 |将GPIO引脚设置为中断模式 |
> |unsetIrq |**cntlr**:结构体指针,核心层GPIO控制器;<br />**local**:uint16_t,GPIO端口标识号 ; |无| HDF_STATUS相关状态 |取消GPIO中断设置 |
> |enableIrq |**cntlr**:结构体指针,核心层GPIO控制器;<br />**local**:uint16_t,GPIO端口标识号; |无| HDF_STATUS相关状态 |使能GPIO管脚中断 |
> |disableIrq|**cntlr**:结构体指针,核心层GPIO控制器;<br />**local**:uint16_t,GPIO端口标识号; |无| HDF_STATUS相关状态 |禁止GPIO管脚中断 |
## 开发实例 <a name="3"></a>
下方将以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将驱动入口注册到HDF框架中
HDF_INIT
(
g_gpioDriverEntry
);
```
2.
完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,并在 gpio_config.hcs 中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值与核心层GpioCntlr 成员的默认值或限制范围有密切关系。
**本例只有一个GPIO控制器,如有多个器件信息,则需要在device_info文件增加deviceNode信息,以及在gpio_config文件中增加对应的器件属性**
。
-
device_info.hcs 配置参考
```
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 配置参考
```
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
;
//【必要】共享中断
}
}
}
}
```
3.
完成驱动入口注册之后,最后一步就是以核心层GpioCntlr对象的初始化为核心,包括厂商自定义结构体(传递参数和数据),实例化GpioCntlr成员GpioMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)
-
自定义结构体参考
> 从驱动的角度看,自定义结构体是参数和数据的载体,而且gpio_config.hcs文件中的数值会被HDF读入通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层GpioCntlr对象,例如索引、管脚数等。
```
c
struct
Pl061GpioCntlr
{
struct
GpioCntlr
cntlr
;
//【必要】 是核心层控制对象,其成员定义见下面
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
Pl061GpioGroup
{
//包括寄存器地址,中断号,中断函数和和锁
volatile
unsigned
char
*
regBase
;
unsigned
int
index
;
unsigned
int
irq
;
OsalIRQHandle
irqFunc
;
OsalSpinlock
lock
;
};
// GpioCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值
struct
GpioCntlr
{
struct
IDeviceIoService
service
;
struct
HdfDeviceObject
*
device
;
struct
GpioMethod
*
ops
;
struct
DListHead
list
;
OsalSpinlock
spin
;
uint16_t
start
;
uint16_t
count
;
struct
GpioInfo
*
ginfos
;
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这个是整个驱动对外暴露的接口参数,具备HCS配置文件的信息
>
> **返回值:**
> HDF_STATUS相关状态(下表为部分展示,如需使用其他状态,可见//drivers/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 |初始化失败|
>
> **函数说明:**
> 初始化自定义结构体对象,初始化GpioCntlr成员,调用核心层GpioCntlrAdd函数,【可选】接入VFS
```
c
static
int32_t
Pl061GpioInit
(
struct
HdfDeviceObject
*
device
)
{
...
struct
Pl061GpioCntlr
*
pl061
=
&
g_pl061
;
//利用静态全局变量完成初始化
//static struct Pl061GpioCntlr g_pl061 = {
// .groups = NULL,
// .groupNum = PL061_GROUP_MAX,
// .bitNum = PL061_BIT_MAX,
//};
ret
=
Pl061GpioReadDrs
(
pl061
,
device
->
property
);
//利用从gpio_config.HCS文件读取的属性值来初始化自定义结构体对象成员
...
pl061
->
regBase
=
OsalIoRemap
(
pl061
->
phyBase
,
pl061
->
groupNum
*
pl061
->
regStep
);
//地址映射
...
ret
=
Pl061GpioInitCntlrMem
(
pl061
);
// 内存分配
...
pl061
->
cntlr
.
count
=
pl061
->
groupNum
*
pl061
->
bitNum
;
//【必要】管脚数量计算
pl061
->
cntlr
.
priv
=
(
void
*
)
device
->
property
;
//【必要】存储设备属性
pl061
->
cntlr
.
ops
=
&
g_method
;
// 【必要】GpioMethod的实例化对象的挂载
pl061
->
cntlr
.
device
=
device
;
// 【必要】使HdfDeviceObject与GpioCntlr可以相互转化的前提
ret
=
GpioCntlrAdd
(
&
pl061
->
cntlr
);
// 【必要】调用此函数填充核心层结构体,返回成功信号后驱动才完全接入平台核心层
...
Pl061GpioDebugCntlr
(
pl061
);
#ifdef PL061_GPIO_USER_SUPPORT //【可选】若支持用户级的虚拟文件系统,则接入
if
(
GpioAddVfs
(
pl061
->
bitNum
)
!=
HDF_SUCCESS
)
{
HDF_LOGE
(
"%s: add vfs fail!"
,
__func__
);
}
#endif
...
}
```
-
**Release 函数参考**
> **入参:**
> HdfDeviceObject 是整个驱动对外暴露的接口参数,具备 HCS 配置文件的信息
>
> **返回值:**
> 无
>
> **函数说明:**
> 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给 Release 接口, 当HDF框架调用Init函数初始化驱动失败时,可以调用 Release 释放驱动资源。所有强制转换获取相应对象的操作**前提**是在Init函数中具备对应赋值的操作。
```
c
static
void
Pl061GpioRelease
(
struct
HdfDeviceObject
*
device
)
{
struct
GpioCntlr
*
cntlr
=
NULL
;
struct
Pl061GpioCntlr
*
pl061
=
NULL
;
...
cntlr
=
GpioCntlrFromDevice
(
device
);
//【必要】通过强制转换获取核心层控制对象
//return (device == NULL) ? NULL : (struct GpioCntlr *)device->service;
...
#ifdef PL061_GPIO_USER_SUPPORT
GpioRemoveVfs
();
//与Init中GpioAddVfs相反
#endif
GpioCntlrRemove
(
cntlr
);
//【必要】取消设备信息、服务等内容在核心层上的挂载
pl061
=
ToPl061GpioCntlr
(
cntlr
);
//return (struct Pl061GpioCntlr *)cntlr;
Pl061GpioRleaseCntlrMem
(
pl061
);
//【必要】锁和内存的释放
OsalIoUnmap
((
void
*
)
pl061
->
regBase
);
//【必要】解除地址映射
pl061
->
regBase
=
NULL
;
}
```
zh-cn/device-dev/driver/driver-platform-i2c-develop.md
0 → 100755
浏览文件 @
bc3eb53a
# I2C
-
[
概述
](
#1
)
-
[
开发步骤
](
#2
)
-
[
开发实例
](
#3
)
## 概述 <a name="1"></a>
I2C(Inter Integrated Circuit)总线是由Philips公司开发的一种简单、双向二线制同步串行总线,在HDF框架中,I2C模块接口适配模式采用统一服务模式,这需要一个设备服务来作为I2C模块的管理器,统一处理外部访问,这会在配置文件中有所体现。统一服务模式适合于同类型设备对象较多的情况,如I2C可能同时具备十几个控制器,采用独立服务模式需要配置更多的设备节点,且服务会占据内存资源。
图 1 统一服务模式结构图
![
image1
](
figure/统一服务模式.png
)
## 开发步骤 <a name="2"></a>
I2C模块适配的三个环节是配置属性文件,实例化驱动入口,以及实例化核心层接口函数。
1.
**实例化驱动入口:**
-
实例化HdfDriverEntry结构体成员。
-
调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
2.
**配置属性文件:**
-
在device_info.hcs文件中添加deviceNode描述。
-
【可选】添加i2c_config.hcs器件属性文件。
3.
**实例化I2C控制器对象:**
-
初始化I2cCntlr成员。
-
实例化I2cCntlr成员I2cMethod和I2cLockMethod,其定义和成员说明见下
4.
**驱动调试:**
-
【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,消息传输的成功与否等。
> ![](../public_sys-resources/icon-note.gif) **说明:**
> I2cMethod和I2cLockMethod定义
>
> ```c
> struct I2cMethod {
> int32_t (*transfer)(struct I2cCntlr *cntlr, struct I2cMsg *msgs, int16_t count);
> };
> struct I2cLockMethod {//锁机制操作结构体
> int32_t (*lock)(struct I2cCntlr *cntlr);//加锁
> void (*unlock)(struct I2cCntlr *cntlr); //解锁
> };
> ```
> 表1 I2cMethod结构体成员的回调函数功能说明
> |函数成员|入参|出参|返回值|功能|
> |-|-|-|-|-|
> |transfer |**cntlr**:结构体指针,核心层I2C控制器;<br />**msgs**:结构体指针,用户消息 ;<br />**count**:uint16_t,消息数量 |无|HDF_STATUS相关状态| 传递用户消息|
## 开发实例 <a name="3"></a>
下方将以i2c_hi35xx.c为示例,展示需要厂商提供哪些内容来完整实现设备功能。
1.
驱动开发首先需要实例化驱动入口,驱动入口必须为HdfDriverEntry(在 hdf_device_desc.h 中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
-
I2C驱动入口参考
> I2C模块这种类型的控制器会出现很多个设备挂接的情况,因而在HDF框架中首先会为这类型的设备创建一个管理器对象,并同时对外发布一个管理器服务来统一处理外部访问。这样,用户需要打开某个设备时,会先获取到管理器服务,然后管理器服务根据用户指定参数查找到指定设备。
>
> I2C管理器服务的驱动由核心层实现,**厂商不需要关注这部分内容的实现,这个但在实现Init函数的时候需要调用核心层的I2cCntlrAdd函数,它会实现相应功能。**
```
c
struct
HdfDriverEntry
g_i2cDriverEntry
=
{
.
moduleVersion
=
1
,
.
Init
=
Hi35xxI2cInit
,
.
Release
=
Hi35xxI2cRelease
,
.
moduleName
=
"hi35xx_i2c_driver"
,
//【必要且与config.hcs文件里面匹配】
};
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文件中device0对应
};
HDF_INIT
(
g_i2cManagerEntry
);
```
2.
完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,并在i2c_config.hcs中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值对于厂商驱动的实现以及核心层I2cCntlr相关成员的默认值或限制范围有密切关系。
**统一服务模式**
的特点是device_info文件中第一个设备节点必须为I2C管理器,其各项参数必须如下设置:
|成员名|值|
|-|-|
|moduleName | 固定为 HDF_PLATFORM_I2C_MANAGER|
|serviceName| 固定为 HDF_PLATFORM_I2C_MANAGER|
|policy| 具体配置为1或2取决于是否对用户态可见|
|deviceMatchAttr| 没有使用,可忽略|
**从第二个节点开始配置具体I2C控制器信息**
,此节点并不表示某一路I2C控制器,而是代表一个资源性质设备,用于描述一类I2C控制器的信息。多个控制器之间相互区分的参数是busID和reg_pbase,这在i2c_config文件中有所体现。
-
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
;
}
...
}
}
}
```
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函数参考**
> **入参:**
> HdfDeviceObject 是整个驱动对外暴露的接口参数,具备 HCS 配置文件的信息
>
> **返回值:**
> HDF_STATUS相关状态 (下表为部分展示,如需使用其他状态,可见//drivers/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 |传输失败|
>
> **函数说明:**
> 初始化自定义结构体对象,初始化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__:
//不成功的话,需要反向执行初始化相关函数
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
);
//函数定义见下
}
}
static
void
Hi35xxI2cRemoveByNode
(
const
struct
DeviceResourceNode
*
node
)
{
...
//【必要】可以调用 I2cCntlrGet 函数通过设备的 busid 获取 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
);
OsalMemFree
(
hi35xx
);
}
return
;
}
```
zh-cn/device-dev/driver/driver-platform-mipidsi-develop.md
0 → 100755
浏览文件 @
bc3eb53a
# MIPI-DSI
-
[
概述
](
#1
)
-
[
开发步骤
](
#2
)
-
[
开发实例
](
#3
)
## 概述 <a name="1"></a>
DSI(Display Serial Interface)是由移动行业处理器接口联盟(Mobile Industry Processor Interface
\(
MIPI
\)
Alliance)制定的规范。在HDF框架中,
MIPI-DSI的接口适配模式采用无服务模式,用于不需要在用户态提供API的设备类型,或者没有用户态和内核区分的OS系统,其关联方式是DevHandle直接指向设备对象内核态地址(DevHandle是一个void类型指针)。
图 1 无服务模式结构图
![
image1
](
figure/无服务模式.png
)
## 开发步骤 <a name="2"></a>
MIPI-DSI模块适配的三个环节是配置属性文件,实例化驱动入口,以及实例化核心层接口函数。
1.
**实例化驱动入口:**
-
实例化HdfDriverEntry结构体成员。
-
调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
2.
**配置属性文件:**
-
在device_info.hcs文件中添加deviceNode描述。
-
【可选】添加mipidsi_config.hcs器件属性文件。
3.
**实例化MIPIDSI控制器对象:**
-
初始化MipiDsiCntlr成员。
-
实例化MipiDsiCntlr成员MipiDsiCntlrMethod,其定义和成员
**说明**
见下
4.
**驱动调试:**
-
【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,数据传输的成功与否等。
> ![](../public_sys-resources/icon-note.gif) **说明:**
>
> MipiDsiCntlrMethod定义
>
> ```c
> struct MipiDsiCntlrMethod { // 核心层结构体的成员函数
> int32_t (*setCntlrCfg)(struct MipiDsiCntlr *cntlr);
> int32_t (*setCmd)(struct MipiDsiCntlr *cntlr, struct DsiCmdDesc *cmd);
> int32_t (*getCmd)(struct MipiDsiCntlr *cntlr, struct DsiCmdDesc *cmd, uint32_t readLen, uint8_t *out);
> void (*toHs)(struct MipiDsiCntlr *cntlr);
> void (*toLp)(struct MipiDsiCntlr *cntlr);
> void (*enterUlps)(struct MipiDsiCntlr *cntlr);//【可选】进入超低功耗模式
> void (*exitUlps)(struct MipiDsiCntlr *cntlr); //【可选】退出超低功耗模式
> int32_t (*powerControl)(struct MipiDsiCntlr *cntlr, uint8_t enable);//【可选】使能/去使能功耗控制
> int32_t (*attach)(struct MipiDsiCntlr *cntlr);//【可选】将一个DSI设备连接上host
> };
> ```
>
> 表1 MipiDsiCntlrMethod成员的回调函数功能说明
> |成员函数|入参|出参|返回状态|功能|
> |-|-|-|-|-|
> |setCntlrCfg | **cntlr**: 结构体指针,MipiDsi控制器 ; | 无 |HDF_STATUS相关状态 | 设置控制器参数 |
> |setCmd | **cntlr**: 结构体指针,MipiDsi控制器 ;<br />**cmd**: 结构体指针,指令传入值|无|HDF_STATUS相关状态| 向显示设备发送指令 |
> |getCmd | **cntlr**: 结构体指针,MipiDsi控制器 ; |**cmd**: 结构体指针,<br />用于传出指令值;|HDF_STATUS相关状态|从显示设备读取信息指令 |
> |toHs | **cntlr**: 结构体指针,MipiDsi控制器 ; | 无 |HDF_STATUS相关状态 |设置为高速模式|
> |toLp | **cntlr**: 结构体指针,MipiDsi控制器 ; | 无 |HDF_STATUS相关状态 |设置为低电模式 |
## 开发实例 <a name="3"></a>
下方将以mipi_tx_hi35xx.c为示例,展示需要厂商提供哪些内容来完整实现设备功能。
1.
一般来说,驱动开发首先需要在 xx_config.hcs 中配置器件属性,并在device_info.hcs文件中添加deviceNode描述。器件属性值与核心层MipiDsiCntlr 成员的默认值或限制范围有密切关系,deviceNode信息与驱动入口注册相关。
**但本例中MIPI控制器无需配置额外属性,如有厂商需要,则需要在device_info文件的deviceNode增加deviceMatchAttr信息,以及增加mipidsi_config文件**
。
-
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框架中
```
3.
完成驱动入口注册之后,最后一步就是以核心层MipiDsiCntlr对象的初始化为核心,包括厂商自定义结构体(传递参数和数据),实例化MipiDsiCntlr成员MipiDsiCntlrMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)
-
自定义结构体参考
> 从驱动的角度看,自定义结构体是参数和数据的载体,一般来说,config文件中的数值也会用来初始化结构体成员,但本例的mipidsi无器件属性文件,故基本成员结构与MipiDsiCntlr无太大差异。
```
c
typedef
struct
{
unsigned
int
devno
;
// 设备号
short
laneId
[
LANE_MAX_NUM
];
// lane号
OutPutModeTag
outputMode
;
// 输出模式选择:刷新模式,命令行模式和视频流模式
VideoModeTag
videoMode
;
// 显示设备的同步模式
OutputFormatTag
outputFormat
;
// 输出DSI图像数据格式:RGB or 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函数参考**
> **入参:**
> HdfDeviceObject 是整个驱动对外暴露的接口参数,具备 HCS 配置文件的信息
>
> **返回值:**
> HDF_STATUS相关状态 (下表为部分展示,如需使用其他状态,可见//drivers/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 |执行失败|
>
> **函数说明:**
> 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与MmcCntlr可以相互转化的前提
device
->
service
=
&
(
cntlr
->
service
);
//使HdfDeviceObject与MmcCntlr可以相互转化的前提
cntlr
->
priv
=
NULL
;
...
return
HDF_SUCCESS
;
}
...
return
HDF_FAILURE
;
}
```
-
**Release函数参考**
> **入参:**
> HdfDeviceObject 是整个驱动对外暴露的接口参数,具备 HCS 配置文件的信息
>
> **返回值:**
> 无
>
> **函数说明:**
> 该函数需要在驱动入口结构体中赋值给 Release 接口, 当 HDF 框架调用 Init 函数初始化驱动失败时,可以调用 Release 释放驱动资源, 该函数中需包含释放内存和删除控制器等操作。所有强制转换获取相应对象的操作**前提**是在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__
);
}
```
zh-cn/device-dev/driver/driver-platform-mmc-develop.md
0 → 100755
浏览文件 @
bc3eb53a
# MMC
-
[
概述
](
#1
)
-
[
开发步骤
](
#2
)
-
[
开发实例
](
#3
)
## 概述 <a name="1"></a>
MMC(MultiMedia Card),即多媒体卡,在HDF框架中,MMC的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。
独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
图 1 独立服务模式结构图
![
image1
](
figure/独立服务模式.png
)
## 开发步骤 <a name="2"></a>
MMC模块适配的三个环节是配置属性文件,实例化驱动入口,以及实例化核心层接口函数。
1.
**实例化驱动入口:**
-
实例化HdfDriverEntry结构体成员。
-
调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
2.
**配置属性文件:**
-
在device_info.hcs文件中添加deviceNode描述。
-
【可选】添加mmc_config.hcs器件属性文件。
3.
**实例化MMC控制器对象:**
-
初始化MmcCntlr成员。
-
实例化MmcCntlr成员MmcCntlrOps,其定义和成员说明见下
4.
**驱动调试:**
-
【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,设备启动是否成功等。
> ![](../public_sys-resources/icon-note.gif) **说明:**
>
> MmcCntlrOps定义
> ```c
> struct MmcCntlrOps {
> int32_t (*request)(struct MmcCntlr *cntlr, struct MmcCmd *cmd);
> int32_t (*setClock)(struct MmcCntlr *cntlr, uint32_t clock);
> int32_t (*setPowerMode)(struct MmcCntlr *cntlr, enum MmcPowerMode mode);
> int32_t (*setBusWidth)(struct MmcCntlr *cntlr, enum MmcBusWidth width);
> int32_t (*setBusTiming)(struct MmcCntlr *cntlr, enum MmcBusTiming timing);
> int32_t (*setSdioIrq)(struct MmcCntlr *cntlr, bool enable);
> int32_t (*hardwareReset)(struct MmcCntlr *cntlr);
> int32_t (*systemInit)(struct MmcCntlr *cntlr);
> int32_t (*setEnhanceSrobe)(struct MmcCntlr *cntlr, bool enable);
> int32_t (*switchVoltage)(struct MmcCntlr *cntlr, enum MmcVolt volt);
> bool (*devReadOnly)(struct MmcCntlr *cntlr);
> bool (*devPluged)(struct MmcCntlr *cntlr);
> bool (*devBusy)(struct MmcCntlr *cntlr);
> int32_t (*tune)(struct MmcCntlr *cntlr, uint32_t cmdCode);
> int32_t (*rescanSdioDev)(struct MmcCntlr *cntlr);
> };
> ```
>
> 表1 MmcCntlrOps结构体成员的回调函数功能说明
> |成员函数|入参|返回值|功能|
> |-|-|-|-|
> |doRequest |**cntlr**: 核心层结构体指针,mmc控制器 ;<br />**cmd**: 结构体指针,传入命令值 |HDF_STATUS相关状态|request相应处理|
> |setClock |**cntlr**: 核心层结构体指针,mmc控制器 ;<br />**clock**: 时钟传入值 |HDF_STATUS相关状态|设置时钟频率|
> |setPowerMode |**cntlr**: 核心层结构体指针,mmc控制器 ;<br />**mode**: 枚举值(见MmcPowerMode定义),功耗模式 |HDF_STATUS相关状态|设置功耗模式|
> |setBusWidth |**cntlr**: 核心层结构体指针,mmc控制器 ;<br />**width**: 枚举值(见MmcBusWidth定义),总线带宽 |HDF_STATUS相关状态|设置总线带宽|
> |setBusTiming |**cntlr**: 核心层结构体指针,mmc控制器 ;<br />**timing**: 枚举值(见MmcBusTiming定义),总线时序 |HDF_STATUS相关状态|设置总线时序|
> |setSdioIrq |**cntlr**: 核心层结构体指针,mmc控制器 ;<br />**enable**: 布尔值,控制中断|HDF_STATUS相关状态|使能/去使能SDIO中断|
> |hardwareReset |**cntlr**: 核心层结构体指针,mmc控制器 ; |HDF_STATUS相关状态|复位硬件 |
> |systemInit |**cntlr**: 核心层结构体指针,mmc控制器 ; |HDF_STATUS相关状态|系统初始化 |
> |setEnhanceSrobe |**cntlr**: 核心层结构体指针,mmc控制器 ;<br />**enable**: 布尔值,设置功能 |HDF_STATUS相关状态|设置增强选通 |
> |switchVoltage |**cntlr**: 核心层结构体指针,mmc控制器 ;<br />**volt**: 枚举值,电压值(3.3,1.8,1.2V); |HDF_STATUS相关状态|设置电压值 |
> |devReadOnly |**cntlr**: 核心层结构体指针,mmc控制器 ; |布尔值 |检验设备是否只读 |
> |cardPluged |**cntlr**: 核心层结构体指针,mmc控制器 ; |布尔值 |检验设备是否拔出|
> |devBusy |**cntlr**: 核心层结构体指针,mmc控制器 ; |布尔值 |检验设备是否忙碌|
> |tune |**cntlr**: 核心层结构体指针,mmc控制器 ;<br />**cmdCode**: uint32_t,命令代码; |HDF_STATUS相关状态|调谐 |
> |rescanSdioDev |**cntlr**: 核心层结构体指针,mmc控制器 ; |HDF_STATUS相关状态|扫描并添加SDIO设备,|
## 开发实例 <a name="3"></a>
下方将以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信息,并在mmc_config.hcs中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值与核心层MmcCntlr成员的默认值或限制范围有密切关系。
**如有多个器件信息,则需要在device_info文件增加deviceNode信息,以及在mmc_config文件中增加对应的器件属性。**
。
-
device_info.hcs 配置参考
```
c
root
{
device_info
{
match_attr
=
"hdf_manager"
;
platform
::
host
{
hostName
=
"platform_host"
;
priority
=
50
;
device_mmc
::
device
{
device0
::
deviceNode
{
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 配置参考
```
c
root
{
platform
{
mmc_config
{
template
mmc_controller
{
//模板公共参数,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省
match_attr
=
""
;
voltDef
=
0
;
// 3.3V
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
;
}
}
}
}
```
3.
完成驱动入口注册之后,最后一步就是以核心层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
devPluged
;
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
,
.
setEnhanceSrobe
=
HimciSetEnhanceSrobe
,
.
switchVoltage
=
HimciSwitchVoltage
,
.
devReadOnly
=
HimciDevReadOnly
,
.
devPluged
=
HimciCardPluged
,
.
devBusy
=
HimciDevBusy
,
.
tune
=
HimciTune
,
.
rescanSdioDev
=
HimciRescanSdioDev
,
};
```
-
**Bind函数参考**
> **入参:**
> HdfDeviceObject 是整个驱动对外暴露的接口参数,具备 HCS 配置文件的信息
>
> **返回值:**
> HDF_STATUS相关状态 (下表为部分展示,如需使用其他状态,可见//drivers/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 |初始化失败|
>
> **函数说明:**
> MmcCntlr,HimciHost,HdfDeviceObject之间互相赋值,方便其他函数可以相互转化,初始化自定义结构体HimciHost对象,初始化MmcCntlr成员,调用核心层MmcCntlrAdd函数。
```
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是整个驱动对外暴露的接口参数,具备HCS配置文件的信息
>
> **返回值:**
> 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 是整个驱动对外暴露的接口参数,具备 HCS 配置文件的信息
>
> **返回值:**
> 无
>
> **函数说明:**
> 释放内存和删除控制器等操作,该函数需要在驱动入口结构体中赋值给Release接口,当HDF框架调用Init函数初始化驱动失败时,可以调用 Release释放驱动资源。所有强制转换获取相应对象的操作**前提**是在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的强制转化
}
```
zh-cn/device-dev/driver/driver-platform-pwm-develop.md
0 → 100755
浏览文件 @
bc3eb53a
# PWM
-
[
概述
](
#1
)
-
[
开发步骤
](
#2
)
-
[
开发实例
](
#3
)
## 概述 <a name="1"></a>
PWM(Pulse Width Modulator)即脉冲宽度调节器,在HDF框架中,PWM的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。
独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
图 1 独立服务模式结构图
![
image1
](
figure/独立服务模式.png
)
## 开发步骤 <a name="2"></a>
PWM模块适配HDF框架的三个环节是配置属性文件,实例化驱动入口,以及填充核心层接口函数。
1.
**实例化驱动入口:**
-
实例化HdfDriverEntry结构体成员。
-
调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
2.
**配置属性文件:**
-
在device_info.hcs文件中添加deviceNode描述。
-
【可选】添加pwm_config.hcs器件属性文件。
3.
**实例化PWM控制器对象:**
-
初始化PwmDev成员。
-
实例化PwmDev成员PwmMethod,其定义和成员说明见下
4.
**驱动调试:**
-
【可选】针对新增驱动程序,建议验证驱动基本功能,例如PWM控制状态,中断响应情况等。
> ![](../public_sys-resources/icon-note.gif) **说明:**
>
> PwmMethod定义
> ```c
> struct PwmMethod {
> int32_t (*setConfig)(struct PwmDev *pwm, struct PwmConfig *config);
> int32_t (*open)(struct PwmDev *pwm);
> int32_t (*close)(struct PwmDev *pwm);
> };
> ```
>
> 表1 PwmMethod结构体成员的回调函数功能说明
> |成员函数|入参|返回值|功能|
> |-|-|-|-|
> |setConfig|**pwm**: 结构体指针,核心层PWM控制器;<br />**config**: 结构体指针,属性传入值;|HDF_STATUS相关状态|配置属性|
> |open |**pwm**: 结构体指针,核心层PWM控制器;|HDF_STATUS相关状态|打开设备|
> |close|**pwm**: 结构体指针,核心层PWM控制器;|HDF_STATUS相关状态|关闭设备|
## 开发实例 <a name="3"></a>
下方将以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
,
.
Init
=
HdfPwmInit
,
.
Release
=
HdfPwmRelease
,
};
//调用HDF_INIT将驱动入口注册到HDF框架中
HDF_INIT
(
g_hdfPwm
);
```
2.
完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,并在 pwm_config.hcs 中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值与核心层PwmDev成员的默认值或限制范围有密切关系。
**如有更多个器件信息,则需要在device_info文件增加deviceNode信息,以及在pwm_config文件中增加对应的器件属性**
。
-
device_info.hcs 配置参考
```
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 配置参考
```
c
root
{
platform
{
pwm_config
{
template
pwm_device
{
//【必要】模板配置,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省
serviceName
=
""
;
match_attr
=
""
;
num
=
0
;
//【必要】设备号
base
=
0x12070000
;
//【必要】地址映射需要
}
device_0x12070000
::
pwm_device
{
match_attr
=
"hisilicon_hi35xx_pwm_0"
;
//【必要】需要和device_info.hcs中的deviceMatchAttr值一致
}
device_0x12070020
::
pwm_device
{
//存在多个设备时【必须】添加,否则不用
match_attr
=
"hisilicon_hi35xx_pwm_1"
;
num
=
1
;
base
=
0x12070020
;
//【必要】地址映射需要
}
}
}
}
```
3.
完成驱动入口注册之后,最后一步就是以核心层PwmDev对象的初始化为核心,包括厂商自定义结构体(传递参数和数据),实例化PwmDev成员PwmMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)
-
自定义结构体参考
> 从驱动的角度看,自定义结构体是参数和数据的载体,而且pwm_config.hcs文件中的数值会被HDF读入通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象,例如设备号等。
```
c
struct
HiPwm
{
struct
PwmDev
dev
;
//【必要】 核心层结构体
volatile
unsigned
char
*
base
;
struct
HiPwmRegs
*
reg
;
// 设备属性结构体,可自定义
bool
supportPolarity
;
};
// PwmDev是核心层控制器结构体,其中的成员在Init函数中会被赋值
struct
PwmDev
{
struct
IDeviceIoService
service
;
struct
HdfDeviceObject
*
device
;
struct
PwmConfig
cfg
;
//属性结构体,相关定义见下
struct
PwmMethod
*
method
;
//钩子函数模板
bool
busy
;
uint32_t
num
;
//设备号
OsalSpinlock
lock
;
void
*
priv
;
//私有数据,一般存储自定义结构体首地址,方便调用
};
struct
PwmConfig
{
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
// pwm_hi35xx.c 中的示例:钩子函数的填充
struct
PwmMethod
g_pwmOps
=
{
.
setConfig
=
HiPwmSetConfig
,
//配置属性
};
```
-
**Init函数参考**
> **入参:**
> HdfDeviceObject 是整个驱动对外暴露的接口参数,具备 HCS 配置文件的信息
>
> **返回值:**
> HDF_STATUS相关状态 (下表为部分展示,如需使用其他状态,可见//drivers/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函数。
```
c
//此处bind函数为空函数,可与init函数结合,也可根据厂商需要实现相关操作
static
int32_t
HdfPwmBind
(
struct
HdfDeviceObject
*
obj
)
{
(
void
)
obj
;
return
HDF_SUCCESS
;
}
//挂载init的
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 是整个驱动对外暴露的接口参数,具备 HCS 配置文件的信息
>
> **返回值:**
> 无
>
> **函数说明:**
> 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给 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
}
```
\ No newline at end of file
zh-cn/device-dev/driver/driver-platform-rtc-develop.md
0 → 100755
浏览文件 @
bc3eb53a
# RTC
-
[
概述
](
#1
)
-
[
开发步骤
](
#2
)
-
[
开发实例
](
#3
)
## 概述 <a name="1"></a>
RTC(real-time clock)为操作系统中的实时时钟设备,在HDF框架中,RTC的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。
独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
图 1 独立服务模式结构图
![
image1
](
figure/独立服务模式.png
)
## 开发步骤 <a name="2"></a>
RTC模块适配HDF框架的三个环节是配置属性文件,实例化驱动入口,以及填充核心层接口函数。
1.
**实例化驱动入口:**
-
实例化HdfDriverEntry结构体成员。
-
调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
2.
**配置属性文件:**
-
在device_info.hcs文件中添加deviceNode描述。
-
【可选】添加rtc_config.hcs器件属性文件。
3.
**实例化RTC控制器对象:**
-
初始化RtcHost成员。
-
实例化RtcHost成员RtcMethod,其定义和成员说明见下
4.
**驱动调试:**
-
【可选】针对新增驱动程序,建议验证驱动基本功能,例如RTC控制状态,中断响应情况等。
> ![](../public_sys-resources/icon-note.gif) **说明:**
>
> RtcMethod定义
>
> ```c
> struct RtcMethod {
> int32_t (*ReadTime)(struct RtcHost *host, struct RtcTime *time);
> int32_t (*WriteTime)(struct RtcHost *host, const struct RtcTime *time);
> int32_t (*ReadAlarm)(struct RtcHost *host, enum RtcAlarmIndex alarmIndex, struct RtcTime *time);
> int32_t (*WriteAlarm)(struct RtcHost *host, enum RtcAlarmIndex alarmIndex, const struct RtcTime *time);
> int32_t (*RegisterAlarmCallback)(struct RtcHost *host, enum RtcAlarmIndex alarmIndex, RtcAlarmCallback cb);
> int32_t (*AlarmInterruptEnable)(struct RtcHost *host, enum RtcAlarmIndex alarmIndex, uint8_t enable);
> int32_t (*GetFreq)(struct RtcHost *host, uint32_t *freq);
> int32_t (*SetFreq)(struct RtcHost *host, uint32_t freq);
> int32_t (*Reset)(struct RtcHost *host);
> int32_t (*ReadReg)(struct RtcHost *host, uint8_t usrDefIndex, uint8_t *value);
> int32_t (*WriteReg)(struct RtcHost *host, uint8_t usrDefIndex, uint8_t value);
> };
> ```
>
> 表1 RtcMethod结构体成员的回调函数功能说明
>
> |函数|入参|出参|返回值|功能|
> |-|-|-|-|-|
> |ReadTime |**host**: 结构体指针,核心层RTC控制器 ; |**time**: 结构体指针,<br />传出的时间值; |HDF_STATUS相关状态| 读RTC时间信息[^2] |
> |WriteTime |**host**: 结构体指针,核心层RTC控制器 ;<br />**time**: 结构体指针,时间传入值; |无 |HDF_STATUS相关状态| 写RTC时间信息(包括毫秒~年) |
> |ReadAlarm |**host**: 结构体指针,核心层RTC控制器 ;<br />**alarmIndex**: 枚举值,闹钟报警索引 ;|**time**: 结构体指针,<br />传出的时间值;|HDF_STATUS相关状态| 读RTC报警时间信息 |
> |WriteAlarm |**host**: 结构体指针,核心层RTC控制器 ;<br />**alarmIndex**: 枚举值,闹钟报警索引 ;<br />**time**: 结构体指针,时间传入值;|无|HDF_STATUS相关状态| 写RTC报警时间信息 |
> |RegisterAlarmCallback|**host**: 结构体指针,核心层RTC控制器 ;<br />**alarmIndex**: 枚举值,闹钟报警索引 ;<br />**cb**:函数指针,回调函数; |无|HDF_STATUS相关状态| 注册报警超时回调函数|
> |AlarmInterruptEnable |**host**: 结构体指针,核心层RTC控制器 ;<br />**alarmIndex**: 枚举值,闹钟报警索引 ;<br />**enable**: 布尔值,控制报警; |无|HDF_STATUS相关状态| 使能/去使能RTC报警中断 |
> |GetFreq |**host**: 结构体指针,核心层RTC控制器 ; |**freq**: uint32_t指针,<br />传出的频率值;|HDF_STATUS相关状态| 读RTC外接晶振频率 |
> |SetFreq |**host**: 结构体指针,核心层RTC控制器 ;<br />**freq**: uint32_t,频率传入值; |无|HDF_STATUS相关状态| 配置RTC外接晶振频率 |
> |Reset |**host**: 结构体指针,核心层RTC控制器 ; |无|HDF_STATUS相关状态| RTC复位 |
> |ReadReg |**host**: 结构体指针,核心层RTC控制器 ;<br />**usrDefIndex**: 结构体,用户自定义寄存器索引; |**value**: uint8_t指针,<br />传出的寄存器值;|HDF_STATUS相关状态| 按照用户定义的寄存器索引,<br />读取对应的寄存器配置,<br />一个索引对应一字节的配置值 |
> |WriteReg |**host**: 结构体指针,核心层RTC控制器 ;<br />**usrDefIndex**: 结构体,用户自定义寄存器索引;<br />**value**: uint8_t,寄存器传入值; |无|HDF_STATUS相关状态| 按照用户定义的寄存器索引,<br />设置对应的寄存器配置,<br />一个索引对应一字节的配置值|
## 开发实例 <a name="3"></a>
下方将以rtc_hi35xx.c为示例,展示需要厂商提供哪些内容来完整实现设备功能。
1.
驱动开发首先需要实例化驱动入口,驱动入口必须为HdfDriverEntry(在 hdf_device_desc.h 中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
-
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
);
```
2.
完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,并在 rtc_config.hcs 中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值与核心层RtcHost成员的默认值或限制范围有密切关系。
**本例只有一个RTC控制器,如有多个器件信息,则需要在device_info文件增加deviceNode信息,以及在rtc_config文件中增加对应的器件属性**
。
-
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"
;
//【必要】驱动注册名字
serviceName
=
"HDF_PLATFORM_RTC"
;
//【必要】驱动对外发布服务的名称,必须唯一
deviceMatchAttr
=
"hisilicon_hi35xx_rtc"
;
//【必要】需要与设备hcs文件中的 match_attr 匹配
}
}
}
}
}
```
-
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
;
}
}
}
}
```
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/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 |初始化失败|
> **函数说明:**
> 链接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 提供参数,...函数内部处理失败后内存释放等操作
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 配置文件的信息
>
> **返回值:**
> 无
>
> **函数说明:**
> 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给 Release 接口, 当HDF框架调用Init函数初始化驱动失败时,可以调用 Release 释放驱动资源。所有强制转换获取相应对象的操作**前提**是在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
}
```
zh-cn/device-dev/driver/driver-platform-sdio-develop.md
0 → 100755
浏览文件 @
bc3eb53a
# SDIO
-
[
概述
](
#1
)
-
[
开发步骤
](
#2
)
-
[
开发实例
](
#3
)
## 概述 <a name="1"></a>
SDIO由SD卡发展而来,被统称为mmc(MultiMediaCard),相关技术差别不大,在HDF框架中,
SDIO的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。
独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
图 1 独立服务模式结构图
![
image1
](
figure/独立服务模式.png
)
## 开发步骤 <a name="2"></a>
SDIO模块适配HDF框架的三个环节是配置属性文件,实例化驱动入口,以及填充核心层接口函数。
1.
**实例化驱动入口:**
-
实例化HdfDriverEntry结构体成员。
-
调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
2.
**配置属性文件:**
-
在device_info.hcs文件中添加deviceNode描述。
-
【可选】添加sdio_config.hcs器件属性文件。
3.
**实例化SDIO控制器对象:**
-
初始化SdioDevice成员。
-
实例化SdioDevice成员SdioDeviceOps,其定义和成员说明见下
4.
**驱动调试:**
-
【可选】针对新增驱动程序,建议验证驱动基本功能,例如SDIO控制状态,中断响应情况等。
> ![](../public_sys-resources/icon-note.gif) **说明:**
>
> 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);
> int32_t (*fixedAddrReadBytes)(struct SdioDevice *dev, uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen);
> int32_t (*fixedAddrWriteBytes)(struct SdioDevice *dev, uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen);
> int32_t (*func0ReadBytes)(struct SdioDevice *dev, uint8_t *data, uint32_t addr, uint32_t size);
> int32_t (*func0WriteBytes)(struct SdioDevice *dev, uint8_t *data, uint32_t addr, uint32_t size);
> int32_t (*setBlockSize)(struct SdioDevice *dev, uint32_t blockSize);
> int32_t (*getCommonInfo)(struct SdioDevice *dev, SdioCommonInfo *info, uint32_t infoType);
> int32_t (*setCommonInfo)(struct SdioDevice *dev, SdioCommonInfo *info, uint32_t infoType);
> int32_t (*flushData)(struct SdioDevice *dev);
> int32_t (*enableFunc)(struct SdioDevice *dev);
> int32_t (*disableFunc)(struct SdioDevice *dev);
> int32_t (*claimIrq)(struct SdioDevice *dev, SdioIrqHandler *irqHandler);
> int32_t (*releaseIrq)(struct SdioDevice *dev);
> int32_t (*findFunc)(struct SdioDevice *dev, struct SdioFunctionConfig *configData);
> int32_t (*claimHost)(struct SdioDevice *dev);
> int32_t (*releaseHost)(struct SdioDevice *dev);
> };
> ```
>
> 表1 SdioDeviceOps结构体成员的回调函数功能说明
>
> |函数|入参|出参|返回值|功能|
> |-|-|-|-|-|
> |incrAddrReadBytes, |**dev**: 结构体指针,SDIO设备控制器;<br />**addr**: uint32_t,地址值;<br />**size**: uint32_t,大小 |**data**: uint8_t指针,传出值;|HDF_STATUS相关状态| 从指定的SDIO地址增量读取给定长度的数据 |
> |incrAddrWriteBytes, |**dev**: 结构体指针,SDIO设备控制器;<br />**data**: uint8_t指针,传入值;<br />**addr**: uint32_t,地址值;<br />**size**: uint32_t,大小 |无|HDF_STATUS相关状态| 将给定长度的数据增量写入指定的SDIO地址 |
> |fixedAddrReadBytes, |**dev**: 结构体指针,SDIO设备控制器;<br />**addr**: uint32_t,地址值;<br />**size**: uint32_t,大小;<br />**scatterLen**: uint32_t,数据长度;|**data**: uint8_t指针,传出值;|HDF_STATUS相关状态| 从固定SDIO地址读取给定长度的数据。 |
> |fixedAddrWriteBytes,|**dev**: 结构体指针,SDIO设备控制器;<br />**data**: uint8_t指针,传入值;<br />**addr**: uint32_t,地址值;<br />**size**: uint32_t,大小;<br />**scatterLen**: uint32_t,数据长度;|无|HDF_STATUS相关状态| 将给定长度的数据写入固定SDIO地址 |
> |func0ReadBytes, |**dev**: 结构体指针,SDIO设备控制器;<br />**addr**: uint32_t,地址值;<br />**size**: uint32_t,大小; |**data**: uint8_t指针,传出值;|HDF_STATUS相关状态| 从SDIO函数0的地址空间读取给定长度的数据。 |
> |func0WriteBytes, |**dev**: 结构体指针,SDIO设备控制器;<br />**data**: uint8_t指针,传入值;<br />**addr**: uint32_t,地址值;<br />**size**: uint32_t,大小; |无|HDF_STATUS相关状态| 将给定长度的数据写入SDIO函数0的地址空间。 |
> |setBlockSize, |**dev**: 结构体指针,SDIO设备控制器;<br />**blockSize**: uint32_t,Block大小 |无|HDF_STATUS相关状态| 设置block大小|
> |getCommonInfo, |**dev**: 联合体指针,SDIO设备控制器;<br />**infoType**: uint32_t,info类型; |**info**: 结构体指针,传出SdioFuncInfo信息;|HDF_STATUS相关状态| 获取CommonInfo,说明见下 |
> |setCommonInfo, |**dev**: 结构体指针,SDIO设备控制器; <br />**info**: 联合体指针,SdioFuncInfo信息传入;<br />**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设备 |
> |claimIrq, |**dev**: 结构体指针,SDIO设备控制器;<br />**irqHandler**: void函数指针; |无|HDF_STATUS相关状态|注册SDIO中断 |
> |releaseIrq, |**dev**: 结构体指针,SDIO设备控制器; |无|HDF_STATUS相关状态|释放SDIO中断|
> |findFunc, |**dev**: 结构体指针,SDIO设备控制器;<br />**configData**: 结构体指针, SDIO函数关键信息 |无|HDF_STATUS相关状态|寻找匹配的funcNum|
> |claimHost, |**dev**: 结构体指针,SDIO设备控制器; |无|HDF_STATUS相关状态|独占HOST |
> |releaseHost, |**dev**: 结构体指针,SDIO设备控制器; |无|HDF_STATUS相关状态|释放HOST |
>
> >![](../public_sys-resources/icon-note.gif) **CommonInfo说明:**
> > 包括maxBlockNum(单个request中最大block数), maxBlockSize(单个block最大字节数), maxRequestSize(单个Request最大字节数), enTimeout(最大超时时间,毫秒), funcNum(功能编号1~7), irqCap(IRQ capabilities), (void \*)data.
## 开发实例 <a name="3"></a>
下方将以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.
完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,并在 sdio_config.hcs 中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值与核心层SdioDevice成员的默认值或限制范围有密切关系。
**本例只有一个SDIO控制器,如有多个器件信息,则需要在device_info文件增加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值一致
}
}
}
```
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
;
/* irq 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/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 |初始化失败|
>
> **函数说明:**
> 将 MmcCntlr 对象同 HdfDeviceObject 进行了关联
>
> **函数说明:**
> 初始化自定义结构体对象,初始化SdioCntlr成员,调用核心层SdioCntlrAdd函数,以及其他厂商自定义初始化操作
```
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相关状态
>
> **函数说明:**
> 无操作,可根据厂商需要添加
```
c
static
int32_t
Hi35xxLinuxSdioInit
(
struct
HdfDeviceObject
*
obj
)
{
(
void
)
obj
;
//无操作,可根据厂商需要添加
HDF_LOGD
(
"Hi35xxLinuxSdioInit: Success!"
);
return
HDF_SUCCESS
;
}
```
-
**Release函数参考**
> **入参:**
> HdfDeviceObject 是整个驱动对外暴露的接口参数,具备 HCS 配置文件的信息
>
> **返回值:**
> 无
>
> **函数说明:**
> 释放内存和删除控制器,该函数需要在驱动入口结构体中赋值给 Release 接口, 当HDF框架调用Init函数初始化驱动失败时,可以调用 Release 释放驱动资源。所有强制转换获取相应对象的操作**前提**是在Bnit函数中具备对应赋值的操作。
```
c
static
void
Hi35xxLinuxSdioRelease
(
struct
HdfDeviceObject
*
obj
)
{
if
(
obj
==
NULL
)
{
return
;
}
Hi35xxLinuxSdioDeleteCntlr
((
struct
MmcCntlr
*
)
obj
->
service
);
//【必要】自定义的内存释放函数,这里有HdfDeviceObject到MmcCntlr的强制转化
}
```
zh-cn/device-dev/driver/driver-platform-spi-develop.md
0 → 100755
浏览文件 @
bc3eb53a
# SPI
-
[
概述
](
#1
)
-
[
开发步骤
](
#2
)
-
[
开发实例
](
#3
)
## 概述 <a name="1"></a>
SPI是串行外设接口(Serial Peripheral Interface)的缩写,在HDF框架中,SPI的接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。
独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
图 1 独立服务模式结构图
![
image1
](
figure/独立服务模式.png
)
## 开发步骤 <a name="2"></a>
SPI模块适配HDF框架的三个环节是配置属性文件,实例化驱动入口,以及填充核心层接口函数。
1.
**实例化驱动入口:**
-
实例化HdfDriverEntry结构体成员。
-
调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
2.
**配置属性文件:**
-
在device_info.hcs文件中添加deviceNode描述。
-
【可选】添加spi_config.hcs器件属性文件。
3.
**实例化SPI控制器对象:**
-
初始化SpiCntlr成员。
-
实例化SpiCntlr成员SpiCntlrMethod,其定义和成员说明见下
4.
**驱动调试:**
-
【可选】针对新增驱动程序,建议验证驱动基本功能,例如spi控制状态,中断响应情况等。
> ![](../public_sys-resources/icon-note.gif) **说明:**
>
> SpiCntlrMethod定义
> ```c
> struct SpiCntlrMethod {
> int32_t (*GetCfg)(struct SpiCntlr *cntlr, struct SpiCfg *cfg);
> int32_t (*SetCfg)(struct SpiCntlr *cntlr, struct SpiCfg *cfg);
> int32_t (*Transfer)(struct SpiCntlr *cntlr, struct SpiMsg *msg, uint32_t count);
> int32_t (*Open)(struct SpiCntlr *cntlr);
> int32_t (*Close)(struct SpiCntlr *cntlr);
> };
> ```
> 表1 SpiCntlrMethod结构体成员的回调函数功能说明
>
> |成员函数|入参|返回值|功能|
> |-|-|-|-|
> |Transfer |**cntlr**: 结构体指针,核心层spi控制器 ;<br />**msg**: 结构体指针,Spi消息;<br />**count**: uint32_t,消息个数 |HDF_STATUS相关状态|传输消息|
> |SetCfg |**cntlr**: 结构体指针,核心层spi控制器 ;<br />**cfg**: 结构体指针,Spi属性 |HDF_STATUS相关状态|设置控制器属性 |
> |GetCfg |**cntlr**: 结构体指针,核心层spi控制器 ;<br />**cfg**: 结构体指针,Spi属性 |HDF_STATUS相关状态|获取控制器属性 |
> |Open |**cntlr**: 结构体指针,核心层spi控制器 ; |HDF_STATUS相关状态|打开SPI |
> |Close |**cntlr**: 结构体指针,核心层spi控制器 ; |HDF_STATUS相关状态|关闭SPI |
## 开发实例 <a name="3"></a>
下方将以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.
完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,并在 spi_config.hcs 中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值与核心层SpiCntlr 成员的默认值或限制范围有密切关系。
**本例只有一个SPI控制器,如有多个器件信息,则需要在device_info文件增加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
=
1
;
priority
=
60
;
permission
=
0644
;
moduleName
=
"HDF_PLATFORM_SPI"
;
serviceName
=
"HDF_PLATFORM_SPI_0"
;
deviceMatchAttr
=
"hisilicon_hi35xx_spi_0"
;
}
device1
::
deviceNode
{
policy
=
1
;
priority
=
60
;
permission
=
0644
;
moduleName
=
"HDF_PLATFORM_SPI"
;
// 【必要】用于指定驱动名称,该字段的值必须和驱动入口结构的moduleName值一致
serviceName
=
"HDF_PLATFORM_SPI_1"
;
// 【必要且唯一】驱动对外发布服务的名称
deviceMatchAttr
=
"hisilicon_hi35xx_spi_1"
;
// 需要与设备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 添加对应的节点
}
}
}
```
3.
完成驱动入口注册之后,最后一步就是以核心层SpiCntlr对象的初始化为核心,包括厂商自定义结构体(传递参数和数据),实例化SpiCntlr成员SpiCntlrMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)
-
自定义结构体参考
> 从驱动的角度看,自定义结构体是参数和数据的载体,而且spi_config.hcs文件中的数值会被HDF读入通过DeviceResourceIface来初始化结构体成员,一些重要数值也会传递给核心层对象,例如设备号、总线号等。
```
c
struct
Pl022
{
//对应于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 函数参考**
> **入参:**
> 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相关状态 (下表为部分展示,如需使用其他状态,可见//drivers/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 |初始化失败|
>
> **函数说明:**
> 初始化自定义结构体对象,初始化SpiCntlr成员。
```
c
//挂载init的
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
;
//挂载总线号
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 释放驱动资源。所有强制转换获取相应对象的操作**前提**是在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对象
}
```
zh-cn/device-dev/driver/driver-platform-uart-develop.md
0 → 100755
浏览文件 @
bc3eb53a
# UART
-
[
概述
](
#1
)
-
[
开发步骤
](
#2
)
-
[
开发实例
](
#3
)
## 概述 <a name="1"></a>
UART是通用异步收发传输器(Universal Asynchronous Receiver/Transmitter)的缩写,在HDF框架中,uart的接口适配模式采用独立服务模式。在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。
独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
图 1 独立服务模式结构图
![
image1
](
figure/独立服务模式.png
)
## 开发步骤 <a name="2"></a>
uart模块适配HDF框架的三个环节是配置属性文件,实例化驱动入口,以及填充核心层接口函数。
1.
**实例化驱动入口:**
-
实例化HdfDriverEntry结构体成员。
-
调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
2.
**配置属性文件:**
-
在device_info.hcs文件中添加deviceNode描述。
-
【可选】添加uart_config.hcs器件属性文件。
3.
**实例化UART控制器对象:**
-
初始化UartHost成员。
-
实例化UartHost成员UartHostMethod,其定义和成员说明见下
4.
**驱动调试:**
-
【可选】针对新增驱动程序,建议验证驱动基本功能,例如UART控制状态,中断响应情况等。
> ![](../public_sys-resources/icon-note.gif) **说明:**
> UartHostMethod定义
>
> ```c
> struct UartHostMethod {
> int32_t (*Init)(struct UartHost *host);
> int32_t (*Deinit)(struct UartHost *host);
> int32_t (*Read)(struct UartHost *host, uint8_t *data, uint32_t size);
> int32_t (*Write)(struct UartHost *host, uint8_t *data, uint32_t size);
> int32_t (*GetBaud)(struct UartHost *host, uint32_t *baudRate);
> int32_t (*SetBaud)(struct UartHost *host, uint32_t baudRate);
> int32_t (*GetAttribute)(struct UartHost *host, struct UartAttribute *attribute);
> int32_t (*SetAttribute)(struct UartHost *host, struct UartAttribute *attribute);
> int32_t (*SetTransMode)(struct UartHost *host, enum UartTransMode mode);
> int32_t (*pollEvent)(struct UartHost *host, void *filep, void *table);
> };
> ```
>
> 表1 UartHostMethod结构体成员的回调函数功能说明
>
> |函数|入参|出参|返回值|功能|
> |-|-|-|-|-|
> |Init |**host**: 结构体指针,核心层uart控制器; |无|HDF_STATUS相关状态|初始化Uart设备|
> |Deinit |**host**: 结构体指针,核心层uart控制器; |无|HDF_STATUS相关状态| 去初始化Uart设备 |
> |Read |**host**: 结构体指针,核心层uart控制器;<br />**size**:uint32_t,数据大小; |**data**: uint8_t指针,传出的数据 |HDF_STATUS相关状态|接收数据 RX |
> |Write |**host**: 结构体指针,核心层uart控制器;<br />**data**:uint8_t指针,传入数据;<br />**size**:uint32_t,数据大小; |无|HDF_STATUS相关状态|发送数据 TX |
> |SetBaud |**host**: 结构体指针,核心层uart控制器;<br />**baudRate**: uint32_t指针,波特率传入值; |无|HDF_STATUS相关状态| 设置波特率 |
> |GetBaud |**host**: 结构体指针,核心层uart控制器; |**baudRate**: uint32_t指针,传出的波特率; |HDF_STATUS相关状态|获取当前设置的波特率|
> |GetAttribute |**host**: 结构体指针,核心层uart控制器; |**attribute**: 结构体指针,传出的属性值<br />(见uart_if.h中UartAttribute定义)|HDF_STATUS相关状态| 获取设备uart相关属性|
> |SetAttribute |**host**: 结构体指针,核心层uart控制器;<br />**attribute**: 结构体指针,属性传入值; |无|HDF_STATUS相关状态| 设置设备uart相关属性 |
> |SetTransMode |**host**: 结构体指针,核心层uart控制器;<br />**mode**: 枚举值(见uart_if.h中<br />UartTransMode定义),传输模式 |无|HDF_STATUS相关状态| 设置传输模式 |
> |PollEvent |**host**: 结构体指针,核心层uart控制器;<br />**filep**: void 指针,file ;<br />**table**: void 指针,poll_table ;|无|HDF_STATUS相关状态|poll机制|
## 开发实例 <a name="3"></a>
下方将以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 里面的名字匹配】
.
Bind
=
HdfUartDeviceBind
,
//见Bind参考
.
Init
=
HdfUartDeviceInit
,
//见Init参考
.
Release
=
HdfUartDeviceRelease
,
//见Release参考
};
//调用HDF_INIT将驱动入口注册到HDF框架中
HDF_INIT
(
g_hdfUartDevice
);
```
2.
完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,并在 uart_config.hcs 中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值与核心层UartHost成员的默认值或限制范围有密切关系。
**本例只有一个UART控制器,如有多个器件信息,则需要在device_info文件增加deviceNode信息,以及在uart_config文件中增加对应的器件属性**
。
-
device_info.hcs 配置参考
```
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 配置参考
```
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"
;
}
...
// 【可选】可新增,但需要在 device_info.hcs 添加对应的节点
}
}
```
3.
完成驱动入口注册之后,最后一步就是以核心层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_SUSPENED 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
;
//自定义函数指针结构体,详情见device/hisilicon/drivers/uart/uart_pl011.c
void
*
private
;
//一般用来存储UartPl011Port首地址,方便调用
};
// UartHost是核心层控制器结构体,其中的成员在Init函数中会被赋值
struct
UartHost
{
struct
IDeviceIoService
service
;
struct
HdfDeviceObject
*
device
;
uint32_t
num
;
OsalAtomic
atom
;
void
*
priv
;
//一般存储厂商自定义结构体首地址,方便后者被调用
struct
UartHostMethod
*
method
;
//核心层钩子函数,厂商需要实现其成员函数功能并填充
};
```
-
**【重要】**
UartHost成员回调函数结构体UartHostMethod的实例化,其他成员在Bind函数中初始化
```
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 这个是整个驱动对外暴露的接口参数,具备 HCS 配置文件的信息
>
> **返回值:**
> HDF_STATUS相关状态 (下表为部分展示,如需使用其他状态,可见//drivers/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 |初始化失败|
>
> **函数说明:**
> 初始化自定义结构体对象,初始化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 是整个驱动对外暴露的接口参数,具备 HCS 配置文件的信息
>
> **返回值:**
> HDF_STATUS相关状态
>
> **函数说明:**
> 初始化自定义结构体对象,初始化UartHost成员,调用核心层UartAddDev函数,接入VFS
```
c
//挂载init的
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
;
//udd 和 port 对象是厂商自定义的结构体对象,可根据需要实现相关功能
struct
UartDriverData
*
udd
=
NULL
;
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 是整个驱动对外暴露的接口参数,具备 HCS 配置文件的信息
>
> **返回值:**
> 无
>
> **函数说明:**
> 该函数需要在驱动入口结构体中赋值给 Release 接口, 当HDF框架调用Init函数初始化驱动失败时,可以调用 Release 释放驱动资源, 该函数中需包含释放内存和删除控制器等操作。所有强制转换获取相应对象的操作**前提**是在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
);
//地址反映射
}
(
void
)
OsalMemFree
(
port
);
udd
->
private
=
NULL
;
}
(
void
)
OsalMemFree
(
udd
);
//释放UartDriverData
host
->
priv
=
NULL
;
}
```
zh-cn/device-dev/driver/driver-platform-watchdog-develop.md
0 → 100755
浏览文件 @
bc3eb53a
# WatchDog
-
[
概述
](
#1
)
-
[
开发步骤
](
#2
)
-
[
开发实例
](
#3
)
## 概述 <a name="1"></a>
看门狗(watchdog),又叫看门狗计时器(watchdog timer),是一种硬件的计时设备,在HDF框架中,watchdog接口适配模式采用独立服务模式,在这种模式下,每一个设备对象会独立发布一个设备服务来处理外部访问,设备管理器收到API的访问请求之后,通过提取该请求的参数,达到调用实际设备对象的相应内部方法的目的。
独立服务模式可以直接借助HDFDeviceManager的服务管理能力,但需要为每个设备单独配置设备节点,增加内存占用。
图 1 独立服务模式结构图
![
image1
](
figure/独立服务模式.png
)
## 开发步骤 <a name="2"></a>
watchdog模块适配HDF框架的三个环节是配置属性文件,实例化驱动入口,以及填充核心层接口函数。
1.
**实例化驱动入口:**
-
实例化HdfDriverEntry结构体成员。
-
调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
2.
**配置属性文件:**
-
在device_info.hcs文件中添加deviceNode描述。
-
【可选】添加watchdog_config.hcs器件属性文件。
3.
**实例化Watchdog控制器对象:**
-
初始化WatchdogCntlr成员。
-
实例化WatchdogCntlr成员WatchdogMethod,其定义和成员
**说明**
见下
4.
**驱动调试:**
-
【可选】针对新增驱动程序,建议验证驱动基本功能,例如挂载后的信息反馈,超时时间设置的成功与否等。
> ![](../public_sys-resources/icon-note.gif) **说明:**
> WatchdogMethod定义
>
> ```c
> struct WatchdogMethod {
> int32_t (*getStatus)(struct WatchdogCntlr *wdt, int32_t *status);
> int32_t (*setTimeout)(struct WatchdogCntlr *wdt, uint32_t seconds);
> int32_t (*getTimeout)(struct WatchdogCntlr *wdt, uint32_t *seconds);
> int32_t (*start)(struct WatchdogCntlr *wdt);
> int32_t (*stop)(struct WatchdogCntlr *wdt);
> int32_t (*feed)(struct WatchdogCntlr *wdt);
> int32_t (*getPriv)(struct WatchdogCntlr *wdt); //【可选】如果WatchdogCntlr 中的priv成员存在,则按需填充
> void (*releasePriv)(struct WatchdogCntlr *wdt);//【可选】
> };
> ```
>
> 表1 WatchdogMethod成员的回调函数功能说明
> |成员函数|入参|出参|返回值|功能|
> |-|-|-|-|-|
> |getStatus |**wdt**: 结构体指针,核心层WDG控制器; |**status**: int32_t指针,<br />表示狗的状态(打开或关闭); |HDF_STATUS相关状态|获取看门狗所处的状态|
> |start |**wdt**: 结构体指针,核心层WDG控制器; |无 |HDF_STATUS相关状态|打开开门狗 |
> |stop |**wdt**: 结构体指针,核心层WDG控制器; |无 |HDF_STATUS相关状态|关闭开门狗 |
> |setTimeout|**wdt**: 结构体指针,核心层WDG控制器;<br />**seconds**: uint32_t,时间传入值;|无|HDF_STATUS相关状态|设置超时时间值,<br />需要与设置的时间相对应,<br />与厂商看门狗的时钟周期相关 |
> |getTimeout|**wdt**: 结构体指针,核心层WDG控制器; |**seconds**: uint32_t,<br />传出的时间值|HDF_STATUS相关状态|回读设置的超时时间值 |
> |feed |**wdt**: 结构体指针,核心层WDG控制器; |无 |HDF_STATUS相关状态|喂狗 |
## 开发实例 <a name="3"></a>
下方将以watchdog_hi35xx.c为示例,展示需要厂商提供哪些内容来完整实现设备功能。
1.
驱动开发首先需要实例化驱动入口,驱动入口必须为HdfDriverEntry(在 hdf_device_desc.h 中定义)类型的全局变量,且moduleName要和device_info.hcs中保持一致。HDF框架会将所有加载的驱动的HdfDriverEntry对象首地址汇总,形成一个类似数组的段地址空间,方便上层调用。
一般在加载驱动时HDF会先调用Bind函数,再调用Init函数加载该驱动。当Init调用异常时,HDF框架会调用Release释放驱动资源并退出。
-
watchdog驱动入口参考
```
c
struct
HdfDriverEntry
g_watchdogDriverEntry
=
{
.
moduleVersion
=
1
,
.
Bind
=
Hi35xxWatchdogBind
,
//见Bind参考
.
Init
=
Hi35xxWatchdogInit
,
//见Init参考
.
Release
=
Hi35xxWatchdogRelease
,
//见Release参考
.
moduleName
=
"HDF_PLATFORM_WATCHDOG"
,
//【必要且与HCS文件中里面的moduleName匹配】
};
HDF_INIT
(
g_watchdogDriverEntry
);
//调用HDF_INIT将驱动入口注册到HDF框架中
```
2.
完成驱动入口注册之后,下一步请在device_info.hcs文件中添加deviceNode信息,并在 watchdog_config.hcs 中配置器件属性。deviceNode信息与驱动入口注册相关,器件属性值与核心层WatchdogCntlr 成员的默认值或限制范围有密切关系。
**本例只有一个Watchdog控制器,如有多个器件信息,则需要在device_info文件增加deviceNode信息,以及在watchdog_config文件中增加对应的器件属性**
。
-
device_info.hcs 配置参考
```
c
root
{
device_info
{
match_attr
=
"hdf_manager"
;
device_watchdog
::
device
{
// 设备节点
device0
::
deviceNode
{
// 驱动的DeviceNode节点
policy
=
1
;
// policy字段是驱动服务发布的策略,如果需要面向用户态,则为2
priority
=
20
;
// 驱动启动优先级
permission
=
0644
;
// 驱动创建设备节点权限
moduleName
=
"HDF_PLATFORM_WATCHDOG"
;
// 【必要】驱动名称,该字段的值必须和驱动入口结构的moduleName值一致
serviceName
=
"HDF_PLATFORM_WATCHDOG_0"
;
// 【必要且唯一】驱动对外发布服务的名称
deviceMatchAttr
=
"hisilicon_hi35xx_watchdog_0"
;
// 【必要】驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等
}
}
}
}
```
-
watchdog_config.hcs 配置参考
```
c
root
{
platform
{
template
watchdog_controller
{
//【必要】模板配置,继承该模板的节点如果使用模板中的默认值,则节点字段可以缺省
id
=
0
;
match_attr
=
""
;
regBase
=
0x12050000
;
//【必要】地址映射需要
regStep
=
0x1000
;
//【必要】地址映射需要
}
controller_0x12050000
::
watchdog_controller
{
//【必要】是作为设备驱动私有数据匹配的关键字
match_attr
=
"hisilicon_hi35xx_watchdog_0"
;
//【必要】必须和device_info.hcs中的deviceMatchAttr值一致
}
//存在多个 watchdog 时【必须】添加,否则不用
...
}
}
```
3.
完成驱动入口注册之后,最后一步就是以核心层WatchdogCntlr对象的初始化为核心,包括厂商自定义结构体(传递参数和数据),实例化WatchdogCntlr成员WatchdogMethod(让用户可以通过接口来调用驱动底层函数),实现HdfDriverEntry成员函数(Bind,Init,Release)
-
自定义结构体参考
> 从驱动的角度看,自定义结构体是参数和数据的载体,而且watchdog_config.hcs文件中的数值会被HDF读入通过DeviceResourceIface来初始化结构体成员,其中一些重要数值也会传递给核心层WatchdogCntlr对象,例如索引、管脚数等。
```
c
struct
Hi35xxWatchdog
{
struct
WatchdogCntlr
wdt
;
//【必要】是链接上下层的载体,具体描述见下面
OsalSpinlock
lock
;
//【可选】可挂载到 WatchdogCntlr 的lock成员上,两个是相同的定义
volatile
unsigned
char
*
regBase
;
//【必要】地址映射需要
uint32_t
phyBase
;
//【必要】地址映射需要
uint32_t
regStep
;
//【必要】地址映射需要
};
//WatchdogCntlr是核心层控制器结构体,其中的成员在Init函数中会被赋值
struct
WatchdogCntlr
{
struct
IDeviceIoService
service
;
//驱动服务,【无需挂载】
struct
HdfDeviceObject
*
device
;
//驱动设备,需要挂载 bind 函数的入参:struct HdfDeviceObject *device
OsalSpinlock
lock
;
//在HDF核心层调用时从系统代码实现了一个自旋锁的机制,挂载的变量需要是相同的变量,不建议挂载
struct
WatchdogMethod
*
ops
;
//接口回调函数
int16_t
wdtId
;
//WDG设备的识别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/framework/include/utils/hdf_base.h中HDF_STATUS 定义)
>
> |状态(值)|问题描述|
> |:-|:-:|
> |HDF_ERR_INVALID_OBJECT|找不到 WDG 设备|
> |HDF_ERR_MALLOC_FAIL |内存分配失败|
> |HDF_ERR_IO |I/O 错误|
> |HDF_SUCCESS |初始化成功|
> |HDF_FAILURE |初始化失败|
>
> **函数说明:**
> 初始化自定义结构体对象,初始化WatchdogCntlr成员,调用核心层WatchdogCntlrAdd函数。
```
c
//一般而言,初始化函数需要根据传入设备的属性实现 Hi35xxWatchdog 结构的填充,
//但此示例中的这部分集成在 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 成员,需要实现对应的钩子函数填充 WatchdogMethod
// 结构体的 getPriv 和 releasePriv 成员函数
hwdt
->
wdt
.
ops
=
&
g_method
;
//【必要】WatchdogMethod的实例化对象的挂载
hwdt
->
wdt
.
device
=
device
;
//【必要】使HdfDeviceObject与WatchdogcCntlr可以相互转化的前提
ret
=
WatchdogCntlrAdd
(
&
hwdt
->
wdt
);
//【必要】调用此函数填充核心层结构体,返回成功信号后驱动才完全接入平台核心层
if
(
ret
!=
HDF_SUCCESS
)
{
//不成功的话,需要反向执行初始化相关函数
OsalIoUnmap
((
void
*
)
hwdt
->
regBase
);
OsalMemFree
(
hwdt
);
return
ret
;
}
return
HDF_SUCCESS
;
}
```
-
**Release函数参考**
> **入参:**
> HdfDeviceObject :HDF框架给每一个驱动创建的设备对象,用来保存设备相关的私有数据和服务接口
>
> **返回值:**
> 无
>
> **函数说明:**
> 该函数需要在驱动入口结构体中赋值给Release,当HDF框架调用Init函数初始化驱动失败时,可以调用Release释放驱动资源。该函数中需包含释放内存和删除控制器等操作。所有强制转换获取相应对象的操作**前提**是在Init函数中具备对应赋值的操作。
```
c
static
void
Hi35xxWatchdogRelease
(
struct
HdfDeviceObject
*
device
)
{
struct
WatchdogCntlr
*
wdt
=
NULL
;
struct
Hi35xxWatchdog
*
hwdt
=
NULL
;
...
wdt
=
WatchdogCntlrFromDevice
(
device
);
//这里有HdfDeviceObject到MmcCntlr的强制转化,通过service成员(第一个成员),赋值见Bind函数
//return (device == NULL) ? NULL : (struct WatchdogCntlr *)device->service;
if
(
wdt
==
NULL
)
{
return
;
}
WatchdogCntlrRemove
(
wdt
);
//核心层函数,实际执行wdt->device->service = NULL以及cntlr->lock的释放
hwdt
=
(
struct
Hi35xxWatchdog
*
)
wdt
;
//这里有MmcCntlr到HimciHost的强制转化
if
(
hwdt
->
regBase
!=
NULL
)
{
//地址反映射
OsalIoUnmap
((
void
*
)
hwdt
->
regBase
);
hwdt
->
regBase
=
NULL
;
}
OsalMemFree
(
hwdt
);
//厂商自定义对象的内存释放
}
```
zh-cn/device-dev/driver/driver-platform.md
浏览文件 @
bc3eb53a
# 平台驱动<a name="ZH-CN_TOPIC_0000001111199424"></a>
# 平台驱动
使用
<a name="ZH-CN_TOPIC_0000001111199424"></a>
-
**[GPIO](driver-platform-gpio-des.md)**
...
...
zh-cn/device-dev/driver/figure/无服务模式.png
0 → 100755
浏览文件 @
bc3eb53a
39.3 KB
zh-cn/device-dev/driver/figure/独立服务模式.png
0 → 100755
浏览文件 @
bc3eb53a
51.0 KB
zh-cn/device-dev/driver/figure/统一服务模式.png
0 → 100755
浏览文件 @
bc3eb53a
57.2 KB
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录