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

嵌入式Linux驱动开发9—WTD驱动程序以及测试过程记录WTD 驱动的实现以及测试案例 wtd sys

大家好,欢迎来到IT知识分享网。

前提 开发环境

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

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

一 WTD的概念

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

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



二 WTD 首要寄存器

WDT_CR寄存器:

WDT_TORR寄存器

一套简单的配置

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

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

//寄存器以及其偏移值的映射 #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) 

实现硬件操作的接口

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

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

dw_wdt_arm_system_reset

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

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

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

四 驱动框架的理解

节点注册的调用关系图

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

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

static struct miscdevice watchdog_miscdev = { 
    .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &watchdog_fops, }; 
static const struct file_operations watchdog_fops = { 
    .owner = THIS_MODULE, .write = watchdog_write, .unlocked_ioctl = watchdog_ioctl, .open = watchdog_open, .release = watchdog_release, }; 

实现fops里的接口

write实现
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; } 
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; } 

匹配

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

五 编写测试App

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

#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <pthread.h> #include <sys/ioctl.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/time.h> #include <unistd.h> #include <time.h> #include <getopt.h> #include <sys/signal.h> //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; } 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/116705.html

(0)
上一篇 2025-11-26 21:26
下一篇 2025-11-26 21:45

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信