第一部分: WDT驱动原理

WDT在内核中通常都实现为misc驱动。

WDT介绍

一个Watchdog Timer(WDT)是一个在软件出错的时候可以复位计算机系统的硬件电路。

通常一个用户空间守护进程会在正常的时间间隔内通过/dev/watchdog特殊设备文件来通知内核的watchdog驱动,用户空间仍然正常。当这样的一个通知发生时,驱动通常会告诉硬件watchdog一切正常,然后watchdog应该再等待一段时间来复位系统。如果用户空间出问题(RAM错误,内核bug等),则通知将会停止,然后硬件watchdog将在超时后复位系统。

Linux的watchdog API是一个相当特别的东西,不同的驱动实现是不同的,而且有时部分是不兼容的。这个文档正是要尝试着去说明已经出现的用法,并且使以后的驱动作者把它作为一份参考。

最简单的 API:


所有的设备驱动都支持的基本的操作模式,一旦/dev/watchdog被打开,则watchdog激活,并且除非喂狗,否则将在一段时间之后重启,这个时间被称为timeout或margin。最简单的喂狗方法就是写一些数据到设备。一个非常简单的watchdog守护进程看起来就像这个文件这样:

Documentation/watchdog/src/watchdog-simple.c

  1. #include <stdio.h>

  2. #include <stdlib.h>

  3. #include <unistd.h>

  4. #include <fcntl.h>


  5. int main(void)

  6. {

  7. int fd = open("/dev/watchdog", O_WRONLY);

  8. int ret = 0;

  9. if(fd ==-1){

  10.         perror("watchdog");

  11. exit(EXIT_FAILURE);

  12. }

  13. while(1){

  14.         ret = write(fd,"\0", 1);

  15. if(ret != 1){

  16.             ret =-1;

  17.             break;

  18. }

  19.         ret = fsync(fd);

  20. if(ret)

  21.             break;

  22.         sleep(10);

  23. }

  24.     close(fd);

  25.     return ret;

  26. }


一个高级一些的驱动在喂狗之前,可能还会做一些其他的事情,比如说检查HTTP服务器是否依然可以相应。


当设备关闭的时候,除非支持"Magic Close"特性。否则watchdog被关闭。这并不总是一个好主意,比如watchdog守护进程出现了bug并且崩溃了,则系统将不会重启。因此,某些驱动支持"Disable watchdog shutdown on close", CONFIG_WATCHDOG_NOWAYOUT配置选项。当编译内核的时候这个选项被设置为Y,则一旦watchdog被启动,则将没有办法能够停止。这样,则当watchdog守护进程崩溃的时候,系统仍将在超时后重启。Watchdog设备常常也支持nowayout模块参数,这样这个选项就可以在运行时进行控制。


Magic Close 特性:


如果一个驱动支持"Magic Close",则除非在关闭文件前,魔幻字符'V'被发送到/dev/watchdog,驱动将不停止watchdog。如果用户空间守护进程在关闭文件前没有发送这个字符,则驱动认为用户空间崩溃,并在关闭watchdog前停止喂狗。

这样的话,如果没有在一定的时间内重新打开watchdog,则将导致一个重启。


ioctl API:


所有标准的驱动也应该支持一个ioctl API。


喂狗使用一个ioctl:

所有的驱动都有一个ioctl接口支持至少一个ioctl命令,KEEPALIVE。这个 ioctl 做的事和一个写watchdog设备完全一样,所以,上面程序的主循环可以替换为:

  1. while(1){


  2.        ioctl(fd, WDIOC_KEEPALIVE, 0);


  3.        sleep(10);


  4. }


ioctl的参数被忽略。


设置和获得超时值:

对于某些驱动来说,在上层使用SETTIMEOUT ioctl命令改变watchdog的超时值是可能的,那些驱动在他们的选项与中有WDIOF_SETTIMEOUT标志。参数是一个代表以秒为单位的超时值,驱动将在同一个变量中返回实际使用的超时值,这个超时值可能由于硬件的限制,而不同于所请求的超时值

int timeout = 45;

ioctl(fd, WDIOC_SETTIMEOUT, &timeout);

printf("The timeout was set to %d seconds\n", timeout);

如果设备的超时值的粒度只能到分钟,则这个例子可能实际打印"The timeout was set to 60 seconds"。

自从Linux 2.4.18内核,通过GETTIMEOUT ioctl命令查询当前超时值也是可能的:

