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

1     /*
2      *	Acquire Single Board Computer Watchdog Timer driver for Linux 2.1.x
3      *
4      *      Based on wdt.c. Original copyright messages:
5      *
6      *	(c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
7      *				http://www.redhat.com
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      *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide 
15      *	warranty for any of this software. This material is provided 
16      *	"AS-IS" and at no charge.	
17      *
18      *	(c) Copyright 1995    Alan Cox <alan@redhat.com>
19      *
20      */
21     
22     #include <linux/config.h>
23     #include <linux/module.h>
24     #include <linux/version.h>
25     #include <linux/types.h>
26     #include <linux/errno.h>
27     #include <linux/kernel.h>
28     #include <linux/sched.h>
29     #include <linux/miscdevice.h>
30     #include <linux/watchdog.h>
31     #include <linux/slab.h>
32     #include <linux/ioport.h>
33     #include <linux/fcntl.h>
34     #include <asm/io.h>
35     #include <asm/uaccess.h>
36     #include <asm/system.h>
37     #include <linux/notifier.h>
38     #include <linux/reboot.h>
39     #include <linux/init.h>
40     #include <linux/spinlock.h>
41     #include <linux/smp_lock.h>
42     
43     static int acq_is_open;
44     static spinlock_t acq_lock;
45     
46     /*
47      *	You must set these - there is no sane way to probe for this board.
48      */
49      
50     #define WDT_STOP 0x43
51     #define WDT_START 0x443
52     
53     #define WD_TIMO (100*60)		/* 1 minute */
54     
55     
56     /*
57      *	Kernel methods.
58      */
59      
60     
61     static void acq_ping(void)
62     {
63     	/* Write a watchdog value */
64     	inb_p(WDT_START);
65     }
66     
67     static ssize_t acq_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
68     {
69     	/*  Can't seek (pwrite) on this device  */
70     	if (ppos != &file->f_pos)
71     		return -ESPIPE;
72     
73     	if(count)
74     	{
75     		acq_ping();
76     		return 1;
77     	}
78     	return 0;
79     }
80     
81     static ssize_t acq_read(struct file *file, char *buf, size_t count, loff_t *ppos)
82     {
83     	return -EINVAL;
84     }
85     
86     
87     
88     static int acq_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
89     	unsigned long arg)
90     {
91     	static struct watchdog_info ident=
92     	{
93     		WDIOF_KEEPALIVEPING, 1, "Acquire WDT"
94     	};
95     	
96     	switch(cmd)
97     	{
98     	case WDIOC_GETSUPPORT:
99     	  if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)))
100     	    return -EFAULT;
101     	  break;
102     	  
103     	case WDIOC_GETSTATUS:
104     	  if (copy_to_user((int *)arg, &acq_is_open,  sizeof(int)))
105     	    return -EFAULT;
106     	  break;
107     
108     	case WDIOC_KEEPALIVE:
109     	  acq_ping();
110     	  break;
111     
112     	default:
113     	  return -ENOTTY;
114     	}
115     	return 0;
116     }
117     
118     static int acq_open(struct inode *inode, struct file *file)
119     {
120     	switch(MINOR(inode->i_rdev))
121     	{
122     		case WATCHDOG_MINOR:
123     			spin_lock(&acq_lock);
124     			if(acq_is_open)
125     			{
126     				spin_unlock(&acq_lock);
127     				return -EBUSY;
128     			}
129     			/*
130     			 *	Activate 
131     			 */
132     	 
133     			acq_is_open=1;
134     			inb_p(WDT_START);      
135     			spin_unlock(&acq_lock);
136     			return 0;
137     		default:
138     			return -ENODEV;
139     	}
140     }
141     
142     static int acq_close(struct inode *inode, struct file *file)
143     {
144     	lock_kernel();
145     	if(MINOR(inode->i_rdev)==WATCHDOG_MINOR)
146     	{
147     		spin_lock(&acq_lock);
148     #ifndef CONFIG_WATCHDOG_NOWAYOUT	
149     		inb_p(WDT_STOP);
150     #endif		
151     		acq_is_open=0;
152     		spin_unlock(&acq_lock);
153     	}
154     	unlock_kernel();
155     	return 0;
156     }
157     
158     /*
159      *	Notifier for system down
160      */
161     
162     static int acq_notify_sys(struct notifier_block *this, unsigned long code,
163     	void *unused)
164     {
165     	if(code==SYS_DOWN || code==SYS_HALT)
166     	{
167     		/* Turn the card off */
168     		inb_p(WDT_STOP);
169     	}
170     	return NOTIFY_DONE;
171     }
172      
173     /*
174      *	Kernel Interfaces
175      */
176      
177      
178     static struct file_operations acq_fops = {
179     	owner:		THIS_MODULE,
180     	read:		acq_read,
181     	write:		acq_write,
182     	ioctl:		acq_ioctl,
183     	open:		acq_open,
184     	release:	acq_close,
185     };
186     
187     static struct miscdevice acq_miscdev=
188     {
189     	WATCHDOG_MINOR,
190     	"watchdog",
191     	&acq_fops
192     };
193     
194     
195     /*
196      *	The WDT card needs to learn about soft shutdowns in order to
197      *	turn the timebomb registers off. 
198      */
199      
200     static struct notifier_block acq_notifier=
201     {
202     	acq_notify_sys,
203     	NULL,
204     	0
205     };
206     
207     static int __init acq_init(void)
208     {
209     	printk("WDT driver for Acquire single board computer initialising.\n");
210     
211     	spin_lock_init(&acq_lock);
212     	misc_register(&acq_miscdev);
213     	request_region(WDT_STOP, 1, "Acquire WDT");
214     	request_region(WDT_START, 1, "Acquire WDT");
215     	register_reboot_notifier(&acq_notifier);
216     	return 0;
217     }
218     	
219     static void __exit acq_exit(void)
220     {
221     	misc_deregister(&acq_miscdev);
222     	unregister_reboot_notifier(&acq_notifier);
223     	release_region(WDT_STOP,1);
224     	release_region(WDT_START,1);
225     }
226     
227     module_init(acq_init);
228     module_exit(acq_exit);
229     
230     MODULE_LICENSE("GPL");
231     EXPORT_NO_SYMBOLS;
232