2005-04-17 02:20:36 +04:00
/* UML hardware watchdog, shamelessly stolen from:
*
* SoftDog 0.05 : A Software Watchdog Device
*
* ( c ) Copyright 1996 Alan Cox < alan @ redhat . com > , All Rights Reserved .
* http : //www.redhat.com
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
2007-02-10 12:43:58 +03:00
*
* Neither Alan Cox nor CymruNet Ltd . admit liability nor provide
* warranty for any of this software . This material is provided
* " AS-IS " and at no charge .
2005-04-17 02:20:36 +04:00
*
* ( c ) Copyright 1995 Alan Cox < alan @ lxorguk . ukuu . org . uk >
*
* Software only watchdog driver . Unlike its big brother the WDT501P
* driver this won ' t always recover a failed machine .
*
* 03 / 96 : Angelo Haritsis < ah @ doc . ic . ac . uk > :
* Modularised .
* Added soft_margin ; use upon insmod to change the timer delay .
* NB : uses same minor as wdt ( WATCHDOG_MINOR ) ; we could use separate
* minors .
*
* 19980911 Alan Cox
* Made SMP safe for 2.3 . x
*
* 20011127 Joel Becker ( jlbec @ evilplan . org >
2007-02-10 12:43:58 +03:00
* Added soft_noboot ; Allows testing the softdog trigger without
2005-04-17 02:20:36 +04:00
* requiring a recompile .
* Added WDIOC_GETTIMEOUT and WDIOC_SETTIMOUT .
*/
2007-02-10 12:43:58 +03:00
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/fs.h>
# include <linux/mm.h>
# include <linux/miscdevice.h>
# include <linux/watchdog.h>
# include <linux/reboot.h>
# include <linux/smp_lock.h>
# include <linux/init.h>
2007-02-10 12:43:57 +03:00
# include <linux/spinlock.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# include "mconsole.h"
MODULE_LICENSE ( " GPL " ) ;
2007-02-10 12:43:57 +03:00
static DEFINE_SPINLOCK ( lock ) ;
2005-04-17 02:20:36 +04:00
static int timer_alive ;
static int harddog_in_fd = - 1 ;
static int harddog_out_fd = - 1 ;
/*
* Allow only one person to hold it open
*/
2007-02-10 12:43:58 +03:00
2005-04-17 02:20:36 +04:00
extern int start_watchdog ( int * in_fd_ret , int * out_fd_ret , char * sock ) ;
static int harddog_open ( struct inode * inode , struct file * file )
{
2007-02-10 12:43:57 +03:00
int err = - EBUSY ;
2005-04-17 02:20:36 +04:00
char * sock = NULL ;
2008-05-20 21:16:54 +04:00
lock_kernel ( ) ;
2007-02-10 12:43:57 +03:00
spin_lock ( & lock ) ;
2005-04-17 02:20:36 +04:00
if ( timer_alive )
2007-02-10 12:43:57 +03:00
goto err ;
2007-10-16 12:26:45 +04:00
# ifdef CONFIG_WATCHDOG_NOWAYOUT
2005-04-17 02:20:36 +04:00
__module_get ( THIS_MODULE ) ;
# endif
# ifdef CONFIG_MCONSOLE
sock = mconsole_notify_socket ( ) ;
# endif
err = start_watchdog ( & harddog_in_fd , & harddog_out_fd , sock ) ;
2007-02-10 12:43:57 +03:00
if ( err )
goto err ;
2005-04-17 02:20:36 +04:00
timer_alive = 1 ;
2007-02-10 12:43:57 +03:00
spin_unlock ( & lock ) ;
2008-05-20 21:16:54 +04:00
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
return nonseekable_open ( inode , file ) ;
2007-02-10 12:43:57 +03:00
err :
spin_unlock ( & lock ) ;
2008-05-20 21:16:54 +04:00
unlock_kernel ( ) ;
2007-02-10 12:43:57 +03:00
return err ;
2005-04-17 02:20:36 +04:00
}
extern void stop_watchdog ( int in_fd , int out_fd ) ;
static int harddog_release ( struct inode * inode , struct file * file )
{
/*
* Shut off the timer .
*/
2007-02-10 12:43:57 +03:00
spin_lock ( & lock ) ;
2005-04-17 02:20:36 +04:00
stop_watchdog ( harddog_in_fd , harddog_out_fd ) ;
harddog_in_fd = - 1 ;
harddog_out_fd = - 1 ;
timer_alive = 0 ;
2007-02-10 12:43:57 +03:00
spin_unlock ( & lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
extern int ping_watchdog ( int fd ) ;
2006-03-31 14:30:15 +04:00
static ssize_t harddog_write ( struct file * file , const char __user * data , size_t len ,
2005-04-17 02:20:36 +04:00
loff_t * ppos )
{
/*
* Refresh the timer .
*/
if ( len )
2007-02-10 12:43:58 +03:00
return ping_watchdog ( harddog_out_fd ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2010-05-19 17:08:17 +04:00
static int harddog_ioctl_unlocked ( struct file * file ,
unsigned int cmd , unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2006-03-31 14:30:15 +04:00
void __user * argp = ( void __user * ) arg ;
2005-04-17 02:20:36 +04:00
static struct watchdog_info ident = {
WDIOC_SETTIMEOUT ,
0 ,
" UML Hardware Watchdog "
} ;
switch ( cmd ) {
default :
return - ENOTTY ;
case WDIOC_GETSUPPORT :
2006-03-31 14:30:15 +04:00
if ( copy_to_user ( argp , & ident , sizeof ( ident ) ) )
2005-04-17 02:20:36 +04:00
return - EFAULT ;
return 0 ;
case WDIOC_GETSTATUS :
case WDIOC_GETBOOTSTATUS :
2006-03-31 14:30:15 +04:00
return put_user ( 0 , ( int __user * ) argp ) ;
2005-04-17 02:20:36 +04:00
case WDIOC_KEEPALIVE :
2007-02-10 12:43:58 +03:00
return ping_watchdog ( harddog_out_fd ) ;
2005-04-17 02:20:36 +04:00
}
}
2010-05-19 17:08:17 +04:00
static long harddog_ioctl ( struct file * file ,
unsigned int cmd , unsigned long arg )
{
long ret ;
lock_kernel ( ) ;
ret = harddog_ioctl_unlocked ( file , cmd , arg ) ;
unlock_kernel ( ) ;
return ret ;
}
2007-02-12 11:55:31 +03:00
static const struct file_operations harddog_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. write = harddog_write ,
2010-05-19 17:08:17 +04:00
. unlocked_ioctl = harddog_ioctl ,
2005-04-17 02:20:36 +04:00
. open = harddog_open ,
. release = harddog_release ,
} ;
static struct miscdevice harddog_miscdev = {
. minor = WATCHDOG_MINOR ,
. name = " watchdog " ,
. fops = & harddog_fops ,
} ;
static char banner [ ] __initdata = KERN_INFO " UML Watchdog Timer \n " ;
static int __init harddog_init ( void )
{
int ret ;
ret = misc_register ( & harddog_miscdev ) ;
if ( ret )
return ret ;
printk ( banner ) ;
2007-02-10 12:43:58 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static void __exit harddog_exit ( void )
{
misc_deregister ( & harddog_miscdev ) ;
}
module_init ( harddog_init ) ;
module_exit ( harddog_exit ) ;