ioctl(fd, WDIOC_GETTIMEOUT, &timeout);

printf("The timeout was is %d seconds\n", timeout);


预处理:

Pretimeouts:

一些watchdog定时器,可以被设置为,在他们实际复位系统前,有一个触发。这可能通过一个NMI,中断,或其他机制。这将允许在它复位系统前Linux去记录一些有用的信息(比如panic信息和内核转储)。

pretimeout = 10;

ioctl(fd, WDIOC_SETPRETIMEOUT, &pretimeout);

注意,预超时值应该是一个相对于超时值提前的秒数。而不是直到预超时的秒数。

比如,如果你设置超时值为60秒,预超时值为10秒,那么预超时将在50秒后到达。设置为0则是禁用它。预超时还有一个get功能:

ioctl(fd, WDIOC_GETPRETIMEOUT, &timeout);

printf("The pretimeout was is %d seconds\n", timeout);

不是所有的watchdog驱动都支持一个预超时的。


获得重启前的秒数

一些watchdog驱动有一个报告在重启前的剩余时间的功能。WDIOC_GETTIMELEFT就是返回重启前的秒数的ioctl命令。

ioctl(fd, WDIOC_GETTIMELEFT, &timeleft);

printf("The timeout was is %d seconds\n", timeleft);


环境监视:

Environmental monitoring:


所有的watchdog驱动都被要求返回更多关于系统的信息,有些返回温度,风扇和功率水平监测,依稀可以告诉你上一次重启系统的原因。GETSUPPORT ioctl可以用来查询设备可以做什么:

struct watchdog_info ident;

ioctl(fd, WDIOC_GETSUPPORT, &ident);


ident结构中返回的字段是:

identity一个标识watchdog驱动的字符串

firmware_version如果可用的话,就是卡的固件版本

options一个描述设备支持什么的标志

options字段可以有下面的位集,和描述GET_STATUS 和 GET_BOOT_STATUS ioctls可以返回什么种类的信息。

第二部分: WDT驱动源码

