| ========================================================= |
| Converting old watchdog drivers to the watchdog framework |
| ========================================================= |
| |
| by Wolfram Sang <wsa@kernel.org> |
| |
| 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 |
| |
| WDIOC_GETTIMELEFT: |
| It needs get_timeleft() callback to be defined. Otherwise it |
| will return EOPNOTSUPP |
| |
| 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() 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, |
| +}; |
| |
| |
| Handle the 'nowayout' feature |
| ----------------------------- |
| |
| A few drivers use nowayout statically, i.e. there is no module parameter for it |
| and only CONFIG_WATCHDOG_NOWAYOUT determines if the feature is going to be |
| used. This needs to be converted by initializing the status variable of the |
| watchdog_device like this:: |
| |
| .status = WATCHDOG_NOWAYOUT_INIT_STATUS, |
| |
| Most drivers, however, also allow runtime configuration of nowayout, usually |
| by adding a module parameter. The conversion for this would be something like:: |
| |
| watchdog_set_nowayout(&s3c2410_wdd, nowayout); |
| |
| The module parameter itself needs to stay, everything else related to nowayout |
| can go, though. This will likely be some code in open(), close() or write(). |
| |
| |
| 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/process/submitting-patches.rst and send your patch to |
| linux-watchdog@vger.kernel.org. We are looking forward to it :) |