2011-07-22 22:55:18 +04:00
/*
* watchdog_dev . c
*
* ( c ) Copyright 2008 - 2011 Alan Cox < alan @ lxorguk . ukuu . org . uk > ,
* All Rights Reserved .
*
* ( c ) Copyright 2008 - 2011 Wim Van Sebroeck < wim @ iguana . be > .
*
*
* This source code is part of the generic code that can be used
* by all the watchdog timer drivers .
*
* This part of the generic code takes care of the following
* misc device : / dev / watchdog .
*
* Based on source code of the following authors :
* Matt Domsch < Matt_Domsch @ dell . com > ,
* Rob Radez < rob @ osinvestor . com > ,
* Rusty Lynch < rusty @ linux . co . intel . com >
* Satyam Sharma < satyam @ infradead . org >
* Randy Dunlap < randy . dunlap @ oracle . 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 .
*
* Neither Alan Cox , CymruNet Ltd . , Wim Van Sebroeck nor Iguana vzw .
* admit liability nor provide warranty for any of this software .
* This material is provided " AS-IS " and at no charge .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/module.h> /* For module stuff/... */
# include <linux/types.h> /* For standard types (like size_t) */
# include <linux/errno.h> /* For the -ENODEV/... values */
# include <linux/kernel.h> /* For printk/panic/... */
# include <linux/fs.h> /* For file operations */
# include <linux/watchdog.h> /* For watchdog specific items */
# include <linux/miscdevice.h> /* For handling misc devices */
# include <linux/init.h> /* For __init/__exit/... */
# include <linux/uaccess.h> /* For copy_to_user/put_user/... */
2012-05-21 17:31:06 +04:00
# include "watchdog_core.h"
2012-04-21 00:28:24 +04:00
2012-05-10 23:48:59 +04:00
/* the dev_t structure to store the dynamically allocated watchdog devices */
static dev_t watchdog_devt ;
2011-07-22 22:55:18 +04:00
/* the watchdog device behind /dev/watchdog */
2012-05-10 23:48:59 +04:00
static struct watchdog_device * old_wdd ;
2011-07-22 22:55:18 +04:00
/*
* watchdog_ping : ping the watchdog .
* @ wddev : the watchdog device to ping
*
* If the watchdog has no own ping operation then it needs to be
* restarted via the start operation . This wrapper function does
* exactly that .
2011-07-22 22:57:55 +04:00
* We only ping when the watchdog device is running .
2011-07-22 22:55:18 +04:00
*/
static int watchdog_ping ( struct watchdog_device * wddev )
{
2012-05-22 13:40:26 +04:00
int err = 0 ;
2012-05-22 13:40:26 +04:00
mutex_lock ( & wddev - > lock ) ;
2012-05-22 13:40:26 +04:00
if ( test_bit ( WDOG_UNREGISTERED , & wddev - > status ) ) {
err = - ENODEV ;
goto out_ping ;
}
2012-05-22 13:40:26 +04:00
if ( ! watchdog_active ( wddev ) )
goto out_ping ;
if ( wddev - > ops - > ping )
err = wddev - > ops - > ping ( wddev ) ; /* ping the watchdog */
else
err = wddev - > ops - > start ( wddev ) ; /* restart watchdog */
out_ping :
2012-05-22 13:40:26 +04:00
mutex_unlock ( & wddev - > lock ) ;
2012-05-22 13:40:26 +04:00
return err ;
2011-07-22 22:57:55 +04:00
}
/*
* watchdog_start : wrapper to start the watchdog .
* @ wddev : the watchdog device to start
*
* Start the watchdog if it is not active and mark it active .
* This function returns zero on success or a negative errno code for
* failure .
*/
static int watchdog_start ( struct watchdog_device * wddev )
{
2012-05-22 13:40:26 +04:00
int err = 0 ;
2011-07-22 22:57:55 +04:00
2012-05-22 13:40:26 +04:00
mutex_lock ( & wddev - > lock ) ;
2012-05-22 13:40:26 +04:00
if ( test_bit ( WDOG_UNREGISTERED , & wddev - > status ) ) {
err = - ENODEV ;
goto out_start ;
}
2012-05-22 13:40:26 +04:00
if ( watchdog_active ( wddev ) )
goto out_start ;
2011-07-22 22:57:55 +04:00
2012-05-22 13:40:26 +04:00
err = wddev - > ops - > start ( wddev ) ;
if ( err = = 0 )
2011-08-04 02:38:20 +04:00
set_bit ( WDOG_ACTIVE , & wddev - > status ) ;
2012-05-22 13:40:26 +04:00
out_start :
2012-05-22 13:40:26 +04:00
mutex_unlock ( & wddev - > lock ) ;
2012-05-22 13:40:26 +04:00
return err ;
2011-07-22 22:57:55 +04:00
}
/*
* watchdog_stop : wrapper to stop the watchdog .
* @ wddev : the watchdog device to stop
*
* Stop the watchdog if it is still active and unmark it active .
* This function returns zero on success or a negative errno code for
* failure .
2011-07-22 22:59:17 +04:00
* If the ' nowayout ' feature was set , the watchdog cannot be stopped .
2011-07-22 22:57:55 +04:00
*/
static int watchdog_stop ( struct watchdog_device * wddev )
{
2012-05-22 13:40:26 +04:00
int err = 0 ;
2012-05-22 13:40:26 +04:00
mutex_lock ( & wddev - > lock ) ;
2012-05-22 13:40:26 +04:00
if ( test_bit ( WDOG_UNREGISTERED , & wddev - > status ) ) {
err = - ENODEV ;
goto out_stop ;
}
2012-05-22 13:40:26 +04:00
if ( ! watchdog_active ( wddev ) )
goto out_stop ;
2011-07-22 22:59:17 +04:00
2011-08-04 02:38:20 +04:00
if ( test_bit ( WDOG_NO_WAY_OUT , & wddev - > status ) ) {
2012-05-11 14:00:22 +04:00
dev_info ( wddev - > dev , " nowayout prevents watchdog being stopped! \n " ) ;
2012-05-22 13:40:26 +04:00
err = - EBUSY ;
goto out_stop ;
2011-07-22 22:59:17 +04:00
}
2011-07-22 22:57:55 +04:00
2012-05-22 13:40:26 +04:00
err = wddev - > ops - > stop ( wddev ) ;
if ( err = = 0 )
2011-08-04 02:38:20 +04:00
clear_bit ( WDOG_ACTIVE , & wddev - > status ) ;
2012-05-22 13:40:26 +04:00
out_stop :
2012-05-22 13:40:26 +04:00
mutex_unlock ( & wddev - > lock ) ;
2012-05-22 13:40:26 +04:00
return err ;
}
/*
* watchdog_get_status : wrapper to get the watchdog status
* @ wddev : the watchdog device to get the status from
* @ status : the status of the watchdog device
*
* Get the watchdog ' s status flags .
*/
static int watchdog_get_status ( struct watchdog_device * wddev ,
unsigned int * status )
{
int err = 0 ;
* status = 0 ;
if ( ! wddev - > ops - > status )
return - EOPNOTSUPP ;
2012-05-22 13:40:26 +04:00
mutex_lock ( & wddev - > lock ) ;
2012-05-22 13:40:26 +04:00
if ( test_bit ( WDOG_UNREGISTERED , & wddev - > status ) ) {
err = - ENODEV ;
goto out_status ;
}
2012-05-22 13:40:26 +04:00
* status = wddev - > ops - > status ( wddev ) ;
2012-05-22 13:40:26 +04:00
out_status :
2012-05-22 13:40:26 +04:00
mutex_unlock ( & wddev - > lock ) ;
2012-05-22 13:40:26 +04:00
return err ;
}
/*
* watchdog_set_timeout : set the watchdog timer timeout
* @ wddev : the watchdog device to set the timeout for
* @ timeout : timeout to set in seconds
*/
static int watchdog_set_timeout ( struct watchdog_device * wddev ,
unsigned int timeout )
{
int err ;
if ( ( wddev - > ops - > set_timeout = = NULL ) | |
! ( wddev - > info - > options & WDIOF_SETTIMEOUT ) )
return - EOPNOTSUPP ;
2013-01-08 14:04:10 +04:00
if ( watchdog_timeout_invalid ( wddev , timeout ) )
2012-05-22 13:40:26 +04:00
return - EINVAL ;
2012-05-22 13:40:26 +04:00
mutex_lock ( & wddev - > lock ) ;
2012-05-22 13:40:26 +04:00
if ( test_bit ( WDOG_UNREGISTERED , & wddev - > status ) ) {
err = - ENODEV ;
goto out_timeout ;
}
2012-05-22 13:40:26 +04:00
err = wddev - > ops - > set_timeout ( wddev , timeout ) ;
2012-05-22 13:40:26 +04:00
out_timeout :
2012-05-22 13:40:26 +04:00
mutex_unlock ( & wddev - > lock ) ;
2012-05-22 13:40:26 +04:00
return err ;
}
/*
* watchdog_get_timeleft : wrapper to get the time left before a reboot
* @ wddev : the watchdog device to get the remaining time from
* @ timeleft : the time that ' s left
*
* Get the time before a watchdog will reboot ( if not pinged ) .
*/
static int watchdog_get_timeleft ( struct watchdog_device * wddev ,
unsigned int * timeleft )
{
int err = 0 ;
* timeleft = 0 ;
if ( ! wddev - > ops - > get_timeleft )
return - EOPNOTSUPP ;
2012-05-22 13:40:26 +04:00
mutex_lock ( & wddev - > lock ) ;
2012-05-22 13:40:26 +04:00
if ( test_bit ( WDOG_UNREGISTERED , & wddev - > status ) ) {
err = - ENODEV ;
goto out_timeleft ;
}
2012-05-22 13:40:26 +04:00
* timeleft = wddev - > ops - > get_timeleft ( wddev ) ;
2012-05-22 13:40:26 +04:00
out_timeleft :
2012-05-22 13:40:26 +04:00
mutex_unlock ( & wddev - > lock ) ;
2012-05-22 13:40:26 +04:00
return err ;
}
/*
* watchdog_ioctl_op : call the watchdog drivers ioctl op if defined
* @ wddev : the watchdog device to do the ioctl on
* @ cmd : watchdog command
* @ arg : argument pointer
*/
static int watchdog_ioctl_op ( struct watchdog_device * wddev , unsigned int cmd ,
unsigned long arg )
{
int err ;
if ( ! wddev - > ops - > ioctl )
return - ENOIOCTLCMD ;
2012-05-22 13:40:26 +04:00
mutex_lock ( & wddev - > lock ) ;
2012-05-22 13:40:26 +04:00
if ( test_bit ( WDOG_UNREGISTERED , & wddev - > status ) ) {
err = - ENODEV ;
goto out_ioctl ;
}
2012-05-22 13:40:26 +04:00
err = wddev - > ops - > ioctl ( wddev , cmd , arg ) ;
2012-05-22 13:40:26 +04:00
out_ioctl :
2012-05-22 13:40:26 +04:00
mutex_unlock ( & wddev - > lock ) ;
2012-05-22 13:40:26 +04:00
return err ;
2011-07-22 22:55:18 +04:00
}
/*
* watchdog_write : writes to the watchdog .
* @ file : file from VFS
* @ data : user address of data
* @ len : length of data
* @ ppos : pointer to the file offset
*
* A write to a watchdog device is defined as a keepalive ping .
2011-07-22 22:58:54 +04:00
* Writing the magic ' V ' sequence allows the next close to turn
2011-07-22 22:59:17 +04:00
* off the watchdog ( if ' nowayout ' is not set ) .
2011-07-22 22:55:18 +04:00
*/
static ssize_t watchdog_write ( struct file * file , const char __user * data ,
size_t len , loff_t * ppos )
{
2012-05-10 23:48:59 +04:00
struct watchdog_device * wdd = file - > private_data ;
2011-07-22 22:55:18 +04:00
size_t i ;
char c ;
if ( len = = 0 )
return 0 ;
2011-07-22 22:58:54 +04:00
/*
* Note : just in case someone wrote the magic character
* five months ago . . .
*/
clear_bit ( WDOG_ALLOW_RELEASE , & wdd - > status ) ;
/* scan to see whether or not we got the magic character */
2011-07-22 22:55:18 +04:00
for ( i = 0 ; i ! = len ; i + + ) {
if ( get_user ( c , data + i ) )
return - EFAULT ;
2011-07-22 22:58:54 +04:00
if ( c = = ' V ' )
set_bit ( WDOG_ALLOW_RELEASE , & wdd - > status ) ;
2011-07-22 22:55:18 +04:00
}
/* someone wrote to us, so we send the watchdog a keepalive ping */
watchdog_ping ( wdd ) ;
return len ;
}
2011-07-22 22:56:38 +04:00
/*
* watchdog_ioctl : handle the different ioctl ' s for the watchdog device .
* @ file : file handle to the device
* @ cmd : watchdog command
* @ arg : argument pointer
*
* The watchdog API defines a common set of functions for all watchdogs
* according to their available features .
*/
static long watchdog_ioctl ( struct file * file , unsigned int cmd ,
unsigned long arg )
{
2012-05-10 23:48:59 +04:00
struct watchdog_device * wdd = file - > private_data ;
2011-07-22 22:56:38 +04:00
void __user * argp = ( void __user * ) arg ;
int __user * p = argp ;
unsigned int val ;
2011-07-22 22:57:55 +04:00
int err ;
2011-07-22 22:56:38 +04:00
2012-05-22 13:40:26 +04:00
err = watchdog_ioctl_op ( wdd , cmd , arg ) ;
if ( err ! = - ENOIOCTLCMD )
return err ;
2011-07-22 22:59:49 +04:00
2011-07-22 22:56:38 +04:00
switch ( cmd ) {
case WDIOC_GETSUPPORT :
return copy_to_user ( argp , wdd - > info ,
sizeof ( struct watchdog_info ) ) ? - EFAULT : 0 ;
case WDIOC_GETSTATUS :
2012-05-22 13:40:26 +04:00
err = watchdog_get_status ( wdd , & val ) ;
2012-06-26 22:07:21 +04:00
if ( err = = - ENODEV )
2012-05-22 13:40:26 +04:00
return err ;
2011-07-22 22:56:38 +04:00
return put_user ( val , p ) ;
case WDIOC_GETBOOTSTATUS :
return put_user ( wdd - > bootstatus , p ) ;
2011-07-22 22:57:55 +04:00
case WDIOC_SETOPTIONS :
if ( get_user ( val , p ) )
return - EFAULT ;
if ( val & WDIOS_DISABLECARD ) {
err = watchdog_stop ( wdd ) ;
if ( err < 0 )
return err ;
}
if ( val & WDIOS_ENABLECARD ) {
err = watchdog_start ( wdd ) ;
if ( err < 0 )
return err ;
}
return 0 ;
2011-07-22 22:57:23 +04:00
case WDIOC_KEEPALIVE :
if ( ! ( wdd - > info - > options & WDIOF_KEEPALIVEPING ) )
return - EOPNOTSUPP ;
watchdog_ping ( wdd ) ;
return 0 ;
2011-07-22 22:58:21 +04:00
case WDIOC_SETTIMEOUT :
if ( get_user ( val , p ) )
return - EFAULT ;
2012-05-22 13:40:26 +04:00
err = watchdog_set_timeout ( wdd , val ) ;
2011-07-22 22:58:21 +04:00
if ( err < 0 )
return err ;
/* If the watchdog is active then we send a keepalive ping
* to make sure that the watchdog keep ' s running ( and if
* possible that it takes the new timeout ) */
watchdog_ping ( wdd ) ;
/* Fall */
case WDIOC_GETTIMEOUT :
/* timeout == 0 means that we don't know the timeout */
if ( wdd - > timeout = = 0 )
return - EOPNOTSUPP ;
return put_user ( wdd - > timeout , p ) ;
2012-03-16 12:14:00 +04:00
case WDIOC_GETTIMELEFT :
2012-05-22 13:40:26 +04:00
err = watchdog_get_timeleft ( wdd , & val ) ;
if ( err )
return err ;
return put_user ( val , p ) ;
2011-07-22 22:56:38 +04:00
default :
return - ENOTTY ;
}
}
2011-07-22 22:55:18 +04:00
/*
2012-05-10 23:48:59 +04:00
* watchdog_open : open the / dev / watchdog * devices .
2011-07-22 22:55:18 +04:00
* @ inode : inode of device
* @ file : file handle to device
*
2012-05-10 23:48:59 +04:00
* When the / dev / watchdog * device gets opened , we start the watchdog .
2011-07-22 22:55:18 +04:00
* Watch out : the / dev / watchdog device is single open , so we make sure
* it can only be opened once .
*/
static int watchdog_open ( struct inode * inode , struct file * file )
{
int err = - EBUSY ;
2012-05-10 23:48:59 +04:00
struct watchdog_device * wdd ;
/* Get the corresponding watchdog device */
if ( imajor ( inode ) = = MISC_MAJOR )
wdd = old_wdd ;
else
wdd = container_of ( inode - > i_cdev , struct watchdog_device , cdev ) ;
2011-07-22 22:55:18 +04:00
/* the watchdog is single open! */
if ( test_and_set_bit ( WDOG_DEV_OPEN , & wdd - > status ) )
return - EBUSY ;
/*
* If the / dev / watchdog device is open , we don ' t want the module
* to be unloaded .
*/
if ( ! try_module_get ( wdd - > ops - > owner ) )
goto out ;
2011-07-22 22:57:55 +04:00
err = watchdog_start ( wdd ) ;
2011-07-22 22:55:18 +04:00
if ( err < 0 )
goto out_mod ;
2012-05-10 23:48:59 +04:00
file - > private_data = wdd ;
2012-05-22 13:40:26 +04:00
if ( wdd - > ops - > ref )
wdd - > ops - > ref ( wdd ) ;
2011-07-22 22:55:18 +04:00
/* dev/watchdog is a virtual (and thus non-seekable) filesystem */
return nonseekable_open ( inode , file ) ;
out_mod :
module_put ( wdd - > ops - > owner ) ;
out :
clear_bit ( WDOG_DEV_OPEN , & wdd - > status ) ;
return err ;
}
/*
2012-05-10 23:48:59 +04:00
* watchdog_release : release the watchdog device .
* @ inode : inode of device
* @ file : file handle to device
2011-07-22 22:55:18 +04:00
*
2011-07-22 22:58:54 +04:00
* This is the code for when / dev / watchdog gets closed . We will only
2011-07-22 22:59:17 +04:00
* stop the watchdog when we have received the magic char ( and nowayout
* was not set ) , else the watchdog will keep running .
2011-07-22 22:55:18 +04:00
*/
static int watchdog_release ( struct inode * inode , struct file * file )
{
2012-05-10 23:48:59 +04:00
struct watchdog_device * wdd = file - > private_data ;
2011-07-22 22:58:54 +04:00
int err = - EBUSY ;
/*
* We only stop the watchdog if we received the magic character
2011-07-22 22:59:17 +04:00
* or if WDIOF_MAGICCLOSE is not set . If nowayout was set then
* watchdog_stop will fail .
2011-07-22 22:58:54 +04:00
*/
if ( test_and_clear_bit ( WDOG_ALLOW_RELEASE , & wdd - > status ) | |
! ( wdd - > info - > options & WDIOF_MAGICCLOSE ) )
err = watchdog_stop ( wdd ) ;
2011-07-22 22:55:18 +04:00
2011-07-22 22:58:54 +04:00
/* If the watchdog was not stopped, send a keepalive ping */
2011-07-22 22:57:55 +04:00
if ( err < 0 ) {
2012-05-22 13:40:26 +04:00
mutex_lock ( & wdd - > lock ) ;
if ( ! test_bit ( WDOG_UNREGISTERED , & wdd - > status ) )
dev_crit ( wdd - > dev , " watchdog did not stop! \n " ) ;
mutex_unlock ( & wdd - > lock ) ;
2011-07-22 22:55:18 +04:00
watchdog_ping ( wdd ) ;
}
/* Allow the owner module to be unloaded again */
module_put ( wdd - > ops - > owner ) ;
/* make sure that /dev/watchdog can be re-opened */
clear_bit ( WDOG_DEV_OPEN , & wdd - > status ) ;
2012-05-22 13:40:26 +04:00
/* Note wdd may be gone after this, do not use after this! */
if ( wdd - > ops - > unref )
wdd - > ops - > unref ( wdd ) ;
2011-07-22 22:55:18 +04:00
return 0 ;
}
static const struct file_operations watchdog_fops = {
. owner = THIS_MODULE ,
. write = watchdog_write ,
2011-07-22 22:56:38 +04:00
. unlocked_ioctl = watchdog_ioctl ,
2011-07-22 22:55:18 +04:00
. open = watchdog_open ,
. release = watchdog_release ,
} ;
static struct miscdevice watchdog_miscdev = {
. minor = WATCHDOG_MINOR ,
. name = " watchdog " ,
. fops = & watchdog_fops ,
} ;
/*
2012-05-10 23:48:59 +04:00
* watchdog_dev_register : register a watchdog device
2011-07-22 22:55:18 +04:00
* @ watchdog : watchdog device
*
2012-05-10 23:48:59 +04:00
* Register a watchdog device including handling the legacy
* / dev / watchdog node . / dev / watchdog is actually a miscdevice and
* thus we set it up like that .
2011-07-22 22:55:18 +04:00
*/
int watchdog_dev_register ( struct watchdog_device * watchdog )
{
2012-05-10 23:48:59 +04:00
int err , devno ;
if ( watchdog - > id = = 0 ) {
2013-04-06 08:22:43 +04:00
old_wdd = watchdog ;
2012-05-11 14:00:20 +04:00
watchdog_miscdev . parent = watchdog - > parent ;
2012-05-10 23:48:59 +04:00
err = misc_register ( & watchdog_miscdev ) ;
if ( err ! = 0 ) {
pr_err ( " %s: cannot register miscdev on minor=%d (err=%d). \n " ,
watchdog - > info - > identity , WATCHDOG_MINOR , err ) ;
if ( err = = - EBUSY )
pr_err ( " %s: a legacy watchdog module is probably present. \n " ,
watchdog - > info - > identity ) ;
2013-04-06 08:22:43 +04:00
old_wdd = NULL ;
2012-05-10 23:48:59 +04:00
return err ;
}
2011-07-22 22:55:18 +04:00
}
2012-05-10 23:48:59 +04:00
/* Fill in the data structures */
devno = MKDEV ( MAJOR ( watchdog_devt ) , watchdog - > id ) ;
cdev_init ( & watchdog - > cdev , & watchdog_fops ) ;
watchdog - > cdev . owner = watchdog - > ops - > owner ;
/* Add the device */
err = cdev_add ( & watchdog - > cdev , devno , 1 ) ;
if ( err ) {
pr_err ( " watchdog%d unable to add device %d:%d \n " ,
watchdog - > id , MAJOR ( watchdog_devt ) , watchdog - > id ) ;
if ( watchdog - > id = = 0 ) {
misc_deregister ( & watchdog_miscdev ) ;
old_wdd = NULL ;
}
2011-07-22 22:55:18 +04:00
}
return err ;
}
/*
2012-05-10 23:48:59 +04:00
* watchdog_dev_unregister : unregister a watchdog device
2011-07-22 22:55:18 +04:00
* @ watchdog : watchdog device
*
2012-05-10 23:48:59 +04:00
* Unregister the watchdog and if needed the legacy / dev / watchdog device .
2011-07-22 22:55:18 +04:00
*/
int watchdog_dev_unregister ( struct watchdog_device * watchdog )
{
2012-05-22 13:40:26 +04:00
mutex_lock ( & watchdog - > lock ) ;
set_bit ( WDOG_UNREGISTERED , & watchdog - > status ) ;
mutex_unlock ( & watchdog - > lock ) ;
2012-05-10 23:48:59 +04:00
cdev_del ( & watchdog - > cdev ) ;
if ( watchdog - > id = = 0 ) {
misc_deregister ( & watchdog_miscdev ) ;
old_wdd = NULL ;
2011-07-22 22:55:18 +04:00
}
return 0 ;
}
2012-05-10 23:48:59 +04:00
/*
* watchdog_dev_init : init dev part of watchdog core
*
* Allocate a range of chardev nodes to use for watchdog devices
*/
int __init watchdog_dev_init ( void )
{
int err = alloc_chrdev_region ( & watchdog_devt , 0 , MAX_DOGS , " watchdog " ) ;
if ( err < 0 )
pr_err ( " watchdog: unable to allocate char dev region \n " ) ;
return err ;
}
/*
* watchdog_dev_exit : exit dev part of watchdog core
*
* Release the range of chardev nodes used for watchdog devices
*/
void __exit watchdog_dev_exit ( void )
{
unregister_chrdev_region ( watchdog_devt , MAX_DOGS ) ;
}