本文转载自:http://blog.chinaunix.net/uid-25014876-id-112879.html

linux设备驱动归纳总结(十一):写个简单的看门狗驱动

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

设备驱动的归纳已经差不多了,趁着知识点还没有遗忘,写点代码巩固一下,来个简单的看门狗驱动——静态平台类的杂设备看门狗驱动,有定时和重启两个基本功能。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

一、S3C2440中的看门狗——具体请看s3c2440文档

看门狗应该可以算是S3C2440中最简单的一个设备的,仅仅只有三个寄存器需要配置。S3C2440中的看门狗有两种功能(只能二选一):

1、复位功能,在指定时间内,如果没有执行喂狗操作。指定时间到达后会执行系统重启操作。

2、定时功能,每隔指定的时间,看门狗就会往处理器发送中断,执行中断处理函数。

接下来就要看看控制看门狗的三个寄存器:

1、控制寄存器——WTCON:

首先要看看分频系数Prescaler value[15:8]和时钟分频Clock select[4:3]。看门狗的时钟频率就是通过这两个值分频得出的:

t_watchdog = 1/[ PCLK / (Prescaler value + 1) / Division_factor ]

每个(1/t_watchdog)秒,看门狗计数器减一。

Watchdog timer[5]是看门狗的是能控制位,初始化时必须将此为置一,不然看门狗无法操作。

Interrupt generation[2]是控制中断的产生,如果你使用看门狗来执行定时功能,置一代表使能看门狗产生中断,反之不产生中断。

Reset enable/disable[0]用控制看门狗是否复位,如果置一,代表当时间到达后系统重启。

2、数据寄存器WTDAT和计数寄存器WTCNT

这两个都是用于存放16位数据的寄存器。

1、如果是复位模式,从WTCON[5]置一开始,每隔(1/t_watchdog)秒,WTCNT中的值减一,直到WTCNT为0,系统就会复位。在WTCNT还没有为0前,可以重新往WTCNT中赋值,这样WTCNT就会从新的数值开始减一,这就是所谓的喂狗操作,重复地在WTCNT变0前喂狗,就可以让系统不复位。

2、如果是定时模式, 从WTCON[5]置一开始,每隔(1/t_watchdog)秒,WTCNT中的值减一,直到WTCNT为0,看门狗往处理器发送中断信号,并自动将WTDAT赋值给WTCNT,让WTCNT重新开始减一。这样的重复操作就实现了看门狗的定时。

注意:不管是哪一种模式,一开始时看门狗都不会将WTDAT的值赋给WTCNT,都是直接以WTCNT的值开始计数。所以,在是能看门狗之前,必须先设备WTCNT的值,指定时间长短。

接下来的程序我需要的时间间隔是1秒,所以,Prescaler value[15:8]我赋值为99,Clock select[4:3]我赋值为128。S3C2440中的PCLK定义为50000000,通过公式可以计算出:

t_watchdog = 1/[ PCLK / (Prescaler value + 1) / Division_factor ] = 3906.25

所以,看门狗的计数间隔是(1/3906.25)秒,次。

看门狗介绍完毕,接着就开始写驱动了,在写驱动前贴张之前我介绍过的图,我写驱动的步骤:

接在来我就会按这个顺序来写个简单的看门狗驱动,实现看门狗的两种功能,定时和复位。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

二、第一个看门狗程序,代码路径11th_wdt/1st。

写驱动前先要写一些基本的操作代码,检验一下该设备是否能正常工作。

第一个看门狗程序做了三步,简单实现了看门狗的复位操作:

1、定义了一个维护看门狗数据的结构体:

9 struct _wdt_t {

10      unsigned long phys, virt; //存放物理地址和对应的虚拟地址

11     unsigned long wtcon, wtdat, wtcnt; //存放寄存器

12     unsigned long reg;

13

14     void (*init_reset)(struct _wdt_t *); //操作看门狗的函数指针

15 };

2、实现了看门狗的复位操作:

19 static void s3c_wdt_init_reset(struct _wdt_t *wdt)

20 {

21     iowrite32((int)(WDT_1S * 10), wdt->wtdat); //其实这个不设置也可以

22     iowrite32((int)(WDT_1S * 10), wdt->wtcnt); //设置10秒后系统复位

23     iowrite32(0x6339, wdt->wtcon); //设置wtcon寄存器为0x6339,使能了看门狗和复位功能

24 }

3、封装了设备的初始化和注销函数:

