瑞昱rtl819x-SDK-v3.4.14b的watchdog分析

watchdog

看门狗,又叫watchdog timer,是一个定时器电路,一般有一个输入,叫喂狗或踢狗;一个输出到MCU 的 RST 端,MCU 正常工作的时候,每隔一段时间输出一个信号到喂狗端,给 WDT 清零,如果超过规定的时间不喂狗(一般在程序跑飞时),WDT 定时超过,就会给出一个复位信号到 MCU,使 MCU 复位。防止 MCU 死机。

整体思路

  1. 内核模块初始化watchdog控制寄存器并使能watchdog
  2. 用户看门狗进程定时踢狗

一、用户空间代码分析

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <stdarg.h>

static void
die(const char *msg, ...)
{
    va_list ap;

    va_start(ap, msg);
    fprintf(stderr, "%s: ERROR: ", "watchdog");
    vfprintf(stderr, msg, ap);
    va_end(ap);

    exit(1);
}


static void
watchdog_write_pidfile(void)
{
    char pidfile[80];
    char pidbuf[16];
    int fd;
    int ret;

    snprintf(pidfile, sizeof(pidfile), "/var/run/%s.pid", "watchdog");
    fd = open(pidfile, O_RDONLY);
    if (fd < 0) {
        if (errno != ENOENT) {
            die("watchdog_write_pidfile: opening pidfile %s for read: %s
",
                    pidfile, strerror(errno));
        }
        /* ENOENT is good: the pidfile doesn't exist */
    } else {
        /* the pidfile exists: read it and check whether the named pid
           is still around */
        int pid;
        char *end;

        ret = read(fd, pidbuf, sizeof(pidbuf));
        if (ret < 0) {
            die("watchdog_write_pidfile: read of pre-existing %s failed: %s
",
                pidfile, strerror(errno));
        }

        pid = strtol(pidbuf, &end, 10);
        if (*end != '' && *end != '
') {
            die("watchdog_write_pidfile: couldn't parse "%s" as a pid (from file %s); "
                "aborting
", pidbuf, pidfile);
        }
        ret = kill(pid, 0); /* try sending signal 0 to the pid to check it exists */
        if (ret == 0) {
            die("watchdog_write_pidfile: %s contains pid %d which is still running; aborting
",
                pidfile, pid);
        }
        /* pid doesn't exist, looks like we can proceed */
        close(fd);
    }

    /* re-open pidfile for write, possibly creating it */
    fd = open(pidfile, O_WRONLY|O_CREAT|O_TRUNC, 0644);
    if (fd < 0)
    die("watchdog_write_pidfile: open %s for write/create: %s
", pidfile, strerror(errno));
    snprintf(pidbuf, sizeof(pidbuf), "%d
", getpid());
    ret = write(fd, pidbuf, strlen(pidbuf));
    if (ret < 0) {
        die("watchdog_write_pidfile: writing my PID to lockfile %s: %s
",
            pidfile, strerror(errno));
    }
    close(fd);
}


void watchdog_func()
{
    FILE *file;
    file = fopen("/proc/watchdog_kick","w+"); 
    if(file)
    {
        fputs("111", file);
        fclose(file);
    }    
}

int
main(int argc, char **argv)
{ 
    pid_t    pid; 
    char tmpBuff[30] = {0};
    int res = 0;  
    int fd;
    int interval;
    int sec,micro_sec;
    sigset_t sigset;

    if(argc >= 2)
        interval = atoi(argv[1]);
    else
        interval = 500;

    if(interval >= 10000){
        printf("watchdog interval too long,should not more than 10s
");
        interval = 1000;
    }

    sec = interval/1000;
    micro_sec = (interval % 1000) * 1000;

    watchdog_write_pidfile();

    /* unblock sigalarm and sigterm signal */
    sigaddset(&sigset,SIGALRM);
    if(sigprocmask(SIG_UNBLOCK,&sigset,NULL) < 0)
        printf("sigprocmask error
");
  
      // Register watchdog_func to SIGALRM  
    signal(SIGALRM, watchdog_func);
    
    struct itimerval tick;  
    memset(&tick, 0, sizeof(tick));  
    //printf("interval:%d.
",interval);

    // Timeout to run function first time  
    tick.it_value.tv_sec = sec;  // sec  
    tick.it_value.tv_usec = micro_sec; // micro sec.  

    // Interval time to run function  
    tick.it_interval.tv_sec = sec;  
    tick.it_interval.tv_usec = micro_sec;  

    pid = getpid(); 
    snprintf(tmpBuff,30,"renice -19 %d",pid);
    system(tmpBuff);
    //stop watchdog first
    system("echo 1 > /proc/watchdog_kick");    
    system("echo enable 0 interval 0 > /proc/watchdog_cmd");    
    
    // resume watchdog
#ifdef CONFIG_RTL_8197F
    system("echo enable 1 interval 32 > /proc/watchdog_cmd");
#else
    system("echo enable 1 interval 10 > /proc/watchdog_cmd");
#endif
    system("echo 1 > /proc/watchdog_kick");
    res = setitimer(ITIMER_REAL, &tick, NULL);  
    if (res) {  
        printf("Set watchdog timer failed!!/n"); 
        return -1;  
    }  
 
    while(1) {  
        pause();  
    }  
    return 0;    
}

用户进程主要做了两件事:

1. 用户进程设置watchdog intervel
echo enable 1 interval 32 > /proc/watchdog_cmd
2. 定时器定时踢狗
void watchdog_func()
{
    FILE *file;
    file = fopen("/proc/watchdog_kick","w+"); 
    if(file)
    {
        fputs("111", file);
        fclose(file);
    }    
}

二、内核部分

1. 看门狗定时器控制寄存器初始化

void bsp_enable_watchdog( void )
{
    bBspWatchdog = 1;
    *(volatile unsigned long *)(0xb800311C)=0x00240000; // 2^24
}

void __init plat_time_init(void) // mips-ori
{
    printk(COLOR_RED"[%s:%d] [watchdog] platform timer init
"COLOR_CLEAR, __FUNCTION__, __LINE__);
    {/* 省略部分代码 */}
    
#ifdef CONFIG_RTL_WTDOG
    /* 配置时钟分频寄存器 */
    REG32(BSP_CDBR)=(BSP_DIVISOR) << BSP_DIVF_OFFSET;
    printk(COLOR_RED"[%s:%d] [watchdog] BSP enable watchdog, BSP_CDBR=0x%x
"COLOR_CLEAR, 
                    __FUNCTION__, __LINE__, ((BSP_DIVISOR) << BSP_DIVF_OFFSET));
    /* 使能watchdog */
    bsp_enable_watchdog();

    wtdog_cdbr = (REG32(BSP_CDBR) >> 16) & 0xffff;
    printk(COLOR_RED"[%s:%d] [watchdog] watchdog cdbr=%u
"COLOR_CLEAR, __FUNCTION__, __LINE__, wtdog_cdbr);
    
#endif /* CONFIG_RTL_WTDOG */

    {/* 省略部分代码 */}
}

看门狗控制寄存器初始化为:0x00240000,表示 OVSEL 的高两位是 10, OVSEL 的低两位是 01, OVSEL=1001

2. 创建proc节点(watchdog_cmd 和 watchdog_kick)

为应用层配置watchdog提供两个接口,watchdog_cmd用来设置interval,watchdog_kick用来踢狗

int __init bsp_watchdog_proc_init(void)
{
    proc_create_data("watchdog_reboot", 0, &proc_root,
            &watchdog_reboot_proc_fops, NULL);
#ifdef  CONFIG_RTL_USERSPACE_WTDOG
    proc_create_data("watchdog_cmd", 0, &proc_root,
            &watchdog_cmd_proc_fops, NULL);
            
    proc_create_data("watchdog_kick", 0, &proc_root,
            &watchdog_kick_proc_fops, NULL);
#endif

    return 0;
}

2.1 watchdog_cmd的write接口

应用层向该节点写入watchdog interval的配置,该接口去配置watchdog寄存器的OVSEL位

如:echo enable 1 interval 32 > /proc/watchdog_cmd

static ssize_t watchdog_cmd_single_write(struct file * file, const char __user * userbuf,
             size_t count, loff_t * off)
{
    char flag[64];
    int enable,interval;

    extern void bsp_enable_watchdog(void);
    extern void bsp_disable_watchdog(void);
    
    if (count < 2)
        return -EFAULT;
    if (userbuf && !copy_from_user(&flag, userbuf, 63)) {
        int i;
        unsigned int wtdog_intervel,wtdog_intervel0 = 0,wtdog_cdbr,wtdog_maxtime;
        sscanf(flag,"enable %d interval %d",&enable,&interval);

        if (enable == 0) {
            /* disable watchdog */
            bsp_disable_watchdog();
        } else if (enable == 1) {
            if (watchdog_default_flag == 0) {
                watchdog_default_flag = 1;
                watchdog_default_val = interval;
            } else {
                if(interval < watchdog_default_val){
                    printk("	
watchdog timeout time should not less than default val,default=%d
",watchdog_default_val);
                    return -1;
                }
            }

            wtdog_cdbr = (REG32(BSP_CDBR) >> 16) & 0xffff;
            i = sizeof(wtdog_tbl)/sizeof(WTDOG_REGTBL_T) - 1;

            /* interval计算方法 */
            wtdog_maxtime = wtdog_tbl[i].wtdog_val/(LXBUS_CLOCK/wtdog_cdbr);

            if (interval > wtdog_maxtime) {
                printk("	
 watchdog max intervale time is %d,please check the set value
",  wtdog_maxtime);
                return -1;
            }

            for(i = 0; i < sizeof(wtdog_tbl)/sizeof(WTDOG_REGTBL_T); i++) {
                wtdog_intervel = wtdog_tbl[i].wtdog_val/(LXBUS_CLOCK/wtdog_cdbr);
                printk(COLOR_GREEN"[%s:%d] [watchdog] Supported interval=%ds
"COLOR_CLEAR, 
                                    __FUNCTION__, __LINE__, wtdog_intervel);
                if (interval >= wtdog_intervel0
                    && interval <= wtdog_intervel) {
                    goto END;
                }
                wtdog_intervel0 = wtdog_intervel;
            }
            
END:
            printk(COLOR_GREEN"[%s:%d] [watchdog] Do watchdog control register set (index=%d).
"COLOR_CLEAR, 
                                __FUNCTION__, __LINE__, i);
            printk(COLOR_GREEN"[%s:%d] [watchdog] oversel_l = 0x%x, oversel_h = 0x%x.
"COLOR_CLEAR, 
                                __FUNCTION__, __LINE__, wtdog_tbl[i].oversel_l, wtdog_tbl[i].oversel_h);

            /* 设置wathdog寄存器 */
            REG32(BSP_WDTCNR) = ( wtdog_tbl[i].oversel_l << 21) | ( wtdog_tbl[i].oversel_h << 17);
        }
        return count;
    }
    
    return -EFAULT;    
}

2.2 watchdog_kick的write接口

static ssize_t watchdog_kick_single_write(struct file * file, const char __user * userbuf,
             size_t count, loff_t * off)
{
    char flag[20];

    if (count < 2)
        return -EFAULT;
#ifdef CONFIG_RTL_WTDOG
    { 
        /*If kernel fault. reboot whole system so softwatch dog can not kick even*/
        extern int is_fault;
        if(is_fault)
            return count;
    }
#endif
    if (userbuf && !copy_from_user(&flag, userbuf, 1)) {
        if(flag[0] == '1'){
            watchdog_kick_state = RTL_WATCHDOG_KICK;
            /* kick watchdog here */
            *(volatile unsigned long *)(0xB800311c) |=  1 << 23;
        }else {
            watchdog_kick_state = 0;
        }
        return count;
    }
    
    return -EFAULT;    
}

原文:

瑞昱rtl819x-SDK-v3.4.14b的watchdog分析 - 走看看 (zoukankan.com)

猜你喜欢

转载自blog.csdn.net/buhuidage/article/details/128425734