diff options
author | David S. Miller <davem@davemloft.net> | 2006-06-26 02:19:30 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2006-06-26 02:19:30 -0400 |
commit | 8e48aec714f1faf581949f23ae0e3d6e2317433b (patch) | |
tree | 0b0c34ba577d64f54c2a29c5f748f93f2b1bf0d1 /drivers/sbus | |
parent | 3d824a46b7210ea3b0a13ab0d0fbd7f6e2e91ddf (diff) |
[OPENPROM]: Rewrite driver to use in-kernel device tree.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/sbus')
-rw-r--r-- | drivers/sbus/char/openprom.c | 593 |
1 files changed, 336 insertions, 257 deletions
diff --git a/drivers/sbus/char/openprom.c b/drivers/sbus/char/openprom.c index cf5b476b5496..d7e4bb41bd79 100644 --- a/drivers/sbus/char/openprom.c +++ b/drivers/sbus/char/openprom.c | |||
@@ -29,8 +29,6 @@ | |||
29 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 29 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
30 | */ | 30 | */ |
31 | 31 | ||
32 | #define PROMLIB_INTERNAL | ||
33 | |||
34 | #include <linux/config.h> | 32 | #include <linux/config.h> |
35 | #include <linux/module.h> | 33 | #include <linux/module.h> |
36 | #include <linux/kernel.h> | 34 | #include <linux/kernel.h> |
@@ -39,10 +37,10 @@ | |||
39 | #include <linux/slab.h> | 37 | #include <linux/slab.h> |
40 | #include <linux/string.h> | 38 | #include <linux/string.h> |
41 | #include <linux/miscdevice.h> | 39 | #include <linux/miscdevice.h> |
42 | #include <linux/smp_lock.h> | ||
43 | #include <linux/init.h> | 40 | #include <linux/init.h> |
44 | #include <linux/fs.h> | 41 | #include <linux/fs.h> |
45 | #include <asm/oplib.h> | 42 | #include <asm/oplib.h> |
43 | #include <asm/prom.h> | ||
46 | #include <asm/system.h> | 44 | #include <asm/system.h> |
47 | #include <asm/uaccess.h> | 45 | #include <asm/uaccess.h> |
48 | #include <asm/openpromio.h> | 46 | #include <asm/openpromio.h> |
@@ -51,15 +49,20 @@ | |||
51 | #include <asm/pbm.h> | 49 | #include <asm/pbm.h> |
52 | #endif | 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 | /* Private data kept by the driver for each descriptor. */ | 57 | /* Private data kept by the driver for each descriptor. */ |
55 | typedef struct openprom_private_data | 58 | typedef struct openprom_private_data |
56 | { | 59 | { |
57 | int current_node; /* Current node for SunOS ioctls. */ | 60 | struct device_node *current_node; /* Current node for SunOS ioctls. */ |
58 | int lastnode; /* Last valid node used by BSD ioctls. */ | 61 | struct device_node *lastnode; /* Last valid node used by BSD ioctls. */ |
59 | } DATA; | 62 | } DATA; |
60 | 63 | ||
61 | /* ID of the PROM node containing all of the EEPROM options. */ | 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 | * Copy an openpromio structure into kernel space from user space. | 68 | * Copy an openpromio structure into kernel space from user space. |
@@ -87,9 +90,8 @@ static int copyin(struct openpromio __user *info, struct openpromio **opp_p) | |||
87 | if (bufsize > OPROMMAXPARAM) | 90 | if (bufsize > OPROMMAXPARAM) |
88 | bufsize = OPROMMAXPARAM; | 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 | return -ENOMEM; | 94 | return -ENOMEM; |
92 | memset(*opp_p, 0, sizeof(int) + bufsize + 1); | ||
93 | 95 | ||
94 | if (copy_from_user(&(*opp_p)->oprom_array, | 96 | if (copy_from_user(&(*opp_p)->oprom_array, |
95 | &info->oprom_array, bufsize)) { | 97 | &info->oprom_array, bufsize)) { |
@@ -107,10 +109,9 @@ static int getstrings(struct openpromio __user *info, struct openpromio **opp_p) | |||
107 | if (!info || !opp_p) | 109 | if (!info || !opp_p) |
108 | return -EFAULT; | 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 | return -ENOMEM; | 113 | return -ENOMEM; |
112 | 114 | ||
113 | memset(*opp_p, 0, sizeof(int) + OPROMMAXPARAM + 1); | ||
114 | (*opp_p)->oprom_size = 0; | 115 | (*opp_p)->oprom_size = 0; |
115 | 116 | ||
116 | n = bufsize = 0; | 117 | n = bufsize = 0; |
@@ -140,16 +141,164 @@ static int copyout(void __user *info, struct openpromio *opp, int len) | |||
140 | return 0; | 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 | * SunOS and Solaris /dev/openprom ioctl calls. | 293 | * SunOS and Solaris /dev/openprom ioctl calls. |
145 | */ | 294 | */ |
146 | static int openprom_sunos_ioctl(struct inode * inode, struct file * file, | 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; | 299 | DATA *data = file->private_data; |
150 | char buffer[OPROMMAXPARAM+1], *buf; | ||
151 | struct openpromio *opp; | 300 | struct openpromio *opp; |
152 | int bufsize, len, error = 0; | 301 | int bufsize, error = 0; |
153 | static int cnt; | 302 | static int cnt; |
154 | void __user *argp = (void __user *)arg; | 303 | void __user *argp = (void __user *)arg; |
155 | 304 | ||
@@ -164,119 +313,35 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, | |||
164 | switch (cmd) { | 313 | switch (cmd) { |
165 | case OPROMGETOPT: | 314 | case OPROMGETOPT: |
166 | case OPROMGETPROP: | 315 | case OPROMGETPROP: |
167 | len = prom_getproplen(node, opp->oprom_array); | 316 | error = opromgetprop(argp, dp, opp, bufsize); |
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); | ||
181 | break; | 317 | break; |
182 | 318 | ||
183 | case OPROMNXTOPT: | 319 | case OPROMNXTOPT: |
184 | case OPROMNXTPROP: | 320 | case OPROMNXTPROP: |
185 | buf = prom_nextprop(node, opp->oprom_array, buffer); | 321 | error = opromnxtprop(argp, dp, opp, bufsize); |
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); | ||
198 | break; | 322 | break; |
199 | 323 | ||
200 | case OPROMSETOPT: | 324 | case OPROMSETOPT: |
201 | case OPROMSETOPT2: | 325 | case OPROMSETOPT2: |
202 | buf = opp->oprom_array + strlen(opp->oprom_array) + 1; | 326 | error = opromsetopt(dp, opp, bufsize); |
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; | ||
210 | break; | 327 | break; |
211 | 328 | ||
212 | case OPROMNEXT: | 329 | case OPROMNEXT: |
213 | case OPROMCHILD: | 330 | case OPROMCHILD: |
214 | case OPROMSETCUR: | 331 | case OPROMSETCUR: |
215 | if (bufsize < sizeof(int)) { | 332 | error = opromnext(argp, cmd, dp, opp, bufsize, data); |
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)); | ||
233 | break; | 333 | break; |
234 | 334 | ||
235 | case OPROMPCI2NODE: | 335 | case OPROMPCI2NODE: |
236 | error = -EINVAL; | 336 | error = oprompci2node(argp, dp, opp, bufsize, data); |
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 | } | ||
255 | break; | 337 | break; |
256 | 338 | ||
257 | case OPROMPATH2NODE: | 339 | case OPROMPATH2NODE: |
258 | node = prom_finddevice(opp->oprom_array); | 340 | error = oprompath2node(argp, dp, opp, bufsize, data); |
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)); | ||
264 | break; | 341 | break; |
265 | 342 | ||
266 | case OPROMGETBOOTARGS: | 343 | case OPROMGETBOOTARGS: |
267 | buf = saved_command_line; | 344 | error = opromgetbootargs(argp, opp, bufsize); |
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)); | ||
280 | break; | 345 | break; |
281 | 346 | ||
282 | case OPROMU2P: | 347 | case OPROMU2P: |
@@ -297,25 +362,14 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file, | |||
297 | return error; | 362 | return error; |
298 | } | 363 | } |
299 | 364 | ||
300 | 365 | static struct device_node *get_node(phandle n, DATA *data) | |
301 | /* Return nonzero if a specific node is in the PROM device tree. */ | ||
302 | static int intree(int root, int node) | ||
303 | { | 366 | { |
304 | for (; root != 0; root = prom_getsibling(root)) | 367 | struct device_node *dp = of_find_node_by_phandle(n); |
305 | if (root == node || intree(prom_getchild(root),node)) | ||
306 | return 1; | ||
307 | return 0; | ||
308 | } | ||
309 | 368 | ||
310 | /* Return nonzero if a specific node is "valid". */ | 369 | if (dp) |
311 | static int goodnode(int n, DATA *data) | 370 | data->lastnode = dp; |
312 | { | 371 | |
313 | if (n == data->lastnode || n == prom_root_node || n == options_node) | 372 | return dp; |
314 | return 1; | ||
315 | if (n == 0 || n == -1 || !intree(prom_root_node,n)) | ||
316 | return 0; | ||
317 | data->lastnode = n; | ||
318 | return 1; | ||
319 | } | 373 | } |
320 | 374 | ||
321 | /* Copy in a whole string from userspace into kernelspace. */ | 375 | /* Copy in a whole string from userspace into kernelspace. */ |
@@ -330,7 +384,7 @@ static int copyin_string(char __user *user, size_t len, char **ptr) | |||
330 | if (!tmp) | 384 | if (!tmp) |
331 | return -ENOMEM; | 385 | return -ENOMEM; |
332 | 386 | ||
333 | if(copy_from_user(tmp, user, len)) { | 387 | if (copy_from_user(tmp, user, len)) { |
334 | kfree(tmp); | 388 | kfree(tmp); |
335 | return -EFAULT; | 389 | return -EFAULT; |
336 | } | 390 | } |
@@ -345,162 +399,187 @@ static int copyin_string(char __user *user, size_t len, char **ptr) | |||
345 | /* | 399 | /* |
346 | * NetBSD /dev/openprom ioctl calls. | 400 | * NetBSD /dev/openprom ioctl calls. |
347 | */ | 401 | */ |
348 | static int openprom_bsd_ioctl(struct inode * inode, struct file * file, | 402 | static int opiocget(void __user *argp, DATA *data) |
349 | unsigned int cmd, unsigned long arg) | ||
350 | { | 403 | { |
351 | DATA *data = (DATA *) file->private_data; | ||
352 | void __user *argp = (void __user *)arg; | ||
353 | struct opiocdesc op; | 404 | struct opiocdesc op; |
354 | int error, node, len; | 405 | struct device_node *dp; |
355 | char *str, *tmp; | 406 | char *str; |
356 | char buffer[64]; | 407 | void *pval; |
357 | static int cnt; | 408 | int err, len; |
358 | |||
359 | switch (cmd) { | ||
360 | case OPIOCGET: | ||
361 | if (copy_from_user(&op, argp, sizeof(op))) | ||
362 | return -EFAULT; | ||
363 | |||
364 | if (!goodnode(op.op_nodeid,data)) | ||
365 | return -EINVAL; | ||
366 | 409 | ||
367 | error = copyin_string(op.op_name, op.op_namelen, &str); | 410 | if (copy_from_user(&op, argp, sizeof(op))) |
368 | if (error) | 411 | return -EFAULT; |
369 | return error; | ||
370 | 412 | ||
371 | len = prom_getproplen(op.op_nodeid,str); | 413 | dp = get_node(op.op_nodeid, data); |
372 | 414 | ||
373 | if (len > op.op_buflen) { | 415 | err = copyin_string(op.op_name, op.op_namelen, &str); |
374 | kfree(str); | 416 | if (err) |
375 | return -ENOMEM; | 417 | return err; |
376 | } | ||
377 | 418 | ||
419 | pval = of_get_property(dp, str, &len); | ||
420 | err = 0; | ||
421 | if (!pval || len > op.op_buflen) { | ||
422 | err = -EINVAL; | ||
423 | } else { | ||
378 | op.op_buflen = len; | 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) { | 431 | return err; |
381 | kfree(str); | 432 | } |
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 | } | ||
388 | 433 | ||
389 | tmp = kmalloc(len + 1, GFP_KERNEL); | 434 | static int opiocnextprop(void __user *argp, DATA *data) |
390 | if (!tmp) { | 435 | { |
391 | kfree(str); | 436 | struct opiocdesc op; |
392 | return -ENOMEM; | 437 | struct device_node *dp; |
393 | } | 438 | struct property *prop; |
439 | char *str; | ||
440 | int err, len; | ||
394 | 441 | ||
395 | cnt = prom_getproperty(op.op_nodeid, str, tmp, len); | 442 | if (copy_from_user(&op, argp, sizeof(op))) |
396 | if (cnt <= 0) { | 443 | return -EFAULT; |
397 | error = -EINVAL; | ||
398 | } else { | ||
399 | tmp[len] = '\0'; | ||
400 | 444 | ||
401 | if (__copy_to_user(argp, &op, sizeof(op)) != 0 || | 445 | dp = get_node(op.op_nodeid, data); |
402 | copy_to_user(op.op_buf, tmp, len) != 0) | 446 | if (!dp) |
403 | error = -EFAULT; | 447 | return -EINVAL; |
404 | } | ||
405 | 448 | ||
406 | kfree(tmp); | 449 | err = copyin_string(op.op_name, op.op_namelen, &str); |
407 | kfree(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: | 462 | if (!prop) |
412 | if (copy_from_user(&op, argp, sizeof(op))) | 463 | len = 0; |
413 | return -EFAULT; | 464 | else |
465 | len = prop->length; | ||
414 | 466 | ||
415 | if (!goodnode(op.op_nodeid,data)) | 467 | if (len > op.op_buflen) |
416 | return -EINVAL; | 468 | len = op.op_buflen; |
417 | 469 | ||
418 | error = copyin_string(op.op_name, op.op_namelen, &str); | 470 | if (copy_to_user(argp, &op, sizeof(op))) |
419 | if (error) | 471 | return -EFAULT; |
420 | return error; | ||
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) { | 477 | return 0; |
425 | len = strlen(tmp); | 478 | } |
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 | } | ||
433 | 479 | ||
434 | if (!access_ok(VERIFY_WRITE, argp, sizeof(op))) { | 480 | static int opiocset(void __user *argp, DATA *data) |
435 | kfree(str); | 481 | { |
436 | return -EFAULT; | 482 | struct opiocdesc op; |
437 | } | 483 | struct device_node *dp; |
484 | char *str, *tmp; | ||
485 | int err; | ||
438 | 486 | ||
439 | if (!access_ok(VERIFY_WRITE, op.op_buf, len)) { | 487 | if (copy_from_user(&op, argp, sizeof(op))) |
440 | kfree(str); | 488 | return -EFAULT; |
441 | return -EFAULT; | 489 | |
442 | } | 490 | dp = get_node(op.op_nodeid, data); |
491 | if (!dp) | ||
492 | return -EINVAL; | ||
443 | 493 | ||
444 | error = __copy_to_user(argp, &op, sizeof(op)); | 494 | err = copyin_string(op.op_name, op.op_namelen, &str); |
445 | if (!error) error = __copy_to_user(op.op_buf, tmp, len); | 495 | if (err) |
496 | return err; | ||
446 | 497 | ||
498 | err = copyin_string(op.op_buf, op.op_buflen, &tmp); | ||
499 | if (err) { | ||
447 | kfree(str); | 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: | 506 | kfree(str); |
452 | if (copy_from_user(&op, argp, sizeof(op))) | 507 | kfree(tmp); |
453 | return -EFAULT; | ||
454 | 508 | ||
455 | if (!goodnode(op.op_nodeid,data)) | 509 | return err; |
456 | return -EINVAL; | 510 | } |
457 | 511 | ||
458 | error = copyin_string(op.op_name, op.op_namelen, &str); | 512 | static int opiocgetnext(unsigned int cmd, void __user *argp) |
459 | if (error) | 513 | { |
460 | return error; | 514 | struct device_node *dp; |
515 | phandle nd; | ||
461 | 516 | ||
462 | error = copyin_string(op.op_buf, op.op_buflen, &tmp); | 517 | BUILD_BUG_ON(sizeof(phandle) != sizeof(int)); |
463 | if (error) { | ||
464 | kfree(str); | ||
465 | return error; | ||
466 | } | ||
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 | return -EINVAL; | 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); | 541 | return 0; |
474 | kfree(tmp); | 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: | 551 | switch (cmd) { |
479 | if (copy_to_user(argp, &options_node, sizeof(int))) | 552 | case OPIOCGET: |
480 | return -EFAULT; | 553 | err = opiocget(argp, data); |
481 | return 0; | 554 | break; |
482 | 555 | ||
483 | case OPIOCGETNEXT: | 556 | case OPIOCNEXTPROP: |
484 | case OPIOCGETCHILD: | 557 | err = opiocnextprop(argp, data); |
485 | if (copy_from_user(&node, argp, sizeof(int))) | 558 | break; |
486 | return -EFAULT; | ||
487 | 559 | ||
488 | if (cmd == OPIOCGETNEXT) | 560 | case OPIOCSET: |
489 | node = __prom_getsibling(node); | 561 | err = opiocset(argp, data); |
490 | else | 562 | break; |
491 | node = __prom_getchild(node); | 563 | |
564 | case OPIOCGETOPTNODE: | ||
565 | BUILD_BUG_ON(sizeof(phandle) != sizeof(int)); | ||
492 | 566 | ||
493 | if (__copy_to_user(argp, &node, sizeof(int))) | 567 | if (copy_to_user(argp, &options_node->node, sizeof(phandle))) |
494 | return -EFAULT; | 568 | return -EFAULT; |
495 | 569 | ||
496 | return 0; | 570 | return 0; |
497 | 571 | ||
572 | case OPIOCGETNEXT: | ||
573 | case OPIOCGETCHILD: | ||
574 | err = opiocgetnext(cmd, argp); | ||
575 | break; | ||
576 | |||
498 | default: | 577 | default: |
499 | if (cnt++ < 10) | ||
500 | printk(KERN_INFO "openprom_bsd_ioctl: cmd 0x%X\n", cmd); | ||
501 | return -EINVAL; | 578 | return -EINVAL; |
502 | 579 | ||
503 | } | 580 | }; |
581 | |||
582 | return err; | ||
504 | } | 583 | } |
505 | 584 | ||
506 | 585 | ||
@@ -511,7 +590,6 @@ static int openprom_ioctl(struct inode * inode, struct file * file, | |||
511 | unsigned int cmd, unsigned long arg) | 590 | unsigned int cmd, unsigned long arg) |
512 | { | 591 | { |
513 | DATA *data = (DATA *) file->private_data; | 592 | DATA *data = (DATA *) file->private_data; |
514 | static int cnt; | ||
515 | 593 | ||
516 | switch (cmd) { | 594 | switch (cmd) { |
517 | case OPROMGETOPT: | 595 | case OPROMGETOPT: |
@@ -563,10 +641,8 @@ static int openprom_ioctl(struct inode * inode, struct file * file, | |||
563 | return openprom_bsd_ioctl(inode,file,cmd,arg); | 641 | return openprom_bsd_ioctl(inode,file,cmd,arg); |
564 | 642 | ||
565 | default: | 643 | default: |
566 | if (cnt++ < 10) | ||
567 | printk("openprom_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg); | ||
568 | return -EINVAL; | 644 | return -EINVAL; |
569 | } | 645 | }; |
570 | } | 646 | } |
571 | 647 | ||
572 | static long openprom_compat_ioctl(struct file *file, unsigned int cmd, | 648 | static long openprom_compat_ioctl(struct file *file, unsigned int cmd, |
@@ -594,9 +670,7 @@ static long openprom_compat_ioctl(struct file *file, unsigned int cmd, | |||
594 | case OPROMSETCUR: | 670 | case OPROMSETCUR: |
595 | case OPROMPCI2NODE: | 671 | case OPROMPCI2NODE: |
596 | case OPROMPATH2NODE: | 672 | case OPROMPATH2NODE: |
597 | lock_kernel(); | ||
598 | rval = openprom_ioctl(file->f_dentry->d_inode, file, cmd, arg); | 673 | rval = openprom_ioctl(file->f_dentry->d_inode, file, cmd, arg); |
599 | lock_kernel(); | ||
600 | break; | 674 | break; |
601 | } | 675 | } |
602 | 676 | ||
@@ -607,13 +681,13 @@ static int openprom_open(struct inode * inode, struct file * file) | |||
607 | { | 681 | { |
608 | DATA *data; | 682 | DATA *data; |
609 | 683 | ||
610 | data = (DATA *) kmalloc(sizeof(DATA), GFP_KERNEL); | 684 | data = kmalloc(sizeof(DATA), GFP_KERNEL); |
611 | if (!data) | 685 | if (!data) |
612 | return -ENOMEM; | 686 | return -ENOMEM; |
613 | 687 | ||
614 | data->current_node = prom_root_node; | 688 | data->current_node = of_find_node_by_path("/"); |
615 | data->lastnode = prom_root_node; | 689 | data->lastnode = data->current_node; |
616 | file->private_data = (void *)data; | 690 | file->private_data = (void *) data; |
617 | 691 | ||
618 | return 0; | 692 | return 0; |
619 | } | 693 | } |
@@ -634,24 +708,30 @@ static struct file_operations openprom_fops = { | |||
634 | }; | 708 | }; |
635 | 709 | ||
636 | static struct miscdevice openprom_dev = { | 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 | static int __init openprom_init(void) | 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); | 721 | err = misc_register(&openprom_dev); |
645 | if (error) { | 722 | if (err) |
646 | printk(KERN_ERR "openprom: unable to get misc minor\n"); | 723 | return err; |
647 | return error; | ||
648 | } | ||
649 | 724 | ||
650 | options_node = prom_getchild(prom_root_node); | 725 | dp = of_find_node_by_path("/"); |
651 | options_node = prom_searchsiblings(options_node,"options"); | 726 | dp = dp->child; |
727 | while (dp) { | ||
728 | if (!strcmp(dp->name, "options")) | ||
729 | break; | ||
730 | dp = dp->sibling; | ||
731 | } | ||
732 | options_node = dp; | ||
652 | 733 | ||
653 | if (options_node == 0 || options_node == -1) { | 734 | if (!options_node) { |
654 | printk(KERN_ERR "openprom: unable to find options node\n"); | ||
655 | misc_deregister(&openprom_dev); | 735 | misc_deregister(&openprom_dev); |
656 | return -EIO; | 736 | return -EIO; |
657 | } | 737 | } |
@@ -666,4 +746,3 @@ static void __exit openprom_cleanup(void) | |||
666 | 746 | ||
667 | module_init(openprom_init); | 747 | module_init(openprom_init); |
668 | module_exit(openprom_cleanup); | 748 | module_exit(openprom_cleanup); |
669 | MODULE_LICENSE("GPL"); | ||