2011-07-22 22:55:18 +04:00
/*
* watchdog_core . 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 .
*
* 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 EXPORT_SYMBOL/module stuff/... */
# include <linux/types.h> /* For standard types */
# include <linux/errno.h> /* For the -ENODEV/... values */
# include <linux/kernel.h> /* For printk/panic/... */
# include <linux/watchdog.h> /* For watchdog specific items */
# include <linux/init.h> /* For __init/__exit/... */
2012-05-10 23:48:59 +04:00
# include <linux/idr.h> /* For ida_* macros */
2012-05-11 14:00:20 +04:00
# include <linux/err.h> /* For IS_ERR macros */
2011-07-22 22:55:18 +04:00
2012-05-21 17:31:06 +04:00
# include "watchdog_core.h" /* For watchdog_dev_register/... */
2011-07-22 22:55:18 +04:00
2012-05-10 23:48:59 +04:00
static DEFINE_IDA ( watchdog_ida ) ;
2012-05-11 14:00:20 +04:00
static struct class * watchdog_class ;
2012-05-10 23:48:59 +04:00
2011-07-22 22:55:18 +04:00
/**
* watchdog_register_device ( ) - register a watchdog device
* @ wdd : watchdog device
*
* Register a watchdog device with the kernel so that the
* watchdog timer can be accessed from userspace .
*
* A zero is returned on success and a negative errno code for
* failure .
*/
int watchdog_register_device ( struct watchdog_device * wdd )
{
2012-05-11 14:00:20 +04:00
int ret , id , devno ;
2011-07-22 22:55:18 +04:00
if ( wdd = = NULL | | wdd - > info = = NULL | | wdd - > ops = = NULL )
return - EINVAL ;
/* Mandatory operations need to be supported */
if ( wdd - > ops - > start = = NULL | | wdd - > ops - > stop = = NULL )
return - EINVAL ;
2011-07-22 23:00:16 +04:00
/*
* Check that we have valid min and max timeout values , if
* not reset them both to 0 ( = not used or unknown )
*/
if ( wdd - > min_timeout > wdd - > max_timeout ) {
pr_info ( " Invalid min and max timeout values, resetting to 0! \n " ) ;
wdd - > min_timeout = 0 ;
wdd - > max_timeout = 0 ;
}
2011-07-22 22:55:18 +04:00
/*
* Note : now that all watchdog_device data has been verified , we
* will not check this anymore in other functions . If data gets
* corrupted in a later stage then we expect a kernel panic !
*/
2012-05-10 23:48:59 +04:00
id = ida_simple_get ( & watchdog_ida , 0 , MAX_DOGS , GFP_KERNEL ) ;
if ( id < 0 )
return id ;
wdd - > id = id ;
2011-07-22 22:55:18 +04:00
ret = watchdog_dev_register ( wdd ) ;
if ( ret ) {
2012-05-10 23:48:59 +04:00
ida_simple_remove ( & watchdog_ida , id ) ;
if ( ! ( id = = 0 & & ret = = - EBUSY ) )
return ret ;
/* Retry in case a legacy watchdog module exists */
id = ida_simple_get ( & watchdog_ida , 1 , MAX_DOGS , GFP_KERNEL ) ;
if ( id < 0 )
return id ;
wdd - > id = id ;
ret = watchdog_dev_register ( wdd ) ;
if ( ret ) {
ida_simple_remove ( & watchdog_ida , id ) ;
return ret ;
}
2011-07-22 22:55:18 +04:00
}
2012-05-11 14:00:20 +04:00
devno = wdd - > cdev . dev ;
wdd - > dev = device_create ( watchdog_class , wdd - > parent , devno ,
NULL , " watchdog%d " , wdd - > id ) ;
if ( IS_ERR ( wdd - > dev ) ) {
watchdog_dev_unregister ( wdd ) ;
ida_simple_remove ( & watchdog_ida , id ) ;
ret = PTR_ERR ( wdd - > dev ) ;
return ret ;
}
2011-07-22 22:55:18 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( watchdog_register_device ) ;
/**
* watchdog_unregister_device ( ) - unregister a watchdog device
* @ wdd : watchdog device to unregister
*
* Unregister a watchdog device that was previously successfully
* registered with watchdog_register_device ( ) .
*/
void watchdog_unregister_device ( struct watchdog_device * wdd )
{
int ret ;
2012-05-11 14:00:20 +04:00
int devno = wdd - > cdev . dev ;
2011-07-22 22:55:18 +04:00
if ( wdd = = NULL )
return ;
ret = watchdog_dev_unregister ( wdd ) ;
if ( ret )
2012-02-16 03:06:19 +04:00
pr_err ( " error unregistering /dev/watchdog (err=%d) \n " , ret ) ;
2012-05-11 14:00:20 +04:00
device_destroy ( watchdog_class , devno ) ;
2012-05-10 23:48:59 +04:00
ida_simple_remove ( & watchdog_ida , wdd - > id ) ;
2012-05-11 14:00:20 +04:00
wdd - > dev = NULL ;
2011-07-22 22:55:18 +04:00
}
EXPORT_SYMBOL_GPL ( watchdog_unregister_device ) ;
2012-05-10 23:48:59 +04:00
static int __init watchdog_init ( void )
{
2012-05-11 14:00:20 +04:00
int err ;
watchdog_class = class_create ( THIS_MODULE , " watchdog " ) ;
if ( IS_ERR ( watchdog_class ) ) {
pr_err ( " couldn't create class \n " ) ;
return PTR_ERR ( watchdog_class ) ;
}
err = watchdog_dev_init ( ) ;
if ( err < 0 ) {
class_destroy ( watchdog_class ) ;
return err ;
}
return 0 ;
2012-05-10 23:48:59 +04:00
}
static void __exit watchdog_exit ( void )
{
watchdog_dev_exit ( ) ;
2012-05-11 14:00:20 +04:00
class_destroy ( watchdog_class ) ;
2012-05-10 23:48:59 +04:00
ida_destroy ( & watchdog_ida ) ;
}
subsys_initcall ( watchdog_init ) ;
module_exit ( watchdog_exit ) ;
2011-07-22 22:55:18 +04:00
MODULE_AUTHOR ( " Alan Cox <alan@lxorguk.ukuu.org.uk> " ) ;
MODULE_AUTHOR ( " Wim Van Sebroeck <wim@iguana.be> " ) ;
MODULE_DESCRIPTION ( " WatchDog Timer Driver Core " ) ;
MODULE_LICENSE ( " GPL " ) ;