Re: [PATCH RFC] watchdog: add a new driver for VIA chipsets

From: Wim Van Sebroeck
Date: Wed Nov 23 2011 - 16:46:55 EST


Hi Marc,

> > Coming back to now: if the driver incorporates the timer (like all other
> > devices that can be started but not stopped once started do), then the start
> > and stop functions are not empty.
> > The timer (with the example that I prepared as part of the generic code) is
> > not so difficult and will be a good solution for the time being. (Unless
> > Dmitry Artamonow's comment about the fact that the watchdog could perhaps
> > be started and stopped is correct... This should be investigated first imho).
> >
> The watchdog can not be started and stopped from the driver. This is
> what I investigated first and found impossible (and explains why this
> driver is still missing).
>
> I do not fully understand yet what has to be done with the timer.

Below the example driver that should get included in the Documentation.

Kind regards,
Wim.
--------------------------------------------------------------------------------
/*
* Watchdog timer driver example with timer.
*
* Copyright (C) 2009-2011 Wim Van Sebroeck <wim@xxxxxxxxx>
*
* 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.
*/

/*
* Some watchdog device can't stop once started. To support
* the magic_close feature we therefor need to use an internal
* timer to keep the watchdog being pinged when /dev/watchdog has
* been closed correctly.
*
* This is an example driver for these kind of watchdog devices.
*/

#define DRV_NAME KBUILD_MODNAME
#define pr_fmt(fmt) DRV_NAME ": " fmt

#include <linux/init.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/watchdog.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <linux/platform_device.h>

/* Hardware heartbeat in seconds */
#define WDT_HW_HEARTBEAT 2

/* Timer heartbeat (500ms) */
#define WDT_HEARTBEAT (HZ/2) /* should be <= ((WDT_HW_HEARTBEAT*HZ)/2) */

/* User land timeout */
#define WDT_TIMEOUT 15
static int timeout = WDT_TIMEOUT;
module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. "
"(default = " __MODULE_STRING(WDT_TIMEOUT) ")");

static int nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started. "
"(default = " __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");

static struct watchdog_device wdt_dev;
static void wdt_timer_tick(unsigned long data);
static DEFINE_TIMER(timer, wdt_timer_tick, 0, 0);
/* The timer that pings the watchdog */
static unsigned long next_heartbeat; /* the next_heartbeat for the timer */
static unsigned long running; /* is watchdog running for userspace? */

static struct platform_device *wdt_platform_device;

/*
* Reset the watchdog timer. (ie, pat the watchdog)
*/
static inline void wdt_reset(void)
{
/* Reset the watchdog timer hardware here */
}

/*
* Timer tick: the timer will make sure that the watchdog timer hardware
* is being reset in time. The conditions to do this are:
* 1) the watchog timer has been started and /dev/watchdog is open
* and there is still time left before userspace should send the
* next heartbeat/ping. (note: the internal heartbeat is much smaller
* then the external/userspace heartbeat).
* 2) the watchdog timer has been stopped by userspace.
*/
static void wdt_timer_tick(unsigned long data)
{
if (time_before(jiffies, next_heartbeat) ||
(!test_bit(WDOG_ACTIVE, &wdt_dev.status))) {
wdt_reset();
mod_timer(&timer, jiffies + WDT_HEARTBEAT);
} else
pr_crit("I will reboot your machine !\n");
}

/*
* The watchdog operations
*/
static int wdt_ping(struct watchdog_device *wdd)
{
/* calculate when the next userspace timeout will be */
next_heartbeat = jiffies + timeout * HZ;
return 0;
}

static int wdt_start(struct watchdog_device *wdd)
{
/* calculate the next userspace timeout and modify the timer */
wdt_ping(wdd);
mod_timer(&timer, jiffies + WDT_HEARTBEAT);

/* Start the watchdog timer hardware here */
pr_info("wdt_start\n");

running = 1;
return 0;
}

static int wdt_stop(struct watchdog_device *wdd)
{
/* The watchdog timer hardware can not be stopped... */
pr_info("wdt_stop\n");

running = 0;
return 0;
}

static unsigned int wdt_status(struct watchdog_device *wdd)
{
return WDIOF_FANFAULT;
}

static int wdt_set_timeout(struct watchdog_device *wdd, unsigned int new_timeout)
{
if (new_timeout < 1)
return -EINVAL;
return 0;
}

/*
* The watchdog kernel structures
*/
static const struct watchdog_info wdt_info = {
.identity = DRV_NAME,
.options = WDIOF_SETTIMEOUT |
WDIOF_MAGICCLOSE |
WDIOF_KEEPALIVEPING,
};

static const struct watchdog_ops wdt_ops = {
.owner = THIS_MODULE,
.start = wdt_start,
.stop = wdt_stop,
.ping = wdt_ping,
.status = wdt_status,
.set_timeout = wdt_set_timeout,
};

static struct watchdog_device wdt_dev = {
.info = &wdt_info,
.ops = &wdt_ops,
};

/*
* The watchdog timer drivers init and exit routines
*/
static int __devinit wdt_probe(struct platform_device *pdev)
{
int res;

/* Register other stuff */

/* Set watchdog_device parameters */
wdt_dev.timeout = timeout;
/* wdt_dev.dev.parent = &pdev->dev;*/
if (nowayout)
set_bit(WDOG_NO_WAY_OUT, &wdt_dev.status);

/* Register the watchdog timer device */
res = watchdog_register_device(&wdt_dev);
if (res) {
pr_err("watchdog_register_device returned %d\n", res);
return res;
}

pr_info("enabled (timeout=%d sec)\n", timeout);

return 0;
}

static int __devexit wdt_remove(struct platform_device *pdev)
{
/* Unregister the watchdog timer device */
watchdog_unregister_device(&wdt_dev);

/* stop and delete the timer */
pr_warn("I quit now, hardware will probably reboot!\n");
del_timer(&timer);

/* Unregister other stuff */
return 0;
}

static struct platform_driver wdt_driver = {
.probe = wdt_probe,
.remove = __devexit_p(wdt_remove),
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
};

static int __init wdt_init(void)
{
int err;

pr_info("WDT driver initialising.\n");

err = platform_driver_register(&wdt_driver);
if (err)
return err;

wdt_platform_device = platform_device_register_simple(DRV_NAME,
-1, NULL, 0);
if (IS_ERR(wdt_platform_device)) {
err = PTR_ERR(wdt_platform_device);
goto unreg_platform_driver;
}

return 0;

unreg_platform_driver:
platform_driver_unregister(&wdt_driver);
return err;
}

static void __exit wdt_exit(void)
{
platform_device_unregister(wdt_platform_device);
platform_driver_unregister(&wdt_driver);
pr_info("Watchdog Module Unloaded.\n");
}

module_init(wdt_init);
module_exit(wdt_exit);

MODULE_AUTHOR("Wim Van Sebroeck <wim@xxxxxxxxx>");
MODULE_DESCRIPTION("WatchDog Timer Driver example with timer");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/