74cd4c6739
Signed-off-by: Wolfram Sang <w.sang@pengutronix.de> Cc: Randy Dunlap <rdunlap@xenotime.net> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
196 lines
6.6 KiB
Plaintext
196 lines
6.6 KiB
Plaintext
Converting old watchdog drivers to the watchdog framework
|
|
by Wolfram Sang <w.sang@pengutronix.de>
|
|
=========================================================
|
|
|
|
Before the watchdog framework came into the kernel, every driver had to
|
|
implement the API on its own. Now, as the framework factored out the common
|
|
components, those drivers can be lightened making it a user of the framework.
|
|
This document shall guide you for this task. The necessary steps are described
|
|
as well as things to look out for.
|
|
|
|
|
|
Remove the file_operations struct
|
|
---------------------------------
|
|
|
|
Old drivers define their own file_operations for actions like open(), write(),
|
|
etc... These are now handled by the framework and just call the driver when
|
|
needed. So, in general, the 'file_operations' struct and assorted functions can
|
|
go. Only very few driver-specific details have to be moved to other functions.
|
|
Here is a overview of the functions and probably needed actions:
|
|
|
|
- open: Everything dealing with resource management (file-open checks, magic
|
|
close preparations) can simply go. Device specific stuff needs to go to the
|
|
driver specific start-function. Note that for some drivers, the start-function
|
|
also serves as the ping-function. If that is the case and you need start/stop
|
|
to be balanced (clocks!), you are better off refactoring a separate start-function.
|
|
|
|
- close: Same hints as for open apply.
|
|
|
|
- write: Can simply go, all defined behaviour is taken care of by the framework,
|
|
i.e. ping on write and magic char ('V') handling.
|
|
|
|
- ioctl: While the driver is allowed to have extensions to the IOCTL interface,
|
|
the most common ones are handled by the framework, supported by some assistance
|
|
from the driver:
|
|
|
|
WDIOC_GETSUPPORT:
|
|
Returns the mandatory watchdog_info struct from the driver
|
|
|
|
WDIOC_GETSTATUS:
|
|
Needs the status-callback defined, otherwise returns 0
|
|
|
|
WDIOC_GETBOOTSTATUS:
|
|
Needs the bootstatus member properly set. Make sure it is 0 if you
|
|
don't have further support!
|
|
|
|
WDIOC_SETOPTIONS:
|
|
No preparations needed
|
|
|
|
WDIOC_KEEPALIVE:
|
|
If wanted, options in watchdog_info need to have WDIOF_KEEPALIVEPING
|
|
set
|
|
|
|
WDIOC_SETTIMEOUT:
|
|
Options in watchdog_info need to have WDIOF_SETTIMEOUT set
|
|
and a set_timeout-callback has to be defined. The core will also
|
|
do limit-checking, if min_timeout and max_timeout in the watchdog
|
|
device are set. All is optional.
|
|
|
|
WDIOC_GETTIMEOUT:
|
|
No preparations needed
|
|
|
|
Other IOCTLs can be served using the ioctl-callback. Note that this is mainly
|
|
intended for porting old drivers; new drivers should not invent private IOCTLs.
|
|
Private IOCTLs are processed first. When the callback returns with
|
|
-ENOIOCTLCMD, the IOCTLs of the framework will be tried, too. Any other error
|
|
is directly given to the user.
|
|
|
|
Example conversion:
|
|
|
|
-static const struct file_operations s3c2410wdt_fops = {
|
|
- .owner = THIS_MODULE,
|
|
- .llseek = no_llseek,
|
|
- .write = s3c2410wdt_write,
|
|
- .unlocked_ioctl = s3c2410wdt_ioctl,
|
|
- .open = s3c2410wdt_open,
|
|
- .release = s3c2410wdt_release,
|
|
-};
|
|
|
|
Check the functions for device-specific stuff and keep it for later
|
|
refactoring. The rest can go.
|
|
|
|
|
|
Remove the miscdevice
|
|
---------------------
|
|
|
|
Since the file_operations are gone now, you can also remove the 'struct
|
|
miscdevice'. The framework will create it on watchdog_dev_register() called by
|
|
watchdog_register_device().
|
|
|
|
-static struct miscdevice s3c2410wdt_miscdev = {
|
|
- .minor = WATCHDOG_MINOR,
|
|
- .name = "watchdog",
|
|
- .fops = &s3c2410wdt_fops,
|
|
-};
|
|
|
|
|
|
Remove obsolete includes and defines
|
|
------------------------------------
|
|
|
|
Because of the simplifications, a few defines are probably unused now. Remove
|
|
them. Includes can be removed, too. For example:
|
|
|
|
- #include <linux/fs.h>
|
|
- #include <linux/miscdevice.h> (if MODULE_ALIAS_MISCDEV is not used)
|
|
- #include <linux/uaccess.h> (if no custom IOCTLs are used)
|
|
|
|
|
|
Add the watchdog operations
|
|
---------------------------
|
|
|
|
All possible callbacks are defined in 'struct watchdog_ops'. You can find it
|
|
explained in 'watchdog-kernel-api.txt' in this directory. start(), stop() and
|
|
owner must be set, the rest are optional. You will easily find corresponding
|
|
functions in the old driver. Note that you will now get a pointer to the
|
|
watchdog_device as a parameter to these functions, so you probably have to
|
|
change the function header. Other changes are most likely not needed, because
|
|
here simply happens the direct hardware access. If you have device-specific
|
|
code left from the above steps, it should be refactored into these callbacks.
|
|
|
|
Here is a simple example:
|
|
|
|
+static struct watchdog_ops s3c2410wdt_ops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .start = s3c2410wdt_start,
|
|
+ .stop = s3c2410wdt_stop,
|
|
+ .ping = s3c2410wdt_keepalive,
|
|
+ .set_timeout = s3c2410wdt_set_heartbeat,
|
|
+};
|
|
|
|
A typical function-header change looks like:
|
|
|
|
-static void s3c2410wdt_keepalive(void)
|
|
+static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
|
|
{
|
|
...
|
|
+
|
|
+ return 0;
|
|
}
|
|
|
|
...
|
|
|
|
- s3c2410wdt_keepalive();
|
|
+ s3c2410wdt_keepalive(&s3c2410_wdd);
|
|
|
|
|
|
Add the watchdog device
|
|
-----------------------
|
|
|
|
Now we need to create a 'struct watchdog_device' and populate it with the
|
|
necessary information for the framework. The struct is also explained in detail
|
|
in 'watchdog-kernel-api.txt' in this directory. We pass it the mandatory
|
|
watchdog_info struct and the newly created watchdog_ops. Often, old drivers
|
|
have their own record-keeping for things like bootstatus and timeout using
|
|
static variables. Those have to be converted to use the members in
|
|
watchdog_device. Note that the timeout values are unsigned int. Some drivers
|
|
use signed int, so this has to be converted, too.
|
|
|
|
Here is a simple example for a watchdog device:
|
|
|
|
+static struct watchdog_device s3c2410_wdd = {
|
|
+ .info = &s3c2410_wdt_ident,
|
|
+ .ops = &s3c2410wdt_ops,
|
|
+};
|
|
|
|
|
|
Register the watchdog device
|
|
----------------------------
|
|
|
|
Replace misc_register(&miscdev) with watchdog_register_device(&watchdog_dev).
|
|
Make sure the return value gets checked and the error message, if present,
|
|
still fits. Also convert the unregister case.
|
|
|
|
- ret = misc_register(&s3c2410wdt_miscdev);
|
|
+ ret = watchdog_register_device(&s3c2410_wdd);
|
|
|
|
...
|
|
|
|
- misc_deregister(&s3c2410wdt_miscdev);
|
|
+ watchdog_unregister_device(&s3c2410_wdd);
|
|
|
|
|
|
Update the Kconfig-entry
|
|
------------------------
|
|
|
|
The entry for the driver now needs to select WATCHDOG_CORE:
|
|
|
|
+ select WATCHDOG_CORE
|
|
|
|
|
|
Create a patch and send it to upstream
|
|
--------------------------------------
|
|
|
|
Make sure you understood Documentation/SubmittingPatches and send your patch to
|
|
linux-watchdog@vger.kernel.org. We are looking forward to it :)
|
|
|