26 int init_wdt_device(struct _wdt_t *wdt)

27 {

28     int ret = 0;

29     //ioremap

30     wdt->phys = 0x53000000;

31     wdt->virt = (unsigned long)ioremap(wdt->phys, 0x0c);

32      wdt->wtcon = wdt->virt + 0x0;

33      wdt->wtdat = wdt->virt + 0x4;

34      wdt->wtcnt = wdt->virt + 0x8;

35

36     //function

37      wdt->init_reset = s3c_wdt_init_reset;

38     return ret;

39 }

40

41 void destroy_wdt_device(struct _wdt_t *wdt)

42 {

43     iounmap((void *)wdt->virt);

44 }

写完后编译,效果就是加载模块后十秒系统重启,看门狗的复位功能验证成功。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

三、第二个看门狗程序,代码路径11th_wdt/2nd。

第一个看门狗程序实现了复位操作,接下来我就要在原来代码的基础上加上看门狗的定时功能。很简单,只贴上就三个函数,其他细节请看源代码:

1、初始化看门狗,设置为定时功能,每隔一秒产生一次中断:

29 static void s3c_wdt_init_interrupt(struct _wdt_t *wdt)

30 {

31     iowrite32((int)(WDT_1S), wdt->wtdat);

32     iowrite32((int)(WDT_1S), wdt->wtcnt);

33     iowrite32(0x6338, wdt->wtcon);

34 }

2、初始话后还不能产生中断,还需要实现开启和关闭中断操作,其实就是设置寄存器的一位:

36 static void s3c_wdt_start(struct _wdt_t *wdt)

37 {

38      wdt->reg = ioread32(wdt->wtcon);

39      wdt->reg |= (1 << 2);

40     iowrite32(wdt->reg, wdt->wtcon);

41 }

42

43 static void s3c_wdt_stop(struct _wdt_t *wdt)

44 {

45     wdt->reg = ioread32(wdt->wtcon);

46     wdt->reg &= (1 << 2);

47     iowrite32(wdt->reg, wdt->wtcon);

48 }

既然是产生了中断,就要注册中断和实现中断处理函数,中断的注册操作应该放在设备初始化函数中:

50 irqreturn_t wdt_irq_handler(int irqno, void *dev_id)

51 {

52     printk("wang wang wang ...\n");

53     return IRQ_HANDLED;

54 }

55

56 int init_wdt_device(struct _wdt_t *wdt)

57 {

58     int ret = 0;

59     //ioremap

60     wdt->phys = 0x53000000;

61     wdt->virt = (unsigned long)ioremap(wdt->phys, 0x0c);

62     wdt->wtcon = wdt->virt + 0x0;

63     wdt->wtdat = wdt->virt + 0x4;

64     wdt->wtcnt = wdt->virt + 0x8;

65

66     //irq

67     ret = request_irq(IRQ_S3C2440_WDT, wdt_irq_handler, IRQF_TRIGGER_NONE,

68                                         "s3c2440_wdt-irq", NULL);

69     if(ret){

70         printk("request wdt-irq failed!\n");

71         goto err;

72     }

73

74     //function

75     wdt->init_reset = s3c_wdt_init_reset;

76     wdt->init_interrupt = s3c_wdt_init_interrupt;

77     wdt->start = s3c_wdt_start;

78     wdt->stop = s3c_wdt_stop;

79

80     return ret;

81

82 err:

83     iounmap((void *)wdt->virt);

84     return ret;

85 }

86

87 void destroy_wdt_device(struct _wdt_t *wdt)

88 {

89     free_irq(IRQ_S3C2440_WDT, NULL);

90     iounmap((void *)wdt->virt);

91 }

写完后编译,效果就是加载模块后每隔一秒打印出一句话,看门狗的定时功能验证成功。

注意:一般是不能加载成功的,运行命令”cat /proc/interrupt”就知道,系统中本身就注册了一个看门狗中断,为了能够执行成功,方法有两个:(之前介绍过的两男共享一妞)

1、干掉系统中的看门狗中断:

方法:配置内核时不要选上看门狗设备。

2、修改内核源代码,添加共享标记。

我这里使用的是第一种解决方法。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

四、第三个看门狗程序,代码路径11th_wdt/3rd。

上面已经实现了图上的三步:定义面向对象结构体,实现基本的硬件操作函数、定义设备的初始化和注销函数。

接下来就将它修改成静态平台类驱动。方法很简单,将之前放在wdt_init的操作放在probe函数中,配对成功后自动调用,将之前放在wdt_exit的操作放在remove函数中。

