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