zoukankan      html  css  js  c++  java
  • 瑞昱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接口

    清空watchdog计数器(喂狗)

    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;    
    }

     

  • 相关阅读:
    DragDrop 注册失败的问题
    《精通.NET互操作:P/Invoke,C++ Interop和COM Interop》推荐的工具列表
    勇于探索,及时总结,拥有自信!个人工作感受总结篇……
    C#4.0新特性对.NET互操作的影响(转载)
    UCHome: IP控制
    UCHome: SQL注入式攻击
    Windows下Cygwin模拟GCC开发
    序列化和反序列化
    UCHome: 解读common.php(转)
    UCHome: 提交检查
  • 原文地址:https://www.cnblogs.com/shelmean/p/14649689.html
Copyright © 2011-2022 走看看