File: /usr/src/linux/drivers/char/mixcomwd.c

1     /*
2      * MixCom Watchdog: A Simple Hardware Watchdog Device
3      * Based on Softdog driver by Alan Cox and PC Watchdog driver by Ken Hollis
4      *
5      * Author: Gergely Madarasz <gorgo@itc.hu>
6      *
7      * Copyright (c) 1999 ITConsult-Pro Co. <info@itc.hu>
8      *
9      * This program is free software; you can redistribute it and/or
10      * modify it under the terms of the GNU General Public License
11      * as published by the Free Software Foundation; either version
12      * 2 of the License, or (at your option) any later version.
13      *
14      * Version 0.1 (99/04/15):
15      *		- first version
16      *
17      * Version 0.2 (99/06/16):
18      *		- added kernel timer watchdog ping after close
19      *		  since the hardware does not support watchdog shutdown
20      *
21      * Version 0.3 (99/06/21):
22      *		- added WDIOC_GETSTATUS and WDIOC_GETSUPPORT ioctl calls
23      *
24      * Version 0.3.1 (99/06/22):
25      *		- allow module removal while internal timer is active,
26      *		  print warning about probable reset
27      *
28      * Version 0.4 (99/11/15):
29      *		- support for one more type board
30      *	
31      */
32     
33     #define VERSION "0.4" 
34       
35     #include <linux/module.h>
36     #include <linux/config.h>
37     #include <linux/types.h>
38     #include <linux/kernel.h>
39     #include <linux/fs.h>
40     #include <linux/mm.h>
41     #include <linux/miscdevice.h>
42     #include <linux/ioport.h>
43     #include <linux/watchdog.h>
44     #include <linux/reboot.h>
45     #include <linux/init.h>
46     #include <linux/smp_lock.h>
47     #include <asm/uaccess.h>
48     #include <asm/io.h>
49     
50     static int mixcomwd_ioports[] = { 0x180, 0x280, 0x380, 0x000 };
51     
52     #define MIXCOM_WATCHDOG_OFFSET 0xc10
53     #define MIXCOM_ID 0x11
54     #define FLASHCOM_WATCHDOG_OFFSET 0x4
55     #define FLASHCOM_ID 0x18
56     
57     static long mixcomwd_opened; /* long req'd for setbit --RR */
58     
59     static int watchdog_port;
60     
61     #ifndef CONFIG_WATCHDOG_NOWAYOUT
62     static int mixcomwd_timer_alive;
63     static struct timer_list mixcomwd_timer;
64     #endif
65     
66     static void mixcomwd_ping(void)
67     {
68     	outb_p(55,watchdog_port);
69     	return;
70     }
71     
72     #ifndef CONFIG_WATCHDOG_NOWAYOUT
73     static void mixcomwd_timerfun(unsigned long d)
74     {
75     	mixcomwd_ping();
76     	
77     	mod_timer(&mixcomwd_timer,jiffies+ 5*HZ);
78     }
79     #endif
80     
81     /*
82      *	Allow only one person to hold it open
83      */
84      
85     static int mixcomwd_open(struct inode *inode, struct file *file)
86     {
87     	if(test_and_set_bit(0,&mixcomwd_opened)) {
88     		return -EBUSY;
89     	}
90     	mixcomwd_ping();
91     	
92     #ifndef CONFIG_WATCHDOG_NOWAYOUT
93     	if(mixcomwd_timer_alive) {
94     		del_timer(&mixcomwd_timer);
95     		mixcomwd_timer_alive=0;
96     	} 
97     #endif
98     	return 0;
99     }
100     
101     static int mixcomwd_release(struct inode *inode, struct file *file)
102     {
103     
104     	lock_kernel();
105     #ifndef CONFIG_WATCHDOG_NOWAYOUT
106     	if(mixcomwd_timer_alive) {
107     		printk(KERN_ERR "mixcomwd: release called while internal timer alive");
108     		unlock_kernel();
109     		return -EBUSY;
110     	}
111     	init_timer(&mixcomwd_timer);
112     	mixcomwd_timer.expires=jiffies + 5 * HZ;
113     	mixcomwd_timer.function=mixcomwd_timerfun;
114     	mixcomwd_timer.data=0;
115     	mixcomwd_timer_alive=1;
116     	add_timer(&mixcomwd_timer);
117     #endif
118     
119     	clear_bit(0,&mixcomwd_opened);
120     	unlock_kernel();
121     	return 0;
122     }
123     
124     
125     static ssize_t mixcomwd_write(struct file *file, const char *data, size_t len, loff_t *ppos)
126     {
127     	if (ppos != &file->f_pos) {
128     		return -ESPIPE;
129     	}
130     
131     	if(len)
132     	{
133     		mixcomwd_ping();
134     		return 1;
135     	}
136     	return 0;
137     }
138     
139     static int mixcomwd_ioctl(struct inode *inode, struct file *file,
140     	unsigned int cmd, unsigned long arg)
141     {
142     	int status;
143             static struct watchdog_info ident = {
144     		WDIOF_KEEPALIVEPING, 1, "MixCOM watchdog"
145     	};
146                                             
147     	switch(cmd)
148     	{
149     		case WDIOC_GETSTATUS:
150     			status=mixcomwd_opened;
151     #ifndef CONFIG_WATCHDOG_NOWAYOUT
152     			status|=mixcomwd_timer_alive;
153     #endif
154     			if (copy_to_user((int *)arg, &status, sizeof(int))) {
155     				return -EFAULT;
156     			}
157     			break;
158     		case WDIOC_GETSUPPORT:
159     			if (copy_to_user((struct watchdog_info *)arg, &ident, 
160     			    sizeof(ident))) {
161     				return -EFAULT;
162     			}
163     			break;
164     		case WDIOC_KEEPALIVE:
165     			mixcomwd_ping();
166     			break;
167     		default:
168     			return -ENOTTY;
169     	}
170     	return 0;
171     }
172     
173     static struct file_operations mixcomwd_fops=
174     {
175     	owner:		THIS_MODULE,
176     	write:		mixcomwd_write,
177     	ioctl:		mixcomwd_ioctl,
178     	open:		mixcomwd_open,
179     	release:	mixcomwd_release,
180     };
181     
182     static struct miscdevice mixcomwd_miscdev=
183     {
184     	WATCHDOG_MINOR,
185     	"watchdog",
186     	&mixcomwd_fops
187     };
188     
189     static int __init mixcomwd_checkcard(int port)
190     {
191     	int id;
192     
193     	if(check_region(port+MIXCOM_WATCHDOG_OFFSET,1)) {
194     		return 0;
195     	}
196     	
197     	id=inb_p(port + MIXCOM_WATCHDOG_OFFSET) & 0x3f;
198     	if(id!=MIXCOM_ID) {
199     		return 0;
200     	}
201     	return 1;
202     }
203     
204     static int __init flashcom_checkcard(int port)
205     {
206     	int id;
207     	
208     	if(check_region(port + FLASHCOM_WATCHDOG_OFFSET,1)) {
209     		return 0;
210     	}
211     	
212     	id=inb_p(port + FLASHCOM_WATCHDOG_OFFSET);
213      	if(id!=FLASHCOM_ID) {
214     		return 0;
215     	}
216      	return 1;
217      }
218      
219     static int __init mixcomwd_init(void)
220     {
221     	int i;
222     	int ret;
223     	int found=0;
224     
225     	for (i = 0; !found && mixcomwd_ioports[i] != 0; i++) {
226     		if (mixcomwd_checkcard(mixcomwd_ioports[i])) {
227     			found = 1;
228     			watchdog_port = mixcomwd_ioports[i] + MIXCOM_WATCHDOG_OFFSET;
229     		}
230     	}
231     	
232     	/* The FlashCOM card can be set up at 0x300 -> 0x378, in 0x8 jumps */
233     	for (i = 0x300; !found && i < 0x380; i+=0x8) {
234     		if (flashcom_checkcard(i)) {
235     			found = 1;
236     			watchdog_port = i + FLASHCOM_WATCHDOG_OFFSET;
237     		}
238     	}
239     	
240     	if (!found) {
241     		printk("mixcomwd: No card detected, or port not available.\n");
242     		return -ENODEV;
243     	}
244     
245     	request_region(watchdog_port,1,"MixCOM watchdog");
246     		
247     	ret = misc_register(&mixcomwd_miscdev);
248     	if (ret)
249     		return ret;
250     	
251     	printk(KERN_INFO "MixCOM watchdog driver v%s, watchdog port at 0x%3x\n",VERSION,watchdog_port);
252     
253     	return 0;
254     }	
255     
256     static void __exit mixcomwd_exit(void)
257     {
258     #ifndef CONFIG_WATCHDOG_NOWAYOUT
259     	if(mixcomwd_timer_alive) {
260     		printk(KERN_WARNING "mixcomwd: I quit now, hardware will"
261     			" probably reboot!\n");
262     		del_timer(&mixcomwd_timer);
263     		mixcomwd_timer_alive=0;
264     	}
265     #endif
266     	release_region(watchdog_port,1);
267     	misc_deregister(&mixcomwd_miscdev);
268     }
269     
270     module_init(mixcomwd_init);
271     module_exit(mixcomwd_exit);
272     
273     MODULE_LICENSE("GPL");
274     EXPORT_NO_SYMBOLS;
275