Wolfram Sang | 74cd4c6 | 2011-09-26 15:40:13 +0200 | [diff] [blame] | 1 | Converting old watchdog drivers to the watchdog framework |
| 2 | by Wolfram Sang <w.sang@pengutronix.de> |
| 3 | ========================================================= |
| 4 | |
| 5 | Before the watchdog framework came into the kernel, every driver had to |
| 6 | implement the API on its own. Now, as the framework factored out the common |
| 7 | components, those drivers can be lightened making it a user of the framework. |
| 8 | This document shall guide you for this task. The necessary steps are described |
| 9 | as well as things to look out for. |
| 10 | |
| 11 | |
| 12 | Remove the file_operations struct |
| 13 | --------------------------------- |
| 14 | |
| 15 | Old drivers define their own file_operations for actions like open(), write(), |
| 16 | etc... These are now handled by the framework and just call the driver when |
| 17 | needed. So, in general, the 'file_operations' struct and assorted functions can |
| 18 | go. Only very few driver-specific details have to be moved to other functions. |
| 19 | Here is a overview of the functions and probably needed actions: |
| 20 | |
| 21 | - open: Everything dealing with resource management (file-open checks, magic |
| 22 | close preparations) can simply go. Device specific stuff needs to go to the |
| 23 | driver specific start-function. Note that for some drivers, the start-function |
| 24 | also serves as the ping-function. If that is the case and you need start/stop |
| 25 | to be balanced (clocks!), you are better off refactoring a separate start-function. |
| 26 | |
| 27 | - close: Same hints as for open apply. |
| 28 | |
| 29 | - write: Can simply go, all defined behaviour is taken care of by the framework, |
| 30 | i.e. ping on write and magic char ('V') handling. |
| 31 | |
| 32 | - ioctl: While the driver is allowed to have extensions to the IOCTL interface, |
| 33 | the most common ones are handled by the framework, supported by some assistance |
| 34 | from the driver: |
| 35 | |
| 36 | WDIOC_GETSUPPORT: |
| 37 | Returns the mandatory watchdog_info struct from the driver |
| 38 | |
| 39 | WDIOC_GETSTATUS: |
| 40 | Needs the status-callback defined, otherwise returns 0 |
| 41 | |
| 42 | WDIOC_GETBOOTSTATUS: |
| 43 | Needs the bootstatus member properly set. Make sure it is 0 if you |
| 44 | don't have further support! |
| 45 | |
| 46 | WDIOC_SETOPTIONS: |
| 47 | No preparations needed |
| 48 | |
| 49 | WDIOC_KEEPALIVE: |
| 50 | If wanted, options in watchdog_info need to have WDIOF_KEEPALIVEPING |
| 51 | set |
| 52 | |
| 53 | WDIOC_SETTIMEOUT: |
| 54 | Options in watchdog_info need to have WDIOF_SETTIMEOUT set |
| 55 | and a set_timeout-callback has to be defined. The core will also |
| 56 | do limit-checking, if min_timeout and max_timeout in the watchdog |
| 57 | device are set. All is optional. |
| 58 | |
| 59 | WDIOC_GETTIMEOUT: |
| 60 | No preparations needed |
| 61 | |
Viresh Kumar | fd7b673 | 2012-03-16 09:14:00 +0100 | [diff] [blame] | 62 | WDIOC_GETTIMELEFT: |
| 63 | It needs get_timeleft() callback to be defined. Otherwise it |
| 64 | will return EOPNOTSUPP |
| 65 | |
Wolfram Sang | 74cd4c6 | 2011-09-26 15:40:13 +0200 | [diff] [blame] | 66 | Other IOCTLs can be served using the ioctl-callback. Note that this is mainly |
| 67 | intended for porting old drivers; new drivers should not invent private IOCTLs. |
| 68 | Private IOCTLs are processed first. When the callback returns with |
| 69 | -ENOIOCTLCMD, the IOCTLs of the framework will be tried, too. Any other error |
| 70 | is directly given to the user. |
| 71 | |
| 72 | Example conversion: |
| 73 | |
| 74 | -static const struct file_operations s3c2410wdt_fops = { |
| 75 | - .owner = THIS_MODULE, |
| 76 | - .llseek = no_llseek, |
| 77 | - .write = s3c2410wdt_write, |
| 78 | - .unlocked_ioctl = s3c2410wdt_ioctl, |
| 79 | - .open = s3c2410wdt_open, |
| 80 | - .release = s3c2410wdt_release, |
| 81 | -}; |
| 82 | |
| 83 | Check the functions for device-specific stuff and keep it for later |
| 84 | refactoring. The rest can go. |
| 85 | |
| 86 | |
| 87 | Remove the miscdevice |
| 88 | --------------------- |
| 89 | |
| 90 | Since the file_operations are gone now, you can also remove the 'struct |
| 91 | miscdevice'. The framework will create it on watchdog_dev_register() called by |
| 92 | watchdog_register_device(). |
| 93 | |
| 94 | -static struct miscdevice s3c2410wdt_miscdev = { |
| 95 | - .minor = WATCHDOG_MINOR, |
| 96 | - .name = "watchdog", |
| 97 | - .fops = &s3c2410wdt_fops, |
| 98 | -}; |
| 99 | |
| 100 | |
| 101 | Remove obsolete includes and defines |
| 102 | ------------------------------------ |
| 103 | |
| 104 | Because of the simplifications, a few defines are probably unused now. Remove |
| 105 | them. Includes can be removed, too. For example: |
| 106 | |
| 107 | - #include <linux/fs.h> |
| 108 | - #include <linux/miscdevice.h> (if MODULE_ALIAS_MISCDEV is not used) |
| 109 | - #include <linux/uaccess.h> (if no custom IOCTLs are used) |
| 110 | |
| 111 | |
| 112 | Add the watchdog operations |
| 113 | --------------------------- |
| 114 | |
| 115 | All possible callbacks are defined in 'struct watchdog_ops'. You can find it |
| 116 | explained in 'watchdog-kernel-api.txt' in this directory. start(), stop() and |
| 117 | owner must be set, the rest are optional. You will easily find corresponding |
| 118 | functions in the old driver. Note that you will now get a pointer to the |
| 119 | watchdog_device as a parameter to these functions, so you probably have to |
| 120 | change the function header. Other changes are most likely not needed, because |
| 121 | here simply happens the direct hardware access. If you have device-specific |
| 122 | code left from the above steps, it should be refactored into these callbacks. |
| 123 | |
| 124 | Here is a simple example: |
| 125 | |
| 126 | +static struct watchdog_ops s3c2410wdt_ops = { |
| 127 | + .owner = THIS_MODULE, |
| 128 | + .start = s3c2410wdt_start, |
| 129 | + .stop = s3c2410wdt_stop, |
| 130 | + .ping = s3c2410wdt_keepalive, |
| 131 | + .set_timeout = s3c2410wdt_set_heartbeat, |
| 132 | +}; |
| 133 | |
| 134 | A typical function-header change looks like: |
| 135 | |
| 136 | -static void s3c2410wdt_keepalive(void) |
| 137 | +static int s3c2410wdt_keepalive(struct watchdog_device *wdd) |
| 138 | { |
| 139 | ... |
| 140 | + |
| 141 | + return 0; |
| 142 | } |
| 143 | |
| 144 | ... |
| 145 | |
| 146 | - s3c2410wdt_keepalive(); |
| 147 | + s3c2410wdt_keepalive(&s3c2410_wdd); |
| 148 | |
| 149 | |
| 150 | Add the watchdog device |
| 151 | ----------------------- |
| 152 | |
| 153 | Now we need to create a 'struct watchdog_device' and populate it with the |
| 154 | necessary information for the framework. The struct is also explained in detail |
| 155 | in 'watchdog-kernel-api.txt' in this directory. We pass it the mandatory |
| 156 | watchdog_info struct and the newly created watchdog_ops. Often, old drivers |
| 157 | have their own record-keeping for things like bootstatus and timeout using |
| 158 | static variables. Those have to be converted to use the members in |
| 159 | watchdog_device. Note that the timeout values are unsigned int. Some drivers |
| 160 | use signed int, so this has to be converted, too. |
| 161 | |
| 162 | Here is a simple example for a watchdog device: |
| 163 | |
| 164 | +static struct watchdog_device s3c2410_wdd = { |
| 165 | + .info = &s3c2410_wdt_ident, |
| 166 | + .ops = &s3c2410wdt_ops, |
| 167 | +}; |
| 168 | |
| 169 | |
Wolfram Sang | 02861cc | 2011-12-02 00:43:11 +0100 | [diff] [blame] | 170 | Handle the 'nowayout' feature |
| 171 | ----------------------------- |
| 172 | |
| 173 | A few drivers use nowayout statically, i.e. there is no module parameter for it |
| 174 | and only CONFIG_WATCHDOG_NOWAYOUT determines if the feature is going to be |
| 175 | used. This needs to be converted by initializing the status variable of the |
| 176 | watchdog_device like this: |
| 177 | |
| 178 | .status = WATCHDOG_NOWAYOUT_INIT_STATUS, |
| 179 | |
| 180 | Most drivers, however, also allow runtime configuration of nowayout, usually |
| 181 | by adding a module parameter. The conversion for this would be something like: |
| 182 | |
| 183 | watchdog_set_nowayout(&s3c2410_wdd, nowayout); |
| 184 | |
| 185 | The module parameter itself needs to stay, everything else related to nowayout |
| 186 | can go, though. This will likely be some code in open(), close() or write(). |
| 187 | |
| 188 | |
Wolfram Sang | 74cd4c6 | 2011-09-26 15:40:13 +0200 | [diff] [blame] | 189 | Register the watchdog device |
| 190 | ---------------------------- |
| 191 | |
| 192 | Replace misc_register(&miscdev) with watchdog_register_device(&watchdog_dev). |
| 193 | Make sure the return value gets checked and the error message, if present, |
| 194 | still fits. Also convert the unregister case. |
| 195 | |
| 196 | - ret = misc_register(&s3c2410wdt_miscdev); |
| 197 | + ret = watchdog_register_device(&s3c2410_wdd); |
| 198 | |
| 199 | ... |
| 200 | |
| 201 | - misc_deregister(&s3c2410wdt_miscdev); |
| 202 | + watchdog_unregister_device(&s3c2410_wdd); |
| 203 | |
| 204 | |
| 205 | Update the Kconfig-entry |
| 206 | ------------------------ |
| 207 | |
| 208 | The entry for the driver now needs to select WATCHDOG_CORE: |
| 209 | |
| 210 | + select WATCHDOG_CORE |
| 211 | |
| 212 | |
| 213 | Create a patch and send it to upstream |
| 214 | -------------------------------------- |
| 215 | |
| 216 | Make sure you understood Documentation/SubmittingPatches and send your patch to |
| 217 | linux-watchdog@vger.kernel.org. We are looking forward to it :) |
| 218 | |