1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27#include <linux/config.h>
28#include <linux/module.h>
29#include <linux/types.h>
30#include <linux/errno.h>
31#include <linux/kernel.h>
32#include <linux/miscdevice.h>
33#include <linux/watchdog.h>
34#include <linux/ioport.h>
35#include <linux/fcntl.h>
36#include <asm/io.h>
37#include <asm/uaccess.h>
38#include <asm/system.h>
39#include <linux/notifier.h>
40#include <linux/reboot.h>
41#include <linux/init.h>
42
43static unsigned long advwdt_is_open;
44static char adv_expect_close;
45
46
47
48
49
50
51
52
53
54
55
56
57static int wdt_stop = 0x443;
58static int wdt_start = 0x443;
59
60static int wd_margin = 60;
61
62#ifdef CONFIG_WATCHDOG_NOWAYOUT
63static int nowayout = 1;
64#else
65static int nowayout = 0;
66#endif
67
68MODULE_PARM(nowayout,"i");
69MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
70
71
72
73
74
75#ifndef MODULE
76
77static int __init adv_setup(char *str)
78{
79 int ints[4];
80
81 str = get_options(str, ARRAY_SIZE(ints), ints);
82
83 if(ints[0] > 0){
84 wdt_stop = ints[1];
85 if(ints[0] > 1)
86 wdt_start = ints[2];
87 }
88
89 return 1;
90}
91
92__setup("advwdt=", adv_setup);
93
94#endif
95
96MODULE_PARM(wdt_stop, "i");
97MODULE_PARM_DESC(wdt_stop, "Advantech WDT 'stop' io port (default 0x443)");
98MODULE_PARM(wdt_start, "i");
99MODULE_PARM_DESC(wdt_start, "Advantech WDT 'start' io port (default 0x443)");
100
101static void
102advwdt_ping(void)
103{
104
105 outb_p(wd_margin, wdt_start);
106}
107
108static void
109advwdt_disable(void)
110{
111 inb_p(wdt_stop);
112}
113
114static ssize_t
115advwdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
116{
117
118 if (ppos != &file->f_pos)
119 return -ESPIPE;
120
121 if (count) {
122 if (!nowayout) {
123 size_t i;
124
125 adv_expect_close = 0;
126
127 for (i = 0; i != count; i++) {
128 char c;
129 if(get_user(c, buf+i))
130 return -EFAULT;
131 if (c == 'V')
132 adv_expect_close = 42;
133 }
134 }
135 advwdt_ping();
136 }
137 return count;
138}
139
140static int
141advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
142 unsigned long arg)
143{
144 int new_margin;
145 static struct watchdog_info ident = {
146 options: WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
147 firmware_version: 0,
148 identity: "Advantech WDT"
149 };
150
151 switch (cmd) {
152 case WDIOC_GETSUPPORT:
153 if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)))
154 return -EFAULT;
155 break;
156
157 case WDIOC_GETSTATUS:
158 case WDIOC_GETBOOTSTATUS:
159 return put_user(0, (int *)arg);
160
161 case WDIOC_KEEPALIVE:
162 advwdt_ping();
163 break;
164
165 case WDIOC_SETTIMEOUT:
166 if (get_user(new_margin, (int *)arg))
167 return -EFAULT;
168 if ((new_margin < 1) || (new_margin > 63))
169 return -EINVAL;
170 wd_margin = new_margin;
171 advwdt_ping();
172
173
174 case WDIOC_GETTIMEOUT:
175 return put_user(wd_margin, (int *)arg);
176
177 case WDIOC_SETOPTIONS:
178 {
179 int options, retval = -EINVAL;
180
181 if (get_user(options, (int *)arg))
182 return -EFAULT;
183
184 if (options & WDIOS_DISABLECARD) {
185 advwdt_disable();
186 retval = 0;
187 }
188
189 if (options & WDIOS_ENABLECARD) {
190 advwdt_ping();
191 retval = 0;
192 }
193
194 return retval;
195 }
196
197 default:
198 return -ENOTTY;
199 }
200 return 0;
201}
202
203static int
204advwdt_open(struct inode *inode, struct file *file)
205{
206 if (test_and_set_bit(0, &advwdt_is_open))
207 return -EBUSY;
208
209
210
211
212 advwdt_ping();
213 return 0;
214}
215
216static int
217advwdt_close(struct inode *inode, struct file *file)
218{
219 if (adv_expect_close == 42) {
220 advwdt_disable();
221 } else {
222 printk(KERN_CRIT "advancetechwdt: Unexpected close, not stopping watchdog!\n");
223 advwdt_ping();
224 }
225 clear_bit(0, &advwdt_is_open);
226 adv_expect_close = 0;
227 return 0;
228}
229
230
231
232
233
234static int
235advwdt_notify_sys(struct notifier_block *this, unsigned long code,
236 void *unused)
237{
238 if (code == SYS_DOWN || code == SYS_HALT) {
239
240 advwdt_disable();
241 }
242 return NOTIFY_DONE;
243}
244
245
246
247
248
249static struct file_operations advwdt_fops = {
250 owner: THIS_MODULE,
251 llseek: no_llseek,
252 write: advwdt_write,
253 ioctl: advwdt_ioctl,
254 open: advwdt_open,
255 release: advwdt_close,
256};
257
258static struct miscdevice advwdt_miscdev = {
259 minor: WATCHDOG_MINOR,
260 name: "watchdog",
261 fops: &advwdt_fops,
262};
263
264
265
266
267
268
269static struct notifier_block advwdt_notifier = {
270 advwdt_notify_sys,
271 NULL,
272 0
273};
274
275static int __init
276advwdt_init(void)
277{
278 printk(KERN_INFO "WDT driver for Advantech single board computer initialising.\n");
279
280 misc_register(&advwdt_miscdev);
281 if(wdt_stop != wdt_start)
282 request_region(wdt_stop, 1, "Advantech WDT");
283 request_region(wdt_start, 1, "Advantech WDT");
284 register_reboot_notifier(&advwdt_notifier);
285 return 0;
286}
287
288static void __exit
289advwdt_exit(void)
290{
291 misc_deregister(&advwdt_miscdev);
292 unregister_reboot_notifier(&advwdt_notifier);
293 if(wdt_stop != wdt_start)
294 release_region(wdt_stop,1);
295 release_region(wdt_start,1);
296}
297
298module_init(advwdt_init);
299module_exit(advwdt_exit);
300
301MODULE_LICENSE("GPL");
302MODULE_AUTHOR("Marek Michalkiewicz <marekm@linux.org.pl>");
303MODULE_DESCRIPTION("Advantech Single Board Computer WDT driver");
304EXPORT_NO_SYMBOLS;
305
306
307
308