1/* UML hardware watchdog, shamelessly stolen from:
2 *
3 *	SoftDog	0.05:	A Software Watchdog Device
4 *
5 *	(c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
6 *				http://www.redhat.com
7 *
8 *	This program is free software; you can redistribute it and/or
9 *	modify it under the terms of the GNU General Public License
10 *	as published by the Free Software Foundation; either version
11 *	2 of the License, or (at your option) any later version.
12 *
13 *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
14 *	warranty for any of this software. This material is provided
15 *	"AS-IS" and at no charge.
16 *
17 *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
18 *
19 *	Software only watchdog driver. Unlike its big brother the WDT501P
20 *	driver this won't always recover a failed machine.
21 *
22 *  03/96: Angelo Haritsis <ah@doc.ic.ac.uk> :
23 *	Modularised.
24 *	Added soft_margin; use upon insmod to change the timer delay.
25 *	NB: uses same minor as wdt (WATCHDOG_MINOR); we could use separate
26 *	    minors.
27 *
28 *  19980911 Alan Cox
29 *	Made SMP safe for 2.3.x
30 *
31 *  20011127 Joel Becker (jlbec@evilplan.org>
32 *	Added soft_noboot; Allows testing the softdog trigger without
33 *	requiring a recompile.
34 *	Added WDIOC_GETTIMEOUT and WDIOC_SETTIMOUT.
35 */
36
37#include <linux/module.h>
38#include <linux/types.h>
39#include <linux/kernel.h>
40#include <linux/fs.h>
41#include <linux/mm.h>
42#include <linux/miscdevice.h>
43#include <linux/watchdog.h>
44#include <linux/reboot.h>
45#include <linux/smp_lock.h>
46#include <linux/init.h>
47#include <linux/spinlock.h>
48#include <asm/uaccess.h>
49#include "mconsole.h"
50
51MODULE_LICENSE("GPL");
52
53static DEFINE_SPINLOCK(lock);
54static int timer_alive;
55static int harddog_in_fd = -1;
56static int harddog_out_fd = -1;
57
58/*
59 *	Allow only one person to hold it open
60 */
61
62extern int start_watchdog(int *in_fd_ret, int *out_fd_ret, char *sock);
63
64static int harddog_open(struct inode *inode, struct file *file)
65{
66	int err = -EBUSY;
67	char *sock = NULL;
68
69	spin_lock(&lock);
70	if(timer_alive)
71		goto err;
72#ifdef CONFIG_HARDDOG_NOWAYOUT
73	__module_get(THIS_MODULE);
74#endif
75
76#ifdef CONFIG_MCONSOLE
77	sock = mconsole_notify_socket();
78#endif
79	err = start_watchdog(&harddog_in_fd, &harddog_out_fd, sock);
80	if(err)
81		goto err;
82
83	timer_alive = 1;
84	spin_unlock(&lock);
85	return nonseekable_open(inode, file);
86err:
87	spin_unlock(&lock);
88	return err;
89}
90
91extern void stop_watchdog(int in_fd, int out_fd);
92
93static int harddog_release(struct inode *inode, struct file *file)
94{
95	/*
96	 *	Shut off the timer.
97	 */
98
99	spin_lock(&lock);
100
101	stop_watchdog(harddog_in_fd, harddog_out_fd);
102	harddog_in_fd = -1;
103	harddog_out_fd = -1;
104
105	timer_alive=0;
106	spin_unlock(&lock);
107
108	return 0;
109}
110
111extern int ping_watchdog(int fd);
112
113static ssize_t harddog_write(struct file *file, const char __user *data, size_t len,
114			     loff_t *ppos)
115{
116	/*
117	 *	Refresh the timer.
118	 */
119	if(len)
120		return ping_watchdog(harddog_out_fd);
121	return 0;
122}
123
124static int harddog_ioctl(struct inode *inode, struct file *file,
125			 unsigned int cmd, unsigned long arg)
126{
127	void __user *argp= (void __user *)arg;
128	static struct watchdog_info ident = {
129		WDIOC_SETTIMEOUT,
130		0,
131		"UML Hardware Watchdog"
132	};
133	switch (cmd) {
134		default:
135			return -ENOTTY;
136		case WDIOC_GETSUPPORT:
137			if(copy_to_user(argp, &ident, sizeof(ident)))
138				return -EFAULT;
139			return 0;
140		case WDIOC_GETSTATUS:
141		case WDIOC_GETBOOTSTATUS:
142			return put_user(0,(int __user *)argp);
143		case WDIOC_KEEPALIVE:
144			return ping_watchdog(harddog_out_fd);
145	}
146}
147
148static const struct file_operations harddog_fops = {
149	.owner		= THIS_MODULE,
150	.write		= harddog_write,
151	.ioctl		= harddog_ioctl,
152	.open		= harddog_open,
153	.release	= harddog_release,
154};
155
156static struct miscdevice harddog_miscdev = {
157	.minor		= WATCHDOG_MINOR,
158	.name		= "watchdog",
159	.fops		= &harddog_fops,
160};
161
162static char banner[] __initdata = KERN_INFO "UML Watchdog Timer\n";
163
164static int __init harddog_init(void)
165{
166	int ret;
167
168	ret = misc_register(&harddog_miscdev);
169
170	if (ret)
171		return ret;
172
173	printk(banner);
174
175	return 0;
176}
177
178static void __exit harddog_exit(void)
179{
180	misc_deregister(&harddog_miscdev);
181}
182
183module_init(harddog_init);
184module_exit(harddog_exit);
185