mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	The old one is defunct. However, I think it makes sense that I am still the primary contact person for updates here. Signed-off-by: Wolfram Sang <wsa@kernel.org> Link: https://lore.kernel.org/r/20200502143103.19473-1-wsa@kernel.org Signed-off-by: Jonathan Corbet <corbet@lwn.net>
		
			
				
	
	
		
			219 lines
		
	
	
	
		
			7.6 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			219 lines
		
	
	
	
		
			7.6 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
=========================================================
 | 
						|
Converting old watchdog drivers to the watchdog framework
 | 
						|
=========================================================
 | 
						|
 | 
						|
by Wolfram Sang <wsa@kernel.org>
 | 
						|
 | 
						|
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
 | 
						|
linux-watchdog@vger.kernel.org. We are looking forward to it :)
 |