xref: /OK3568_Linux_fs/kernel/Documentation/watchdog/convert_drivers_to_kernel_api.rst (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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