162306a36Sopenharmony_ci========================================================= 262306a36Sopenharmony_ciConverting old watchdog drivers to the watchdog framework 362306a36Sopenharmony_ci========================================================= 462306a36Sopenharmony_ci 562306a36Sopenharmony_ciby Wolfram Sang <wsa@kernel.org> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ciBefore the watchdog framework came into the kernel, every driver had to 862306a36Sopenharmony_ciimplement the API on its own. Now, as the framework factored out the common 962306a36Sopenharmony_cicomponents, those drivers can be lightened making it a user of the framework. 1062306a36Sopenharmony_ciThis document shall guide you for this task. The necessary steps are described 1162306a36Sopenharmony_cias well as things to look out for. 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ciRemove the file_operations struct 1562306a36Sopenharmony_ci--------------------------------- 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ciOld drivers define their own file_operations for actions like open(), write(), 1862306a36Sopenharmony_cietc... These are now handled by the framework and just call the driver when 1962306a36Sopenharmony_cineeded. So, in general, the 'file_operations' struct and assorted functions can 2062306a36Sopenharmony_cigo. Only very few driver-specific details have to be moved to other functions. 2162306a36Sopenharmony_ciHere is a overview of the functions and probably needed actions: 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci- open: Everything dealing with resource management (file-open checks, magic 2462306a36Sopenharmony_ci close preparations) can simply go. Device specific stuff needs to go to the 2562306a36Sopenharmony_ci driver specific start-function. Note that for some drivers, the start-function 2662306a36Sopenharmony_ci also serves as the ping-function. If that is the case and you need start/stop 2762306a36Sopenharmony_ci to be balanced (clocks!), you are better off refactoring a separate start-function. 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci- close: Same hints as for open apply. 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci- write: Can simply go, all defined behaviour is taken care of by the framework, 3262306a36Sopenharmony_ci i.e. ping on write and magic char ('V') handling. 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci- ioctl: While the driver is allowed to have extensions to the IOCTL interface, 3562306a36Sopenharmony_ci the most common ones are handled by the framework, supported by some assistance 3662306a36Sopenharmony_ci from the driver: 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci WDIOC_GETSUPPORT: 3962306a36Sopenharmony_ci Returns the mandatory watchdog_info struct from the driver 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci WDIOC_GETSTATUS: 4262306a36Sopenharmony_ci Needs the status-callback defined, otherwise returns 0 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci WDIOC_GETBOOTSTATUS: 4562306a36Sopenharmony_ci Needs the bootstatus member properly set. Make sure it is 0 if you 4662306a36Sopenharmony_ci don't have further support! 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci WDIOC_SETOPTIONS: 4962306a36Sopenharmony_ci No preparations needed 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci WDIOC_KEEPALIVE: 5262306a36Sopenharmony_ci If wanted, options in watchdog_info need to have WDIOF_KEEPALIVEPING 5362306a36Sopenharmony_ci set 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci WDIOC_SETTIMEOUT: 5662306a36Sopenharmony_ci Options in watchdog_info need to have WDIOF_SETTIMEOUT set 5762306a36Sopenharmony_ci and a set_timeout-callback has to be defined. The core will also 5862306a36Sopenharmony_ci do limit-checking, if min_timeout and max_timeout in the watchdog 5962306a36Sopenharmony_ci device are set. All is optional. 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci WDIOC_GETTIMEOUT: 6262306a36Sopenharmony_ci No preparations needed 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci WDIOC_GETTIMELEFT: 6562306a36Sopenharmony_ci It needs get_timeleft() callback to be defined. Otherwise it 6662306a36Sopenharmony_ci will return EOPNOTSUPP 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci Other IOCTLs can be served using the ioctl-callback. Note that this is mainly 6962306a36Sopenharmony_ci intended for porting old drivers; new drivers should not invent private IOCTLs. 7062306a36Sopenharmony_ci Private IOCTLs are processed first. When the callback returns with 7162306a36Sopenharmony_ci -ENOIOCTLCMD, the IOCTLs of the framework will be tried, too. Any other error 7262306a36Sopenharmony_ci is directly given to the user. 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ciExample conversion:: 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci -static const struct file_operations s3c2410wdt_fops = { 7762306a36Sopenharmony_ci - .owner = THIS_MODULE, 7862306a36Sopenharmony_ci - .llseek = no_llseek, 7962306a36Sopenharmony_ci - .write = s3c2410wdt_write, 8062306a36Sopenharmony_ci - .unlocked_ioctl = s3c2410wdt_ioctl, 8162306a36Sopenharmony_ci - .open = s3c2410wdt_open, 8262306a36Sopenharmony_ci - .release = s3c2410wdt_release, 8362306a36Sopenharmony_ci -}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ciCheck the functions for device-specific stuff and keep it for later 8662306a36Sopenharmony_cirefactoring. The rest can go. 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ciRemove the miscdevice 9062306a36Sopenharmony_ci--------------------- 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ciSince the file_operations are gone now, you can also remove the 'struct 9362306a36Sopenharmony_cimiscdevice'. The framework will create it on watchdog_dev_register() called by 9462306a36Sopenharmony_ciwatchdog_register_device():: 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci -static struct miscdevice s3c2410wdt_miscdev = { 9762306a36Sopenharmony_ci - .minor = WATCHDOG_MINOR, 9862306a36Sopenharmony_ci - .name = "watchdog", 9962306a36Sopenharmony_ci - .fops = &s3c2410wdt_fops, 10062306a36Sopenharmony_ci -}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ciRemove obsolete includes and defines 10462306a36Sopenharmony_ci------------------------------------ 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ciBecause of the simplifications, a few defines are probably unused now. Remove 10762306a36Sopenharmony_cithem. Includes can be removed, too. For example:: 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci - #include <linux/fs.h> 11062306a36Sopenharmony_ci - #include <linux/miscdevice.h> (if MODULE_ALIAS_MISCDEV is not used) 11162306a36Sopenharmony_ci - #include <linux/uaccess.h> (if no custom IOCTLs are used) 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ciAdd the watchdog operations 11562306a36Sopenharmony_ci--------------------------- 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ciAll possible callbacks are defined in 'struct watchdog_ops'. You can find it 11862306a36Sopenharmony_ciexplained in 'watchdog-kernel-api.txt' in this directory. start() and 11962306a36Sopenharmony_ciowner must be set, the rest are optional. You will easily find corresponding 12062306a36Sopenharmony_cifunctions in the old driver. Note that you will now get a pointer to the 12162306a36Sopenharmony_ciwatchdog_device as a parameter to these functions, so you probably have to 12262306a36Sopenharmony_cichange the function header. Other changes are most likely not needed, because 12362306a36Sopenharmony_cihere simply happens the direct hardware access. If you have device-specific 12462306a36Sopenharmony_cicode left from the above steps, it should be refactored into these callbacks. 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ciHere is a simple example:: 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci +static struct watchdog_ops s3c2410wdt_ops = { 12962306a36Sopenharmony_ci + .owner = THIS_MODULE, 13062306a36Sopenharmony_ci + .start = s3c2410wdt_start, 13162306a36Sopenharmony_ci + .stop = s3c2410wdt_stop, 13262306a36Sopenharmony_ci + .ping = s3c2410wdt_keepalive, 13362306a36Sopenharmony_ci + .set_timeout = s3c2410wdt_set_heartbeat, 13462306a36Sopenharmony_ci +}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ciA typical function-header change looks like:: 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci -static void s3c2410wdt_keepalive(void) 13962306a36Sopenharmony_ci +static int s3c2410wdt_keepalive(struct watchdog_device *wdd) 14062306a36Sopenharmony_ci { 14162306a36Sopenharmony_ci ... 14262306a36Sopenharmony_ci + 14362306a36Sopenharmony_ci + return 0; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci ... 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci - s3c2410wdt_keepalive(); 14962306a36Sopenharmony_ci + s3c2410wdt_keepalive(&s3c2410_wdd); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ciAdd the watchdog device 15362306a36Sopenharmony_ci----------------------- 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ciNow we need to create a 'struct watchdog_device' and populate it with the 15662306a36Sopenharmony_cinecessary information for the framework. The struct is also explained in detail 15762306a36Sopenharmony_ciin 'watchdog-kernel-api.txt' in this directory. We pass it the mandatory 15862306a36Sopenharmony_ciwatchdog_info struct and the newly created watchdog_ops. Often, old drivers 15962306a36Sopenharmony_cihave their own record-keeping for things like bootstatus and timeout using 16062306a36Sopenharmony_cistatic variables. Those have to be converted to use the members in 16162306a36Sopenharmony_ciwatchdog_device. Note that the timeout values are unsigned int. Some drivers 16262306a36Sopenharmony_ciuse signed int, so this has to be converted, too. 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ciHere is a simple example for a watchdog device:: 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci +static struct watchdog_device s3c2410_wdd = { 16762306a36Sopenharmony_ci + .info = &s3c2410_wdt_ident, 16862306a36Sopenharmony_ci + .ops = &s3c2410wdt_ops, 16962306a36Sopenharmony_ci +}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ciHandle the 'nowayout' feature 17362306a36Sopenharmony_ci----------------------------- 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ciA few drivers use nowayout statically, i.e. there is no module parameter for it 17662306a36Sopenharmony_ciand only CONFIG_WATCHDOG_NOWAYOUT determines if the feature is going to be 17762306a36Sopenharmony_ciused. This needs to be converted by initializing the status variable of the 17862306a36Sopenharmony_ciwatchdog_device like this:: 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci .status = WATCHDOG_NOWAYOUT_INIT_STATUS, 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ciMost drivers, however, also allow runtime configuration of nowayout, usually 18362306a36Sopenharmony_ciby adding a module parameter. The conversion for this would be something like:: 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci watchdog_set_nowayout(&s3c2410_wdd, nowayout); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ciThe module parameter itself needs to stay, everything else related to nowayout 18862306a36Sopenharmony_cican go, though. This will likely be some code in open(), close() or write(). 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ciRegister the watchdog device 19262306a36Sopenharmony_ci---------------------------- 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ciReplace misc_register(&miscdev) with watchdog_register_device(&watchdog_dev). 19562306a36Sopenharmony_ciMake sure the return value gets checked and the error message, if present, 19662306a36Sopenharmony_cistill fits. Also convert the unregister case:: 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci - ret = misc_register(&s3c2410wdt_miscdev); 19962306a36Sopenharmony_ci + ret = watchdog_register_device(&s3c2410_wdd); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci ... 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci - misc_deregister(&s3c2410wdt_miscdev); 20462306a36Sopenharmony_ci + watchdog_unregister_device(&s3c2410_wdd); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ciUpdate the Kconfig-entry 20862306a36Sopenharmony_ci------------------------ 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ciThe entry for the driver now needs to select WATCHDOG_CORE: 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci + select WATCHDOG_CORE 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ciCreate a patch and send it to upstream 21662306a36Sopenharmony_ci-------------------------------------- 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ciMake sure you understood Documentation/process/submitting-patches.rst and send your patch to 21962306a36Sopenharmony_cilinux-watchdog@vger.kernel.org. We are looking forward to it :) 220