驱动架构比较简单,由于kernel启动时,定义并加入了watchdog的platform_device,所以驱动定义并注册watchdog 的platform_driver
  1. /* linux/drivers/char/watchdog/s3c2410_wdt.c

  2. *

  3. * Copyright (c) 2004 Simtec Electronics

  4. *  Ben Dooks <ben@simtec.co.uk>

  5. *

  6. * S3C2410 Watchdog Timer Support

  7. *

  8. * Based on, softdog.c by Alan Cox,

  9. *(c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>

  10. *

  11. * This program is free software; you can redistribute it and/or modify

  12. * it under the terms of the GNU General Public License as published by

  13. * the Free Software Foundation; either version 2 of the License,or

  14. *(at your option) any later version.

  15. *

  16. * This program is distributed in the hope that it will be useful,

  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of

  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

  19. * GNU General Public License for more details.

  20. *

  21. * You should have received a copy of the GNU General Public License

  22. * along with this program;ifnot, write to the Free Software

  23. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  24. */


  25. #include <linux/module.h>

  26. #include <linux/moduleparam.h>

  27. #include <linux/types.h>

  28. #include <linux/timer.h>

  29. #include <linux/miscdevice.h>

  30. #include <linux/watchdog.h>

  31. #include <linux/fs.h>

  32. #include <linux/init.h>

  33. #include <linux/platform_device.h>

  34. #include <linux/interrupt.h>

  35. #include <linux/clk.h>

  36. #include <linux/uaccess.h>

  37. #include <linux/io.h>


  38. #include <mach/map.h>


  39. #undef S3C_VA_WATCHDOG

  40. #define S3C_VA_WATCHDOG (0)


  41. #include <plat/regs-watchdog.h>


  42. #define PFX "s3c2410-wdt: "


  43. #define CONFIG_S3C2410_WATCHDOG_ATBOOT      (0)

  44. #define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME    (15)


  45. static int nowayout = WATCHDOG_NOWAYOUT;

  46. static int tmr_margin   = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME;

  47. static int tmr_atboot   = CONFIG_S3C2410_WATCHDOG_ATBOOT;

  48. static int soft_noboot;

  49. static int debug;


  50. module_param(tmr_margin,int, 0);

  51. module_param(tmr_atboot,int, 0);

  52. module_param(nowayout,int, 0);

  53. module_param(soft_noboot,int, 0);

  54. module_param(debug,int, 0);


  55. MODULE_PARM_DESC(tmr_margin,"Watchdog tmr_margin in seconds. default="

  56.         __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME)")");

  57. MODULE_PARM_DESC(tmr_atboot,

  58. "Watchdog is started at boot time if set to 1, default="

  59.             __MODULE_STRING(CONFIG_S3C2410_WATCHDOG_ATBOOT));

  60. MODULE_PARM_DESC(nowayout,"Watchdog cannot be stopped once started (default="

  61.             __MODULE_STRING(WATCHDOG_NOWAYOUT)")");

  62. MODULE_PARM_DESC(soft_noboot,"Watchdog action, set to 1 to ignore reboots, "

  63. "0 to reboot (default depends on ONLY_TESTING)");

  64. MODULE_PARM_DESC(debug,"Watchdog debug, set to >1 for debug, (default 0)");


  65. static unsigned long open_lock;

  66. static struct device    *wdt_dev;/* platform device attached to*/

  67. static struct resource  *wdt_mem;

  68. static struct resource  *wdt_irq;

  69. static struct clk   *wdt_clock;

  70. static void __iomem *wdt_base;

  71. static unsigned int  wdt_count;

  72. static char      expect_close;

  73. static DEFINE_SPINLOCK(wdt_lock);


  74. /* watchdog control routines */


  75. #define DBG(msg...)do{\

  76. if(debug)\

  77.         printk(KERN_INFO msg);\

  78. }while(0)

  79. /* functions */


  80. static void s3c2410wdt_keepalive(void)

  81. {

  82.     spin_lock(&wdt_lock);

  83.     writel(wdt_count, wdt_base + S3C2410_WTCNT);

  84.     spin_unlock(&wdt_lock);

  85. }


  86. static void __s3c2410wdt_stop(void)

  87. {

  88.     unsigned long wtcon;


  89.     wtcon = readl(wdt_base + S3C2410_WTCON);

  90.     wtcon &=~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);

  91.     writel(wtcon, wdt_base + S3C2410_WTCON);

  92. }


  93. static void s3c2410wdt_stop(void)

  94. {

  95.     spin_lock(&wdt_lock);

  96.     __s3c2410wdt_stop();

  97.     spin_unlock(&wdt_lock);

  98. }


  99. static void s3c2410wdt_start(void)

  100. {

  101.     unsigned long wtcon;


  102.     spin_lock(&wdt_lock);


  103.     __s3c2410wdt_stop();


  104.     wtcon = readl(wdt_base + S3C2410_WTCON);

  105.     wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;


  106. if(soft_noboot){

  107.         wtcon |= S3C2410_WTCON_INTEN;

  108.         wtcon &=~S3C2410_WTCON_RSTEN;

  109. }else{

  110.         wtcon &=~S3C2410_WTCON_INTEN;

  111.         wtcon |= S3C2410_WTCON_RSTEN;

  112. }


  113.     DBG("%s: wdt_count=0x%08x, wtcon=%08lx\n",

  114.         __func__, wdt_count, wtcon);


  115.     writel(wdt_count, wdt_base + S3C2410_WTDAT);

  116.     writel(wdt_count, wdt_base + S3C2410_WTCNT);

  117.     writel(wtcon, wdt_base + S3C2410_WTCON);

  118.     spin_unlock(&wdt_lock);

  119. }


  120. static int s3c2410wdt_set_heartbeat(int timeout)

  121. {

  122.     unsigned int freq = clk_get_rate(wdt_clock);

  123.     unsigned int count;

  124.     unsigned int divisor = 1;

  125.     unsigned long wtcon;

  126. if(timeout < 1)

  127.         return -EINVAL;


  128.     freq /= 128;

  129.     count = timeout * freq;


  130.     DBG("%s: count=%d, timeout=%d, freq=%d\n",

  131.         __func__, count, timeout, freq);


  132. /*if the count is bigger than the watchdog register,

  133. then work out what we need todo(andif) we can

  134.        actually make this value

  135. */


  136. if(count >= 0x10000){

  137. for(divisor = 1; divisor <= 0x100; divisor++){

  138. if((count / divisor)< 0x10000)

  139.                 break;

  140. }


  141. if((count / divisor)>= 0x10000){

  142.             dev_err(wdt_dev,"timeout %d too big\n", timeout);

  143.             return -EINVAL;

  144. }

  145. }


  146.     tmr_margin = timeout;


  147.     DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n",

  148.         __func__, timeout, divisor, count, count/divisor);


  149.     count /= divisor;

  150.     wdt_count = count;


  151. /* update the pre-scaler */

  152.     wtcon = readl(wdt_base + S3C2410_WTCON);

  153.     wtcon &=~S3C2410_WTCON_PRESCALE_MASK;

  154.     wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);


  155.     writel(count, wdt_base + S3C2410_WTDAT);

  156.     writel(wtcon, wdt_base + S3C2410_WTCON);


  157.     return 0;

  158. }


  159. /*

  160. */dev/watchdog handling

  161. */


  162. static int s3c2410wdt_open(struct inode *inode, struct file *file)

  163. {

  164. if(test_and_set_bit(0,&open_lock))

  165.         return -EBUSY;


  166. if(nowayout)

  167.         __module_get(THIS_MODULE);


  168.     expect_close = 0;


  169. /* start the timer */

  170.     s3c2410wdt_start();

  171.     return nonseekable_open(inode, file);

  172. }


  173. static int s3c2410wdt_release(struct inode *inode, struct file *file)

  174. {

  175. /*

  176. *  Shut off the timer.

  177. *  Lock it inif it's a module and we set nowayout

  178. */


  179. if(expect_close == 42)

  180.         s3c2410wdt_stop();

  181. else{

  182.         dev_err(wdt_dev,"Unexpected close, not stopping watchdog\n");

  183.         s3c2410wdt_keepalive();

  184. }

  185.     expect_close = 0;

  186.     clear_bit(0,&open_lock);

  187.     return 0;

  188. }


  189. static ssize_t s3c2410wdt_write(struct file *file,const char __user *data,

  190.                 size_t len, loff_t *ppos)

  191. {

  192. /*

  193. *  Refresh the timer.

  194. */

  195. if(len){

  196. if(!nowayout){

  197.             size_t i;


  198. /*Incase it was set long ago */

  199.             expect_close = 0;


  200. for(i = 0; i !=len; i++){

  201.                 char c;


  202. if(get_user(c, data + i))

  203.                     return -EFAULT;

  204. if(c =='V')

  205.                     expect_close = 42;

  206. }

  207. }

  208.         s3c2410wdt_keepalive();

  209. }

  210.     return len;

  211. }


  212. #define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)


  213. static const struct watchdog_info s3c2410_wdt_ident ={

  214. .options          =     OPTIONS,

  215. .firmware_version = 0,

  216. .identity         ="S3C2410 Watchdog",

  217. };



  218. static long s3c2410wdt_ioctl(struct file *file, unsigned int cmd,

  219.                             unsigned long arg)

  220. {

  221.     void __user *argp =(void __user *)arg;

  222. int __user *p = argp;

  223. int new_margin;


  224.     switch (cmd){

  225. case WDIOC_GETSUPPORT:

  226.         return copy_to_user(argp,&s3c2410_wdt_ident,

  227.             sizeof(s3c2410_wdt_ident))?-EFAULT : 0;

  228. case WDIOC_GETSTATUS:

  229. case WDIOC_GETBOOTSTATUS:

  230.         return put_user(0, p);

  231. case WDIOC_KEEPALIVE:

  232.         s3c2410wdt_keepalive();

  233.         return 0;

  234. case WDIOC_SETTIMEOUT:

  235. if(get_user(new_margin, p))

  236.             return -EFAULT;

  237. if(s3c2410wdt_set_heartbeat(new_margin))

  238.             return -EINVAL;

  239.         s3c2410wdt_keepalive();

  240.         return put_user(tmr_margin, p);

  241. case WDIOC_GETTIMEOUT:

  242.         return put_user(tmr_margin, p);

  243.     default:

  244.         return -ENOTTY;

  245. }

  246. }


  247. /* kernel interface */


  248. static const struct file_operations s3c2410wdt_fops ={

  249. .owner      = THIS_MODULE,

  250. .llseek     = no_llseek,

  251. .write      = s3c2410wdt_write,

  252. .unlocked_ioctl = s3c2410wdt_ioctl,

  253. .open       = s3c2410wdt_open,

  254. .release    = s3c2410wdt_release,

  255. };


  256. static struct miscdevice s3c2410wdt_miscdev ={

  257. .minor      = WATCHDOG_MINOR,

  258. .name       ="watchdog",

  259. .fops       =&s3c2410wdt_fops,

  260. };


  261. /* interrupt handler code */


  262. static irqreturn_t s3c2410wdt_irq(int irqno, void *param)

  263. {

  264.     dev_info(wdt_dev,"watchdog timer expired (irq)\n");


  265.     s3c2410wdt_keepalive();

  266.     return IRQ_HANDLED;

  267. }

  268. /* device interface */


  269. static int __devinit s3c2410wdt_probe(struct platform_device *pdev)

  270. {

  271.     struct resource *res;

  272.     struct device *dev;

  273.     unsigned int wtcon;

  274. int started = 0;

  275. int ret;

  276. int size;


  277.     DBG("%s: probe=%p\n", __func__, pdev);


  278.     dev =&pdev->dev;

  279.     wdt_dev =&pdev->dev;


  280. /*get the memory region for the watchdog timer -- flags is IORESOURCE_MEM */

  281.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

  282. if(res ==NULL){

  283.         dev_err(dev,"no memory resource specified\n");

  284.         return -ENOENT;

  285. }


  286.     size =(res->end- res->start)+ 1;


  287. //请求分配指定的I/O内存资源

  288.    wdt_mem = request_mem_region(res->start, size, pdev->name);

  289. if(wdt_mem ==NULL){

  290.         dev_err(dev,"failed to get memory region\n");

  291.         ret =-ENOENT;

  292.         goto err_req;

  293. }


  294. //将一个IO地址空间映射到内核的虚拟地址空间上去,便于访问

  295.    wdt_base = ioremap(res->start, size);

  296. if(wdt_base ==NULL){

  297.         dev_err(dev,"failed to ioremap() region\n");

  298.         ret =-EINVAL;

  299.         goto err_req;

  300. }


  301.     DBG("probe: mapped wdt_base=%p\n", wdt_base);


  302. /*get the memory region for the watchdog timer -- flags is IORESOURCE_IRQ */

  303.     wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

  304. if(wdt_irq ==NULL){

  305.         dev_err(dev,"no irq resource specified\n");

  306.         ret =-ENOENT;

  307.         goto err_map;

  308. }


  309. //注册中断服务函数s3c2410wdt_irq()

  310.     ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);

  311. if(ret != 0){

  312.         dev_err(dev,"failed to install irq (%d)\n", ret);

  313.         goto err_map;

  314. }


  315. //从平台时钟队列中获取clk

  316.     wdt_clock = clk_get(&pdev->dev,"watchdog");

  317. if(IS_ERR(wdt_clock)){

  318.         dev_err(dev,"failed to find watchdog clock source\n");

  319.         ret = PTR_ERR(wdt_clock);

  320.         goto err_irq;

  321. }


  322. //inform the system when the clock source should be running

  323.     clk_enable(wdt_clock);


  324. /* see if we can actually set the requested timer margin,andif

  325. *not, try the default value */


  326. if(s3c2410wdt_set_heartbeat(tmr_margin)){

  327.         started = s3c2410wdt_set_heartbeat(

  328.                     CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);


  329. if(started == 0)

  330.             dev_info(dev,

  331. "tmr_margin value out of range, default %d used\n",

  332.                    CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);

  333. else

  334.             dev_info(dev,"default timer value is out of range, "

  335. "cannot start\n");

  336. }


  337.     ret = misc_register(&s3c2410wdt_miscdev);

  338. if(ret){

  339.         dev_err(dev,"cannot register miscdev on minor=%d (%d)\n",

  340.             WATCHDOG_MINOR, ret);

  341.         goto err_clk;

  342. }


  343. if(tmr_atboot && started == 0){

  344.         dev_info(dev,"starting watchdog timer\n");

  345.         s3c2410wdt_start();

  346. }elseif(!tmr_atboot){

  347. /*if we're not enabling the watchdog,then ensure it is

  348. * disabled if it has been left running from the bootloader

  349. *or other source */


  350.         s3c2410wdt_stop();

  351. }


  352. /* print out a statement of readiness */


  353.     wtcon = readl(wdt_base + S3C2410_WTCON);


  354.     dev_info(dev,"watchdog %sactive, reset %sabled, irq %sabled\n",

  355. (wtcon & S3C2410_WTCON_ENABLE)?"":"in",

  356. (wtcon & S3C2410_WTCON_RSTEN)?"":"dis",

  357. (wtcon & S3C2410_WTCON_INTEN)?"":"en");


  358.     return 0;


  359.  err_clk:

  360.     clk_disable(wdt_clock);

  361.     clk_put(wdt_clock);


  362.  err_irq:

  363.     free_irq(wdt_irq->start, pdev);


  364.  err_map:

  365.     iounmap(wdt_base);


  366.  err_req:

  367.     release_resource(wdt_mem);

  368.     kfree(wdt_mem);


  369.     return ret;

  370. }


  371. static int __devexit s3c2410wdt_remove(struct platform_device *dev)

  372. {

  373.     release_resource(wdt_mem);

  374.     kfree(wdt_mem);

  375.     wdt_mem =NULL;


  376.     free_irq(wdt_irq->start, dev);

  377.     wdt_irq =NULL;


  378.     clk_disable(wdt_clock);

  379.     clk_put(wdt_clock);

  380.     wdt_clock =NULL;


  381.     iounmap(wdt_base);

  382.     misc_deregister(&s3c2410wdt_miscdev);

  383.     return 0;

  384. }


  385. static void s3c2410wdt_shutdown(struct platform_device *dev)

  386. {

  387.     s3c2410wdt_stop();

  388. }


  389. #ifdef CONFIG_PM


  390. static unsigned long wtcon_save;

  391. static unsigned long wtdat_save;


  392. static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state)

  393. {

  394. /* Save watchdog state,and turn it off.*/

  395.     wtcon_save = readl(wdt_base + S3C2410_WTCON);

  396.     wtdat_save = readl(wdt_base + S3C2410_WTDAT);


  397. /* Note that WTCNT doesn't need to be saved.*/

  398.     s3c2410wdt_stop();


  399.     return 0;

  400. }


  401. static int s3c2410wdt_resume(struct platform_device *dev)

  402. {

  403. /* Restore watchdog state.*/


  404.     writel(wtdat_save, wdt_base + S3C2410_WTDAT);

  405.     writel(wtdat_save, wdt_base + S3C2410_WTCNT);/* Reset count */

  406.     writel(wtcon_save, wdt_base + S3C2410_WTCON);


  407.     printk(KERN_INFO PFX "watchdog %sabled\n",

  408. (wtcon_save & S3C2410_WTCON_ENABLE)?"en":"dis");


  409.     return 0;

  410. }

  411. #else

  412. #define s3c2410wdt_suspend NULL

  413. #define s3c2410wdt_resume  NULL

  414. #endif /* CONFIG_PM */



  415. /*

  416. *platform_driver s3c2410wdt_driver 与 platform_device s3c_device_wdt 对应

  417. *s3c_device_wdt 在arch/arm/plat-s3c24xx/devs.c中定义

  418. *两者的工作顺序是先定义platform_device -> 注册 platform_device->

  419. *在mini2440_machine_init()中完成

  420. *再定义 platform_driver-> 注册 platform_driver

  421. */

  422. static struct platform_driver s3c2410wdt_driver ={

  423. .probe      = s3c2410wdt_probe,//设备的检测,所以需要先注册设备

  424. .remove     = __devexit_p(s3c2410wdt_remove),//删除该设备

  425. .shutdown   = s3c2410wdt_shutdown,//关闭该设备

  426. .suspend    = s3c2410wdt_suspend,

  427. .resume= s3c2410wdt_resume,

  428. .driver     ={//设备驱动

  429. .owner  = THIS_MODULE,

  430.        /*
            *对应 struct platform_device s3c_device_wdt = {
            *     .name         = "s3c2410-wdt",
            *       ...
            *    };
            */

  431. .name   ="s3c2410-wdt",

  432. },

  433. };



  434. static char banner[] __initdata =

  435.     KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n";


  436. static int __init watchdog_init(void)//模块初始化

  437. {

  438.     printk(banner);//打印信息

  439.    return platform_driver_register(&s3c2410wdt_driver);//注册设备的驱动程序

  440. }


  441. static void __exit watchdog_exit(void)//移除模块

  442. {

  443.     platform_driver_unregister(&s3c2410wdt_driver);//unregister a driver for platform-level devices

  444. }


  445. module_init(watchdog_init);

  446. module_exit(watchdog_exit);


  447. MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, "

  448. "Dimitry Andric <dimitry.andric@tomtom.com>");

  449. MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");

  450. MODULE_LICENSE("GPL");

  451. MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);

  452. MODULE_ALIAS("platform:s3c2410-wdt");