贴上部分代码:

97 static int s3c_wdt_probe(struct platform_device *pdev)

98 {

99       printk("[%s]\n", __FUNCTION__);

100     my_wdt.phys = pdev->resource[0].start;

101     my_wdt.irqno = pdev->resource[1].start;

102     init_wdt_device(&my_wdt);

103     my_wdt.init_interrupt(&my_wdt);

104     my_wdt.start(&my_wdt);

105     return 0;

106 }

107

108 static int s3c_wdt_remove(struct platform_device *pdev)

109 {

110     printk("[%s]\n", __FUNCTION__);

111     my_wdt.stop(&my_wdt);

112     destroy_wdt_device(&my_wdt);

113     return 0;

114 }

115

116 static struct platform_driver wdt_pdrv = {

117     .probe = s3c_wdt_probe,

118     .remove = s3c_wdt_remove,

119     .driver = {

120         .name = "s3c_wdt_xb",

121     },

122 };

123

124 static int __init wdt_init(void)

125 {

126     platform_driver_register(&wdt_pdrv);

127     printk("hello wdt!\n");

128     return 0;

129 }

130

131 static void __exit wdt_exit(void)

132 {

133     platform_driver_unregister(&wdt_pdrv);

134     printk("bye wdt!\n");

135 }

136

137 module_init(wdt_init);

138 module_exit(wdt_exit);

既然说是静态平台类驱动,那就是需要修改内核代码,方法在linux设备驱动归纳总结(九):1.platform设备驱动有介绍,在三处文件添加代码:

1、arch/arm/mach-s3c2440/mach-mini2440.c

250 static struct platform_device *mini2440_devices[] __initdata = {

251     &s3c_device_usb,

252     &s3c_device_rtc,

253     &s3c_device_lcd,

254     &s3c_device_wdt,

255     &s3c_device_led,

256     &s3c_device_wdt_xb, //这是我新加的

257     &s3c_device_i2c0,

258     &s3c_device_iis,

259     &s3c_device_dm9k,

260     &net_device_cs8900,

261     &s3c24xx_uda134x,

262 };

2、arch/arm/plat-s3c24xx/devs.c

379 /* Watchdog xiaobai*/

380

381 static struct resource s3c_wdt_xb_resource[] = {

382     [0] = {

383         .start = 0x53000000,

384         .end = 0x530000ff,

385         .flags = IORESOURCE_MEM,

386     },

387     [1] = {

388         .start = IRQ_S3C2440_WDT,

389         .end = IRQ_S3C2440_WDT,

390         .flags = IORESOURCE_IRQ,

391     }

392 };

393

394 struct platform_device s3c_device_wdt_xb = {

395     .name = "s3c_wdt_xb",

396     .id = -1,

397     .num_resources = ARRAY_SIZE(s3c_wdt_xb_resource),

398     .resource = s3c_wdt_xb_resource,

399 };

400

401 EXPORT_SYMBOL(s3c_device_wdt_xb);

3、arch/arm/plat-s3c/include/plat/devs.h

27 extern struct platform_device s3c_device_dm9k;

28 extern struct platform_device net_device_cs8900;

29 extern struct platform_device s3c_device_fb;

30 extern struct platform_device s3c_device_usb;

31 extern struct platform_device s3c_device_lcd;

32 extern struct platform_device s3c_device_wdt;

33 extern struct platform_device s3c_device_led;

34 extern struct platform_device s3c_device_wdt_xb; //这是我添加的

35 extern struct platform_device s3c_device_i2c0;

36 extern struct platform_device s3c_device_i2c1;

37 extern struct platform_device s3c_device_iis;

修改后重新编译内核,加载模块后,内核会自动调用probe函数,具体效果就是每隔一秒打印一句话。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

五、第四个看门狗程序,代码路径11th_wdt/4th。

接下来就要注册杂设备类驱动,并且提供ioctl命令给用户空间控制看门狗。

先要在头文件中实现几个命令:

/*11th_wdt/4th/ioctl_wdt.h*/

1 #ifndef _WDT_H

2 #define _WDT_H

3

4 #define MAGIC 'x'

5 #define WDT_RESET _IO(MAGIC, 0) //产生一个数字

6 #define WDT_INTERRUPT _IO(MAGIC, 1)

7 #define WDT_START _IO(MAGIC, 2)

8 #define WDT_STOP _IO(MAGIC, 3)

