这个驱动被我注释到天花乱坠,确实要看懂它得去学平台设备驱动跟锁机制,所以耽搁了一小段时间,在加载进板子前需要将之前板子的内核里边的看门狗驱动给去掉,必须先在内核目录下make munuconfig,然后在Device driver里边的Watchdog 的星号给去掉,然后重新编译内核烧进板子里。直接上驱动:Watchdog.c:
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/timer.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <mach/map.h>
#include <plat/regs-watchdog.h>
#define S3C_VA_WATCHDOG (0)
#define PFX "s3c2410-wdt:"
#define CONFIG_S3C2410_WATCHDOG_ATBOOT (0)
#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15)
static int nowayout = WATCHDOG_NOWAYOUT;//nowayout与CONFIG_WATCHDOG_NOWAYOUT配置相关,其一旦配置应用层调用close函数将不能关闭看门狗
static int tmr_margin = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME;//默认的喂狗时间
static int tmr_atboot = CONFIG_S3C2410_WATCHDOG_ATBOOT;//关于tmr_atboot,要想系统一上电自动使能看门狗,就为1。最好使用0,由于我们想通过open函数来打开看门狗
static int soft_noboot;//soft_noboot为1时看门狗将作为一般的中断定时器使用,为0时作为可复位电路的看门狗,默认为0
static int debug; 调式模式
module_param(tmr_margin, int, 0);
module_param(tmr_atboot, int, 0);
module_param(nowayout, int, 0);
module_param(soft_noboot, int, 0);
module_param(debug, int, 0);
//下面的这些没什么大的作用,就是为了做参数描述
MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. default="
__MODULE_STRING(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME) ")");
MODULE_PARM_DESC(tmr_atboot, "Watchdog is started at boot time if set to 1, default="
__MODULE_STRING(CONFIG_S3C2410_WATCHDOG_ATBOOT));
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, "
"0 to reboot (default depends on ONLY_TESTING)");
MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug, (default 0)");
static unsigned long open_lock;
static struct device *wdt_dev;
//用来保存watchdog的IO端口占用的IO空间和经过虚拟映射后的内存地址
static struct resource *wdt_mem;
static void __iomem *wdt_base;
static struct resource *wdt_irq;
//保存从平台时钟队列中获取watchdog的时钟
static struct clk *wdt_clock;
//用于保存经计算后得到的计数寄存器WTCNT的计数值
static unsigned int wdt_count;
static char expect_close;
static DEFINE_SPINLOCK(wdt_lock);
#define DBG(msg...) \
do { \
if (debug) \
printk(KERN_INFO msg); \
} while (0) //打印调试信息
static void s3c2410wdt_keepalive(void) //喂狗
{
spin_lock(&wdt_lock);//获取自旋锁保护临界区资源
writel(wdt_count, wdt_base + S3C2410_WTCNT);//往计数寄存器WTCNT重新写入计数值
spin_unlock(&wdt_lock);//释放自旋锁
}
static void __s3c2410wdt_stop(void)
{
unsigned long wtcon;
//停止看门狗定时器
wtcon = readl(wdt_base + S3C2410_WTCON);
wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);
writel(wtcon, wdt_base + S3C2410_WTCON);//wtcon->wdt_base + s3c2410_WTCON_RSTEN
}
static void s3c2410wdt_stop(void)
{
spin_lock(&wdt_lock);//获取自旋锁保护临界区资源
__s3c2410wdt_stop();
spin_unlock(&wdt_lock);//释放自旋锁,即解锁
}
static void s3c2410wdt_start(void)
{
unsigned long wtcon;
spin_lock(&wdt_lock);
__s3c2410wdt_stop();
wtcon = readl(wdt_base + S3C2410_WTCON);
wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;
if (soft_noboot) {
wtcon |= S3C2410_WTCON_INTEN;//该为1时,看门狗为一般定时器产生中断
wtcon &= ~S3C2410_WTCON_RSTEN;//重启无效
} else {
wtcon &= ~S3C2410_WTCON_INTEN;//默认为可复位
wtcon |= S3C2410_WTCON_RSTEN;
}
DBG("%s: wdt_count=0xx, wtcon=lx\n",__func__, wdt_count, wtcon);
writel(wdt_count, wdt_base + S3C2410_WTDAT);
writel(wdt_count, wdt_base + S3C2410_WTCNT);
writel(wtcon, wdt_base + S3C2410_WTCON);
spin_unlock(&wdt_lock);
}
//计算并设置看门狗定时器时钟周期值并初始化看门狗定时器
static int s3c2410wdt_set_heartbeat(int timeout)//timeout是默认的喂狗时间
{
unsigned int freq = clk_get_rate(wdt_clock);//clk_get_rate — obtain the current clock rate (in Hz) for a clock source. This is only valid once the clock source has been enabled.
unsigned int count;
unsigned int divisor = 1;
unsigned long wtcon;
if (timeout < 1)
return -EINVAL;
freq /= 128;
count = timeout * freq;
DBG("%s: count=%d, timeout=%d, freq=%d\n",__func__, count, timeout, freq);
if (count >= 0x10000) {
for (divisor = 1; divisor <= 0x100; divisor++) {
if ((count / divisor) < 0x10000)
break;
}
if ((count / divisor) >= 0x10000) {
dev_err(wdt_dev, "timeout %d too big\n", timeout);
return -EINVAL;
}
}
tmr_margin = timeout;
DBG("%s: timeout=%d, divisor=%d, count=%d (x)\n",__func__, timeout, divisor, count, count/divisor);
count /= divisor;
wdt_count = count;
//看数据手册得到,wtcon=1000000000100001 这是控制寄存器的默认值
wtcon = readl(wdt_base + S3C2410_WTCON);
wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;
wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);
//设置看门狗定时器数据寄存器WTDAT的值,然后WTDAT的值会自动加载到WTCNT中
writel(count, wdt_base + S3C2410_WTDAT);
writel(wtcon, wdt_base + S3C2410_WTCON);
return 0;
}
//看门狗设备驱动的打开接口函数
static int s3c2410wdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(0, &open_lock))//试着获取信号量(即:加锁),如果获取不成功,说明其他进程此时占用了,就返回忙
return -EBUSY;
//如果内核配置了CONFIG_WATCHDOG_NOWAYOUT项,则使模块使用计数加1,nowayout与CONFIG_WATCHDOG_NOWAYOUT配置相关,其一旦配置应用层调用close函数将不能关闭看门狗
if (nowayout)
__module_get(THIS_MODULE); //用于增加模块使用计数;若返回为0,表示调用失败,希望使用的模块没有被加载或正在被卸载中。
expect_close = 0;
//开启看门狗定时器
s3c2410wdt_start();
//表示返回的这个设备文件是不可以被seek操作的,nonseekable_open定义在fs.h中
return nonseekable_open(inode, file);
}
//看门狗设备驱动的关闭接口函数
static int s3c2410wdt_release(struct inode *inode, struct file *file)
{
// 如果判断到当前操作状态是可以关闭看门狗定时器时就关闭,否则就是“喂狗”状态
if (expect_close == 42)
s3c2410wdt_stop();
else {
dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n");
s3c2410wdt_keepalive();//喂狗
}
expect_close = 0;
clear_bit(0, &open_lock);
return 0;
}
static ssize_t s3c2410wdt_write(struct file *file, const char __user *data,size_t len, loff_t *ppos)
{
//刷新定时器
if (len) {
if (!nowayout) {
size_t i;
expect_close = 0;
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')//写入字符 V容许关闭看门狗,但前提还是不配置CONFIG_WATCHDOG_NOWAYOUT
expect_close = 42;
}
}
s3c2410wdt_keepalive();//喂狗
}
return len;
}
//用于支持看门狗IO控制中获取看门狗信息的命令WDIOC_GETSUPPORT,下面的宏和看门狗信息结构体定义在watchdog.h中
#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
static const struct watchdog_info s3c2410_wdt_ident = {
.options = OPTIONS,
.firmware_version = 0,
.identity = "S3C2410 Watchdog",
};
//看门狗设备驱动的IO控制接口函数
static long s3c2410wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
void __user *argp = (void __user *)arg;
int __user *p = argp;
int new_margin;
//以下对看门狗定时器IO控制的命令定义在watchdog.h中
switch (cmd) {
case WDIOC_GETSUPPORT://获取看门狗的支持信息,wdt_ident定义在上面
return copy_to_user(argp, &s3c2410_wdt_ident,sizeof(s3c2410_wdt_ident)) ? -EFAULT : 0;
case WDIOC_GETSTATUS://获取看门狗状态
case WDIOC_GETBOOTSTATUS:
return put_user(0, p);
case WDIOC_KEEPALIVE://喂狗命令
s3c2410wdt_keepalive();
return 0;
case WDIOC_SETTIMEOUT://设置定时器溢出时间值命令(以秒为单位)
if (get_user(new_margin, p))//获取时间值
return -EFAULT; //返回错误
if (s3c2410wdt_set_heartbeat(new_margin))//设置到计数寄存器WTCNT中,成功返回0
return -EINVAL;//非0时返回错误
s3c2410wdt_keepalive();//喂狗
return put_user(tmr_margin, p);
case WDIOC_GETTIMEOUT://读取定时器默认溢出时间值命令
return put_user(tmr_margin, p);
default:
return -ENOTTY;
}
}
//字符设备的相关操作实现
static const struct file_operations s3c2410wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,//定义为不可定位,即屏蔽seek操作,no_llseek定义在fs.h中
.write = s3c2410wdt_write,//从用户获取数据如果收到V则令expect_close == 42,它可以stop看门狗,喂狗一次
.unlocked_ioctl = s3c2410wdt_ioctl,//各种cmd,arg的作用体现在测试程序中
.open = s3c2410wdt_open,//开启看门狗
.release = s3c2410wdt_release,//expect_close == 42时就stop看门狗,清除信号量,令它为0
};
//混杂设备结构体
static struct miscdevice s3c2410wdt_miscdev = {
.minor = WATCHDOG_MINOR,//WATCHDOG_MINOR为次设备号定义在miscdevice.h中为130
.name = "watchdog", //设备名称
.fops = &s3c2410wdt_fops,//实现字符设备的相关操作
};
//看门狗定时器中断服务程序
static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
{
dev_info(wdt_dev, "watchdog timer expired (irq)\n");//设备信息,打印出来?
//主要要做的事情是在看门狗定时器计数寄存器值递减到0之前重新写入新值(即:“喂狗”)
s3c2410wdt_keepalive();
return IRQ_HANDLED;
}
static int __devinit s3c2410wdt_probe(struct platform_device *pdev) //完成了申请IO端口资源,映射,获取中断号,申请中断服务,初始化看门狗,注册杂项设备,打印看门狗的最终状态,最后是错误处理
{
struct resource *res; //定义一个资源,用来保存获取的watchdog的IO资源
struct device *dev;
unsigned int wtcon;
int started = 0;
int ret;
int size;
//DBG一般在其出现的文件的开始部分,作为打印debug信息用.这句话实现打印它所在函数的函数名称
DBG("%s: probe=%p\n", __func__, pdev);
dev = &pdev->dev;
wdt_dev = &pdev->dev;
//获取watchdog平台设备所使用的IO端口资源,注意这个IORESOURCE_MEM标志和watchdog平台设备定义中的一致
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(dev, "no memory resource specified\n");
return -ENOENT;
}
size = (res->end - res->start) + 1;
//request_mem_region 标记这段物理地址自己使用了,其他人不要使用而已,也不是必须的。
wdt_mem = request_mem_region(res->start, size, pdev->name);
if (wdt_mem == NULL) {
dev_err(dev, "failed to get memory region\n");
ret = -ENOENT;
goto err_req;
}
//将watchdog的IO端口占用的这段IO空间映射到内存的虚拟地址,被映射数据的长度由size参数设定。
//该函数的实质是把一块物理区域二次映射到一个可以从驱动程序里访问的虚拟地址上去。
wdt_base = ioremap(res->start, size);//物理地址映射到虚拟地址
if (wdt_base == NULL) {
dev_err(dev, "failed to ioremap() region\n");
ret = -EINVAL;
goto err_req;
}
DBG("probe: mapped wdt_base=%p\n", wdt_base);
//在系统定义的watchdog平台设备中获取watchdog中断号platform_get_irq定义在platform_device.h中
wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (wdt_irq == NULL) {//
wdt_clock = clk_get(&pdev->dev, "watchdog");
if (IS_ERR(wdt_clock)) {
dev_err(dev, "failed to find watchdog clock source\n");
ret = PTR_ERR(wdt_clock);
goto err_irq;
}
//时钟获取后要使能后才可以使用,clk_enable定义在arch/arm/plat-s3c/clock.c中
clk_enable(wdt_clock);
//通过上面的步骤已经将watchdog的资源都准备好了,下面就开始使用啦
//这里是计算并设置看门狗定时器时钟周期值,其实这里就是初始化看门狗定时器,wdt_set_heartbeat定义在下面。
if(s3c2410wdt_set_heartbeat(tmr_margin)) {
//调用两次的意思是看能不能设置成期望的值,如果不能就设置默认的值
started = s3c2410wdt_set_heartbeat(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
if (started == 0)
dev_info(dev, "tmr_margin value out of range, default %d used\n",CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
else
dev_info(dev, "default timer value is out of range, cannot start\n");
}
//把wdt又注册为一个杂项设备
ret = misc_register(&s3c2410wdt_miscdev);//失败返回1
if (ret) {
dev_err(dev, "cannot register miscdev on minor=%d (%d)\n",
WATCHDOG_MINOR, ret);
goto err_clk;
}
if (tmr_atboot && started == 0) { //tmr_atboot如果为1开机自动启动看门狗,我们要用open打开看门狗,所以还是设置为0
dev_info(dev, "starting watchdog timer\n");
s3c2410wdt_start();//如果此时看门狗还没有开启,现在就调用函数开启
} else if (!tmr_atboot) {
s3c2410wdt_stop();
}
wtcon = readl(wdt_base + S3C2410_WTCON);
dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n",
(wtcon & S3C2410_WTCON_ENABLE) ? " " : "in",
(wtcon & S3C2410_WTCON_RSTEN) ? "" : "dis",
(wtcon & S3C2410_WTCON_INTEN) ? "" : "en");
return 0;
err_clk:
clk_disable(wdt_clock);
clk_put(wdt_clock);
err_irq:
free_irq(wdt_irq->start, pdev);
err_map:
iounmap(wdt_base);
err_req:
release_resource(wdt_mem);
kfree(wdt_mem);
return ret;
}
static int __devexit s3c2410wdt_remove(struct platform_device *dev)
{
release_resource(wdt_mem);//释放获取的watchdog平台设备的IO资源
kfree(wdt_mem);
wdt_mem = NULL;
//同watchdog_probe中中断的申请相对应,在那里申请中断,这里就释放中断
free_irq(wdt_irq->start, dev);
wdt_irq = NULL;
//释放获取的Watchdog平台设备的时钟
clk_disable(wdt_clock);
clk_put(wdt_clock);
wdt_clock = NULL;
//释放Watchdog设备虚拟地址映射空间
iounmap(wdt_base);
//注销misc设备
misc_deregister(&s3c2410wdt_miscdev);
return 0;
}
//Watchdog平台驱动的设备关闭接口函数的实现
static void s3c2410wdt_shutdown(struct platform_device *dev)
{
s3c2410wdt_stop();
}
#ifdef CONFIG_PM
static unsigned long wtcon_save;
static unsigned long wtdat_save;
//Watchdog平台驱动的设备挂起接口函数的实现
static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state)
{
//保存看门狗的状态值,并且关闭它
wtcon_save = readl(wdt_base + S3C2410_WTCON);
wtdat_save = readl(wdt_base + S3C2410_WTDAT);
//停止看门狗定时器
s3c2410wdt_stop();
return 0;
}
//Watchdog平台驱动的设备恢复接口函数的实现
static int s3c2410wdt_resume(struct platform_device *dev)
{
//恢复看门狗的状态
writel(wtdat_save, wdt_base + S3C2410_WTDAT);
writel(wtdat_save, wdt_base + S3C2410_WTCNT); //重置寄存器
writel(wtcon_save, wdt_base + S3C2410_WTCON);
printk(KERN_INFO PFX "watchdog %sabled\n",(wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");
return 0;
}
#else //如果配置内核时没选上电源管理,Watchdog平台驱动的设备挂起和恢复功能均无效,这两个函数也就无需实现了
#define s3c2410wdt_suspend NULL
#define s3c2410wdt_resume NULL
#endif
static struct platform_driver s3c2410wdt_driver = {
.probe = s3c2410wdt_probe,//与设备匹配成功后调用这个函数
.remove = __devexit_p(s3c2410wdt_remove),//移除
.shutdown = s3c2410wdt_shutdown,//关闭
.suspend = s3c2410wdt_suspend,//暂停
.resume = s3c2410wdt_resume,//重新开始
//注意这里的名称一定要和系统中定义平台设备的地方一致,这样才能把平台设备与该平台设备的驱动关联起来
.driver = {
.owner = THIS_MODULE,
.name = "s3c2410-wdt",//平台设备中的设备名,国嵌视频已经详细讲解
},
};
static char banner[] __initdata = KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n";
static int __init watchdog_init(void)
{
printk(banner); //输出上边的字符串
//将Watchdog注册成平台设备驱动
return platform_driver_register(&s3c2410wdt_driver);
}
static void __exit watchdog_exit(void)
{
//注销Watchdog平台设备驱动
platform_driver_unregister(&s3c2410wdt_driver);
}
module_init(watchdog_init);
module_exit(watchdog_exit);
MODULE_AUTHOR("hanyan225");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
然后是这个驱动的Makefile文件:
KERNELDIR = /opt/EmbedSky/linux-2.6.30.4
PWD := $(shell pwd)
CROSS_COMPILE = arm-linux-
CC = $(CROSS_COMPILE)gcc
obj-m := Watchdog.o
.PHONY: modules modules_install clean
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
编译之后可以得到Watchdog.ko文件
然后是测试程序:watchdog_test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <sys/time.h>
#include <unistd.h>
#include <time.h>
#include <getopt.h>
#include <sys/signal.h>
#include <termios.h>
struct watchdog_info{
unsigned int options; //options the card/driver supprots 19
unsigned int firmware_version; //firmcard version of the card
unsigned char identity[32]; //identity of the board 21
};
#define WATCHDOG_IOCTL_BASE 'W'
#define WDIOC_GETSUPPORT _IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)
#define WDIOC_SETTIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
#define WDIOC_GETTIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 7, int) //27
#define WDIOS_DISABLECARD 0x0001
#define WDIOS_ENABLECARD 0x0002
#define WDIOC_SETOPTIONS _IOR(WATCHDOG_IOCTL_BASE, 4, int)
#define WDIOC_KEEPALIVE _IOR(WATCHDOG_IOCTL_BASE, 5, int)
int Getch (void) //无回显的从屏幕输入字符,来达到喂狗的目的
{
int ch;
struct termios oldt, newt; //终端设备结构体
tcgetattr(STDIN_FILENO, &oldt); //获得终端属性
newt = oldt;
newt.c_lflag &= ~(ECHO|ICANON); //设置无回显属性
tcsetattr(STDIN_FILENO, TCSANOW, &newt); //设置新的终端属性
ch = getchar(); //从键盘输入一个数据
tcsetattr(STDIN_FILENO, TCSANOW, &oldt); //恢复终端设备初始设置
return ch;
}
//suspend some seconds
int zsleep(int millisecond)
{
unsigned long usec;
usec=1000*millisecond;
usleep(usec); //usleep(1)睡眠一微秒(10E-6),这里也就是0.1s
}
int Init()
{
int fd;
//open device file
fd = open("/dev/watchdog",O_RDWR); //打开看门狗设备
if(fd < 0)
{
printf("device open fail\n");
return -1;
}
printf("open success\n");
return fd;
}
int main(int argc,char **argv)//**argv可以看为一个二级数组, 123 456 789 A【2,1】指的是8
{
int fd,ch;
int i,j;
char c;
struct watchdog_info wi;
fd=Init(); //打开终端看门狗设备
//读板卡信息,但不常用
ioctl(fd,WDIOC_GETSUPPORT,&wi);
printf("options is %d,identity is %s\n",wi.options,wi.identity);
//读看门狗溢出时间,默认是5s
//重新设置时间为10s
i=10;
printf("put_user return,if 0,success:%d\n",ioctl(fd,WDIOC_SETTIMEOUT,&i));
//读新的设置时间
printf("put_usr return,if 0,success:%d\n",ioctl(fd,WDIOC_GETTIMEOUT,&i));
printf("reset time is %d\n",i);
//看门狗开始和停止工作,打开和关闭设备具有同样的功能
//关闭
i=WDIOS_DISABLECARD;//WDIOC_SETOPTIONS=0X0001
printf("return ENOTTY,if -1,success:%d\n",ioctl(fd,WDIOC_SETOPTIONS,&i));
//打开
i=WDIOS_ENABLECARD;//WDIOS_ENABLECARD 0x0002
printf("return ENOTTY,if -1,success:%d\n",ioctl(fd,WDIOC_SETOPTIONS,&i));
while(1)
{
zsleep(100);
if((c=Getch())!=27){
//输入如果不是ESC,就喂狗,否则不喂狗,到时间后系统重启
printf("keep alive \n");
ioctl(fd,WDIOC_KEEPALIVE,NULL);
//write(fd,NULL,1); //同样是喂狗
}
}
close(fd); //关闭设备
return 0;
}
然后就是这个测试程序的makefile:
CROSS=arm-linux-
all: watchdog_test
watchdog_test:watchdog_test.c
$(CROSS)gcc -o watchdog_test watchdog_test.c
clean:
@rm -vf watchdog_test *.o *~
通过make之后可以得到一个可执行文件,将Watchdog.ko跟可执行文件watchdog_test烧进板子内核中,然后加载驱动,执行测试程序可以看到如下效果,如果在键盘上有输入除了ESC之外的按键的时候,就提示keep alive,否则没按键按下的时候经过10秒看门狗就会溢出,然后就会复位,打印出uboot,效果图如下所示:
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/timer.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <mach/map.h>
#include <plat/regs-watchdog.h>
#define S3C_VA_WATCHDOG (0)
#define PFX "s3c2410-wdt:"
#define CONFIG_S3C2410_WATCHDOG_ATBOOT
#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME
static int nowayout = WATCHDOG_NOWAYOUT;//nowayout与CONFIG_WATCHDOG_NOWAYOUT配置相关,其一旦配置应用层调用close函数将不能关闭看门狗
static int tmr_margin = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME;//默认的喂狗时间
static int tmr_atboot = CONFIG_S3C2410_WATCHDOG_ATBOOT;//关于tmr_atboot,要想系统一上电自动使能看门狗,就为1。最好使用0,由于我们想通过open函数来打开看门狗
static int soft_noboot;//soft_noboot为1时看门狗将作为一般的中断定时器使用,为0时作为可复位电路的看门狗,默认为0
static int debug;
module_param(tmr_margin, int, 0);
module_param(tmr_atboot, int, 0);
module_param(nowayout, int, 0);
module_param(soft_noboot, int, 0);
module_param(debug, int, 0);
//下面的这些没什么大的作用,就是为了做参数描述
MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. default="
MODULE_PARM_DESC(tmr_atboot, "Watchdog is started at boot time if set to 1, default="
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, "
MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug, (default 0)");
static unsigned long open_lock;
static struct device *wdt_dev;
//用来保存watchdog的IO端口占用的IO空间和经过虚拟映射后的内存地址
static struct resource *wdt_mem;
static void __iomem *wdt_base;
static struct resource *wdt_irq;
//保存从平台时钟队列中获取watchdog的时钟
static struct clk *wdt_clock;
//用于保存经计算后得到的计数寄存器WTCNT的计数值
static unsigned int wdt_count;
static char expect_close;
static DEFINE_SPINLOCK(wdt_lock);
#define DBG(msg...) \
do { \
} while (0) //打印调试信息
static void s3c2410wdt_keepalive(void) //喂狗
{
}
static void __s3c2410wdt_stop(void)
{
}
static void s3c2410wdt_stop(void)
{
}
static void s3c2410wdt_start(void)
{
}
//计算并设置看门狗定时器时钟周期值并初始化看门狗定时器
static int s3c2410wdt_set_heartbeat(int timeout)//timeout是默认的喂狗时间
{
}
//看门狗设备驱动的打开接口函数
static int s3c2410wdt_open(struct inode *inode, struct file *file)
{
//如果内核配置了CONFIG_WATCHDOG_NOWAYOUT项,则使模块使用计数加1,nowayout与CONFIG_WATCHDOG_NOWAYOUT配置相关,其一旦配置应用层调用close函数将不能关闭看门狗
}
//看门狗设备驱动的关闭接口函数
static int s3c2410wdt_release(struct inode *inode, struct file *file)
{
}
static ssize_t s3c2410wdt_write(struct file *file, const char __user *data,size_t len, loff_t *ppos)
{
}
//用于支持看门狗IO控制中获取看门狗信息的命令WDIOC_GETSUPPORT,下面的宏和看门狗信息结构体定义在watchdog.h中
#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
static const struct watchdog_info s3c2410_wdt_ident = {
};
//看门狗设备驱动的IO控制接口函数
static long s3c2410wdt_ioctl(struct file *file,
{
}
//字符设备的相关操作实现
static const struct file_operations s3c2410wdt_fops = {
};
//混杂设备结构体
static struct miscdevice s3c2410wdt_miscdev = {
};
//看门狗定时器中断服务程序
static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
{
}
static int __devinit s3c2410wdt_probe(struct platform_device *pdev) //完成了申请IO端口资源,映射,获取中断号,申请中断服务,初始化看门狗,注册杂项设备,打印看门狗的最终状态,最后是错误处理
{
err_clk:
err_irq:
err_map:
err_req:
}
static int __devexit s3c2410wdt_remove(struct platform_device *dev)
{
}
//Watchdog平台驱动的设备关闭接口函数的实现
static void s3c2410wdt_shutdown(struct platform_device *dev)
{
}
#ifdef CONFIG_PM
static unsigned long wtcon_save;
static unsigned long wtdat_save;
//Watchdog平台驱动的设备挂起接口函数的实现
static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state)
{
}
//Watchdog平台驱动的设备恢复接口函数的实现
static int s3c2410wdt_resume(struct platform_device *dev)
{
}
#else //如果配置内核时没选上电源管理,Watchdog平台驱动的设备挂起和恢复功能均无效,这两个函数也就无需实现了
#define s3c2410wdt_suspend NULL
#define s3c2410wdt_resume
#endif
static struct platform_driver s3c2410wdt_driver = {
};
static char banner[] __initdata = KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n";
static int __init watchdog_init(void)
{
}
static void __exit watchdog_exit(void)
{
}
module_init(watchdog_init);
module_exit(watchdog_exit);
MODULE_AUTHOR("hanyan225");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
然后是这个驱动的Makefile文件:
KERNELDIR = /opt/EmbedSky/linux-2.6.30.4
PWD := $(shell pwd)
CROSS_COMPILE
CC
obj-m := Watchdog.o
.PHONY: modules modules_install clean
modules:
clean:
编译之后可以得到Watchdog.ko文件
然后是测试程序:watchdog_test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <sys/time.h>
#include <unistd.h>
#include <time.h>
#include <getopt.h>
#include <sys/signal.h>
#include <termios.h>
struct watchdog_info{
#define WATCHDOG_IOCTL_BASE 'W'
#define WDIOC_GETSUPPORT _IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)
#define WDIOC_SETTIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
#define WDIOC_GETTIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 7, int) //27
#define WDIOS_DISABLECARD 0x0001
#define WDIOS_ENABLECARD 0x0002
#define WDIOC_SETOPTIONS _IOR(WATCHDOG_IOCTL_BASE, 4, int)
#define WDIOC_KEEPALIVE _IOR(WATCHDOG_IOCTL_BASE, 5, int)
int Getch (void)
{
}
int zsleep(int millisecond)
{
}
int Init()
{
}
int main(int argc,char **argv)//**argv可以看为一个二级数组, 123 456 789 A【2,1】指的是8
{
}
然后就是这个测试程序的makefile:
CROSS=arm-linux-
all: watchdog_test
watchdog_test:watchdog_test.c
clean:
通过make之后可以得到一个可执行文件,将Watchdog.ko跟可执行文件watchdog_test烧进板子内核中,然后加载驱动,执行测试程序可以看到如下效果,如果在键盘上有输入除了ESC之外的按键的时候,就提示keep alive,否则没按键按下的时候经过10秒看门狗就会溢出,然后就会复位,打印出uboot,效果图如下所示: