• 嵌入式Linux驱动开发9---WTD驱动程序以及测试过程记录


    前提 开发环境

    Chip : TsingMicro
    OS : ubuntu20.04
    Compiler : gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf
    Test Mechine : HAPS (FPGA)

    概要 WTD驱动程序开发过程分为四个部分

    1. WTD概念,原理以及配置方式的学习
    2. 硬件操作的代码编写
    3. 驱动框架的理解
    4. 测试

    一 WTD的概念

    WTD全称WatchDog,用于对计算机系统的自动复位。计算机在工作时容易受到各种干扰,导致计算机程序进入死循环,跑飞,或者死机崩溃。看门狗这种硬件用于解救这种情况下的计算机。

    看门狗的工作逻辑如下:
    初始化时配置初值,频率,以及减到0时是否要产生中断等
    开始计数后可以选择在计数减为0之前重新设置初值,也就是所谓的Tick Dog,俗称喂狗,系统正常工作
    如果系统崩溃则无法置初值,WTD在计数减为0时会触发系统复位,从而保证系统工作
    在这里插入图片描述

    二 WTD 首要寄存器

    WDT_CR寄存器:

    Offset address: 0x00, Control Register.
    对RPL域配置,设置pclk cycles 这个值决定了时钟频率,也就是计数快慢
    对RMOD配置,设置响应模式,决定计数到0值时产生系统复位还是产生中断
    对WDT_EN域配置,控制WDT的使能或失能
    在这里插入图片描述

    WDT_TORR寄存器

    Offset address = 0x04, Timeout Range Register.
    在这里插入图片描述
    对TOP_INIT域配置,初始化超时值,在系统复位之前或者之后被写入

    一套简单的配置

    1. 配置WDT_CR 失能WDT
    2. 配置WDT_TORR 设置WDT计数初始值
    3. 配置WDT_CR 使能WDT

    一套复杂的配置

    使用用户定义的计数器配置WDT:

    1. 通过写入看门狗控制寄存器WDT_CR禁用WDT。
    2. 配置WDT_TORR / WDT_TORR_USR / WDT_TORR_USR_INIT。
    3. 配置WDT_PAUSE在默认值和用户定义的超时值之间切换
      价值。
    4. 通过写入WDT_CR来启用WDT。
    5. 通过写入WDT_PAUSE暂停WDT。
    6. 通过写入WDT_PAUSE释放暂停,WDT继续工作。

    三 硬件操作部分的代码

    需实现的驱动接口列表

    包含启动,停止,对WTD设值,喂狗,获取计数剩余值,重启。

    static const struct watchdog_ops dw_wdt_ops = {
    	.owner		= THIS_MODULE,
    	.start		= dw_wdt_start,
    	.stop		= dw_wdt_stop,
    	.ping		= dw_wdt_ping,
    	.set_timeout	= dw_wdt_set_timeout,
    	.get_timeleft	= dw_wdt_get_timeleft,
    	.restart	= dw_wdt_restart,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    硬件相关的宏定义 以及 WDT参数的定义

    这部分代码包含了以下信息:
    寄存器以及其偏移值的映射
    时钟的PLCK一个脉冲的长度
    响应模式
    最大可设超时时间
    默认超时时间
    是否外界可以关闭WTD 作为模块参数可由用户加载模块式配置
    wdt结构体 其继承了watchdog_device

    //寄存器以及其偏移值的映射
    #define WDOG_CONTROL_REG_OFFSET				0x00
    #define WDOG_CONTROL_REG_WDT_EN_MASK		0x01
    #define WDOG_CONTROL_REG_RESP_MODE_MASK		0x02
    #define WDOG_CONTROL_REG_RESP_PULSE_LENGTH_MASK		0x07
    #define WDOG_CONTROL_REG_RESP_PULSE_LENGTH_POS		(2)
    #define WDOG_CONTROL_REG_RESET_MODE_MASK		0x1
    #define WDOG_CONTROL_REG_RESET_MODE_POS			(1)
    
    
    #define WDOG_TIMEOUT_RANGE_REG_OFFSET		0x04
    #define WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT	4
    
    #define WDOG_CURRENT_COUNT_REG_OFFSET		0x08
    #define WDOG_COUNTER_RESTART_REG_OFFSET		0x0c
    #define WDOG_COUNTER_RESTART_KICK_VALUE		0x76
    
    #define WDOG_CONTROL_REG_CLEAR_INT			0x14
    
    //时钟的PLCK一个脉冲的长度
    typedef enum {
    	WDT_ResetPulseLength_2_PCLK_CYCLES = 0,
    	WDT_ResetPulseLength_4_PCLK_CYCLES,
    	WDT_ResetPulseLength_8_PCLK_CYCLES,
    	WDT_ResetPulseLength_16_PCLK_CYCLES,
    	WDT_ResetPulseLength_32_PCLK_CYCLES,
    	WDT_ResetPulseLength_64_PCLK_CYCLES,
    	WDT_ResetPulseLength_128_PCLK_CYCLES,
    	WDT_ResetPulseLength_256_PCLK_CYCLES,
    } eWDT_ResetPulseLength_t;
    
    //响应模式
    typedef enum {
    	WDT_SYSTEM_RESET = 0,
    	WDT_INTERRUPT,
    } eWDT_ResponseMode_t;
    
    //最大可设超时时间
    /* The maximum TOP (timeout period) value that can be set in the watchdog. */
    #define DW_WDT_MAX_TOP		15
    //默认超时时间
    #define DW_WDT_DEFAULT_SECONDS	5
    #define DW_WDT_DEFAULT_RESET_PULSE_LENGTH	WDT_ResetPulseLength_64_PCLK_CYCLES
    #define DW_WDT_DEFAULT_RESET_MODE	WDT_SYSTEM_RESET
    
    //是否外界可以关闭WTD  作为模块参数可由用户加载模块式配置
    static bool nowayout = WATCHDOG_NOWAYOUT;
    module_param(nowayout, bool, 0);
    MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
    		 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
    
    //wdt结构体 继承了watchdog_device
    struct dw_wdt {
    	void __iomem		*regs;
    	struct clk		*clk;
    	unsigned long		rate;
    	struct watchdog_device	wdd;
    	struct reset_control	*rst;
    };
    
    #define to_dw_wdt(wdd)	container_of(wdd, struct dw_wdt, wdd)
    
    • 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
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    实现硬件操作的接口

    按照调用关系排列
    最基础的函数是dw_wdt_set_timeout

    dw_wdt_set_timeout

    该函数采取迭代的方法,先将用户所传入的top_s(秒)值转化为

    static int dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s)
    {
    	struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
    	int i, top_val = DW_WDT_MAX_TOP;
    
    	/*
    	 * Iterate over the timeout values until we find the closest match. We
    	 * always look for >=.
    	 */
    	for (i = 0; i <= DW_WDT_MAX_TOP; ++i)
    		if (dw_wdt_top_in_seconds(dw_wdt, i) >= top_s) {
    			top_val = i;
    			break;
    		}
    
    	/*
    	 * Set the new value in the watchdog.  Some versions of dw_wdt
    	 * have have TOPINIT in the TIMEOUT_RANGE register (as per
    	 * CP_WDT_DUAL_TOP in WDT_COMP_PARAMS_1).  On those we
    	 * effectively get a pat of the watchdog right here.
    	 */
    	writel(top_val | top_val << WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT,
    		   dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
    
    	wdd->timeout = dw_wdt_top_in_seconds(dw_wdt, top_val);
    
    	return 0;
    }
    
    • 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

    dw_wdt_ping

    ping函数主要用于喂狗,往上面所说的寄存器里写值即可

    static int dw_wdt_ping(struct watchdog_device *wdd)
    {
    	struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
    
    	writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt->regs +
    		   WDOG_COUNTER_RESTART_REG_OFFSET);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    dw_wdt_arm_system_reset

    系统重启函数:配置了失能中断模式,计数到0时系统总是会重启
    配置WDT_CR寄存器的EN域,使能watchdog

    static void dw_wdt_arm_system_reset(struct dw_wdt *dw_wdt)
    {
    
    	u32 val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
    
    	/* Disable interrupt mode; always perform system reset. */
    	val &= ~(WDOG_CONTROL_REG_RESET_MODE_MASK << WDOG_CONTROL_REG_RESET_MODE_POS);
    	val |= (DW_WDT_DEFAULT_RESET_MODE << WDOG_CONTROL_REG_RESET_MODE_POS);
    
    	/* Enable watchdog. */
    	val |= WDOG_CONTROL_REG_WDT_EN_MASK;
    	writel(val, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    dw_wdt_start

    wdt的开启函数,调用上面俩函数,设置超时值后将,配置响应模式,系统重启

    static int dw_wdt_start(struct watchdog_device *wdd)
    {
    	struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
    
    	dw_wdt_set_timeout(wdd, wdd->timeout);
    	dw_wdt_arm_system_reset(dw_wdt);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    dw_wdt_stop

    配置wdt的停止函数

    static int dw_wdt_stop(struct watchdog_device *wdd)
    {
    	struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
    	if (!dw_wdt->rst) {
    		set_bit(WDOG_HW_RUNNING, &wdd->status);
    		return 0;
    	}
    	reset_control_assert(dw_wdt->rst);
    	reset_control_deassert(dw_wdt->rst);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    四 驱动框架的理解

    内核单独给WatchDog准备了一套框架,与Platform平台设备类似,WatchDog框架包含以下三部分:
    驱动层watchdog_drv — 核心层watchdog_core — 设备层watchdog_ dev

    前面章节遇到的框架中一般都是要自己手动创建节点或者通过程序运行时自动创建节点
    对于watchdog比较特别,节点创建并登记的函数(watchdog_cdev_register)已经定义在watchdog_dev.c中,并在watchdog_dev_register函数中调用,
    而watchdog_dev_register又是被__watchdog_register_device调用的,
    而__watchdog_register_devicer又是被watchdog_register_device调用。
    所以这个函数watchdog_register_device才是留给我们调用注册节点的接口,需要在probe函数中调用

    节点注册的调用关系图

    probe函数-->
    	watchdog_register_device-->
    		__watchdog_register_device-->
    			watchdog_dev_register-->
    				watchdog_cdev_register-->
    					watchdog_miscdev.parent = wdd->parent;
    					err = misc_register(&watchdog_miscdev);
    					-----------------------
    					cdev_init(&wd_data->cdev, &watchdog_fops);
    					err = cdev_device_add(&wd_data->cdev, &wd_data->dev);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    可见这里层级调用中watchdog_cdev_register注册了一个混杂设备watchdog_miscdev

    static struct miscdevice watchdog_miscdev = {
    	.minor		= WATCHDOG_MINOR,
    	.name		= "watchdog",
    	.fops		= &watchdog_fops,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其fops如下:
    可以看到这里就是应用层调用文件操作函数时最后会调用到的相应的函数接口

    static const struct file_operations watchdog_fops = {
    	.owner		= THIS_MODULE,
    	.write		= watchdog_write,
    	.unlocked_ioctl	= watchdog_ioctl,
    	.open		= watchdog_open,
    	.release	= watchdog_release,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    实现fops里的接口

    write实现

    拿write接口的实现为例:
    最主要的功能就是调用了硬件操作部分已经实现的 watchdog_ping函数,达到喂狗的目的。

    static ssize_t watchdog_write(struct file *file, const char __user *data,
    						size_t len, loff_t *ppos)
    {
    	struct watchdog_core_data *wd_data = file->private_data;
    	struct watchdog_device *wdd;
    	int err;
    	size_t i;
    	char c;
    
    	if (len == 0)
    		return 0;
    
    	/*
    	 * Note: just in case someone wrote the magic character
    	 * five months ago...
    	 */
    	clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status);
    
    	/* scan to see whether or not we got the magic character */
    	for (i = 0; i != len; i++) {
    		if (get_user(c, data + i))
    			return -EFAULT;
    		if (c == 'V')
    			set_bit(_WDOG_ALLOW_RELEASE, &wd_data->status);
    	}
    
    	/* someone wrote to us, so we send the watchdog a keepalive ping */
    
    	err = -ENODEV;
    	mutex_lock(&wd_data->lock);
    	wdd = wd_data->wdd;
    	if (wdd)
    		err = watchdog_ping(wdd);
    	mutex_unlock(&wd_data->lock);
    
    	if (err < 0)
    		return err;
    
    	return len;
    }
    
    • 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
    • 39
    • 40

    ioctl的实现

    可以将ioctl看作一个控制器,(约定好底层驱动对应哪个命令)用户调用这个函数,并指定使用哪个命令,就可以调用到哪个函数

    /*
     *    watchdog_ioctl: handle the different ioctl's for the watchdog device.
     *    @file: file handle to the device
     *    @cmd: watchdog command
     *    @arg: argument pointer
     *
     *    The watchdog API defines a common set of functions for all watchdogs
     *    according to their available features.
     */
     
    static long watchdog_ioctl(struct file *file, unsigned int cmd,
                                unsigned long arg)
    {
        struct watchdog_core_data *wd_data = file->private_data;
        void __user *argp = (void __user *)arg;
        struct watchdog_device *wdd;
        int __user *p = argp;
        unsigned int val;
        int err;
     
        mutex_lock(&wd_data->lock);
     
        wdd = wd_data->wdd;
        if (!wdd) {
            err = -ENODEV;
            goto out_ioctl;
        }
     
        err = watchdog_ioctl_op(wdd, cmd, arg);
        if (err != -ENOIOCTLCMD)
            goto out_ioctl;
     
        switch (cmd) {
        case WDIOC_GETSUPPORT:
            err = copy_to_user(argp, wdd->info,
                sizeof(struct watchdog_info)) ? -EFAULT : 0;
            break;
        case WDIOC_GETSTATUS:
            val = watchdog_get_status(wdd);
            err = put_user(val, p);
            break;
        case WDIOC_GETBOOTSTATUS:
            err = put_user(wdd->bootstatus, p);
            break;
        case WDIOC_SETOPTIONS:
            if (get_user(val, p)) {
                err = -EFAULT;
                break;
            }
            if (val & WDIOS_DISABLECARD) {
                err = watchdog_stop(wdd);
                if (err < 0)
                    break;
            }
            if (val & WDIOS_ENABLECARD)
                err = watchdog_start(wdd);
            break;
        case WDIOC_KEEPALIVE:
            if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) {
                err = -EOPNOTSUPP;
                break;
            }
            err = watchdog_ping(wdd);
            break;
        case WDIOC_SETTIMEOUT:
            if (get_user(val, p)) {
                err = -EFAULT;
                break;
            }
            err = watchdog_set_timeout(wdd, val);
            if (err < 0)
                break;
            /* If the watchdog is active then we send a keepalive ping
             * to make sure that the watchdog keep's running (and if
             * possible that it takes the new timeout) */
            err = watchdog_ping(wdd);
            if (err < 0)
                break;
            /* Fall */
        case WDIOC_GETTIMEOUT:
            /* timeout == 0 means that we don't know the timeout */
            if (wdd->timeout == 0) {
                err = -EOPNOTSUPP;
                break;
            }
            err = put_user(wdd->timeout, p);
            break;
        case WDIOC_GETTIMELEFT:
            err = watchdog_get_timeleft(wdd, &val);
            if (err < 0)
                break;
            err = put_user(val, p);
            break;
        case WDIOC_SETPRETIMEOUT:
            if (get_user(val, p)) {
                err = -EFAULT;
                break;
            }
            err = watchdog_set_pretimeout(wdd, val);
            break;
        case WDIOC_GETPRETIMEOUT:
            err = put_user(wdd->pretimeout, p);
            break;
        default:
            err = -ENOTTY;
            break;
        }
     
    out_ioctl:
        mutex_unlock(&wd_data->lock);
        return err;
    }
    
    • 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
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112

    匹配

    如前面章节剖析的一样,probe函数如何才能被调用?
    答:设备树上注册的.compatible和driver里的.of_match_table能够被匹配上

    static const struct of_device_id dw_wdt_of_match[] = {
        { .compatible = "snps,dw-wdt", },
        { /* sentinel */ }
    };
    MODULE_DEVICE_TABLE(of, dw_wdt_of_match);
     
    static struct platform_driver dw_wdt_driver = {
        .probe        = dw_wdt_drv_probe,
        .remove        = dw_wdt_drv_remove,
        .driver        = {
            .name    = "dw_wdt",
            .of_match_table = of_match_ptr(dw_wdt_of_match),
            .pm    = &dw_wdt_pm_ops,
        },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    五 编写测试App

    应用层代码需要一一检验是否驱动功能已经全部实现:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
     
    //watchdog 
    #define WATCHDOG_IOCTL_BASE     'W'
     
    struct watchdog_info {
        unsigned int options;          /* Options the card/driver supports */
        unsigned int firmware_version; /* Firmware version of the card */
        char identity[32];     /* Identity of the board */
    };
     
    #define WDIOC_GETSUPPORT        _IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)
    #define WDIOC_GETSTATUS         _IOR(WATCHDOG_IOCTL_BASE, 1, int)
    #define WDIOC_GETBOOTSTATUS     _IOR(WATCHDOG_IOCTL_BASE, 2, int)
    #define WDIOC_GETTEMP           _IOR(WATCHDOG_IOCTL_BASE, 3, int)
    #define WDIOC_SETOPTIONS        _IOR(WATCHDOG_IOCTL_BASE, 4, int)
    #define WDIOC_KEEPALIVE         _IOR(WATCHDOG_IOCTL_BASE, 5, int)
    #define WDIOC_SETTIMEOUT        _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
    #define WDIOC_GETTIMEOUT        _IOR(WATCHDOG_IOCTL_BASE, 7, int)
    #define WDIOC_SETPRETIMEOUT     _IOWR(WATCHDOG_IOCTL_BASE, 8, int)
    #define WDIOC_GETPRETIMEOUT     _IOR(WATCHDOG_IOCTL_BASE, 9, int)
    #define WDIOC_GETTIMELEFT       _IOR(WATCHDOG_IOCTL_BASE, 10, int)
     
    #define WDIOF_OVERHEAT          0x0001  /* Reset due to CPU overheat */
    #define WDIOF_FANFAULT          0x0002  /* Fan failed */
    #define WDIOF_EXTERN1           0x0004  /* External relay 1 */
    #define WDIOF_EXTERN2           0x0008  /* External relay 2 */
    #define WDIOF_POWERUNDER        0x0010  /* Power bad/power fault */
    #define WDIOF_CARDRESET         0x0020  /* Card previously reset the CPU */
    #define WDIOF_POWEROVER         0x0040  /* Power over voltage */
    #define WDIOF_SETTIMEOUT        0x0080  /* Set timeout (in seconds) */
    #define WDIOF_MAGICCLOSE        0x0100  /* Supports magic close char */
    #define WDIOF_PRETIMEOUT        0x0200  /* Pretimeout (in seconds), get/set */
    #define WDIOF_KEEPALIVEPING     0x8000  /* Keep alive ping reply */
     
    #define WDIOS_DISABLECARD       0x0001  /* Turn off the watchdog timer */
    #define WDIOS_ENABLECARD        0x0002  /* Turn on the watchdog timer */
    #define WDIOS_TEMPPANIC         0x0004  /* Kernel panic on temperature trip */
     
    int wdt_fd;
    int time_out = 5;
    #define DEFAULT_PING_RATE    1
    void stop_signal()
    {
        int val = 0 , ret = 0 ;
     
        val = WDIOS_DISABLECARD ;
        ret = ioctl(wdt_fd, WDIOC_SETOPTIONS, &val) ;
        if (ret < 0)
            printf("ioctl WDIOC_GETSUPPORT failed with %d.\n", ret);
     
        printf("===watchdow will be closed===\n") ;
        close(wdt_fd) ;
        exit(0);
        
    }
     
    int main(int argc, char *argv[])
    {
        int ret;
        static int count = 0;
        struct watchdog_info wdt_info;
        unsigned int ping_rate = DEFAULT_PING_RATE;
     
        signal(SIGINT, stop_signal) ;
     
        wdt_fd = open("/dev/watchdog0", O_RDWR);
        if(wdt_fd < 0)
        {
            printf("open /dev/watchdog0 failed.\n");
        }
     
        /* get watchdog infomation struct */
        ret = ioctl(wdt_fd, WDIOC_GETSUPPORT, &wdt_info);
        if (ret < 0)
            printf("ioctl WDIOC_GETSUPPORT failed.\n");
        else
        {
            printf("options = 0x%x,id = %s\n", wdt_info.options, wdt_info.identity);
        }
     
        ioctl(wdt_fd, WDIOC_SETTIMEOUT, &time_out);
        if (ret < 0)
            printf("ioctl WDIOC_SETTIMEOUT failed.\n");
        
        while(1)
        {
            
            if(count > 10)
            {
                printf("unfood watchdog, count = %d \n",count++);
            }
            else
            {
                ioctl(wdt_fd,WDIOC_KEEPALIVE,NULL);
                printf("food watchdog, count = %d \n",count++);
            }
            sleep(DEFAULT_PING_RATE);
        }    
     
        close(wdt_fd);
        return 0;
    }
    
    • 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
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
  • 相关阅读:
    AR动态贴纸SDK,让创作更加生动有趣
    23_ue4进阶末日生存游戏开发[随机巡逻]
    Windows用户如何将cpolar内网穿透配置成后台服务,并开机自启动?
    软件工程与计算总结(十)软件体系结构设计与构建
    Java基础教程:多线程(3)-----创建线程的第二种方式
    (免费领源码)java#Springboot#mysql装修选购网站99192-计算机毕业设计项目选题推荐
    工资短信-模板参考
    vue2中,下拉框多选和全选的实现
    什么叫防御式编程
    Gorm 快速入门:高效掌握 MySQL 数据库操作的实用指南
  • 原文地址:https://blog.csdn.net/weixin_43604927/article/details/125963494