9

10 #endif /* _WDT_H */

再看看ioctl的实现,很简单,四个命令对应四个不同的操作:

112 int s3c_wdt_ioctl (struct inode *node, struct file *filp,

113 unsigned int cmd, unsigned long args)

114 {

115     switch(cmd){

116         case WDT_RESET:

117             my_wdt.init_reset(&my_wdt);

118             break;

119         case WDT_INTERRUPT:

120             my_wdt.init_interrupt(&my_wdt);

121             break;

122         case WDT_START:

123             my_wdt.start(&my_wdt);

124             break;

125         case WDT_STOP:

126             my_wdt.stop(&my_wdt);

127             break;

128         default:

129             printk("unknow ioctl cmd!\n");

130             return - EINVAL;

131     }

132     return 0;

133 }

134

135 static struct file_operations wdt_fops = {

136     .ioctl = s3c_wdt_ioctl,

137 };

接着是实现杂设备类驱动:

139 /*******************

140 * misc class *

141 *******************/

142

143 static struct miscdevice wdt_misc = {

144     .name = "s3c_wdt",

145     .fops = &wdt_fops,

146 };

147

148 /*******************

149 * platform总线操作*

150 *******************/

151 static int s3c_wdt_probe(struct platform_device *pdev)

152 {

153     printk("[%s]\n", __FUNCTION__);

154     my_wdt.phys = pdev->resource[0].start;

155     my_wdt.irqno = pdev->resource[1].start;

156

157     init_wdt_device(&my_wdt);

158     misc_register(&wdt_misc);

159     return 0;

160 }

161

162 static int s3c_wdt_remove(struct platform_device *pdev)

163 {

164     printk("[%s]\n", __FUNCTION__);

165     misc_deregister(&wdt_misc);

166     destroy_wdt_device(&my_wdt);

167     return 0;

168 }

最后就可以写个应用程序验证一下了:

/*11th_wdt/4th/app.c */

8 #include "ioctl_wdt.h"

9

10 int main(int argc, char *argv[])

11 {

12     int fd;

13

14     fd = open("/dev/s3c_wdt", O_RDWR);

15     if(fd < 0)

16         perror("open");

17

18     printf("./app [func]\n");

19     printf("func : reset interrupt start stop\n");

20     printf("[%s]\n", argv[1]);

21

22     if(!strncasecmp("reset", argv[1], 5))

23         ioctl(fd, WDT_RESET);

24     if(!strncasecmp("interrupt", argv[1], 9))

25         ioctl(fd, WDT_INTERRUPT);

26     if(!strncasecmp("start", argv[1], 5))

27         ioctl(fd, WDT_START);

28     if(!strncasecmp("stop", argv[1], 4))

29         ioctl(fd, WDT_STOP);

30

31     return 0;

32 }

编译后验证一下,可以使用./app reset启动复位功能,也可以./app interrupt & ./app start启动定时功能。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

六、第五个看门狗程序,代码路径11th_wdt/5th。

看门狗的驱动就基本完成了,最后还有个功能要补充,喂狗,直接上代码:

65 static void s3c_wdt_feed(struct _wdt_t *wdt)

66 {

67     iowrite32((int)(WDT_1S * 10), wdt->wtcnt);

68 }

然后稍稍修改一下其他代码就可以了。

编译后运行,执行./app reset后,只要在10秒内执行./app feed,系统就不会重启。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

七、总结

简单的看门狗驱动基本上就完成了,当然还有很多需要完善的地方,如设定定时的时间等。具体包含了以下知识点:

1、字符设备的方法;

2、io内存——ioremap;

3、中断注册;

4、platform设备驱动;

5、杂设备驱动;

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

