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