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,这里结论有冲突,等待解决中。。。
    • 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_

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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);
    };
  • 你一开始定义的看门狗的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_的结构体中设置

        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
    2
    static 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()会将信息输出到内核日志缓存.