linux设备驱动归纳总结(十一):写个简单的看门狗驱动【转】的更多相关文章

  1. 【Linux开发】linux设备驱动归纳总结(十一):写个简单的看门狗驱动

    linux设备驱动归纳总结(十一):写个简单的看门狗驱动 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  2. 用go写一个简单的看门狗程序(WatchDog)

    简述 因为公司的一些小程序只是临时使用一下(不再维护更新),有的有一些bug会导致崩溃,但又不是很严重,崩溃了重新启动一下就好. 所以写了一个看门狗程序来监控程序,挂了(因为我这里并不关心程序的其他状 ...

  3. RM-Linux驱动--Watch Dog Timer(看门狗)驱动分析

    from:http://blog.csdn.net/geekcome/article/details/6595265 硬件平台:FL2440 内核版本:2.6.28 主机平台:Ubuntu 11,04 ...

  4. 迅为4412开发板Linux设备树的镜像烧写和源码简单优化教程

    1 烧写:   烧写和4412默认镜像的烧写类似,使用fastboot. 先更新uboot,用4412默认uboot更新支持设备树的uboot 用支持设备树的uboot烧写. 进入支持设备树的uboo ...

  5. linux设备驱动归纳总结

    前言: (总结已经基本写完,这段时间我会从新排版和修正.错误总会有的,望能指正!) 前段时间学习了嵌入式驱动,趁着没开始找工作,这段时间我会每天抽出时间来复习. 我的总结是根据学习时的笔记(李杨老师授 ...

  6. 【Linux】linux设备驱动归纳总结

    前言: (总结已经基本写完,这段时间我会从新排版和修正.错误总会有的,望能指正!) 前段时间学习了嵌入式驱动,趁着没开始找工作,这段时间我会每天抽出时间来复习. 我的总结是根据学习时的笔记(李杨老师授 ...

  7. 【分享】iTOP-iMX6UL开发板驱动看门狗 watchdog 以及 Linux-c 测试例程

    iTOP-iMX6UL开发板看门狗测试例程,iTOP-iMX6UL 开发板的看门狗驱动默认已经配置,可以直接使用测试例程. 版本 V1.1:1.格式修改:2.例程修改完善,其中增加喂狗代码.1 看门狗 ...

  8. 蜕变成蝶~Linux设备驱动之watchdog设备驱动

    看门狗(watchdog )分硬件看门狗和软件看门狗.硬件看门狗是利用一个定时器 电路,其定时输出连接到电路的复位端,程序在一定时间范围内对定时器清零 (俗称 “喂狗”),如果程序出现故障,不在定时周 ...

  9. linux设备模型_转

    建议原博文查看,效果更佳. 转自:http://www.cnblogs.com/wwang/category/269350.html Linux设备模型 (1) 随着计算机的周边外设越来越丰富,设备管 ...

随机推荐

  1. div滑入与滑出

    html <div class="pop_tit"> <span class="p_tit1" title="大连未来城LECITY ...

  2. 打印出1,11,21,31,41。。。。。。的shell脚本

    打印出1,11,21,31,41......的shell脚本 方法一:#!/bin/bash ;i<;i=i+));do echo $i #cat -n /etc/services | sed ...

  3. saltstack之(七)配置管理系统初始化init

    saltstack的配置管理分环境管理:①.base为基础环境,一般会存放一些所有服务器都使用的配置管理信息.②.dev为开发测试环境,一般会存放一些开发测试服务器使用的配置管理信息.③.pro为线上 ...

  4. free 命令

    free命令可以显示Linux系统中空闲的.已用的物理内存及swap内存,及被内核使用的buffer.在Linux系统监控的工具中,free命令是最经常使用的命令之一. 1.命令格式: free [参 ...

  5. LUA闭包概念演示

    闭包的一个重要场景,形成一个自治的环境, 让操作可以封闭运行, 即函数运行时有状态的,可以从闭包创建时候的环境独立开来. 例如下面的lua闭包, genFilter 其入参parmIn是 函数的内部变 ...

  6. Install .NET Framework 4.5.2 on a Cloud Service Role

    October Guest OS rollout is starting today October 15 2015, and projected to be released on November ...

  7. Fix the “No Private Key” Error Message

    This article will show you how to correct the “No Private Key” error message in Windows Internet Inf ...

  8. D3D9 浮点精度的问题

    最近在对我们的渲染引擎进行优化的时候,发现一个奇怪的现象,因为我们做了Pre-Z(把比较大的物体先绘制一遍,这个时候关闭颜色写,只开启深度测试和写入,目的是为了减少后面一些不可见像素的计算.),面在绘 ...

  9. C# SQL优化 及 Linq 分页

    每次写博客,第一句话都是这样的:程序员很苦逼,除了会写程序,还得会写博客!当然,希望将来的一天,某位老板看到此博客,给你的程序员职工加点薪资吧!因为程序员的世界除了苦逼就是沉默.我眼中的程序员大多都不 ...

  10. 自动检查点(Automatic Checkpointing)

    自动检查点(Automatic Checkpointing)在oracle10g,支持自动检查点调优,这样可以提高系统可用性.自动检查点调优需要开启参数fast_start_mttr_target. ...