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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]


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-watchdog" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Site Home]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Tools]     [DDR & Rambus]     [Asterisk Internet PBX]     [Linux API]     [Monitors]

Add to Google