forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
219 lines
7.6 KiB
219 lines
7.6 KiB
========================================================= |
|
Converting old watchdog drivers to the watchdog framework |
|
========================================================= |
|
|
|
by Wolfram Sang <[email protected]> |
|
|
|
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 |
|
[email protected]. We are looking forward to it :)
|
|
|