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包含以下两个注册/卸载函数:

  • extern int watchdog_register_device(struct watchdog_device *);
    extern void watchdog_unregister_device(struct watchdog_device *);
    
    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_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
    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_
    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
    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_的结构体中设置
    .status = WATCHDOG_NOWAYOUT_INIT_STATUS,
    1
    2
      这样等同于将WDOG_NO_WAY_OUT_值设置为CONFIG_WATCHDOG_NOWAYOUT
    * 使用下面的函数
    static inline void watchdog_set_nowayout(struct watchdog_device *wdd, int nowayout)
    1
    2
    3
    * (note:)   wdt驱动程序核心支持magic close和 nowayout功能.要使用magic close,需要在看门狗的 info中设置WDIOF_MAGICCLOSE _位.开启 nowayout 会覆盖magic close.

    * 获取或设定驱动程序特殊数据,应该使用以下两个函数:
    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
    4
    5

    * watchdog_set_drvdata函数允许你添加向驱动程序添加特殊数据,此函数的参数为指向看门狗设备的指针,和指向需要添加数据的指针.
    * watchdog_get_drvdata函数运行你读取驱动程序中的特殊数据,此函数的参数为指向你想读取的看门狗设备的指针.

    * 初始化 timeout ,会用到下面的函数
    extern int watchdog_init_timeout(struct watchdog_device *wdd,unsigned int timeout_parm, struct device *dev);
    1
    2
    3
    4

    * watchdog_init_timeout允许使用模块的 timeout 字段初始化 timeout ,当模块的 timeout 字段无效时,设备树中的timeout-sec也可.最好的做法是将watchdog_device_中timeout的值,设置为初始默认值,然后使用到函数的用户可以分别设置自定义值.

    * 重启后禁用看门狗,需要使用以下函数:
    static inline void watchdog_stop_on_reboot(struct watchdog_device *wdd);
    1
    * 卸载看门狗设备时禁用看门狗,必须调用此函数,如果 nowayout 没有设置,这个函数只会停止看门狗.
    static inline void watchdog_stop_on_unregister(struct watchdog_device *wdd);
    1
    2

    * 更改重启处理程序的优先级,(猜测是不同看门狗的优先级)调用下面的函数:
    oid watchdog_set_restart_priority(struct watchdog_device *wdd, int priority);
    1
    2
    3
    4
    5
    6
      * 设定优先级时,用户需要遵循以下规则
    * 0:优先级最低,非常有限的重启能力
    * 128:重启处理的默认选择,如果没有其他的重启处理程序可用,或者没有重启整个系统的重启处理程序可用.
    * 255:最高优先级,会覆盖其他所有重启处理程序

    * 使用pretimeout 通知功能,需要利用以下函数:
    void watchdog_notify_pretimeout(struct watchdog_device *wdd)
    • 这个函数可以在中断上下文中调用,如果启用了watchdog pretimeout governor 框架(kbuild CONFIG_WATCHDOG_PRETIMEOUT_GOV),就会采用进入驱动程序分配好的处理程序,如果没有启用watchdog pretimeout governor 框架,watchdog_notify_pretimeout()会将信息输出到内核日志缓存.