lkml.org 
[lkml]   [2008]   [May]   [20]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    SubjectRe: [PATCH 2/3, RFC] watchdog dev BKL pushdown
    Hi Arnd,

    > > Actually I'd prefer to fix this for real.  This single open stuff aswell
    > > as same set of ioctls are duplicated all over the watchdog drivers.  We'd
    > > be much better off introducing a simple watchdog layer that handles this
    > > plus proper locking and convert drivers over to it gradually.
    >
    > I fully agree, I thought the same thing when I did the patches. I remember
    > that Wim had a git tree doing this, which is still active at
    > http://git.kernel.org/?p=linux/kernel/git/wim/linux-2.6-watchdog-experimental.git;a=commitdiff;h=732c54027e6c866f98857c4a6d1c6c466459dcd5
    >
    > Unfortunately, it hasn't seen much activitity over the last two years, and
    > the number of watchdog drivers seems to have exploded: I count 67 of them,
    > including some outside of drivers/watchdog.
    >
    > Wim, was there anything preventing you from integrating the generic
    > watchdog layer back then?

    check the linux-2.6-watchdog-mm tree. You'll see the patches sitting there
    as the uniform watchdog driver. They are thus also in the -mm tree.
    I'll need to change it also to an unlocked_ioctl (and add documentation!)
    but I'll attach the core code below (This does not seem to be the latest code,
    because I know there was a request to change the alloc_watchdogdev code so that
    it could also allocate a private data-area/space).
    But it gives you an idea where I was going to. Second step would then be to add
    a sysfs interface so that we can start handling mutiple devices.

    Greetings,
    Wim.

    From a223c170e5e7e63e5dd55f56837318db6ad0807f Mon Sep 17 00:00:00 2001
    From: Wim Van Sebroeck <wim@iguana.be>
    Date: Sun, 19 Aug 2007 19:44:24 +0000
    Subject: [PATCH] [WATCHDOG] Uniform Watchdog Device Driver

    The Uniform Watchdog Device Driver is a frame-work
    that contains the common code for all watchdog-driver's.
    It also introduces a watchdog device structure and the
    operations that go with it.

    Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
    ---
    drivers/watchdog/Kconfig | 2 +
    drivers/watchdog/Makefile | 2 +
    drivers/watchdog/core/Kconfig | 34 +++
    drivers/watchdog/core/Makefile | 11 +
    drivers/watchdog/core/watchdog_core.c | 187 +++++++++++++
    drivers/watchdog/core/watchdog_dev.c | 463 +++++++++++++++++++++++++++++++++
    include/linux/watchdog.h | 49 ++++
    7 files changed, 748 insertions(+), 0 deletions(-)
    create mode 100644 drivers/watchdog/core/Kconfig
    create mode 100644 drivers/watchdog/core/Makefile
    create mode 100644 drivers/watchdog/core/watchdog_core.c
    create mode 100644 drivers/watchdog/core/watchdog_dev.c

    diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
    index 37bddc1..a458e87 100644
    --- a/drivers/watchdog/Kconfig
    +++ b/drivers/watchdog/Kconfig
    @@ -37,6 +37,8 @@ config WATCHDOG_NOWAYOUT
    get killed. If you say Y here, the watchdog cannot be stopped once
    it has been started.

    +source "drivers/watchdog/core/Kconfig"
    +
    #
    # General Watchdog drivers
    #
    diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
    index 389f8b1..b61c103 100644
    --- a/drivers/watchdog/Makefile
    +++ b/drivers/watchdog/Makefile
    @@ -2,6 +2,8 @@
    # Makefile for the WatchDog device drivers.
    #

    +obj-$(CONFIG_WATCHDOG) += core/
    +
    # Only one watchdog can succeed. We probe the ISA/PCI/USB based
    # watchdog-cards first, then the architecture specific watchdog
    # drivers and then the architecture independant "softdog" driver.
    diff --git a/drivers/watchdog/core/Kconfig b/drivers/watchdog/core/Kconfig
    new file mode 100644
    index 0000000..a9a23a9
    --- /dev/null
    +++ b/drivers/watchdog/core/Kconfig
    @@ -0,0 +1,34 @@
    +#
    +# Watchdog device driver core
    +#
    +
    +if WATCHDOG
    +
    +config WATCHDOG_CORE
    + tristate "Uniform Watchdog Device Driver"
    + depends on EXPERIMENTAL
    + default m
    + ---help---
    + Say Y here if you want to use the new uniform watchdog device
    + driver. This driver provides a framework for all watchdog
    + device drivers and gives them the /dev/watchdog interface (and
    + later also the sysfs interface).
    +
    + At this moment we have no watchdog device drivers using this new
    + framework.
    +
    + To compile this driver as a module, choose M here: the module will
    + be called watchdog_core.
    +
    +config WATCHDOG_DEBUG_CORE
    + bool "Uniform Watchdog Device Driver debugging output"
    + depends on WATCHDOG_CORE
    + default n
    + ---help---
    + Say Y here if you want the Uniform Watchdog Device Driver to
    + produce debugging information. Select this if you are having a
    + problem with the uniform watchdog device driver and want to see
    + more of what is really happening.
    +
    +endif # WATCHDOG
    +
    diff --git a/drivers/watchdog/core/Makefile b/drivers/watchdog/core/Makefile
    new file mode 100644
    index 0000000..7554d35
    --- /dev/null
    +++ b/drivers/watchdog/core/Makefile
    @@ -0,0 +1,11 @@
    +#
    +# Makefile for the Watchdog Device Drivers generic core.
    +#
    +
    +# The Generic Watchdog Driver
    +obj-$(CONFIG_WATCHDOG_CORE) += watchdog_core.o watchdog_dev.o
    +
    diff --git a/drivers/watchdog/core/watchdog_core.c b/drivers/watchdog/core/watchdog_core.c
    new file mode 100644
    index 0000000..133dc11
    --- /dev/null
    +++ b/drivers/watchdog/core/watchdog_core.c
    @@ -0,0 +1,187 @@
    +/*
    + * watchdog.c
    + *
    + * (c) Copyright 2007 Wim Van Sebroeck <wim@iguana.be>.
    + *
    + * This code is generic code that can be shared by all the
    + * watchdog drivers.
    + *
    + * Based on source code of the following authors:
    + * Alan Cox <alan@redhat.com>,
    + * 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 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.
    + */
    +
    +#include <linux/module.h> /* For module related things/EXPORT_SYMBOL/... */
    +#include <linux/types.h> /* For standard types */
    +#include <linux/errno.h> /* For the -ENODEV/... values */
    +#include <linux/kernel.h> /* For printk/panic/... */
    +#include <linux/mm.h> /* For memory allocations ... */
    +#include <linux/watchdog.h> /* For watchdog specific items */
    +#include <linux/init.h> /* For __init/__exit/... */
    +
    +/*
    + * Version information
    + */
    +#define DRV_VERSION "0.01"
    +#define DRV_NAME "watchdog_core"
    +#define PFX DRV_NAME ": "
    +
    +/*
    + * External functions/procedures
    + */
    +extern int watchdog_dev_register(struct watchdog_device *, struct device *);
    +extern int watchdog_dev_unregister(struct watchdog_device *);
    +
    +/**
    + * alloc_watchdogdev - allocates a watchdog device
    + *
    + * Creates a new watchdog device structure.
    + * Returns the new structure, or NULL if an error occured.
    + */
    +struct watchdog_device *alloc_watchdogdev(void)
    +{
    + int alloc_size = sizeof(struct watchdog_device);
    + void *p;
    + struct watchdog_device *dev;
    +
    + /* allocate memory for our device and initialize it */
    + p = kzalloc(alloc_size, GFP_KERNEL);
    + if (!p) {
    + printk(KERN_ERR PFX "Unable to allocate watchdog device\n");
    + return NULL;
    + }
    + dev = (struct watchdog_device *) p;
    +
    + return dev;
    +}
    +EXPORT_SYMBOL(alloc_watchdogdev);
    +
    +/**
    + * free_watchdogdev - free watchdog device
    + * @dev: watchdog device
    + *
    + * This function does the last stage of destroying an allocated
    + * watchdog device.
    + */
    +int free_watchdogdev(struct watchdog_device *dev)
    +{
    + if (!((dev->watchdog_state == WATCHDOG_UNINITIALIZED) ||
    + (dev->watchdog_state == WATCHDOG_UNREGISTERED))) {
    + printk(KERN_ERR PFX "Unable to destroy a watchdog device that is still in use\n");
    + return -1;
    + }
    +
    + kfree(dev);
    + return 0;
    +}
    +EXPORT_SYMBOL(free_watchdogdev);
    +
    +/**
    + * register_watchdogdevice - register a watchdog device
    + * @dev: watchdog device
    + * @parent: parent device for the watchdog class device
    + *
    + * This function registers a watchdog device in the kernel so
    + * that it can be accessed from userspace.
    + */
    +int register_watchdogdevice(struct watchdog_device *dev, struct device *parent)
    +{
    + int ret;
    +
    + if (dev == NULL ||
    + dev->watchdog_ops == NULL)
    + return -ENODATA;
    +
    + if (dev->watchdog_ops->start == NULL ||
    + dev->watchdog_ops->stop == NULL ||
    + dev->watchdog_ops->keepalive == NULL)
    + return -ENODATA;
    +
    + if (!((dev->watchdog_state == WATCHDOG_UNINITIALIZED) ||
    + (dev->watchdog_state == WATCHDOG_UNREGISTERED))) {
    + printk(KERN_ERR PFX "Unable to register a watchdog device that is allready in use\n");
    + return -1;
    + }
    +
    + dev->options |= WDIOF_MAGICCLOSE;
    + if (dev->watchdog_ops->set_heartbeat) {
    + dev->options |= WDIOF_SETTIMEOUT;
    + } else {
    + dev->options &= ~WDIOF_SETTIMEOUT;
    + }
    +
    + ret = watchdog_dev_register(dev, parent);
    + if (ret) {
    + printk(KERN_ERR PFX "error registering /dev/watchdog (err=%d)",
    + ret);
    + return ret;
    + }
    +
    + dev->watchdog_state = WATCHDOG_REGISTERED;
    + return 0;
    +}
    +EXPORT_SYMBOL(register_watchdogdevice);
    +
    +/**
    + * unregister_watchdogdevice - unregister a watchdog device
    + * @dev: watchdog device
    + *
    + * This function unregisters a watchdog device from the kernel.
    + */
    +int unregister_watchdogdevice(struct watchdog_device *dev)
    +{
    + int ret;
    +
    + if (dev == NULL)
    + return -ENODATA;
    +
    + if ((dev->watchdog_state == WATCHDOG_UNINITIALIZED) ||
    + (dev->watchdog_state == WATCHDOG_UNREGISTERED)) {
    + printk(KERN_ERR PFX "Unable to unregister a watchdog device that has not been registered\n");
    + return -ENODEV;
    + }
    +
    + ret = watchdog_dev_unregister(dev);
    + if (ret) {
    + printk(KERN_ERR PFX "error unregistering /dev/watchdog (err=%d)",
    + ret);
    + return ret;
    + }
    +
    + dev->watchdog_state = WATCHDOG_UNREGISTERED;
    + return 0;
    +}
    +EXPORT_SYMBOL(unregister_watchdogdevice);
    +
    +static int __init watchdog_init(void)
    +{
    + printk(KERN_INFO "Uniform watchdog device driver v%s loaded\n",
    + DRV_VERSION);
    + return 0;
    +}
    +
    +static void __exit watchdog_exit(void)
    +{
    + printk(KERN_INFO "Uniform watchdog device driver unloaded\n");
    +}
    +
    +module_init(watchdog_init);
    +module_exit(watchdog_exit);
    +
    +MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
    +MODULE_DESCRIPTION("Uniform Watchdog Device Driver");
    +MODULE_VERSION(DRV_VERSION);
    +MODULE_LICENSE("GPL");
    +MODULE_SUPPORTED_DEVICE("watchdog");
    +
    diff --git a/drivers/watchdog/core/watchdog_dev.c b/drivers/watchdog/core/watchdog_dev.c
    new file mode 100644
    index 0000000..37520cd
    --- /dev/null
    +++ b/drivers/watchdog/core/watchdog_dev.c
    @@ -0,0 +1,463 @@
    +/*
    + * watchdog_dev.c
    + *
    + * (c) Copyright 2007 Wim Van Sebroeck <wim@iguana.be>.
    + *
    + * This source code is part of the generic code that can be used
    + * by all the watchdog drivers.
    + *
    + * This part of the generic code takes care of the following
    + * misc device: /dev/watchdog.
    + *
    + * Based on source code of the following authors:
    + * Alan Cox <alan@redhat.com>,
    + * 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 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.
    + */
    +
    +#include <linux/module.h> /* For module related things/EXPORT_SYMBOL/... */
    +#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/mutex.h> /* For mutex locking */
    +#include <linux/init.h> /* For __init/__exit/... */
    +#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
    +
    +#ifdef CONFIG_WATCHDOG_DEBUG_CORE
    +#define trace(format, args...) \
    + printk(KERN_INFO "%s(" format ")\n", __FUNCTION__ , ## args)
    +#define dbg(format, arg...) \
    + printk(KERN_DEBUG "%s: " format "\n", __FUNCTION__, ## arg)
    +#else
    +#define trace(format, arg...) do { } while (0)
    +#define dbg(format, arg...) do { } while (0)
    +#endif
    +
    +/*
    + * Version information
    + */
    +#define DRV_VERSION "0.01"
    +#define DRV_NAME "watchdog_dev"
    +#define PFX DRV_NAME ": "
    +
    +/*
    + * Locally used variables
    + */
    +
    +static struct watchdog_device *watchdogdev; /* the watchdog device behind /dev/watchdog */
    +static unsigned long watchdog_dev_open; /* wether or not /dev/watchdog has been opened */
    +static char received_magic_char; /* wether or not we received the magic char */
    +static DEFINE_MUTEX(watchdog_register_mtx); /* prevent races between register & unregister */
    +
    +/*
    + * /dev/watchdog operations
    + */
    +
    +/*
    + * 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 signal.
    + * Writing the magic 'V' sequence allows the next close to turn
    + * off the watchdog (if 'nowayout' is not set).
    + */
    +
    +static ssize_t watchdog_write(struct file *file, const char __user *data,
    + size_t len, loff_t *ppos)
    +{
    + trace("%p, %p, %zu, %p", file, data, len, ppos);
    +
    + if (!watchdogdev ||
    + !watchdogdev->watchdog_ops ||
    + !watchdogdev->watchdog_ops->keepalive)
    + return -ENODEV;
    +
    + /* See if we got the magic character 'V' and reload the timer */
    + if (len) {
    + if (!watchdogdev->nowayout) {
    + size_t i;
    +
    + /* note: just in case someone wrote the magic character
    + * five months ago... */
    + received_magic_char = 0;
    +
    + /* scan to see wether or not we got the magic character */
    + for (i = 0; i != len; i++) {
    + char c;
    + if (get_user(c, data + i))
    + return -EFAULT;
    + if (c == 'V') {
    + received_magic_char = 42;
    + dbg("received the magic character\n");
    + }
    + }
    + }
    +
    + /* someone wrote to us, so we sent the watchdog a keepalive signal if
    + * the watchdog is active */
    + if (watchdogdev->watchdog_state == WATCHDOG_STARTED)
    + watchdogdev->watchdog_ops->keepalive(watchdogdev);
    + }
    + return len;
    +}
    +
    +/*
    + * watchdog_ioctl: handle the different ioctl's for the watchdog device.
    + * @inode: inode of the 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 int watchdog_ioctl(struct inode *inode, struct file *file,
    + unsigned int cmd, unsigned long arg)
    +{
    + int status;
    + int err;
    + int new_options;
    + int new_heartbeat;
    + int time_left;
    + void __user *argp = (void __user *)arg;
    + int __user *p = argp;
    + static struct watchdog_info ident = {
    + .options = 0,
    + .firmware_version = 0,
    + .identity = "Watchdog Device",
    + };
    +
    + trace("%p, %p, %u, %li", inode, file, cmd, arg);
    +
    + if (!watchdogdev || !watchdogdev->watchdog_ops)
    + return -ENODEV;
    +
    + switch (cmd) {
    + case WDIOC_GETSUPPORT:
    + {
    + ident.options = watchdogdev->options;
    + ident.firmware_version = watchdogdev->firmware;
    +
    + strncpy(ident.identity, watchdogdev->name, 31);
    + ident.identity[32] = 0;
    +
    + return copy_to_user(argp, &ident,
    + sizeof(ident)) ? -EFAULT : 0;
    + }
    +
    + case WDIOC_GETSTATUS:
    + {
    + status = 0;
    +
    + if (watchdogdev->watchdog_ops->get_status &&
    + watchdogdev->watchdog_ops->get_status(watchdogdev, &status))
    + return -EFAULT;
    +
    + return put_user(status, p);
    + }
    +
    + case WDIOC_GETBOOTSTATUS:
    + return put_user(watchdogdev->bootstatus, p);
    +
    + case WDIOC_KEEPALIVE:
    + {
    + if (!watchdogdev->watchdog_ops->keepalive)
    + return -EFAULT;
    +
    + /* We only sent a keepalive when the watchdog is active */
    + if (watchdogdev->watchdog_state == WATCHDOG_STARTED)
    + watchdogdev->watchdog_ops->keepalive(watchdogdev);
    +
    + return 0;
    + }
    +
    + case WDIOC_SETOPTIONS:
    + {
    + if (get_user(new_options, p))
    + return -EFAULT;
    +
    + if (!watchdogdev->watchdog_ops->start ||
    + !watchdogdev->watchdog_ops->stop)
    + return -EFAULT;
    +
    + if (new_options & WDIOS_DISABLECARD) {
    + /* only try to stop the watchdog if it's allready running */
    + if (watchdogdev->watchdog_state == WATCHDOG_STARTED) {
    + err = watchdogdev->watchdog_ops->stop(watchdogdev);
    + if (err == 0) {
    + watchdogdev->watchdog_state = WATCHDOG_STOPPED;
    + } else {
    + printk(KERN_CRIT PFX "WDIOS_DISABLECARD not successfull! (err=%d)",
    + err);
    + return -EFAULT;
    + }
    + }
    + }
    +
    + if (new_options & WDIOS_ENABLECARD) {
    + /* if the watchdog is not allready running, try to start it */
    + if (watchdogdev->watchdog_state != WATCHDOG_STARTED) {
    + err = watchdogdev->watchdog_ops->start(watchdogdev);
    + if (err == 0) {
    + watchdogdev->watchdog_state = WATCHDOG_STARTED;
    + } else {
    + printk(KERN_CRIT PFX "WDIOS_ENABLECARD not successfull! (err=%d)",
    + err);
    + return -EFAULT;
    + }
    + }
    + }
    +
    + return 0;
    + }
    +
    + case WDIOC_SETTIMEOUT:
    + {
    + if (!watchdogdev->watchdog_ops->set_heartbeat)
    + return -ENOTTY;
    +
    + if (get_user(new_heartbeat, p))
    + return -EFAULT;
    +
    + if (watchdogdev->watchdog_ops->set_heartbeat(watchdogdev, new_heartbeat))
    + return -EFAULT;
    +
    + /* If the watchdog is active then we sent a keepalive to make sure
    + * that the watchdog keep's running (and if possible takes the new
    + * heartbeat) */
    + if (watchdogdev->watchdog_ops->keepalive &&
    + (watchdogdev->watchdog_state == WATCHDOG_STARTED))
    + watchdogdev->watchdog_ops->keepalive(watchdogdev);
    + /* Fall */
    + }
    +
    + case WDIOC_GETTIMEOUT:
    + return put_user(watchdogdev->heartbeat, p);
    +
    + case WDIOC_GETTIMELEFT:
    + {
    + if (!watchdogdev->watchdog_ops->get_timeleft)
    + return -ENOTTY;
    +
    + if (watchdogdev->watchdog_ops->get_timeleft(watchdogdev, &time_left))
    + return -EFAULT;
    +
    + return put_user(time_left, p);
    + }
    +
    + default:
    + return -ENOTTY;
    + }
    +}
    +
    +/*
    + * watchdog_open: open the /dev/watchdog device.
    + * @inode: inode of device
    + * @file: file handle to device
    + *
    + * When the /dev/watchdog device get's opened, we start the watchdog
    + * and feed it with his first keepalive signal. Watch out: the
    + * /dev/watchdog device is single open, so make sure it can only be
    + * opened once.
    + */
    +
    +static int watchdog_open(struct inode *inode, struct file *file)
    +{
    + trace("%p, %p", inode, file);
    +
    + /* only open if we have a valid watchdog device */
    + if (!watchdogdev ||
    + !watchdogdev->watchdog_ops ||
    + !watchdogdev->watchdog_ops->start ||
    + !watchdogdev->watchdog_ops->stop ||
    + !watchdogdev->watchdog_ops->keepalive)
    + return -EBUSY;
    +
    + /* the watchdog is single open! */
    + if (test_and_set_bit(0, &watchdog_dev_open))
    + return -EBUSY;
    +
    + /* if the watchdog is not allready running, try to start it */
    + if (watchdogdev->watchdog_state != WATCHDOG_STARTED) {
    + if (watchdogdev->watchdog_ops->start(watchdogdev) == 0)
    + watchdogdev->watchdog_state = WATCHDOG_STARTED;
    + }
    +
    + /* if the watchdog started, then feed the watchdog it's first keepalive signal */
    + if (watchdogdev->watchdog_state == WATCHDOG_STARTED)
    + watchdogdev->watchdog_ops->keepalive(watchdogdev);
    +
    + return nonseekable_open(inode, file);
    +}
    +
    +/*
    + * watchdog_release: release the /dev/watchdog device.
    + * @inode: inode of device
    + * @file: file handle to device
    + *
    + * This is the code for when /dev/watchdog get's closed. We will only
    + * stop the watchdog when we have received the magic char, else the
    + * watchdog will keep running.
    + */
    +
    +static int watchdog_release(struct inode *inode, struct file *file)
    +{
    + int err;
    +
    + trace("%p, %p", inode, file);
    + dbg("received_magic_char=%d", received_magic_char);
    +
    + if (watchdogdev && (watchdogdev->watchdog_state == WATCHDOG_STARTED)) {
    + /* Only stop a watchdog if it actually started */
    + if (received_magic_char == 42) {
    + /* we received the magic char -> we can stop the watchdog */
    + if (watchdogdev->watchdog_ops && watchdogdev->watchdog_ops->stop) {
    + err = watchdogdev->watchdog_ops->stop(watchdogdev);
    + if (err == 0) {
    + watchdogdev->watchdog_state = WATCHDOG_STOPPED;
    + } else {
    + printk(KERN_CRIT PFX "Watchdog didn't stop successfull! (err=%d)",
    + err);
    + }
    + } else {
    + printk(KERN_CRIT PFX "Unable to stop watchdog!");
    + }
    + } else {
    + /* If we didn't receive the magic char, then we will close
    + * /dev/watchdog but the watchdog keeps running... */
    + printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!");
    + if (watchdogdev->watchdog_ops && watchdogdev->watchdog_ops->keepalive) {
    + watchdogdev->watchdog_ops->keepalive(watchdogdev);
    + }
    + }
    + }
    +
    + received_magic_char = 0;
    +
    + /* make sure that /dev/watchdog can be re-opened */
    + clear_bit(0, &watchdog_dev_open);
    +
    + return 0;
    +}
    +
    +/*
    + * /dev/watchdog kernel interfaces
    + */
    +
    +static struct file_operations watchdog_fops = {
    + .owner = THIS_MODULE,
    + .llseek = no_llseek,
    + .write = watchdog_write,
    + .ioctl = watchdog_ioctl,
    + .open = watchdog_open,
    + .release = watchdog_release,
    +};
    +
    +static struct miscdevice watchdog_miscdev = {
    + .minor = WATCHDOG_MINOR,
    + .name = "watchdog",
    + .fops = &watchdog_fops,
    +};
    +
    +/*
    + * /dev/watchdog register and unregister functions
    + */
    +
    +/*
    + * watchdog_dev_register:
    + *
    + * Register a watchdog device as /dev/watchdog. /dev/watchdog
    + * is actually a miscdevice and thus we set it up like that.
    + */
    +
    +int watchdog_dev_register(struct watchdog_device *wdd, struct device *parent)
    +{
    + int err = -EBUSY;
    +
    + trace("%p %p", wdd, parent);
    +
    + mutex_lock(&watchdog_register_mtx);
    +
    + if (watchdogdev) {
    + printk(KERN_ERR PFX "another watchdog device is allready registered as /dev/watchdog\n");
    + goto out;
    + }
    +
    + watchdog_miscdev.parent = parent;
    +
    + dbg("Register a new /dev/watchdog device\n");
    + err = misc_register(&watchdog_miscdev);
    + if (err != 0) {
    + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
    + watchdog_miscdev.minor, err);
    + goto out;
    + }
    +
    + watchdogdev = wdd;
    +
    +out:
    + mutex_unlock(&watchdog_register_mtx);
    + return err;
    +}
    +EXPORT_SYMBOL(watchdog_dev_register);
    +
    +/*
    + * watchdog_dev_unregister:
    + *
    + * Deregister the /dev/watchdog device.
    + */
    +
    +int watchdog_dev_unregister(struct watchdog_device *wdd)
    +{
    + trace("%p", wdd);
    +
    + mutex_lock(&watchdog_register_mtx);
    +
    + if (!watchdogdev) {
    + printk(KERN_ERR PFX "there is no watchdog registered\n");
    + mutex_unlock(&watchdog_register_mtx);
    + return -1;
    + }
    +
    + if (!wdd) {
    + printk(KERN_ERR PFX "cannot unregister non-existing watchdog-driver\n");
    + mutex_unlock(&watchdog_register_mtx);
    + return -2;
    + }
    +
    + if (watchdogdev != wdd) {
    + printk(KERN_ERR PFX "another watchdog device is running\n");
    + mutex_unlock(&watchdog_register_mtx);
    + return -3;
    + }
    +
    + dbg("Unregister /dev/watchdog device\n");
    + misc_deregister(&watchdog_miscdev);
    + watchdogdev = NULL;
    + mutex_unlock(&watchdog_register_mtx);
    + return 0;
    +}
    +EXPORT_SYMBOL(watchdog_dev_unregister);
    +
    +MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
    +MODULE_DESCRIPTION("Generic /dev/watchdog Code");
    +MODULE_VERSION(DRV_VERSION);
    +MODULE_LICENSE("GPL");
    +
    diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h
    index 011bcfe..4f57bf4 100644
    --- a/include/linux/watchdog.h
    +++ b/include/linux/watchdog.h
    @@ -53,12 +53,61 @@ struct watchdog_info {

    #ifdef __KERNEL__

    +#include <linux/device.h>
    +
    #ifdef CONFIG_WATCHDOG_NOWAYOUT
    #define WATCHDOG_NOWAYOUT 1
    #else
    #define WATCHDOG_NOWAYOUT 0
    #endif

    +struct watchdog_ops;
    +struct watchdog_device;
    +
    +struct watchdog_ops {
    + /* mandatory routines */
    + /* operation = start watchdog */
    + int (*start)(struct watchdog_device *);
    + /* operation = stop watchdog */
    + int (*stop)(struct watchdog_device *);
    + /* operation = send keepalive ping */
    + int (*keepalive)(struct watchdog_device *);
    + /* optional routines */
    + /* operation = set watchdog's heartbeat */
    + int (*set_heartbeat)(struct watchdog_device *, int);
    + /* operation = get the watchdog's status */
    + int (*get_status)(struct watchdog_device *, int *);
    + /* operation = get the time left before reboot */
    + int (*get_timeleft)(struct watchdog_device *, int *);
    +};
    +
    +struct watchdog_device {
    + unsigned char name[32]; /* The watchdog's 'identity' */
    + unsigned long options; /* The supported capabilities/options */
    + unsigned long firmware; /* The Watchdog's Firmware version */
    + int nowayout; /* The nowayout setting for this watchdog */
    + int heartbeat; /* The watchdog's heartbeat */
    + int bootstatus; /* The watchdog's bootstatus */
    + struct watchdog_ops *watchdog_ops; /* link to watchdog_ops */
    +
    + /* watchdog status (register/unregister) state machine */
    + enum { WATCHDOG_UNINITIALIZED = 0,
    + WATCHDOG_REGISTERED, /* completed register_watchdogdevice */
    + WATCHDOG_STARTED, /* watchdog device started */
    + WATCHDOG_STOPPED, /* watchdog device stopped */
    + WATCHDOG_UNREGISTERED, /* completed unregister_watchdogdevice */
    + } watchdog_state;
    +
    + /* From here on everything is device dependent */
    + void *private;
    +};
    +
    +/* drivers/watchdog/watchdog_core.c */
    +extern struct watchdog_device *alloc_watchdogdev(void);
    +extern int register_watchdogdevice(struct watchdog_device *, struct device *);
    +extern int unregister_watchdogdevice(struct watchdog_device *);
    +extern int free_watchdogdev(struct watchdog_device *);
    +
    #endif /* __KERNEL__ */

    #endif /* ifndef _LINUX_WATCHDOG_H */
    --
    1.5.3.4
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

    \
     
     \ /
      Last update: 2008-05-20 17:49    [W:0.070 / U:32.912 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site