Commit 8e48aec714f1faf581949f23ae0e3d6e2317433b
1 parent
3d824a46b7
[OPENPROM]: Rewrite driver to use in-kernel device tree.
Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 1 changed file with 336 additions and 257 deletions Side-by-side Diff
drivers/sbus/char/openprom.c
... | ... | @@ -29,8 +29,6 @@ |
29 | 29 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
30 | 30 | */ |
31 | 31 | |
32 | -#define PROMLIB_INTERNAL | |
33 | - | |
34 | 32 | #include <linux/config.h> |
35 | 33 | #include <linux/module.h> |
36 | 34 | #include <linux/kernel.h> |
37 | 35 | |
... | ... | @@ -39,10 +37,10 @@ |
39 | 37 | #include <linux/slab.h> |
40 | 38 | #include <linux/string.h> |
41 | 39 | #include <linux/miscdevice.h> |
42 | -#include <linux/smp_lock.h> | |
43 | 40 | #include <linux/init.h> |
44 | 41 | #include <linux/fs.h> |
45 | 42 | #include <asm/oplib.h> |
43 | +#include <asm/prom.h> | |
46 | 44 | #include <asm/system.h> |
47 | 45 | #include <asm/uaccess.h> |
48 | 46 | #include <asm/openpromio.h> |
49 | 47 | |
50 | 48 | |
... | ... | @@ -51,15 +49,20 @@ |
51 | 49 | #include <asm/pbm.h> |
52 | 50 | #endif |
53 | 51 | |
52 | +MODULE_AUTHOR("Thomas K. Dyas (tdyas@noc.rutgers.edu) and Eddie C. Dost (ecd@skynet.be)"); | |
53 | +MODULE_DESCRIPTION("OPENPROM Configuration Driver"); | |
54 | +MODULE_LICENSE("GPL"); | |
55 | +MODULE_VERSION("1.0"); | |
56 | + | |
54 | 57 | /* Private data kept by the driver for each descriptor. */ |
55 | 58 | typedef struct openprom_private_data |
56 | 59 | { |
57 | - int current_node; /* Current node for SunOS ioctls. */ | |
58 | - int lastnode; /* Last valid node used by BSD ioctls. */ | |
60 | + struct device_node *current_node; /* Current node for SunOS ioctls. */ | |
61 | + struct device_node *lastnode; /* Last valid node used by BSD ioctls. */ | |
59 | 62 | } DATA; |
60 | 63 | |
61 | 64 | /* ID of the PROM node containing all of the EEPROM options. */ |
62 | -static int options_node = 0; | |
65 | +static struct device_node *options_node; | |
63 | 66 | |
64 | 67 | /* |
65 | 68 | * Copy an openpromio structure into kernel space from user space. |
66 | 69 | |
... | ... | @@ -87,9 +90,8 @@ |
87 | 90 | if (bufsize > OPROMMAXPARAM) |
88 | 91 | bufsize = OPROMMAXPARAM; |
89 | 92 | |
90 | - if (!(*opp_p = kmalloc(sizeof(int) + bufsize + 1, GFP_KERNEL))) | |
93 | + if (!(*opp_p = kzalloc(sizeof(int) + bufsize + 1, GFP_KERNEL))) | |
91 | 94 | return -ENOMEM; |
92 | - memset(*opp_p, 0, sizeof(int) + bufsize + 1); | |
93 | 95 | |
94 | 96 | if (copy_from_user(&(*opp_p)->oprom_array, |
95 | 97 | &info->oprom_array, bufsize)) { |
96 | 98 | |
... | ... | @@ -107,10 +109,9 @@ |
107 | 109 | if (!info || !opp_p) |
108 | 110 | return -EFAULT; |
109 | 111 | |
110 | - if (!(*opp_p = kmalloc(sizeof(int) + OPROMMAXPARAM + 1, GFP_KERNEL))) | |
112 | + if (!(*opp_p = kzalloc(sizeof(int) + OPROMMAXPARAM + 1, GFP_KERNEL))) | |
111 | 113 | return -ENOMEM; |
112 | 114 | |
113 | - memset(*opp_p, 0, sizeof(int) + OPROMMAXPARAM + 1); | |
114 | 115 | (*opp_p)->oprom_size = 0; |
115 | 116 | |
116 | 117 | n = bufsize = 0; |
117 | 118 | |
118 | 119 | |
119 | 120 | |
... | ... | @@ -140,16 +141,164 @@ |
140 | 141 | return 0; |
141 | 142 | } |
142 | 143 | |
144 | +static int opromgetprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize) | |
145 | +{ | |
146 | + void *pval; | |
147 | + int len; | |
148 | + | |
149 | + pval = of_get_property(dp, op->oprom_array, &len); | |
150 | + if (!pval || len <= 0 || len > bufsize) | |
151 | + return copyout(argp, op, sizeof(int)); | |
152 | + | |
153 | + memcpy(op->oprom_array, pval, len); | |
154 | + op->oprom_array[len] = '\0'; | |
155 | + op->oprom_size = len; | |
156 | + | |
157 | + return copyout(argp, op, sizeof(int) + bufsize); | |
158 | +} | |
159 | + | |
160 | +static int opromnxtprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize) | |
161 | +{ | |
162 | + struct property *prop; | |
163 | + int len; | |
164 | + | |
165 | + if (op->oprom_array[0] == '\0') { | |
166 | + prop = dp->properties; | |
167 | + if (!prop) | |
168 | + return copyout(argp, op, sizeof(int)); | |
169 | + len = strlen(prop->name); | |
170 | + } else { | |
171 | + prop = of_find_property(dp, op->oprom_array, NULL); | |
172 | + | |
173 | + if (!prop || | |
174 | + !prop->next || | |
175 | + (len = strlen(prop->next->name)) + 1 > bufsize) | |
176 | + return copyout(argp, op, sizeof(int)); | |
177 | + | |
178 | + prop = prop->next; | |
179 | + } | |
180 | + | |
181 | + memcpy(op->oprom_array, prop->name, len); | |
182 | + op->oprom_array[len] = '\0'; | |
183 | + op->oprom_size = ++len; | |
184 | + | |
185 | + return copyout(argp, op, sizeof(int) + bufsize); | |
186 | +} | |
187 | + | |
188 | +static int opromsetopt(struct device_node *dp, struct openpromio *op, int bufsize) | |
189 | +{ | |
190 | + char *buf = op->oprom_array + strlen(op->oprom_array) + 1; | |
191 | + int len = op->oprom_array + bufsize - buf; | |
192 | + | |
193 | + return of_set_property(options_node, op->oprom_array, buf, len); | |
194 | +} | |
195 | + | |
196 | +static int opromnext(void __user *argp, unsigned int cmd, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data) | |
197 | +{ | |
198 | + phandle ph; | |
199 | + | |
200 | + BUILD_BUG_ON(sizeof(phandle) != sizeof(int)); | |
201 | + | |
202 | + if (bufsize < sizeof(phandle)) | |
203 | + return -EINVAL; | |
204 | + | |
205 | + ph = *((int *) op->oprom_array); | |
206 | + if (ph) { | |
207 | + dp = of_find_node_by_phandle(ph); | |
208 | + if (!dp) | |
209 | + return -EINVAL; | |
210 | + | |
211 | + switch (cmd) { | |
212 | + case OPROMNEXT: | |
213 | + dp = dp->sibling; | |
214 | + break; | |
215 | + | |
216 | + case OPROMCHILD: | |
217 | + dp = dp->child; | |
218 | + break; | |
219 | + | |
220 | + case OPROMSETCUR: | |
221 | + default: | |
222 | + break; | |
223 | + }; | |
224 | + } else { | |
225 | + /* Sibling of node zero is the root node. */ | |
226 | + if (cmd != OPROMNEXT) | |
227 | + return -EINVAL; | |
228 | + | |
229 | + dp = of_find_node_by_path("/"); | |
230 | + } | |
231 | + | |
232 | + ph = 0; | |
233 | + if (dp) | |
234 | + ph = dp->node; | |
235 | + | |
236 | + data->current_node = dp; | |
237 | + *((int *) op->oprom_array) = ph; | |
238 | + op->oprom_size = sizeof(phandle); | |
239 | + | |
240 | + return copyout(argp, op, bufsize + sizeof(int)); | |
241 | +} | |
242 | + | |
243 | +static int oprompci2node(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data) | |
244 | +{ | |
245 | + int err = -EINVAL; | |
246 | + | |
247 | + if (bufsize >= 2*sizeof(int)) { | |
248 | +#ifdef CONFIG_PCI | |
249 | + struct pci_dev *pdev; | |
250 | + struct pcidev_cookie *pcp; | |
251 | + pdev = pci_find_slot (((int *) op->oprom_array)[0], | |
252 | + ((int *) op->oprom_array)[1]); | |
253 | + | |
254 | + pcp = pdev->sysdata; | |
255 | + if (pcp != NULL) { | |
256 | + dp = pcp->prom_node; | |
257 | + data->current_node = dp; | |
258 | + *((int *)op->oprom_array) = dp->node; | |
259 | + op->oprom_size = sizeof(int); | |
260 | + err = copyout(argp, op, bufsize + sizeof(int)); | |
261 | + } | |
262 | +#endif | |
263 | + } | |
264 | + | |
265 | + return err; | |
266 | +} | |
267 | + | |
268 | +static int oprompath2node(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data) | |
269 | +{ | |
270 | + dp = of_find_node_by_path(op->oprom_array); | |
271 | + data->current_node = dp; | |
272 | + *((int *)op->oprom_array) = dp->node; | |
273 | + op->oprom_size = sizeof(int); | |
274 | + | |
275 | + return copyout(argp, op, bufsize + sizeof(int)); | |
276 | +} | |
277 | + | |
278 | +static int opromgetbootargs(void __user *argp, struct openpromio *op, int bufsize) | |
279 | +{ | |
280 | + char *buf = saved_command_line; | |
281 | + int len = strlen(buf); | |
282 | + | |
283 | + if (len > bufsize) | |
284 | + return -EINVAL; | |
285 | + | |
286 | + strcpy(op->oprom_array, buf); | |
287 | + op->oprom_size = len; | |
288 | + | |
289 | + return copyout(argp, op, bufsize + sizeof(int)); | |
290 | +} | |
291 | + | |
143 | 292 | /* |
144 | 293 | * SunOS and Solaris /dev/openprom ioctl calls. |
145 | 294 | */ |
146 | 295 | static int openprom_sunos_ioctl(struct inode * inode, struct file * file, |
147 | - unsigned int cmd, unsigned long arg, int node) | |
296 | + unsigned int cmd, unsigned long arg, | |
297 | + struct device_node *dp) | |
148 | 298 | { |
149 | - DATA *data = (DATA *) file->private_data; | |
150 | - char buffer[OPROMMAXPARAM+1], *buf; | |
299 | + DATA *data = file->private_data; | |
151 | 300 | struct openpromio *opp; |
152 | - int bufsize, len, error = 0; | |
301 | + int bufsize, error = 0; | |
153 | 302 | static int cnt; |
154 | 303 | void __user *argp = (void __user *)arg; |
155 | 304 | |
156 | 305 | |
157 | 306 | |
158 | 307 | |
159 | 308 | |
160 | 309 | |
161 | 310 | |
... | ... | @@ -164,119 +313,35 @@ |
164 | 313 | switch (cmd) { |
165 | 314 | case OPROMGETOPT: |
166 | 315 | case OPROMGETPROP: |
167 | - len = prom_getproplen(node, opp->oprom_array); | |
168 | - | |
169 | - if (len <= 0 || len > bufsize) { | |
170 | - error = copyout(argp, opp, sizeof(int)); | |
171 | - break; | |
172 | - } | |
173 | - | |
174 | - len = prom_getproperty(node, opp->oprom_array, buffer, bufsize); | |
175 | - | |
176 | - memcpy(opp->oprom_array, buffer, len); | |
177 | - opp->oprom_array[len] = '\0'; | |
178 | - opp->oprom_size = len; | |
179 | - | |
180 | - error = copyout(argp, opp, sizeof(int) + bufsize); | |
316 | + error = opromgetprop(argp, dp, opp, bufsize); | |
181 | 317 | break; |
182 | 318 | |
183 | 319 | case OPROMNXTOPT: |
184 | 320 | case OPROMNXTPROP: |
185 | - buf = prom_nextprop(node, opp->oprom_array, buffer); | |
186 | - | |
187 | - len = strlen(buf); | |
188 | - if (len == 0 || len + 1 > bufsize) { | |
189 | - error = copyout(argp, opp, sizeof(int)); | |
190 | - break; | |
191 | - } | |
192 | - | |
193 | - memcpy(opp->oprom_array, buf, len); | |
194 | - opp->oprom_array[len] = '\0'; | |
195 | - opp->oprom_size = ++len; | |
196 | - | |
197 | - error = copyout(argp, opp, sizeof(int) + bufsize); | |
321 | + error = opromnxtprop(argp, dp, opp, bufsize); | |
198 | 322 | break; |
199 | 323 | |
200 | 324 | case OPROMSETOPT: |
201 | 325 | case OPROMSETOPT2: |
202 | - buf = opp->oprom_array + strlen(opp->oprom_array) + 1; | |
203 | - len = opp->oprom_array + bufsize - buf; | |
204 | - | |
205 | - error = prom_setprop(options_node, opp->oprom_array, | |
206 | - buf, len); | |
207 | - | |
208 | - if (error < 0) | |
209 | - error = -EINVAL; | |
326 | + error = opromsetopt(dp, opp, bufsize); | |
210 | 327 | break; |
211 | 328 | |
212 | 329 | case OPROMNEXT: |
213 | 330 | case OPROMCHILD: |
214 | 331 | case OPROMSETCUR: |
215 | - if (bufsize < sizeof(int)) { | |
216 | - error = -EINVAL; | |
217 | - break; | |
218 | - } | |
219 | - | |
220 | - node = *((int *) opp->oprom_array); | |
221 | - | |
222 | - switch (cmd) { | |
223 | - case OPROMNEXT: node = __prom_getsibling(node); break; | |
224 | - case OPROMCHILD: node = __prom_getchild(node); break; | |
225 | - case OPROMSETCUR: break; | |
226 | - } | |
227 | - | |
228 | - data->current_node = node; | |
229 | - *((int *)opp->oprom_array) = node; | |
230 | - opp->oprom_size = sizeof(int); | |
231 | - | |
232 | - error = copyout(argp, opp, bufsize + sizeof(int)); | |
332 | + error = opromnext(argp, cmd, dp, opp, bufsize, data); | |
233 | 333 | break; |
234 | 334 | |
235 | 335 | case OPROMPCI2NODE: |
236 | - error = -EINVAL; | |
237 | - | |
238 | - if (bufsize >= 2*sizeof(int)) { | |
239 | -#ifdef CONFIG_PCI | |
240 | - struct pci_dev *pdev; | |
241 | - struct pcidev_cookie *pcp; | |
242 | - pdev = pci_find_slot (((int *) opp->oprom_array)[0], | |
243 | - ((int *) opp->oprom_array)[1]); | |
244 | - | |
245 | - pcp = pdev->sysdata; | |
246 | - if (pcp != NULL) { | |
247 | - node = pcp->prom_node->node; | |
248 | - data->current_node = node; | |
249 | - *((int *)opp->oprom_array) = node; | |
250 | - opp->oprom_size = sizeof(int); | |
251 | - error = copyout(argp, opp, bufsize + sizeof(int)); | |
252 | - } | |
253 | -#endif | |
254 | - } | |
336 | + error = oprompci2node(argp, dp, opp, bufsize, data); | |
255 | 337 | break; |
256 | 338 | |
257 | 339 | case OPROMPATH2NODE: |
258 | - node = prom_finddevice(opp->oprom_array); | |
259 | - data->current_node = node; | |
260 | - *((int *)opp->oprom_array) = node; | |
261 | - opp->oprom_size = sizeof(int); | |
262 | - | |
263 | - error = copyout(argp, opp, bufsize + sizeof(int)); | |
340 | + error = oprompath2node(argp, dp, opp, bufsize, data); | |
264 | 341 | break; |
265 | 342 | |
266 | 343 | case OPROMGETBOOTARGS: |
267 | - buf = saved_command_line; | |
268 | - | |
269 | - len = strlen(buf); | |
270 | - | |
271 | - if (len > bufsize) { | |
272 | - error = -EINVAL; | |
273 | - break; | |
274 | - } | |
275 | - | |
276 | - strcpy(opp->oprom_array, buf); | |
277 | - opp->oprom_size = len; | |
278 | - | |
279 | - error = copyout(argp, opp, bufsize + sizeof(int)); | |
344 | + error = opromgetbootargs(argp, opp, bufsize); | |
280 | 345 | break; |
281 | 346 | |
282 | 347 | case OPROMU2P: |
283 | 348 | |
284 | 349 | |
... | ... | @@ -297,25 +362,14 @@ |
297 | 362 | return error; |
298 | 363 | } |
299 | 364 | |
300 | - | |
301 | -/* Return nonzero if a specific node is in the PROM device tree. */ | |
302 | -static int intree(int root, int node) | |
365 | +static struct device_node *get_node(phandle n, DATA *data) | |
303 | 366 | { |
304 | - for (; root != 0; root = prom_getsibling(root)) | |
305 | - if (root == node || intree(prom_getchild(root),node)) | |
306 | - return 1; | |
307 | - return 0; | |
308 | -} | |
367 | + struct device_node *dp = of_find_node_by_phandle(n); | |
309 | 368 | |
310 | -/* Return nonzero if a specific node is "valid". */ | |
311 | -static int goodnode(int n, DATA *data) | |
312 | -{ | |
313 | - if (n == data->lastnode || n == prom_root_node || n == options_node) | |
314 | - return 1; | |
315 | - if (n == 0 || n == -1 || !intree(prom_root_node,n)) | |
316 | - return 0; | |
317 | - data->lastnode = n; | |
318 | - return 1; | |
369 | + if (dp) | |
370 | + data->lastnode = dp; | |
371 | + | |
372 | + return dp; | |
319 | 373 | } |
320 | 374 | |
321 | 375 | /* Copy in a whole string from userspace into kernelspace. */ |
... | ... | @@ -330,7 +384,7 @@ |
330 | 384 | if (!tmp) |
331 | 385 | return -ENOMEM; |
332 | 386 | |
333 | - if(copy_from_user(tmp, user, len)) { | |
387 | + if (copy_from_user(tmp, user, len)) { | |
334 | 388 | kfree(tmp); |
335 | 389 | return -EFAULT; |
336 | 390 | } |
337 | 391 | |
338 | 392 | |
339 | 393 | |
340 | 394 | |
341 | 395 | |
342 | 396 | |
343 | 397 | |
344 | 398 | |
345 | 399 | |
346 | 400 | |
347 | 401 | |
348 | 402 | |
349 | 403 | |
350 | 404 | |
351 | 405 | |
352 | 406 | |
353 | 407 | |
354 | 408 | |
355 | 409 | |
356 | 410 | |
357 | 411 | |
358 | 412 | |
359 | 413 | |
360 | 414 | |
361 | 415 | |
362 | 416 | |
363 | 417 | |
364 | 418 | |
365 | 419 | |
366 | 420 | |
367 | 421 | |
368 | 422 | |
369 | 423 | |
370 | 424 | |
371 | 425 | |
372 | 426 | |
373 | 427 | |
374 | 428 | |
375 | 429 | |
376 | 430 | |
... | ... | @@ -345,162 +399,187 @@ |
345 | 399 | /* |
346 | 400 | * NetBSD /dev/openprom ioctl calls. |
347 | 401 | */ |
348 | -static int openprom_bsd_ioctl(struct inode * inode, struct file * file, | |
349 | - unsigned int cmd, unsigned long arg) | |
402 | +static int opiocget(void __user *argp, DATA *data) | |
350 | 403 | { |
351 | - DATA *data = (DATA *) file->private_data; | |
352 | - void __user *argp = (void __user *)arg; | |
353 | 404 | struct opiocdesc op; |
354 | - int error, node, len; | |
355 | - char *str, *tmp; | |
356 | - char buffer[64]; | |
357 | - static int cnt; | |
405 | + struct device_node *dp; | |
406 | + char *str; | |
407 | + void *pval; | |
408 | + int err, len; | |
358 | 409 | |
359 | - switch (cmd) { | |
360 | - case OPIOCGET: | |
361 | - if (copy_from_user(&op, argp, sizeof(op))) | |
362 | - return -EFAULT; | |
410 | + if (copy_from_user(&op, argp, sizeof(op))) | |
411 | + return -EFAULT; | |
363 | 412 | |
364 | - if (!goodnode(op.op_nodeid,data)) | |
365 | - return -EINVAL; | |
413 | + dp = get_node(op.op_nodeid, data); | |
366 | 414 | |
367 | - error = copyin_string(op.op_name, op.op_namelen, &str); | |
368 | - if (error) | |
369 | - return error; | |
415 | + err = copyin_string(op.op_name, op.op_namelen, &str); | |
416 | + if (err) | |
417 | + return err; | |
370 | 418 | |
371 | - len = prom_getproplen(op.op_nodeid,str); | |
372 | - | |
373 | - if (len > op.op_buflen) { | |
374 | - kfree(str); | |
375 | - return -ENOMEM; | |
376 | - } | |
377 | - | |
419 | + pval = of_get_property(dp, str, &len); | |
420 | + err = 0; | |
421 | + if (!pval || len > op.op_buflen) { | |
422 | + err = -EINVAL; | |
423 | + } else { | |
378 | 424 | op.op_buflen = len; |
425 | + if (copy_to_user(argp, &op, sizeof(op)) || | |
426 | + copy_to_user(op.op_buf, pval, len)) | |
427 | + err = -EFAULT; | |
428 | + } | |
429 | + kfree(str); | |
379 | 430 | |
380 | - if (len <= 0) { | |
381 | - kfree(str); | |
382 | - /* Verified by the above copy_from_user */ | |
383 | - if (__copy_to_user(argp, &op, | |
384 | - sizeof(op))) | |
385 | - return -EFAULT; | |
386 | - return 0; | |
387 | - } | |
431 | + return err; | |
432 | +} | |
388 | 433 | |
389 | - tmp = kmalloc(len + 1, GFP_KERNEL); | |
390 | - if (!tmp) { | |
391 | - kfree(str); | |
392 | - return -ENOMEM; | |
393 | - } | |
434 | +static int opiocnextprop(void __user *argp, DATA *data) | |
435 | +{ | |
436 | + struct opiocdesc op; | |
437 | + struct device_node *dp; | |
438 | + struct property *prop; | |
439 | + char *str; | |
440 | + int err, len; | |
394 | 441 | |
395 | - cnt = prom_getproperty(op.op_nodeid, str, tmp, len); | |
396 | - if (cnt <= 0) { | |
397 | - error = -EINVAL; | |
398 | - } else { | |
399 | - tmp[len] = '\0'; | |
442 | + if (copy_from_user(&op, argp, sizeof(op))) | |
443 | + return -EFAULT; | |
400 | 444 | |
401 | - if (__copy_to_user(argp, &op, sizeof(op)) != 0 || | |
402 | - copy_to_user(op.op_buf, tmp, len) != 0) | |
403 | - error = -EFAULT; | |
404 | - } | |
445 | + dp = get_node(op.op_nodeid, data); | |
446 | + if (!dp) | |
447 | + return -EINVAL; | |
405 | 448 | |
406 | - kfree(tmp); | |
407 | - kfree(str); | |
449 | + err = copyin_string(op.op_name, op.op_namelen, &str); | |
450 | + if (err) | |
451 | + return err; | |
408 | 452 | |
409 | - return error; | |
453 | + if (str[0] == '\0') { | |
454 | + prop = dp->properties; | |
455 | + } else { | |
456 | + prop = of_find_property(dp, str, NULL); | |
457 | + if (prop) | |
458 | + prop = prop->next; | |
459 | + } | |
460 | + kfree(str); | |
410 | 461 | |
411 | - case OPIOCNEXTPROP: | |
412 | - if (copy_from_user(&op, argp, sizeof(op))) | |
413 | - return -EFAULT; | |
462 | + if (!prop) | |
463 | + len = 0; | |
464 | + else | |
465 | + len = prop->length; | |
414 | 466 | |
415 | - if (!goodnode(op.op_nodeid,data)) | |
416 | - return -EINVAL; | |
467 | + if (len > op.op_buflen) | |
468 | + len = op.op_buflen; | |
417 | 469 | |
418 | - error = copyin_string(op.op_name, op.op_namelen, &str); | |
419 | - if (error) | |
420 | - return error; | |
470 | + if (copy_to_user(argp, &op, sizeof(op))) | |
471 | + return -EFAULT; | |
421 | 472 | |
422 | - tmp = prom_nextprop(op.op_nodeid,str,buffer); | |
473 | + if (len && | |
474 | + copy_to_user(op.op_buf, prop->value, len)) | |
475 | + return -EFAULT; | |
423 | 476 | |
424 | - if (tmp) { | |
425 | - len = strlen(tmp); | |
426 | - if (len > op.op_buflen) | |
427 | - len = op.op_buflen; | |
428 | - else | |
429 | - op.op_buflen = len; | |
430 | - } else { | |
431 | - len = op.op_buflen = 0; | |
432 | - } | |
477 | + return 0; | |
478 | +} | |
433 | 479 | |
434 | - if (!access_ok(VERIFY_WRITE, argp, sizeof(op))) { | |
435 | - kfree(str); | |
436 | - return -EFAULT; | |
437 | - } | |
480 | +static int opiocset(void __user *argp, DATA *data) | |
481 | +{ | |
482 | + struct opiocdesc op; | |
483 | + struct device_node *dp; | |
484 | + char *str, *tmp; | |
485 | + int err; | |
438 | 486 | |
439 | - if (!access_ok(VERIFY_WRITE, op.op_buf, len)) { | |
440 | - kfree(str); | |
441 | - return -EFAULT; | |
442 | - } | |
487 | + if (copy_from_user(&op, argp, sizeof(op))) | |
488 | + return -EFAULT; | |
443 | 489 | |
444 | - error = __copy_to_user(argp, &op, sizeof(op)); | |
445 | - if (!error) error = __copy_to_user(op.op_buf, tmp, len); | |
490 | + dp = get_node(op.op_nodeid, data); | |
491 | + if (!dp) | |
492 | + return -EINVAL; | |
446 | 493 | |
494 | + err = copyin_string(op.op_name, op.op_namelen, &str); | |
495 | + if (err) | |
496 | + return err; | |
497 | + | |
498 | + err = copyin_string(op.op_buf, op.op_buflen, &tmp); | |
499 | + if (err) { | |
447 | 500 | kfree(str); |
501 | + return err; | |
502 | + } | |
448 | 503 | |
449 | - return error; | |
504 | + err = of_set_property(dp, str, tmp, op.op_buflen); | |
450 | 505 | |
451 | - case OPIOCSET: | |
452 | - if (copy_from_user(&op, argp, sizeof(op))) | |
453 | - return -EFAULT; | |
506 | + kfree(str); | |
507 | + kfree(tmp); | |
454 | 508 | |
455 | - if (!goodnode(op.op_nodeid,data)) | |
456 | - return -EINVAL; | |
509 | + return err; | |
510 | +} | |
457 | 511 | |
458 | - error = copyin_string(op.op_name, op.op_namelen, &str); | |
459 | - if (error) | |
460 | - return error; | |
512 | +static int opiocgetnext(unsigned int cmd, void __user *argp) | |
513 | +{ | |
514 | + struct device_node *dp; | |
515 | + phandle nd; | |
461 | 516 | |
462 | - error = copyin_string(op.op_buf, op.op_buflen, &tmp); | |
463 | - if (error) { | |
464 | - kfree(str); | |
465 | - return error; | |
466 | - } | |
517 | + BUILD_BUG_ON(sizeof(phandle) != sizeof(int)); | |
467 | 518 | |
468 | - len = prom_setprop(op.op_nodeid,str,tmp,op.op_buflen+1); | |
519 | + if (copy_from_user(&nd, argp, sizeof(phandle))) | |
520 | + return -EFAULT; | |
469 | 521 | |
470 | - if (len != op.op_buflen) | |
522 | + if (nd == 0) { | |
523 | + if (cmd != OPIOCGETNEXT) | |
471 | 524 | return -EINVAL; |
525 | + dp = of_find_node_by_path("/"); | |
526 | + } else { | |
527 | + dp = of_find_node_by_phandle(nd); | |
528 | + nd = 0; | |
529 | + if (dp) { | |
530 | + if (cmd == OPIOCGETNEXT) | |
531 | + dp = dp->sibling; | |
532 | + else | |
533 | + dp = dp->child; | |
534 | + } | |
535 | + } | |
536 | + if (dp) | |
537 | + nd = dp->node; | |
538 | + if (copy_to_user(argp, &nd, sizeof(phandle))) | |
539 | + return -EFAULT; | |
472 | 540 | |
473 | - kfree(str); | |
474 | - kfree(tmp); | |
541 | + return 0; | |
542 | +} | |
475 | 543 | |
476 | - return 0; | |
544 | +static int openprom_bsd_ioctl(struct inode * inode, struct file * file, | |
545 | + unsigned int cmd, unsigned long arg) | |
546 | +{ | |
547 | + DATA *data = (DATA *) file->private_data; | |
548 | + void __user *argp = (void __user *)arg; | |
549 | + int err; | |
477 | 550 | |
478 | - case OPIOCGETOPTNODE: | |
479 | - if (copy_to_user(argp, &options_node, sizeof(int))) | |
480 | - return -EFAULT; | |
481 | - return 0; | |
551 | + switch (cmd) { | |
552 | + case OPIOCGET: | |
553 | + err = opiocget(argp, data); | |
554 | + break; | |
482 | 555 | |
483 | - case OPIOCGETNEXT: | |
484 | - case OPIOCGETCHILD: | |
485 | - if (copy_from_user(&node, argp, sizeof(int))) | |
486 | - return -EFAULT; | |
556 | + case OPIOCNEXTPROP: | |
557 | + err = opiocnextprop(argp, data); | |
558 | + break; | |
487 | 559 | |
488 | - if (cmd == OPIOCGETNEXT) | |
489 | - node = __prom_getsibling(node); | |
490 | - else | |
491 | - node = __prom_getchild(node); | |
560 | + case OPIOCSET: | |
561 | + err = opiocset(argp, data); | |
562 | + break; | |
492 | 563 | |
493 | - if (__copy_to_user(argp, &node, sizeof(int))) | |
564 | + case OPIOCGETOPTNODE: | |
565 | + BUILD_BUG_ON(sizeof(phandle) != sizeof(int)); | |
566 | + | |
567 | + if (copy_to_user(argp, &options_node->node, sizeof(phandle))) | |
494 | 568 | return -EFAULT; |
495 | 569 | |
496 | 570 | return 0; |
497 | 571 | |
572 | + case OPIOCGETNEXT: | |
573 | + case OPIOCGETCHILD: | |
574 | + err = opiocgetnext(cmd, argp); | |
575 | + break; | |
576 | + | |
498 | 577 | default: |
499 | - if (cnt++ < 10) | |
500 | - printk(KERN_INFO "openprom_bsd_ioctl: cmd 0x%X\n", cmd); | |
501 | 578 | return -EINVAL; |
502 | 579 | |
503 | - } | |
580 | + }; | |
581 | + | |
582 | + return err; | |
504 | 583 | } |
505 | 584 | |
506 | 585 | |
... | ... | @@ -511,7 +590,6 @@ |
511 | 590 | unsigned int cmd, unsigned long arg) |
512 | 591 | { |
513 | 592 | DATA *data = (DATA *) file->private_data; |
514 | - static int cnt; | |
515 | 593 | |
516 | 594 | switch (cmd) { |
517 | 595 | case OPROMGETOPT: |
518 | 596 | |
... | ... | @@ -563,10 +641,8 @@ |
563 | 641 | return openprom_bsd_ioctl(inode,file,cmd,arg); |
564 | 642 | |
565 | 643 | default: |
566 | - if (cnt++ < 10) | |
567 | - printk("openprom_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg); | |
568 | 644 | return -EINVAL; |
569 | - } | |
645 | + }; | |
570 | 646 | } |
571 | 647 | |
572 | 648 | static long openprom_compat_ioctl(struct file *file, unsigned int cmd, |
573 | 649 | |
... | ... | @@ -594,9 +670,7 @@ |
594 | 670 | case OPROMSETCUR: |
595 | 671 | case OPROMPCI2NODE: |
596 | 672 | case OPROMPATH2NODE: |
597 | - lock_kernel(); | |
598 | 673 | rval = openprom_ioctl(file->f_dentry->d_inode, file, cmd, arg); |
599 | - lock_kernel(); | |
600 | 674 | break; |
601 | 675 | } |
602 | 676 | |
603 | 677 | |
... | ... | @@ -607,13 +681,13 @@ |
607 | 681 | { |
608 | 682 | DATA *data; |
609 | 683 | |
610 | - data = (DATA *) kmalloc(sizeof(DATA), GFP_KERNEL); | |
684 | + data = kmalloc(sizeof(DATA), GFP_KERNEL); | |
611 | 685 | if (!data) |
612 | 686 | return -ENOMEM; |
613 | 687 | |
614 | - data->current_node = prom_root_node; | |
615 | - data->lastnode = prom_root_node; | |
616 | - file->private_data = (void *)data; | |
688 | + data->current_node = of_find_node_by_path("/"); | |
689 | + data->lastnode = data->current_node; | |
690 | + file->private_data = (void *) data; | |
617 | 691 | |
618 | 692 | return 0; |
619 | 693 | } |
620 | 694 | |
621 | 695 | |
622 | 696 | |
623 | 697 | |
... | ... | @@ -634,24 +708,30 @@ |
634 | 708 | }; |
635 | 709 | |
636 | 710 | static struct miscdevice openprom_dev = { |
637 | - SUN_OPENPROM_MINOR, "openprom", &openprom_fops | |
711 | + .minor = SUN_OPENPROM_MINOR, | |
712 | + .name = "openprom", | |
713 | + .fops = &openprom_fops, | |
638 | 714 | }; |
639 | 715 | |
640 | 716 | static int __init openprom_init(void) |
641 | 717 | { |
642 | - int error; | |
718 | + struct device_node *dp; | |
719 | + int err; | |
643 | 720 | |
644 | - error = misc_register(&openprom_dev); | |
645 | - if (error) { | |
646 | - printk(KERN_ERR "openprom: unable to get misc minor\n"); | |
647 | - return error; | |
721 | + err = misc_register(&openprom_dev); | |
722 | + if (err) | |
723 | + return err; | |
724 | + | |
725 | + dp = of_find_node_by_path("/"); | |
726 | + dp = dp->child; | |
727 | + while (dp) { | |
728 | + if (!strcmp(dp->name, "options")) | |
729 | + break; | |
730 | + dp = dp->sibling; | |
648 | 731 | } |
732 | + options_node = dp; | |
649 | 733 | |
650 | - options_node = prom_getchild(prom_root_node); | |
651 | - options_node = prom_searchsiblings(options_node,"options"); | |
652 | - | |
653 | - if (options_node == 0 || options_node == -1) { | |
654 | - printk(KERN_ERR "openprom: unable to find options node\n"); | |
734 | + if (!options_node) { | |
655 | 735 | misc_deregister(&openprom_dev); |
656 | 736 | return -EIO; |
657 | 737 | } |
... | ... | @@ -666,5 +746,4 @@ |
666 | 746 | |
667 | 747 | module_init(openprom_init); |
668 | 748 | module_exit(openprom_cleanup); |
669 | -MODULE_LICENSE("GPL"); |