|
@@ -0,0 +1,195 @@
|
|
|
+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 :)
|
|
|
+
|