📄 ep9315wdt.c
字号:
/*
*
*
*
*
*
*
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/stat.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/bitops.h>
#include <asm/uaccess.h>
#include "ep9315wdt.h"
MODULE_LICENSE("GPL");
#undef DEBUG
#define DEBUG
#ifdef DEBUG
#define DPRINTK(fmt, arg...) printk(KERN_DEBUG "%s:"fmt, __FUNCTION__, ##arg)
#else
#define DPRINTK(fmt, arg...)
#endif /* #ifdef DEBUG */
#define EP9315_WDT_CLEAR 0x5555
#define EP9315_WDT_DISABLE 0xAA55
#define EP9315_WDT_ENABLE 0xAAAA
static unsigned long ep9315_wdt_users = 0;
#define WDT_OPENED_BITS 1
#define WDT_RUNNING 8
static unsigned long expect_close = 0;
#define EP9315_CLOSE_MAGIC (0x5AFC4453)
static unsigned long boot_status;
#ifdef CONFIG_WATCHDOG_NOWAYOUT
static int nowaystop= 1;
#else
static int nowaystop = 0;
#endif
module_param(nowaystop, int, S_IRUGO);
MODULE_PARM_DESC(nowaystop, "Watchdog cannot be stopped once started (default 0-->NO)");
static int margin_200ms = 300; /* (200ms) default 60s */
static int margin = 60; /* 60s */
module_param(margin, int, S_IRUGO);
MODULE_PARM_DESC(margin, "Watchdog margin in 200ms (default 60s)");
#ifdef CONFIG_WATCHDONG_INSMODSTART
static int insmodstart = 1;
#else
static int insmodstart = 0;
#endif
module_param(insmodstart, int, S_IRUGO);
MODULE_PARM_DESC(insmodstart, "Watchdog start immediately when insmod watchdog module (default 0-->NO)");
struct timer_list WDTtimer;
#define TIMER_INTERVAL ((200*HZ)/1000) //200ms
static int WDTtimercount = 0;
/*
* use timer to delay the outdate time
* timer is use 200ms
*/
static void TimerOutFunc(unsigned long data)
{
if(++WDTtimercount <= margin_200ms)
{ /*continue add_timer */
WDTtimer.expires = jiffies + TIMER_INTERVAL;
add_timer(&WDTtimer);
outl(EP9315_WDT_CLEAR, WATCHDOG);
DPRINTK("%d\n", WDTtimercount);
}
else
{ /* time out, do not add_timer, wait watchdog time out, reset system */
DPRINTK("time out\n");
}
}
/*
* Allow only one person to hold it open
*/
static int ep9315wdt_open(struct inode *inodp, struct file *filp)
{
nonseekable_open(inodp, filp);
if(test_and_set_bit(WDT_OPENED_BITS, &ep9315_wdt_users))
return -EBUSY;
outl(EP9315_WDT_CLEAR, WATCHDOG);
WDTtimer.expires = jiffies + TIMER_INTERVAL;
WDTtimer.function = TimerOutFunc;
WDTtimer.data = 0;
add_timer(&WDTtimer);
set_bit(WDT_RUNNING, &ep9315_wdt_users);
outl(EP9315_WDT_ENABLE, WATCHDOG);
outl(EP9315_WDT_CLEAR, WATCHDOG);
return 0;
}
/*
* shut off the timer.
* lock it in if it's a module and we defined...NOWAYOUT
* oddly, the watchdog can only be enabled, but we can turn off
* the interrupt, which appears to prevent the watchdong timing out.
*/
static int ep9315wdt_release(struct inode *inodp, struct file *filp)
{
int ret = 0;
WDTtimercount = 0;
if(expect_close == EP9315_CLOSE_MAGIC)
{ /* can be stop */
/* stop watchdog */
outl(EP9315_WDT_DISABLE, WATCHDOG);
/* del the timer */
del_timer_sync(&WDTtimer);
clear_bit(WDT_RUNNING, &ep9315_wdt_users);
DPRINTK("stop wdt\n");
}
else
{ /* cann't be stopped */
printk(KERN_CRIT "WATCHDOG: WDT device closed unexpectedly. WDT will not stop!\n");
ret = -EBUSY;
}
clear_bit(WDT_OPENED_BITS, &ep9315_wdt_users);
expect_close = 0;
return ret;
}
static ssize_t ep9315wdt_write(struct file *filp, const char *data, size_t len, loff_t *ppos)
{
if(len)
{
if(!nowaystop)
{
size_t i;
expect_close = 0;
for(i=0; i!=len; i++)
{
char c;
if(get_user(c, data+i))
{
return -EFAULT;
}
if(c == 'V')
{
expect_close = EP9315_CLOSE_MAGIC;
DPRINTK("can stop\n");
}
}
}
/* clear the timer count */
WDTtimercount = 0;
}
return len;
}
static struct watchdog_info ident =
{
.options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE |
WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
.identity = "Ep9315 watchdog",
};
static int ep9315wdt_ioctl(struct inode *inodp, struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret = -ENOIOCTLCMD;
int time;
switch(cmd)
{
case WDIOC_GETSUPPORT:
ret = copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)) ? -EFAULT : 0;
break;
case WDIOC_GETSTATUS:
ret = put_user(0, (int *)arg);
break;
case WDIOC_GETBOOTSTATUS:
ret = put_user(boot_status, (unsigned long *)arg);
break;
case WDIOC_SETTIMEOUT:
ret = get_user(time, (int *)arg);
if(ret)
{
break;
}
if(time<=0 || time > 255)
{
ret = -EINVAL;
break;
}
margin_200ms = (time*HZ)/TIMER_INTERVAL;
/* fall through */
case WDIOC_GETTIMEOUT:
ret = put_user((margin_200ms/(HZ/TIMER_INTERVAL)), (int *)arg);
break;
case WDIOC_KEEPALIVE:
WDTtimercount = 0;
ret = 0;
break;
}
return ret;
}
static struct file_operations ep9315wdt_fops =
{
.owner = THIS_MODULE,
.write = ep9315wdt_write,
.ioctl = ep9315wdt_ioctl,
.open = ep9315wdt_open,
.release = ep9315wdt_release,
};
static struct miscdevice ep9315wdt_miscdev =
{
.minor = WATCHDOG_MINOR,
.name = "watchdog/ep9315wdt",
.fops = &ep9315wdt_fops,
};
static int __init ep9315wdt_init_module(void)
{
int ret = 0;
boot_status = inl(WATCHDOG);
boot_status |= ((insmodstart) ? INSMODE_START : 0);
boot_status |= ((nowaystop) ? NO_WAY_STOP : 0);
DPRINTK("WATCHDOG=0x%X\n", boot_status);
init_timer(&WDTtimer);
DPRINTK("init timer\n");
if(margin > 0)
{
margin_200ms = margin*(HZ/TIMER_INTERVAL);
}
else
{
return -EINVAL;
}
ret = misc_register(&ep9315wdt_miscdev);
if(ret == 0)
{
printk("EP9315 watchdog timer:timer margin %d sec\n", margin);
}
else
{
return ret;
}
if(insmodstart)
{
WDTtimer.expires = jiffies + TIMER_INTERVAL;
WDTtimer.function = TimerOutFunc;
WDTtimer.data = 0;
add_timer(&WDTtimer);
set_bit(WDT_RUNNING, &ep9315_wdt_users);
outl(EP9315_WDT_CLEAR, WATCHDOG);
outl(EP9315_WDT_ENABLE, WATCHDOG);
}
return ret;
}
static void __exit ep9315wdt_exit_module(void)
{
if(test_bit(WDT_RUNNING, &ep9315_wdt_users))
{
del_timer_sync(&WDTtimer);
set_bit(WDT_RUNNING, &ep9315_wdt_users);
}
misc_deregister(&ep9315wdt_miscdev);
}
module_init(ep9315wdt_init_module);
module_exit(ep9315wdt_exit_module);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -