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