第一部分: 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");

转载于:https://blog.51cto.com/dayu1980/1370762

Linux驱动学习之:WDT(watchdog)驱动相关推荐

  1. Linux内核学习-字符设备驱动学习(二)

    在Linux内核学习-字符设备驱动学习(一)中编写字符设备驱动的一种方法,但是需要手动创建设备节点. 有没有能够自动的创建设备节点的呢? 有!使用class_create()和device_creat ...

  2. Linux驱动学习之什么是驱动?

    一.什么是驱动? 1: 驱动一词的字面意思 2: 物理上的驱动 3: 硬件中的驱动 4: linux内核驱动.软件层面上的驱动广义上是指:这一段代码操作了硬件去动,所以这一段代码就叫硬件的驱动程序. ...

  3. linux 驱动学习笔记-ALSA声卡驱动(二)

    前言 ASoC是建立在标准ALSA驱动层上 ,对底层的alsa框架封装了一层,为了更好的支持嵌入式cpu和音频解码器设备的一套软件体系 在ASOC出现之前 解码器驱动和平台CPU驱动联系过于紧密,导致 ...

  4. linux2.6驱动学习笔记之字符驱动

    1.字符驱动组成 1.1字符驱动的模块加载与卸载 //设备结构体模板 struct xxx_dev_t { struct cdev cdev; ...... }xxx_dev; 在字符驱动模块加载函数 ...

  5. Linux内核学习笔记之网卡驱动的详细分析:RTL8139

    学习应该是一个先把问题简单化,在把问题复杂化的过程.一开始就着手处理复杂的问题,难免让 人有心惊胆颤,捉襟见肘的感觉.读Linux网卡驱动也是一样.那长长的源码夹杂着那些我们陌生的变量和符号,望而生畏 ...

  6. Linux驱动学习笔记之触摸屏驱动

    触摸屏归纳为输入子系统,这里主要是针对电阻屏,其使用过程如下 当用触摸笔按下时,产生中断. 在中断处理函数处理函数中启动ADC转换x,y坐标. ADC结束,产生ADC中断 在ADC中断处理函数里上报( ...

  7. linux驱动学习2(kpd驱动初步分析)

    一.kpd_pdrv_probe函数的分析: /*1. 输入设备实例 kpd_input_dev */ 全局变量:static struct input_dev *kpd_input_dev; sta ...

  8. Linux设备与驱动学习之----什么是驱动

    [ 声明:版权所有,欢迎转载,转载请注明出处,请勿用于商业用途] [ 声明:本文属于作者个人理解,如有错误,欢迎大家指正] 在上一篇博文中我们介绍了 Linux 中的device 概念,这篇博文中我们 ...

  9. linux 驱动学习笔记-ALSA声卡驱动(一)

    概述 ALSA作为Liunx现在主流的音频体系结构,提供了内核的驱动框架,也提供了应用层的alsa-lib库,应用层主要调用alsa-lib的API函数就可以实现对声卡的控制.ALSA也提供了alsa ...

最新文章

  1. 《JavaScript面向对象精要》读书笔记
  2. 扎克伯格、张一鸣、宿华都公开道歉,AI算法不灵了吗?
  3. 架构高性能网站秘笈(一)——了解衡量网站性能的指标
  4. Android studio 不显示自定义View 的提示
  5. blob转file对象_C++核心准则C.41:构造函数生成的对象应该被完全初始化
  6. java线程池使用实战,太牛了!
  7. 修改linux默认启动级别(包括Ubuntu)
  8. zabbix邮箱报警设置
  9. 剑指 Offer II 108. 单词演变
  10. Reverse Interger
  11. Codeforces Beta Round #14 (Div. 2) D. Two Paths 树的直径
  12. 华为云盘里面的照片怎么导出来_华为手机误删照片,怎么恢复?别急!只需点击这里...
  13. html表单提交数据保存到mysql中_php – Canonical:如何将HTML表单数据保存到MySQL数据库中...
  14. Godot Timer节点
  15. 前端技术文档及工具汇总
  16. android pdf转word开发,如何在Android手机上将PDF转换为Word文档
  17. Bruce Schneier 和他的密码学以及网络安全经典著作推荐
  18. 三星智能手表取得快速增长,华为和小米则以低价穿戴设备取胜
  19. c语言栈的实现以及操作
  20. 2022-2028全球谋杀悬疑游戏行业调研及趋势分析报告

热门文章

  1. 为学习Hadoop使用VMware准备3台一模一样的Linux虚拟机的详细搭建过程
  2. 拍照翻译软件哪个好用?3个拍照翻译软件推
  3. 给宝宝早教c语言,爸爸用C语言给孩子早教,孩子逐渐崩溃,网友:你别害孩子秃顶...
  4. 科大讯飞超强的合成语音软件InterPhonic下载
  5. 一根RS485线可以并联多少设备
  6. 杭州中欣晶圆半导体股份有限公司完成B轮融资;朗新科技助力无锡打造城运中心 | 全球TMT...
  7. Bentley正洞与斜井
  8. iphone手机版本降级
  9. UE4 材质笔记之岩石(法线初级应用)
  10. 计算机科学与技术专业色弱限制吗,高考录取时,对色弱很严格吗?