- 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
包含以下两个注册/卸载函数:extern int watchdog_register_device(struct watchdog_device *); extern void watchdog_unregister_device(struct watchdog_device *);
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 */ };1
2
3
4
5
6* 注册函数将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里定义的为准了😑:struct 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); };1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17* 包含以下字段:
* 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,这里结论有冲突,等待解决中。。。
* 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 位设置与否)(括号里翻译是否准确存疑)
### struct watchdog_ops
* watchdog_ops_.status = WATCHDOG_NOWAYOUT_INIT_STATUS,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
32
33
34
35
36
37
38* 你一开始定义的看门狗的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 已经不再被支持.
* 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_的结构体中设置static inline void watchdog_set_nowayout(struct watchdog_device *wdd, int nowayout)1
2这样等同于将WDOG_NO_WAY_OUT_值设置为CONFIG_WATCHDOG_NOWAYOUT
* 使用下面的函数static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data) static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)1
2
3* (note:) wdt驱动程序核心支持magic close和 nowayout功能.要使用magic close,需要在看门狗的 info中设置WDIOF_MAGICCLOSE _位.开启 nowayout 会覆盖magic close.
* 获取或设定驱动程序特殊数据,应该使用以下两个函数:extern int watchdog_init_timeout(struct watchdog_device *wdd,unsigned int timeout_parm, struct device *dev);1
2
3
4
5
* watchdog_set_drvdata函数允许你添加向驱动程序添加特殊数据,此函数的参数为指向看门狗设备的指针,和指向需要添加数据的指针.
* watchdog_get_drvdata函数运行你读取驱动程序中的特殊数据,此函数的参数为指向你想读取的看门狗设备的指针.
* 初始化 timeout ,会用到下面的函数static inline void watchdog_stop_on_reboot(struct watchdog_device *wdd);1
2
3
4
* watchdog_init_timeout允许使用模块的 timeout 字段初始化 timeout ,当模块的 timeout 字段无效时,设备树中的timeout-sec也可.最好的做法是将watchdog_device_中timeout的值,设置为初始默认值,然后使用到函数的用户可以分别设置自定义值.
* 重启后禁用看门狗,需要使用以下函数:static inline void watchdog_stop_on_unregister(struct watchdog_device *wdd);1
* 卸载看门狗设备时禁用看门狗,必须调用此函数,如果 nowayout 没有设置,这个函数只会停止看门狗.
oid watchdog_set_restart_priority(struct watchdog_device *wdd, int priority);1
2
* 更改重启处理程序的优先级,(猜测是不同看门狗的优先级)调用下面的函数:void watchdog_notify_pretimeout(struct watchdog_device *wdd)1
2
3
4
5
6* 设定优先级时,用户需要遵循以下规则
* 0:优先级最低,非常有限的重启能力
* 128:重启处理的默认选择,如果没有其他的重启处理程序可用,或者没有重启整个系统的重启处理程序可用.
* 255:最高优先级,会覆盖其他所有重启处理程序
* 使用pretimeout 通知功能,需要利用以下函数:- 这个函数可以在中断上下文中调用,如果启用了watchdog pretimeout governor 框架(kbuild CONFIG_WATCHDOG_PRETIMEOUT_GOV),就会采用进入驱动程序分配好的处理程序,如果没有启用watchdog pretimeout governor 框架,watchdog_notify_pretimeout()会将信息输出到内核日志缓存.