sbc60xxwdt.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:8k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  * 60xx Single Board Computer Watchdog Timer driver for Linux 2.2.x
  3.  *
  4.  *      Based on acquirewdt.c by Alan Cox.
  5.  *
  6.  * This program is free software; you can redistribute it and/or
  7.  * modify it under the terms of the GNU General Public License
  8.  * as published by the Free Software Foundation; either version
  9.  * 2 of the License, or (at your option) any later version.
  10.  *
  11.  * The author does NOT admit liability nor provide warranty for 
  12.  * any of this software. This material is provided "AS-IS" in 
  13.  *      the hope that it may be useful for others.
  14.  *
  15.  * (c) Copyright 2000    Jakob Oestergaard <jakob@ostenfeld.dk>
  16.  *
  17.  *           12/4 - 2000      [Initial revision]
  18.  *           25/4 - 2000      Added /dev/watchdog support
  19.  *           09/5 - 2001      [smj@oro.net] fixed fop_write to "return 1" on success
  20.  *
  21.  *
  22.  *  Theory of operation:
  23.  *  A Watchdog Timer (WDT) is a hardware circuit that can 
  24.  *  reset the computer system in case of a software fault.
  25.  *  You probably knew that already.
  26.  *
  27.  *  Usually a userspace daemon will notify the kernel WDT driver
  28.  *  via the /proc/watchdog special device file that userspace is
  29.  *  still alive, at regular intervals.  When such a notification
  30.  *  occurs, the driver will usually tell the hardware watchdog
  31.  *  that everything is in order, and that the watchdog should wait
  32.  *  for yet another little while to reset the system.
  33.  *  If userspace fails (RAM error, kernel bug, whatever), the
  34.  *  notifications cease to occur, and the hardware watchdog will
  35.  *  reset the system (causing a reboot) after the timeout occurs.
  36.  *
  37.  *  This WDT driver is different from the other Linux WDT 
  38.  *  drivers in several ways:
  39.  *  *)  The driver will ping the watchdog by itself, because this
  40.  *      particular WDT has a very short timeout (one second) and it
  41.  *      would be insane to count on any userspace daemon always
  42.  *      getting scheduled within that time frame.
  43.  *  *)  This driver expects the userspace daemon to send a specific
  44.  *      character code ('V') to /dev/watchdog before closing the
  45.  *      /dev/watchdog file.  If the userspace daemon closes the file
  46.  *      without sending this special character, the driver will assume
  47.  *      that the daemon (and userspace in general) died, and will
  48.  *      stop pinging the WDT without disabling it first.  This will
  49.  *      cause a reboot.
  50.  *
  51.  *  Why `V' ?  Well, `V' is the character in ASCII for the value 86,
  52.  *  and we all know that 86 is _the_ most random number in the universe.
  53.  *  Therefore it is the letter that has the slightest chance of occuring
  54.  *  by chance, when the system becomes corrupted.
  55.  *
  56.  */
  57. #include <linux/module.h>
  58. #include <linux/version.h>
  59. #include <linux/types.h>
  60. #include <linux/errno.h>
  61. #include <linux/kernel.h>
  62. #include <linux/timer.h>
  63. #include <linux/sched.h>
  64. #include <linux/miscdevice.h>
  65. #include <linux/watchdog.h>
  66. #include <linux/slab.h>
  67. #include <linux/ioport.h>
  68. #include <linux/fcntl.h>
  69. #include <linux/smp_lock.h>
  70. #include <asm/io.h>
  71. #include <asm/uaccess.h>
  72. #include <asm/system.h>
  73. #include <linux/notifier.h>
  74. #include <linux/reboot.h>
  75. #include <linux/init.h>
  76. #define OUR_NAME "sbc60xxwdt"
  77. /*
  78.  * You must set these - The driver cannot probe for the settings
  79.  */
  80.  
  81. #define WDT_STOP 0x45
  82. #define WDT_START 0x443
  83. /*
  84.  * The 60xx board can use watchdog timeout values from one second
  85.  * to several minutes.  The default is one second, so if we reset
  86.  * the watchdog every ~250ms we should be safe.
  87.  */
  88. #define WDT_INTERVAL (HZ/4+1)
  89. /*
  90.  * We must not require too good response from the userspace daemon.
  91.  * Here we require the userspace daemon to send us a heartbeat
  92.  * char to /dev/watchdog every 10 seconds.
  93.  * If the daemon pulses us every 5 seconds, we can still afford
  94.  * a 5 second scheduling delay on the (high priority) daemon. That
  95.  * should be sufficient for a box under any load.
  96.  */
  97. #define WDT_HEARTBEAT (HZ * 10)
  98. static void wdt_timer_ping(unsigned long);
  99. static struct timer_list timer;
  100. static unsigned long next_heartbeat;
  101. static int wdt_is_open;
  102. static int wdt_expect_close;
  103. /*
  104.  * Whack the dog
  105.  */
  106. static void wdt_timer_ping(unsigned long data)
  107. {
  108. /* If we got a heartbeat pulse within the WDT_US_INTERVAL
  109.  * we agree to ping the WDT 
  110.  */
  111. if(time_before(jiffies, next_heartbeat)) 
  112. {
  113. /* Ping the WDT by reading from WDT_START */
  114. inb_p(WDT_START);
  115. /* Re-set the timer interval */
  116. timer.expires = jiffies + WDT_INTERVAL;
  117. add_timer(&timer);
  118. } else {
  119. printk(OUR_NAME ": Heartbeat lost! Will not ping the watchdogn");
  120. }
  121. }
  122. /* 
  123.  * Utility routines
  124.  */
  125. static void wdt_startup(void)
  126. {
  127. next_heartbeat = jiffies + WDT_HEARTBEAT;
  128. /* Start the timer */
  129. timer.expires = jiffies + WDT_INTERVAL;
  130. add_timer(&timer);
  131. printk(OUR_NAME ": Watchdog timer is now enabled.n");  
  132. }
  133. static void wdt_turnoff(void)
  134. {
  135. /* Stop the timer */
  136. del_timer(&timer);
  137. inb_p(WDT_STOP);
  138. printk(OUR_NAME ": Watchdog timer is now disabled...n");
  139. }
  140. /*
  141.  * /dev/watchdog handling
  142.  */
  143. static ssize_t fop_write(struct file * file, const char * buf, size_t count, loff_t * ppos)
  144. {
  145. /* We can't seek */
  146. if(ppos != &file->f_pos)
  147. return -ESPIPE;
  148. /* See if we got the magic character */
  149. if(count) 
  150. {
  151. size_t ofs;
  152. /* note: just in case someone wrote the magic character
  153.  * five months ago... */
  154. wdt_expect_close = 0;
  155. /* now scan */
  156. for(ofs = 0; ofs != count; ofs++) 
  157. {
  158. char c;
  159. if(get_user(c, buf+ofs))
  160. return -EFAULT;
  161. if(c == 'V')
  162. wdt_expect_close = 1;
  163. }
  164. /* Well, anyhow someone wrote to us, we should return that favour */
  165. next_heartbeat = jiffies + WDT_HEARTBEAT;
  166. return 1;
  167. }
  168. return 0;
  169. }
  170. static ssize_t fop_read(struct file * file, char * buf, size_t count, loff_t * ppos)
  171. {
  172. /* No can do */
  173. return -EINVAL;
  174. }
  175. static int fop_open(struct inode * inode, struct file * file)
  176. {
  177. switch(MINOR(inode->i_rdev)) 
  178. {
  179. case WATCHDOG_MINOR:
  180. /* Just in case we're already talking to someone... */
  181. if(wdt_is_open)
  182. return -EBUSY;
  183. /* Good, fire up the show */
  184. wdt_is_open = 1;
  185. wdt_startup();
  186. return 0;
  187. default:
  188. return -ENODEV;
  189. }
  190. }
  191. static int fop_close(struct inode * inode, struct file * file)
  192. {
  193. lock_kernel();
  194. if(MINOR(inode->i_rdev) == WATCHDOG_MINOR) 
  195. {
  196. if(wdt_expect_close)
  197. wdt_turnoff();
  198. else {
  199. del_timer(&timer);
  200. printk(OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!n");
  201. }
  202. }
  203. wdt_is_open = 0;
  204. unlock_kernel();
  205. return 0;
  206. }
  207. static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
  208. unsigned long arg)
  209. {
  210. static struct watchdog_info ident=
  211. {
  212. WDIOF_MAGICCLOSE,
  213. 1,
  214. "SB60xx"
  215. };
  216. switch(cmd)
  217. {
  218. default:
  219. return -ENOTTY;
  220. case WDIOC_GETSUPPORT:
  221. return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0;
  222. case WDIOC_KEEPALIVE:
  223. next_heartbeat = jiffies + WDT_HEARTBEAT;
  224. return 0;
  225. }
  226. }
  227. static struct file_operations wdt_fops = {
  228. owner: THIS_MODULE,
  229. llseek: no_llseek,
  230. read: fop_read,
  231. write: fop_write,
  232. open: fop_open,
  233. release: fop_close,
  234. ioctl: fop_ioctl
  235. };
  236. static struct miscdevice wdt_miscdev = {
  237. WATCHDOG_MINOR,
  238. "watchdog",
  239. &wdt_fops
  240. };
  241. /*
  242.  * Notifier for system down
  243.  */
  244. static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
  245. void *unused)
  246. {
  247. if(code==SYS_DOWN || code==SYS_HALT) 
  248. wdt_turnoff();
  249. return NOTIFY_DONE;
  250. }
  251.  
  252. /*
  253.  * The WDT needs to learn about soft shutdowns in order to
  254.  * turn the timebomb registers off. 
  255.  */
  256.  
  257. static struct notifier_block wdt_notifier=
  258. {
  259. wdt_notify_sys,
  260. 0,
  261. 0
  262. };
  263. static void __exit sbc60xxwdt_unload(void)
  264. {
  265. wdt_turnoff();
  266. /* Deregister */
  267. misc_deregister(&wdt_miscdev);
  268. unregister_reboot_notifier(&wdt_notifier);
  269. release_region(WDT_START,1);
  270. // release_region(WDT_STOP,1);
  271. }
  272. static int __init sbc60xxwdt_init(void)
  273. {
  274. int rc = -EBUSY;
  275. // We cannot reserve 0x45 - the kernel already has!
  276. // if (!request_region(WDT_STOP, 1, "SBC 60XX WDT"))
  277. // goto err_out;
  278. if (!request_region(WDT_START, 1, "SBC 60XX WDT"))
  279. goto err_out_region1;
  280. init_timer(&timer);
  281. timer.function = wdt_timer_ping;
  282. timer.data = 0;
  283. rc = misc_register(&wdt_miscdev);
  284. if (rc)
  285. goto err_out_region2;
  286. rc = register_reboot_notifier(&wdt_notifier);
  287. if (rc)
  288. goto err_out_miscdev;
  289. printk(KERN_INFO OUR_NAME ": WDT driver for 60XX single board computer initialised.n");
  290. return 0;
  291. err_out_miscdev:
  292. misc_deregister(&wdt_miscdev);
  293. err_out_region2:
  294. release_region(WDT_START,1);
  295. err_out_region1:
  296. release_region(WDT_STOP,1);
  297. err_out:
  298. return rc;
  299. }
  300. module_init(sbc60xxwdt_init);
  301. module_exit(sbc60xxwdt_unload);
  302. MODULE_LICENSE("GPL");
  303. EXPORT_NO_SYMBOLS;