linux笔记——看门狗内核API文档翻译
- wdt第二弹,内核API,勉强翻译下来了,还需要进一步整理
看门狗定时器驱动内核API
- 最后更新时间 2013.2.12
- Wim Van Sebroeck wim@iguana.be
介绍
- 本文档没有描述看门狗设备或驱动。也不涉及那些在用户空间调用的API,对应在 Documentation/watchdog/watchdog-api.txt
- 本文档描述的是,利用看门狗子系统框架方式进行看门狗驱动编写时所使用到的API。看门狗子系统框架提供了所有与用户空间交互的接口,不需要每次编写重复的代码。这意味这驱动程序只需要提供几个不同的操作函数,就可以控制WDT了。
API
每一个想要使用看门狗核心子系统的驱动程序都必须包含
#include<linux/watchdog.h>
(编写驱动程序是不得不做的工作)。linux/watchdog.h
包含以下两个注册/卸载函数:```shell
extern int watchdog_register_device(struct watchdog_device *);
extern void watchdog_unregister_device(struct watchdog_device *);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
* 注册函数将wdt设备注册进wdt子系统,函数的参数是一个指向struct watchdog_device的结构体_。当注册成功时返回0.注册失败返回一个负值。
* 卸载函数是将wdt设备从wdt子系统卸载,函数参数是一个指向struct watchdog_device的结构体_
* 看门子系统包含了注册时间调整机制(原文是 an registration deferral mechanism 一种延期机制?上下文不太对啊),允许在开机阶段,你可以按照你设定的尽可早的注册一个看门狗设备。
### struct watchdog_device_
* 这里粘贴的是linux4.1里定义的watchdog_device,原文的watchdog_device更长但跟源码对不起来,所以这里就以内核4.1里定义的为准了😑:
```c
struct watchdog_device {
int id;
struct cdev cdev;
struct device *dev;
struct device *parent;
const struct watchdog_info *info;
const struct watchdog_ops *ops;
unsigned int bootstatus;
unsigned int timeout;
unsigned int min_timeout;
unsigned int max_timeout;
void *driver_data;
struct mutex lock;
unsigned long status;
/* Bit numbers for status flags */
#define WDOG_ACTIVE 0 /* Is the watchdog running/active */
#define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */
#define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */
#define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */
#define WDOG_UNREGISTERED 4 /* Has the device been unregistered */
};包含以下字段:
- id:由watchdog_register_device函数注册。id 0是一个特殊的,id 0 有/dev/watchdog0 cdev(动态主设备号,次设备号0) 和 /dev/watchdog miscdev。id 在调用到watchdog_register_device时自动注册。
- NOTE看源码,wdt子系统是基于misc子系统的,注册wdt设备调用的是
misc_register(&watchdog_miscdev);
而misc设备主设备号只能为10,这里结论有冲突,等待解决中。。。
- NOTE看源码,wdt子系统是基于misc子系统的,注册wdt设备调用的是
- parent:在调用watchdog_register_device函数前,设置父设备(或者设置为null)
- info:指向watchdog_info结构体的指针,watchdog_info提供了看门狗本身的一些附加信息(像是看门狗独有的名称之类的)
- ops:指向watchdog_ops结构体的指针,watchdog_ops是看门狗支持操作(函数)的集合。
- bootstatus:启动后设备状态(与看门狗WDIOF_* 标志位一同开启)_
- timeout:看门狗超时的时间(秒为单位).如果设置了WDOG_ACTIVE_启用了看门狗,在这个时间长度后,用户空间还没有发送心跳包,看门狗会将系统复位重启.
- min_timeout:_可设置的看门狗最小超时时间
- max_timeout:_可设置的看门狗最大超时时间
- driver_data:_指向看门狗设备私有数据的指针,这个data只能由watchdog_set_drvdata 和 watchdog_get_drvdata routines函数访问.
- struct mutex lock; 原文档没有🙄
- status:这个字段包含了许多状态位,提供有关设备的额外信息(例如:看门狗的活动状态\限制,现在nowayout 位设置与否)(括号里翻译是否准确存疑)
- id:由watchdog_register_device函数注册。id 0是一个特殊的,id 0 有/dev/watchdog0 cdev(动态主设备号,次设备号0) 和 /dev/watchdog miscdev。id 在调用到watchdog_register_device时自动注册。
struct watchdog_ops
watchdog_ops_
1
2
3
4
5
6
7
8
9
10
11
12
13
14struct watchdog_ops {
struct module *owner;
/* mandatory operations */
int (*start)(struct watchdog_device *);
int (*stop)(struct watchdog_device *);
/* optional operations */
int (*ping)(struct watchdog_device *);
unsigned int (*status)(struct watchdog_device *);
int (*set_timeout)(struct watchdog_device *, unsigned int);
unsigned int (*get_timeleft)(struct watchdog_device *);
void (*ref)(struct watchdog_device *);
void (*unref)(struct watchdog_device *);
long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
};你一开始定义的看门狗的module owner是非常重要的,module owner 在使用看门狗时会同时锁定看门狗设备.(这样避免了在卸载模块时/dev/watchdog依然是打开状态引起的系统崩溃)
某些函数是必须实现的,其他是可选的,必须实现的函数如下:
- *start :*指向看门狗设备启动函数的指针.这个函数参数为一个 struct watchdog_device,_成功返回0,失败返回负数.
- *stop :*通过这个函数关闭看门狗设备.这个函数参数为一个 struct watchdog_device,_成功返回0,失败返回负数.
- 一些硬件看门狗设备只能启动,没有关闭选项.对应这些设备的驱动,不用实现 *stop ,*如果驱动程序没有关闭设备功能,看门狗核心层会在设备关闭后设置 WDOG_HW_RUNNING 并调用驱动程序的 keepalive pings功能.
- 如果看门狗驱动没有实现stop必须设置max_hw_heartbeat_ms_
不是所有的硬件看门狗都有相同的功能,这就是为什么会有可选函数了,只有但这项功能被硬件看门🐶支持时,才编写对应函数.
- ping:这是看门狗驱动实现的喂狗函数,这个函数的参数时一个指向struct watchdog_device的指针_.函数执行成功返回0,失败返回负数.
- 大多数的硬件不支持直接喂狗的特殊功能,一般是直接重启硬件看门狗.
- 看门狗子系统的核心层也是这样做的:但定期喂狗的操作转发到硬件看门狗时,核心层在 ping 实现时调用喂狗函数,但喂狗函数没有实现时,使用 start 对应函数
- ( WDIOC_KEEPALIVE对应的 ioctl命令只有在i看门狗info里的WDIOF_KEEPALIVEPING被设置为1后才会生效 )
- status: 这个函数用来检查看门狗设备的状态,通过看门狗的WDIOF_*_状态标志位报告.WDIOF_MAGICCLOSE and WDIOF_KEEPALIVEPING由看门狗核心层报告.没有必要在驱动程序中报告.此外如果驱动程序没有实现该函数,看门狗核心层会返回 struct watchdog_device_中的bootstatus状态位.
- set_timeout:这个函数设置和检查 超时时间的变化,返回0表示成功,返回-EINVAL表示超出范围,返回-EIO表示无法写入看门狗.设置成功后,函数应该将设定的超时时间写入看门狗.(或许与通过接口获取的超时时间不同,因为看门狗的分辨率不一定能到1s).
- 实现了max_hw_heartbeat_ms的驱动将硬件看门狗心跳值设置为超时时间和max_hw_heartbeat_ms之间的最小值.Those drivers set the timeout value of the watchdog_device either to the requested timeout value (if it is larger than max_hw_heartbeat_ms), or to the achieved timeout value.
- 如果看门狗驱动程序没有执行任何操作,但设置了watchdog_device.timeout_,此回掉函数忽略.
- 如果没有设置 set_timeout,但设置了WDIOF_SETTIMEOUT,看门狗架构会将 watchdog_device中timeout值更新为请求的值.
- 如果使用了pretimeout feature (WDIOF_PRETIMEOUT),那么set_timeout 必须同时检查 pretimeout 是否有效 ,设置相应定时器. 这些操作在核心层没有races无法完成,所以必须在驱动中完成.
- set_pretimeout_:这个函数支持检查/改变看门狗中pretimeout值(pretimeout详情见wdt用户层api翻译).这个函数是可选支持的,因为不是所有的看门狗都支持这个功能.pretimeout 不是绝对时间,其数值是在超时时间之前几秒发生.
- 返回0表示执行成功,返回-EINVAL,表示超出范围,返回-EIO表示未能成功向看门狗写入.
- 设置pretimeou为0值,代表禁用pretimeout功能.(WDIOF_PRETIMEOUT_需要在看门狗的info 结构体中设置)
- 如果驱动没有实现任何功能,但设定了 watchdog_device.pretimeout_,此回掉函数忽略.这意味这,如果没有提供set_pretimeout_函数,但设定了WDIOF_PRETIMEOUT_,看门狗架构会将pretimeout的值更新为请求值.
- get_timeleft:_这个函数返回复位前剩余时间.
- restart:此函数重启看门狗硬件,返回0表示成功,失败返回对应错误码.
- ioctl:如果这个函数存在,在系统进行内部ioctl命令处理前,会首先调用此函数.如果不支持ioctrl命令,应该返回 -ENOIOCTLCMD .这个函数的参数为(watchdog_device, cmd and arg)_
- ref和unref 已经不再被支持.
- ping:这是看门狗驱动实现的喂狗函数,这个函数的参数时一个指向struct watchdog_device的指针_.函数执行成功返回0,失败返回负数.
The status bits should (preferably) be set with the set_bit and clear_bit alike bit-operations. The status bits that are defined are:
WDOG_ACTIVE:_这个状态位标识着从用户空间看一个看门狗设备是否被激活,如果被激活,用户空间需要执行喂狗动作
WDOG_NO_WAY_OUT:_这个状态位标识这nowayout的设置,如果被激活,那看门狗启动后,无法被停止.
WDOG_HW_RUNNING:如果硬件看门狗运行,此状态位被置1.
- 当硬件看门狗不能被停止时,这个状态位必须被置1.
- 如果系统启动后自动启动看门狗,在看门狗打开前,必须设置此状态位.
- 当此状态位被置1,即使WDOG_ACTIVE为0_.看门狗架构也会执行喂狗动作.
- (当你直接注册设置了 WDOG_HW_RUNNING位的看门狗设备时,执行open /dev/watchdog动作,系统会直接跳过启动操作,直接执行喂狗操作 )
要设置WDOG_NO_WAY_OUT_状态位(在注册看门狗设备前)你可以:
在watchdog_device_的结构体中设置
1
.status = WATCHDOG_NOWAYOUT_INIT_STATUS,
这样等同于将WDOG_NO_WAY_OUT_值设置为CONFIG_WATCHDOG_NOWAYOUT
使用下面的函数
1
static inline void watchdog_set_nowayout(struct watchdog_device *wdd, int nowayout)
(note:) wdt驱动程序核心支持magic close和 nowayout功能.要使用magic close,需要在看门狗的 info中设置WDIOF_MAGICCLOSE_位.开启 nowayout 会覆盖magic close.
获取或设定驱动程序特殊数据,应该使用以下两个函数:
1
2static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data)
static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)- watchdog_set_drvdata函数允许你添加向驱动程序添加特殊数据,此函数的参数为指向看门狗设备的指针,和指向需要添加数据的指针.
- watchdog_get_drvdata函数运行你读取驱动程序中的特殊数据,此函数的参数为指向你想读取的看门狗设备的指针.
初始化 timeout ,会用到下面的函数
1
extern int watchdog_init_timeout(struct watchdog_device *wdd,unsigned int timeout_parm, struct device *dev);
- watchdog_init_timeout允许使用模块的 timeout 字段初始化 timeout ,当模块的 timeout 字段无效时,设备树中的timeout-sec也可.最好的做法是将watchdog_device_中timeout的值,设置为初始默认值,然后使用到函数的用户可以分别设置自定义值.
重启后禁用看门狗,需要使用以下函数:
1
static inline void watchdog_stop_on_reboot(struct watchdog_device *wdd);
卸载看门狗设备时禁用看门狗,必须调用此函数,如果 nowayout 没有设置,这个函数只会停止看门狗.
1
static inline void watchdog_stop_on_unregister(struct watchdog_device *wdd);
更改重启处理程序的优先级,(猜测是不同看门狗的优先级)调用下面的函数:
1
oid watchdog_set_restart_priority(struct watchdog_device *wdd, int priority);
- 设定优先级时,用户需要遵循以下规则
- 0:优先级最低,非常有限的重启能力
- 128:重启处理的默认选择,如果没有其他的重启处理程序可用,或者没有重启整个系统的重启处理程序可用.
- 255:最高优先级,会覆盖其他所有重启处理程序
- 设定优先级时,用户需要遵循以下规则
使用pretimeout 通知功能,需要利用以下函数:
1
void watchdog_notify_pretimeout(struct watchdog_device *wdd)
- 这个函数可以在中断上下文中调用,如果启用了watchdog pretimeout governor 框架(kbuild CONFIG_WATCHDOG_PRETIMEOUT_GOV),就会采用进入驱动程序分配好的处理程序,如果没有启用watchdog pretimeout governor 框架,watchdog_notify_pretimeout()会将信息输出到内核日志缓存.