2005-04-17 02:20:36 +04:00
/*
* MixCom Watchdog : A Simple Hardware Watchdog Device
* Based on Softdog driver by Alan Cox and PC Watchdog driver by Ken Hollis
*
* Author : Gergely Madarasz < gorgo @ itc . hu >
*
* Copyright ( c ) 1999 ITConsult - Pro Co . < info @ itc . hu >
*
* 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 .
*
* Version 0.1 ( 99 / 04 / 15 ) :
* - first version
*
* Version 0.2 ( 99 / 06 / 16 ) :
* - added kernel timer watchdog ping after close
* since the hardware does not support watchdog shutdown
*
* Version 0.3 ( 99 / 06 / 21 ) :
* - added WDIOC_GETSTATUS and WDIOC_GETSUPPORT ioctl calls
*
* Version 0.3 .1 ( 99 / 06 / 22 ) :
* - allow module removal while internal timer is active ,
* print warning about probable reset
*
* Version 0.4 ( 99 / 11 / 15 ) :
* - support for one more type board
*
* Version 0.5 ( 2001 / 12 / 14 ) Matt Domsch < Matt_Domsch @ dell . com >
* - added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
*
*/
# define VERSION "0.5"
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/types.h>
# include <linux/miscdevice.h>
# include <linux/ioport.h>
# include <linux/watchdog.h>
# include <linux/fs.h>
# include <linux/reboot.h>
# include <linux/init.h>
2005-10-31 02:03:48 +03:00
# include <linux/jiffies.h>
# include <linux/timer.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# include <asm/io.h>
static int mixcomwd_ioports [ ] = { 0x180 , 0x280 , 0x380 , 0x000 } ;
# define MIXCOM_WATCHDOG_OFFSET 0xc10
# define MIXCOM_ID 0x11
# define FLASHCOM_WATCHDOG_OFFSET 0x4
# define FLASHCOM_ID 0x18
static unsigned long mixcomwd_opened ; /* long req'd for setbit --RR */
static int watchdog_port ;
static int mixcomwd_timer_alive ;
2005-09-10 00:10:40 +04:00
static DEFINE_TIMER ( mixcomwd_timer , NULL , 0 , 0 ) ;
2005-04-17 02:20:36 +04:00
static char expect_close ;
2005-07-27 22:43:58 +04:00
static int nowayout = WATCHDOG_NOWAYOUT ;
2005-04-17 02:20:36 +04:00
module_param ( nowayout , int , 0 ) ;
MODULE_PARM_DESC ( nowayout , " Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT) " ) ;
static void mixcomwd_ping ( void )
{
outb_p ( 55 , watchdog_port ) ;
return ;
}
static void mixcomwd_timerfun ( unsigned long d )
{
mixcomwd_ping ( ) ;
mod_timer ( & mixcomwd_timer , jiffies + 5 * HZ ) ;
}
/*
* Allow only one person to hold it open
*/
static int mixcomwd_open ( struct inode * inode , struct file * file )
{
if ( test_and_set_bit ( 0 , & mixcomwd_opened ) ) {
return - EBUSY ;
}
mixcomwd_ping ( ) ;
if ( nowayout ) {
/*
* fops_get ( ) code via open ( ) has already done
* a try_module_get ( ) so it is safe to do the
* __module_get ( ) .
*/
__module_get ( THIS_MODULE ) ;
} else {
if ( mixcomwd_timer_alive ) {
del_timer ( & mixcomwd_timer ) ;
mixcomwd_timer_alive = 0 ;
}
}
return nonseekable_open ( inode , file ) ;
}
static int mixcomwd_release ( struct inode * inode , struct file * file )
{
if ( expect_close = = 42 ) {
if ( mixcomwd_timer_alive ) {
printk ( KERN_ERR " mixcomwd: release called while internal timer alive " ) ;
return - EBUSY ;
}
init_timer ( & mixcomwd_timer ) ;
mixcomwd_timer . expires = jiffies + 5 * HZ ;
mixcomwd_timer . function = mixcomwd_timerfun ;
mixcomwd_timer . data = 0 ;
mixcomwd_timer_alive = 1 ;
add_timer ( & mixcomwd_timer ) ;
} else {
printk ( KERN_CRIT " mixcomwd: WDT device closed unexpectedly. WDT will not stop! \n " ) ;
}
clear_bit ( 0 , & mixcomwd_opened ) ;
expect_close = 0 ;
return 0 ;
}
static ssize_t mixcomwd_write ( struct file * file , const char __user * data , size_t len , loff_t * ppos )
{
if ( len )
{
if ( ! nowayout ) {
size_t i ;
/* In case it was set long ago */
expect_close = 0 ;
for ( i = 0 ; i ! = len ; i + + ) {
char c ;
if ( get_user ( c , data + i ) )
return - EFAULT ;
if ( c = = ' V ' )
expect_close = 42 ;
}
}
mixcomwd_ping ( ) ;
}
return len ;
}
static int mixcomwd_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
void __user * argp = ( void __user * ) arg ;
int __user * p = argp ;
int status ;
static struct watchdog_info ident = {
. options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE ,
. firmware_version = 1 ,
. identity = " MixCOM watchdog " ,
} ;
switch ( cmd )
{
case WDIOC_GETSTATUS :
status = mixcomwd_opened ;
if ( ! nowayout ) {
status | = mixcomwd_timer_alive ;
}
if ( copy_to_user ( p , & status , sizeof ( int ) ) ) {
return - EFAULT ;
}
break ;
case WDIOC_GETSUPPORT :
if ( copy_to_user ( argp , & ident , sizeof ( ident ) ) ) {
return - EFAULT ;
}
break ;
case WDIOC_KEEPALIVE :
mixcomwd_ping ( ) ;
break ;
default :
2006-09-09 19:34:31 +04:00
return - ENOTTY ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2006-07-03 11:24:21 +04:00
static const struct file_operations mixcomwd_fops =
2005-04-17 02:20:36 +04:00
{
. owner = THIS_MODULE ,
. llseek = no_llseek ,
. write = mixcomwd_write ,
. ioctl = mixcomwd_ioctl ,
. open = mixcomwd_open ,
. release = mixcomwd_release ,
} ;
static struct miscdevice mixcomwd_miscdev =
{
. minor = WATCHDOG_MINOR ,
. name = " watchdog " ,
. fops = & mixcomwd_fops ,
} ;
static int __init mixcomwd_checkcard ( int port )
{
int id ;
port + = MIXCOM_WATCHDOG_OFFSET ;
if ( ! request_region ( port , 1 , " MixCOM watchdog " ) ) {
return 0 ;
}
id = inb_p ( port ) & 0x3f ;
if ( id ! = MIXCOM_ID ) {
release_region ( port , 1 ) ;
return 0 ;
}
return port ;
}
static int __init flashcom_checkcard ( int port )
{
int id ;
port + = FLASHCOM_WATCHDOG_OFFSET ;
if ( ! request_region ( port , 1 , " MixCOM watchdog " ) ) {
return 0 ;
}
id = inb_p ( port ) ;
if ( id ! = FLASHCOM_ID ) {
release_region ( port , 1 ) ;
return 0 ;
}
return port ;
}
static int __init mixcomwd_init ( void )
{
int i ;
int ret ;
int found = 0 ;
for ( i = 0 ; ! found & & mixcomwd_ioports [ i ] ! = 0 ; i + + ) {
watchdog_port = mixcomwd_checkcard ( mixcomwd_ioports [ i ] ) ;
if ( watchdog_port ) {
found = 1 ;
}
}
/* The FlashCOM card can be set up at 0x300 -> 0x378, in 0x8 jumps */
for ( i = 0x300 ; ! found & & i < 0x380 ; i + = 0x8 ) {
watchdog_port = flashcom_checkcard ( i ) ;
if ( watchdog_port ) {
found = 1 ;
}
}
if ( ! found ) {
printk ( " mixcomwd: No card detected, or port not available. \n " ) ;
return - ENODEV ;
}
ret = misc_register ( & mixcomwd_miscdev ) ;
if ( ret )
{
release_region ( watchdog_port , 1 ) ;
return ret ;
}
printk ( KERN_INFO " MixCOM watchdog driver v%s, watchdog port at 0x%3x \n " , VERSION , watchdog_port ) ;
return 0 ;
}
static void __exit mixcomwd_exit ( void )
{
if ( ! nowayout ) {
if ( mixcomwd_timer_alive ) {
printk ( KERN_WARNING " mixcomwd: I quit now, hardware will "
" probably reboot! \n " ) ;
del_timer ( & mixcomwd_timer ) ;
mixcomwd_timer_alive = 0 ;
}
}
release_region ( watchdog_port , 1 ) ;
misc_deregister ( & mixcomwd_miscdev ) ;
}
module_init ( mixcomwd_init ) ;
module_exit ( mixcomwd_exit ) ;
MODULE_AUTHOR ( " Gergely Madarasz <gorgo@itc.hu> " ) ;
MODULE_DESCRIPTION ( " MixCom Watchdog driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_MISCDEV ( WATCHDOG_MINOR ) ;