Generic Watchdog Timer Driver
Subsystems (2)
RFC PATCH 1/10 Generic Watchdog Timer Driver
commit 608917b2dba29a26d352124d1371aafcd81cfb8c
Author: Wim Van Sebroeck <[email protected]> Date: Fri Jun 18 08:40:15 2010 +0000
watchdog: WatchDog Timer Driver Core - Part 1
The WatchDog Timer Driver Core is a framework that contains the common code for all watchdog-driver's. It also introduces a watchdog device structure and the operations that go with it.
This is the introduction of this framework. This part supports the minimal watchdog userspace API (or with other words: the functionality to use /dev/watchdog's open, release and write functionality as defined in the simplest watchdog API). Extra functionality will follow in the next set of patches.
Documentation/watchdog/src/watchdog-with-timer-example.c new file +210 -0
@@ -0,0 +1,210 @@ +/* +* Watchdog timer driver example with timer. +* +* Copyright (C) 2009-2010 Wim Van Sebroeck <[email protected]> +* +* 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. +*/ + +/* +* This is an example driver where the hardware does not support +* stopping the watchdog timer. +*/ + +#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/bitops.h> +#include <linux/uaccess.h> +#include <linux/io.h> +#include <linux/platform_device.h> + +#define DRV_NAME "wdt" +#define PFX DRV_NAME ": " + +/* 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 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) || + (!running)) { +wdt_reset (); +mod_timer (&timer, jiffies +WDT_HEARTBEAT ); + }else + printk(KERN_CRIT PFX "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 */ + printk(KERN_INFO PFX "wdt_start\n"); + + running =1 ; +return 0 ; +} + +static int wdt_stop (struct watchdog_device *wdd) +{ +/* The watchdog timer hardware can not be stopped... */ + printk(KERN_INFO PFX "wdt_stop\n"); + + running =0 ; +return 0 ; +} + +/* +* The watchdog kernel structures +*/ +static const struct watchdog_ops wdt_ops = { + .start = wdt_start, + .stop = wdt_stop, + .ping = wdt_ping, +}; + +static struct watchdog_device wdt_dev = { + .name =DRV_NAME , + .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 */ + +/* Register the watchdog timer device */ + res =register_watchdogdevice (&wdt_dev); +if (res) { + printk(KERN_ERR PFX + "register_watchdogdevice returned %d\n", res); +return res; + } + + printk(KERN_INFO PFX "enabled (timeout=%d sec)\n", timeout); + +return 0 ; +} + +static int __devexit wdt_remove(struct platform_device *pdev) +{ +/* Unregister the watchdog timer device */ +unregister_watchdogdevice (&wdt_dev); + +/* stop and delete the timer */ + printk(KERN_WARNING PFX "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; + +printk (KERN_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); + printk(KERN_INFO PFX "Watchdog Module Unloaded.\n"); +} + +module_init (wdt_init); +module_exit (wdt_exit); + +MODULE_AUTHOR ("Wim Van Sebroeck <[email protected]>" ); +MODULE_DESCRIPTION ("WatchDog Timer Driver example with timer" ); +MODULE_LICENSE ("GPL" ); +MODULE_ALIAS ("platform:" DRV_NAME );
Documentation/watchdog/watchdog-kernel-api.txt new file +97 -0
97 lines not on this page · raw message
drivers/watchdog/Kconfig +2 -0
@@ -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 #
drivers/watchdog/Makefile +2 -0
@@ -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.
drivers/watchdog/core/Kconfig new file +30 -0
@@ -0,0 +1,30 @@ +# +# Watchdog timer driver core +# + +if WATCHDOG + +config WATCHDOG_CORE + tristate "WatchDog Timer Driver Core" + depends on EXPERIMENTAL + default m + ---help--- + Say Y here if you want to use the new watchdog timer driver core. + This driver provides a framework for all watchdog timer drivers + and gives them the /dev/watchdog interface (and later also the + sysfs interface). + + To compile this driver as a module, choose M here: the module will + be called watchdog. + +config WATCHDOG_DEBUG_CORE + bool "WatchDog Timer Driver Core debugging output" + depends on WATCHDOG_CORE + default n + ---help--- + Say Y here if you want the Watchdog Timer Driver Core to + produce debugging information. Select this if you are having a + problem with the watchdog timer driver core and want to see + more of what is really happening. + +endif # WATCHDOG
drivers/watchdog/core/Makefile new file +6 -0
@@ -0,0 +1,6 @@ +# +# Makefile for the WatchDog Timer Driver Core. +# + +watchdog-objs += watchdog_core.o watchdog_dev.o +obj-$(CONFIG_WATCHDOG_CORE) += watchdog.o
drivers/watchdog/core/watchdog_core.c new file +133 -0
133 lines not on this page · raw message
drivers/watchdog/core/watchdog_core.h new file +33 -0
@@ -0,0 +1,33 @@ +/* +* watchdog_core.h +* +* (c) Copyright 2008-2010 Alan Cox <[email protected]>, +* All Rights Reserved. +* +* (c) Copyright 2008-2010 Wim Van Sebroeck <[email protected]>. +* +* 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 <[email protected]>, +* Rob Radez <[email protected]>, +* Rusty Lynch <[email protected]> +* Satyam Sharma <[email protected]> +* Randy Dunlap <[email protected]> +* +* 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. +*/ + +/* +* External functions/procedures +*/ +extern int watchdog_dev_register (struct watchdog_device *); +extern int watchdog_dev_unregister (struct watchdog_device *);
drivers/watchdog/core/watchdog_dev.c new file +281 -0
281 lines not on this page · raw message
include/linux/watchdog.h +25 -0
@@ -59,6 +59,31 @@ struct watchdog_info {#define WATCHDOG_NOWAYOUT 0#endif +struct watchdog_ops ; +struct watchdog_device ; + +/* The watchdog-devices operations */ +struct watchdog_ops { +/* mandatory operations */ +int (*start)(struct watchdog_device *); +int (*stop)(struct watchdog_device *); +/* optional operations */ +int (*ping)(struct watchdog_device *); +}; + +/* The structure that defines a watchdog device */ +struct watchdog_device { +char *name; +const struct watchdog_ops *ops; +long status; +#define WDOG_DEV_OPEN 1/* is the watchdog opened via +* /dev/watchdog */ +}; + +/* drivers/watchdog/core/watchdog_core.c */ +extern int register_watchdogdevice (struct watchdog_device *); +extern void unregister_watchdogdevice (struct watchdog_device *); +#endif /* __KERNEL__ */ #endif /* ifndef _LINUX_WATCHDOG_H */
Hi Wim,
Looks like a nice series, a couple of nitpicks inline but otherwise great! Thanks for doing this, it looks like it'll make a big improvement to the watchdog system!
Jamie
Quoted 50 lines · Wim Van Sebroeck
commit
608917b2dba29a26d352124d1371aafcd81cfb8cAuthor: Wim Van Sebroeck <[email protected]> Date: Fri Jun 18 08:40:15 2010 +0000watchdog: WatchDog Timer Driver Core - Part 1
The WatchDog Timer Driver Core is a framework that contains the common code for all watchdog-driver's. It also introduces a watchdog device structure and the operations that go with it.
This is the introduction of this framework. This part supports the minimal watchdog userspace API (or with other words: the functionality to use /dev/watchdog's open, release and write functionality as defined in the simplest watchdog API). Extra functionality will follow in the next set of patches.
Signed-off-by Alan Cox <[email protected]>Signed-off-by Wim Van Sebroeck <[email protected]>Documentation/watchdog/src/watchdog-with-timer-example.c new file +23
-0@@ -0,0 +1,210 @@ +/* +* Watchdog timer driver example with timer. +* +* Copyright (C) 2009-2010 Wim Van Sebroeck <[email protected]> +* +* 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. +*/ + +/* +* This is an example driver where the hardware does not support +* stopping the watchdog timer. +*/ + +#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>
Documentation/watchdog/src/watchdog-with-timer-example.c line 33
Quoted 10 lines
Documentation/watchdog/src/watchdog-with-timer-example.c new file +10
-0+#include <linux/watchdog.h> +#include <linux/jiffies.h> +#include <linux/timer.h> +#include <linux/bitops.h> +#include <linux/uaccess.h> +#include <linux/io.h> +#include <linux/platform_device.h> + +#define DRV_NAME "wdt" +#define PFX DRV_NAME ": "
Is it worth using pr_fmt() and friends rather than printk() with explicit levels and prefixes throughout the code?
Quoted 434 lines
Documentation/watchdog/src/watchdog-with-timer-example.c new file +177
-0+ +/* 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 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) || + (!running)) { +wdt_reset (); +mod_timer (&timer, jiffies +WDT_HEARTBEAT ); + }else + printk(KERN_CRIT PFX "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 */ + printk(KERN_INFO PFX "wdt_start\n"); + + running =1 ; +return 0 ; +} + +static int wdt_stop (struct watchdog_device *wdd) +{ +/* The watchdog timer hardware can not be stopped... */ + printk(KERN_INFO PFX "wdt_stop\n"); + + running =0 ; +return 0 ; +} + +/* +* The watchdog kernel structures +*/ +static const struct watchdog_ops wdt_ops = { + .start = wdt_start, + .stop = wdt_stop, + .ping = wdt_ping, +}; + +static struct watchdog_device wdt_dev = { + .name =DRV_NAME , + .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 */ + +/* Register the watchdog timer device */ + res =register_watchdogdevice (&wdt_dev); +if (res) { + printk(KERN_ERR PFX + "register_watchdogdevice returned %d\n", res); +return res; + } + + printk(KERN_INFO PFX "enabled (timeout=%d sec)\n", timeout); + +return 0 ; +} + +static int __devexit wdt_remove(struct platform_device *pdev) +{ +/* Unregister the watchdog timer device */ +unregister_watchdogdevice (&wdt_dev); + +/* stop and delete the timer */ + printk(KERN_WARNING PFX "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; + +printk (KERN_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); + printk(KERN_INFO PFX "Watchdog Module Unloaded.\n"); +} + +module_init (wdt_init); +module_exit (wdt_exit); + +MODULE_AUTHOR ("Wim Van Sebroeck <[email protected]>" ); +MODULE_DESCRIPTION ("WatchDog Timer Driver example with timer" ); +MODULE_LICENSE ("GPL" ); +MODULE_ALIAS ("platform:" DRV_NAME );Documentation/watchdog/watchdog-kernel-api.txt new file +97
-0@@ -0,0 +1,97 @@ +The Linux WatchDog Timer Driver Core kernel API. +=============================================== +Last reviewed: 09-Jun-2010 + +Wim Van Sebroeck <[email protected]> + +Introduction +------------ +This document does not describe what a WatchDog Timer (WDT) Driver or Device is. +It also does not describe the API which can be used by user space to communicate +with a WatchDog Timer. If you want to know this then please read the following +file: Documentation/watchdog/watchdog-api.txt . + +So what does this document describe? It describes the API that can be used by +WatchDog Timer Drivers that want to use the WatchDog Timer Driver Core +Framework. This framework provides all interfacing towards user space so that +the same code does not have to be reproduced each time. This also means that +a watchdog timer driver then only needs to provide the different routines +(operations) that control the watchdog timer (WDT). + +The API +------- +Each watchdog timer driver that wants to use the WatchDog Timer Driver Core +must #include <linux/watchdog.h> (you would have to do this anyway when +writing a watchdog device driver). This include file contains following +register/unregister routines: + +extern int register_watchdogdevice(struct watchdog_device *); +extern void unregister_watchdogdevice(struct watchdog_device *); + +The register_watchdogdevice routine registers a watchdog timer device. +The parameter of this routine is a pointer to a watchdog_device structure. +This routine returns zero on success and a negative errno code for failure. + +The unregister_watchdogdevice routine deregisters a registered watchdog timer +device. The parameter of this routine is the pointer to the registered +watchdog_device structure. + +The watchdog device structure looks like this: + +struct watchdog_device { + char *name; + const struct watchdog_ops *ops; + long status; +}; + +It contains following fields: +* name: a pointer to the (preferably unique) name of the watchdog timer device. +* ops: a pointer to the list of watchdog operations that the watchdog supports. +* status: this field contains a number of status bits that give extra + information about the status of the device (Like: is the device opened via + the /dev/watchdog interface or not, ...) + +The list of watchdog operations is defined as: + +struct watchdog_ops { + /* mandatory operations */ + int (*start)(struct watchdog_device *); + int (*stop)(struct watchdog_device *); + /* optional operations */ + int (*ping)(struct watchdog_device *); +}; + +Some operations are mandatory and some are optional. The mandatory operations +are: +* start: this is a pointer to the routine that starts the watchdog timer + device. + The routine needs a pointer to the watchdog timer device structure as a + parameter. It returns zero on success or a negative errno code for failure. +* stop: with this routine the watchdog timer device is being stopped. + The routine needs a pointer to the watchdog timer device structure as a + parameter. It returns zero on success or a negative errno code for failure. + Some watchdog timer hardware can only be started and not be stopped. The + driver supporting this hardware needs to make sure that a start and stop + routine is being provided. This can be done by using a timer in the driver + that regularly sends a keepalive ping to the watchdog timer hardware. + (See Documentation/watchdog/src/watchdog-with-timer-example.c for an + example). + +Not all watchdog timer hardware supports the same functionality. That's why +all other routines/operations are optional. They only need to be provided if +they are supported. These optional routines/operations are: +* ping: this is the routine that sends a keepalive ping to the watchdog timer + hardware. + The routine needs a pointer to the watchdog timer device structure as a + parameter. It returns zero on success or a negative errno code for failure. + Most hardware that does not support this as a separate function uses the + start function to restart the watchdog timer hardware. And that's also what + the watchdog timer driver core does: to send a keepalive ping to the watchdog + timer hardware it will either use the ping operation (when available) or the + start operation (when the ping operation is not available). + +The status bits should (preferably) be set with the set_bit and clear_bit alike +bit-operations. The status bit's that are defined are: +* WDOG_DEV_OPEN: this status bit shows whether or not the watchdog device + was opened via /dev/watchdog. + (This bit should only be used by the WatchDog Timer Driver Core).drivers/watchdog/Kconfig +2
-0@@ -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 #drivers/watchdog/Makefile +2
-0@@ -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.drivers/watchdog/core/Kconfig new file +30
-0@@ -0,0 +1,30 @@ +# +# Watchdog timer driver core +# + +if WATCHDOG + +config WATCHDOG_CORE + tristate "WatchDog Timer Driver Core" + depends on EXPERIMENTAL + default m + ---help--- + Say Y here if you want to use the new watchdog timer driver core. + This driver provides a framework for all watchdog timer drivers + and gives them the /dev/watchdog interface (and later also the + sysfs interface). + + To compile this driver as a module, choose M here: the module will + be called watchdog. + +config WATCHDOG_DEBUG_CORE + bool "WatchDog Timer Driver Core debugging output" + depends on WATCHDOG_CORE + default n + ---help--- + Say Y here if you want the Watchdog Timer Driver Core to + produce debugging information. Select this if you are having a + problem with the watchdog timer driver core and want to see + more of what is really happening. + +endif # WATCHDOGdrivers/watchdog/core/Makefile new file +6
-0@@ -0,0 +1,6 @@ +# +# Makefile for the WatchDog Timer Driver Core. +# + +watchdog-objs += watchdog_core.o watchdog_dev.o +obj-$(CONFIG_WATCHDOG_CORE) += watchdog.odrivers/watchdog/core/watchdog_core.c new file +74
-0@@ -0,0 +1,133 @@ +/* +* watchdog_core.c +* +* (c) Copyright 2008-2010 Alan Cox <[email protected]>, +* All Rights Reserved. +* +* (c) Copyright 2008-2010 Wim Van Sebroeck <[email protected]>. +* +* 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 <[email protected]>, +* Rob Radez <[email protected]>, +* Rusty Lynch <[email protected]> +* Satyam Sharma <[email protected]> +* Randy Dunlap <[email protected]> +* +* 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. +*/ + +#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/... */ + +#include "watchdog_core.h" /* For watchdog_dev_register/... */ + +/* +* Version information +*/ +#define DRV_VERSION "0.01" +#define DRV_NAME "watchdog_core" +#define PFX DRV_NAME ": " + +/** +* register_watchdogdevice - 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 register_watchdogdevice (struct watchdog_device *wdd) +{ +int ret; + +/* Make sure we have a valid watchdog_device structure */ +if (wdd ==NULL || wdd->ops ==NULL ) +return -ENODATA ; + +/* Make sure that the mandatory operations are supported */ +if (wdd->ops->start ==NULL || wdd->ops->stop ==NULL ) +return -ENODATA ; + +/* Note: now that all watchdog_device data has been verified, we +* will not check this anymore in other functions. If data get's +* corrupted in a later stage then we expect a kernel panic! */ + +/* The future version will have to manage a list of all +* registered watchdog devices. To start we will only +* support 1 watchdog device via the /dev/watchdog interface */ +
drivers/watchdog/core/watchdog_core.c line 84
Quoted 10 lines
drivers/watchdog/core/watchdog_core.c new file +10
-0+/* create the /dev/watchdog interface */ + ret =watchdog_dev_register (wdd); +if (ret) { + printk(KERN_ERR PFX "error registering /dev/watchdog (err=%d)", + ret); +return ret; + } + +return 0 ; +}
Should this function also take a reference on the module implementing the driver? Perhaps add a 'struct module *owner' to the 'struct watchdog_ops' and do a try_module_get() here? We'd also need the corresponding module_put() in unregister_watchdogdevice().
Quoted 152 lines
drivers/watchdog/core/watchdog_core.c new file +49
-0+EXPORT_SYMBOL (register_watchdogdevice); + +/** +* unregister_watchdogdevice - unregister a watchdog device +* @wdd: watchdog device to unregister +* +* Unregister a watchdog device that was previously successfully +* registered with register_watchdogdevice(). +*/ +void unregister_watchdogdevice (struct watchdog_device *wdd) +{ +int ret; + +/* Make sure we have a valid watchdog_device structure */ +if (wdd ==NULL ) +return ; + +/* The future version will check if this watchdog can be found +* in the list of registered watchdog devices */ + +/* remove the /dev/watchdog interface */ + ret =watchdog_dev_unregister (wdd); +if (ret) + printk(KERN_ERR PFX +"error unregistering /dev/watchdog (err=%d)" , ret); +} +EXPORT_SYMBOL (unregister_watchdogdevice); + +static int __init watchdog_init (void ) +{ +printk (KERN_INFO "Watchdog timer driver core v%s loaded\n" , +DRV_VERSION ); +return 0 ; +} + +static void __exit watchdog_exit(void) +{ +printk (KERN_INFO "Watchdog timer driver core unloaded\n" ); +} + +module_init (watchdog_init); +module_exit (watchdog_exit); + +MODULE_AUTHOR ("Alan Cox <[email protected]>, " +"Wim Van Sebroeck <[email protected]>" ); +MODULE_DESCRIPTION ("WatchDog Timer Driver Core" ); +MODULE_VERSION (DRV_VERSION ); +MODULE_LICENSE ("GPL" ); +MODULE_SUPPORTED_DEVICE ("watchdog" );drivers/watchdog/core/watchdog_core.h new file +33
-0@@ -0,0 +1,33 @@ +/* +* watchdog_core.h +* +* (c) Copyright 2008-2010 Alan Cox <[email protected]>, +* All Rights Reserved. +* +* (c) Copyright 2008-2010 Wim Van Sebroeck <[email protected]>. +* +* 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 <[email protected]>, +* Rob Radez <[email protected]>, +* Rusty Lynch <[email protected]> +* Satyam Sharma <[email protected]> +* Randy Dunlap <[email protected]> +* +* 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. +*/ + +/* +* External functions/procedures +*/ +extern int watchdog_dev_register (struct watchdog_device *); +extern int watchdog_dev_unregister (struct watchdog_device *);drivers/watchdog/core/watchdog_dev.c new file +58
-0@@ -0,0 +1,281 @@ +/* +* watchdog_dev.c +* +* (c) Copyright 2008-2010 Alan Cox <[email protected]>, +* All Rights Reserved. +* +* (c) Copyright 2008-2010 Wim Van Sebroeck <[email protected]>. +* +* +* This source code is part of the generic code that can be used +* by all the watchdog timer drivers. +* +* This part of the generic code takes care of the following +* misc device: /dev/watchdog. +* +* Based on source code of the following authors: +* Matt Domsch <[email protected]>, +* Rob Radez <[email protected]>, +* Rusty Lynch <[email protected]> +* Satyam Sharma <[email protected]> +* Randy Dunlap <[email protected]> +* +* 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. +*/ + +#include <linux/module.h> /* For EXPORT_SYMBOL/module stuff/... */ +#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/init.h> /* For __init/__exit/... */ +#include <linux/uaccess.h> /* For copy_to_user/put_user/... */ + +/* +* Version information +*/ +#define DRV_VERSION "0.01" +#define DRV_NAME "watchdog_dev" +#define PFX DRV_NAME ": " + +/* +* Debugging Macros +*/ +#ifdef CONFIG_WATCHDOG_DEBUG_CORE +#define trace(format, args...) \ + printk(KERN_INFO PFX "%s(" format ")\n", __func__ , ## args) +#define dbg(format, args...) \ + printk(KERN_DEBUG PFX "%s: " format "\n", __func__, ## args) +#else
drivers/watchdog/core/watchdog_dev.c line 68
Quoted 10 lines
drivers/watchdog/core/watchdog_dev.c new file +10
-0+#define trace(format, args...) do { } while (0) +#define dbg(format, args...) do { } while (0) +#endif + +/* +* Locally used variables +*/ + +/* make sure we only register one /dev/watchdog device */ +static unsigned long register_busy;
I first read this to mean that a registration was in progress not that a watchdog is registered. Rename to watchdog_is_registered or similar?
Quoted 39 lines
drivers/watchdog/core/watchdog_dev.c new file +39
-0+/* the watchdog device behind /dev/watchdog */ +static struct watchdog_device *wdd; + +/* +* /dev/watchdog operations +*/ + +/* +* watchdog_ping: ping the watchdog. +* @wddev: the watchdog device to ping +* +* If the watchdog has no own ping operation then it needs to be +* restarted via the start operation. This wrapper function does +* exactly that. +*/ + +static int watchdog_ping (struct watchdog_device *wddev) +{ +if (wddev->ops->ping) +return wddev->ops->ping (wddev);/* ping the watchdog */ +else +return wddev->ops->start (wddev);/* restart the watchdog */ +} + +/* +* 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 ping. +*/ + +static ssize_t watchdog_write (struct file *file,const char __user *data, +size_t len,loff_t *ppos) +{ +size_t i; +char c;
drivers/watchdog/core/watchdog_dev.c line 117
Quoted 10 lines
drivers/watchdog/core/watchdog_dev.c new file +10
-0+ +trace ("%p, %p, %zu, %p" , file, data, len, ppos); + +if (len ==0 )/* Can we see this even ? */ +return 0 ; + +for (i =0 ; i != len; i++) { +if (get_user (c, data + i)) +return -EFAULT ; + }
Shouldn't this loop be added in the patch where you add the magic close support?
Quoted 204 lines
drivers/watchdog/core/watchdog_dev.c new file +164
-0+ +/* someone wrote to us, so we sent the watchdog a keepalive ping */ +watchdog_ping (wdd); + +return len; +} + +/* +* 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. +* Watch out: the /dev/watchdog device is single open, so we make sure +* it can only be opened once. +*/ + +static int watchdog_open (struct inode *inode,struct file *file) +{ +int err; + +trace ("%p, %p" , inode, file); + +/* the watchdog is single open! */ +if (test_and_set_bit (WDOG_DEV_OPEN , &wdd->status)) +return -EBUSY ; + +/* start the watchdog */ + err = wdd->ops->start (wdd); +if (err <0 ) + goto out; + +/* dev/watchdog is a virtual (and thus non-seekable) filesystem */ +return nonseekable_open (inode, file); + +out: +clear_bit (WDOG_DEV_OPEN , &wdd->status); +return err; +} + +/* +* 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. +*/ + +static int watchdog_release (struct inode *inode,struct file *file) +{ +int err =-1 ; + +trace ("%p, %p" , inode, file); + +/* stop the watchdog */ + err = wdd->ops->stop (wdd); +if (err !=0 ) { +printk (KERN_CRIT "%s: not stopping watchdog!\n" , wdd->name); +watchdog_ping (wdd); + } + +/* make sure that /dev/watchdog can be re-opened */ +clear_bit (WDOG_DEV_OPEN , &wdd->status); + +return 0 ; +} + +/* +* /dev/watchdog kernel interfaces +*/ + +static const struct file_operations watchdog_fops = { + .owner =THIS_MODULE , + .llseek = no_llseek, + .write = watchdog_write, + .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: +* @watchdog: watchdog device +* +* 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 *watchdog) +{ +int err; + +trace ("%p" , watchdog); + +/* Only one device can register for /dev/watchdog */ +if (test_and_set_bit (0 , ®ister_busy)) { + printk(KERN_ERR PFX + "only one watchdog can use /dev/watchdog.\n"); +return -EBUSY ; + } + + wdd = watchdog; + +/* Register the miscdevice */ +dbg ("Register a new /dev/watchdog device" ); + err =misc_register (&watchdog_miscdev); +if (err !=0 ) { +printk (KERN_ERR +"%s: cannot register miscdev on minor=%d (err=%d).\n" , + watchdog->name,WATCHDOG_MINOR , err); + goto out; + } + +return 0 ; + +out: + wdd =NULL ; +clear_bit (0 , ®ister_busy); +return err; +} +EXPORT_SYMBOL (watchdog_dev_register); + +/* +* watchdog_dev_unregister: +* @watchdog: watchdog device +* +* Deregister the /dev/watchdog device. +*/ + +int watchdog_dev_unregister (struct watchdog_device *watchdog) +{ +trace ("%p" , watchdog); + +/* Check that a watchdog device was registered in the past */ +if (!test_bit (0 , ®ister_busy) || !wdd) +return -ENODEV ; + +/* We can only unregister the watchdog device that was registered */ +if (watchdog != wdd) { + printk(KERN_ERR PFX +"%s: watchdog was not registered as /dev/watchdog.\n" , + watchdog->name); +return -ENODEV ; + } + +/* Unregister the miscdevice */ +dbg ("Unregister /dev/watchdog device" ); +misc_deregister (&watchdog_miscdev); + wdd =NULL ; +clear_bit (0 , ®ister_busy); +return 0 ; +} +EXPORT_SYMBOL (watchdog_dev_unregister); + +MODULE_ALIAS_MISCDEV (WATCHDOG_MINOR );include/linux/watchdog.h +25
-1@@ -59,6 +59,31 @@ struct watchdog_info {#define WATCHDOG_NOWAYOUT 0#endif +struct watchdog_ops ; +struct watchdog_device ; + +/* The watchdog-devices operations */ +struct watchdog_ops { +/* mandatory operations */ +int (*start)(struct watchdog_device *); +int (*stop)(struct watchdog_device *); +/* optional operations */ +int (*ping)(struct watchdog_device *); +}; + +/* The structure that defines a watchdog device */ +struct watchdog_device { +char *name; +const struct watchdog_ops *ops; +long status; +#define WDOG_DEV_OPEN 1/* is the watchdog opened via +* /dev/watchdog */ +}; + +/* drivers/watchdog/core/watchdog_core.c */ +extern int register_watchdogdevice (struct watchdog_device *); +extern void unregister_watchdogdevice (struct watchdog_device *); +#endif /* __KERNEL__ */ #endif /* ifndef _LINUX_WATCHDOG_H */ --To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Jamie,
Quoted 2 lines
Is it worth using
pr_fmt()and friends rather thanprintk()with explicit levels and prefixes throughout the code?
patch 12 :-)
Kind regards,
Wim.