- pre2
[davej-history.git] / drivers / sbus / char / openprom.c
blob26e5ae8d95e57afc3a46ef1a16e57cc618ec42b5
1 /*
2 * Linux/SPARC PROM Configuration Driver
3 * Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
4 * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
6 * This character device driver allows user programs to access the
7 * PROM device tree. It is compatible with the SunOS /dev/openprom
8 * driver and the NetBSD /dev/openprom driver. The SunOS eeprom
9 * utility works without any modifications.
11 * The driver uses a minor number under the misc device major. The
12 * file read/write mode determines the type of access to the PROM.
13 * Interrupts are disabled whenever the driver calls into the PROM for
14 * sanity's sake.
17 /* This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License as
19 * published by the Free Software Foundation; either version 2 of the
20 * License, or (at your option) any later version.
22 * This program is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32 #define PROMLIB_INTERNAL
34 #include <linux/config.h>
35 #include <linux/module.h>
36 #include <linux/kernel.h>
37 #include <linux/sched.h>
38 #include <linux/errno.h>
39 #include <linux/malloc.h>
40 #include <linux/string.h>
41 #include <linux/miscdevice.h>
42 #include <linux/init.h>
43 #include <asm/oplib.h>
44 #include <asm/system.h>
45 #include <asm/uaccess.h>
46 #include <asm/openpromio.h>
47 #ifdef CONFIG_PCI
48 #include <linux/pci.h>
49 #include <asm/pbm.h>
50 #endif
52 /* Private data kept by the driver for each descriptor. */
53 typedef struct openprom_private_data
55 int current_node; /* Current node for SunOS ioctls. */
56 int lastnode; /* Last valid node used by BSD ioctls. */
57 } DATA;
59 /* ID of the PROM node containing all of the EEPROM options. */
60 static int options_node = 0;
63 * Copy an openpromio structure into kernel space from user space.
64 * This routine does error checking to make sure that all memory
65 * accesses are within bounds. A pointer to the allocated openpromio
66 * structure will be placed in "*opp_p". Return value is the length
67 * of the user supplied buffer.
69 static int copyin(struct openpromio *info, struct openpromio **opp_p)
71 int bufsize;
73 if (!info || !opp_p)
74 return -EFAULT;
76 if (get_user(bufsize, &info->oprom_size))
77 return -EFAULT;
79 if (bufsize == 0)
80 return -EINVAL;
82 /* If the bufsize is too large, just limit it.
83 * Fix from Jason Rappleye.
85 if (bufsize > OPROMMAXPARAM)
86 bufsize = OPROMMAXPARAM;
88 if (!(*opp_p = kmalloc(sizeof(int) + bufsize + 1, GFP_KERNEL)))
89 return -ENOMEM;
90 memset(*opp_p, 0, sizeof(int) + bufsize + 1);
92 if (copy_from_user(&(*opp_p)->oprom_array,
93 &info->oprom_array, bufsize)) {
94 kfree(*opp_p);
95 return -EFAULT;
97 return bufsize;
100 static int getstrings(struct openpromio *info, struct openpromio **opp_p)
102 int n, bufsize;
103 char c;
105 if (!info || !opp_p)
106 return -EFAULT;
108 if (!(*opp_p = kmalloc(sizeof(int) + OPROMMAXPARAM + 1, GFP_KERNEL)))
109 return -ENOMEM;
111 memset(*opp_p, 0, sizeof(int) + OPROMMAXPARAM + 1);
112 (*opp_p)->oprom_size = 0;
114 n = bufsize = 0;
115 while ((n < 2) && (bufsize < OPROMMAXPARAM)) {
116 if (get_user(c, &info->oprom_array[bufsize])) {
117 kfree(*opp_p);
118 return -EFAULT;
120 if (c == '\0')
121 n++;
122 (*opp_p)->oprom_array[bufsize++] = c;
124 if (!n) {
125 kfree(*opp_p);
126 return -EINVAL;
128 return bufsize;
132 * Copy an openpromio structure in kernel space back to user space.
134 static int copyout(void *info, struct openpromio *opp, int len)
136 if (copy_to_user(info, opp, len))
137 return -EFAULT;
138 return 0;
142 * SunOS and Solaris /dev/openprom ioctl calls.
144 static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
145 unsigned int cmd, unsigned long arg, int node)
147 DATA *data = (DATA *) file->private_data;
148 char buffer[OPROMMAXPARAM+1], *buf;
149 struct openpromio *opp;
150 unsigned long flags;
151 int bufsize, len, error = 0;
152 extern char saved_command_line[];
153 static int cnt;
155 if (cmd == OPROMSETOPT)
156 bufsize = getstrings((void *)arg, &opp);
157 else
158 bufsize = copyin((void *)arg, &opp);
160 if (bufsize < 0)
161 return bufsize;
163 switch (cmd) {
164 case OPROMGETOPT:
165 case OPROMGETPROP:
166 save_and_cli(flags);
167 len = prom_getproplen(node, opp->oprom_array);
168 restore_flags(flags);
170 if (len <= 0 || len > bufsize) {
171 error = copyout((void *)arg, opp, sizeof(int));
172 break;
175 save_and_cli(flags);
176 len = prom_getproperty(node, opp->oprom_array, buffer, bufsize);
177 restore_flags(flags);
179 memcpy(opp->oprom_array, buffer, len);
180 opp->oprom_array[len] = '\0';
181 opp->oprom_size = len;
183 error = copyout((void *)arg, opp, sizeof(int) + bufsize);
184 break;
186 case OPROMNXTOPT:
187 case OPROMNXTPROP:
188 save_and_cli(flags);
189 buf = prom_nextprop(node, opp->oprom_array, buffer);
190 restore_flags(flags);
192 len = strlen(buf);
193 if (len == 0 || len + 1 > bufsize) {
194 error = copyout((void *)arg, opp, sizeof(int));
195 break;
198 memcpy(opp->oprom_array, buf, len);
199 opp->oprom_array[len] = '\0';
200 opp->oprom_size = ++len;
202 error = copyout((void *)arg, opp, sizeof(int) + bufsize);
203 break;
205 case OPROMSETOPT:
206 case OPROMSETOPT2:
207 buf = opp->oprom_array + strlen(opp->oprom_array) + 1;
208 len = opp->oprom_array + bufsize - buf;
210 save_and_cli(flags);
211 error = prom_setprop(options_node, opp->oprom_array,
212 buf, len);
213 restore_flags(flags);
215 if (error < 0)
216 error = -EINVAL;
217 break;
219 case OPROMNEXT:
220 case OPROMCHILD:
221 case OPROMSETCUR:
222 if (bufsize < sizeof(int)) {
223 error = -EINVAL;
224 break;
227 node = *((int *) opp->oprom_array);
229 save_and_cli(flags);
230 switch (cmd) {
231 case OPROMNEXT: node = __prom_getsibling(node); break;
232 case OPROMCHILD: node = __prom_getchild(node); break;
233 case OPROMSETCUR: break;
235 restore_flags(flags);
237 data->current_node = node;
238 *((int *)opp->oprom_array) = node;
239 opp->oprom_size = sizeof(int);
241 error = copyout((void *)arg, opp, bufsize + sizeof(int));
242 break;
244 case OPROMPCI2NODE:
245 error = -EINVAL;
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 *) opp->oprom_array)[0],
252 ((int *) opp->oprom_array)[1]);
254 pcp = pdev->sysdata;
255 if (pcp != NULL && pcp->prom_node != -1 && pcp->prom_node) {
256 node = pcp->prom_node;
257 data->current_node = node;
258 *((int *)opp->oprom_array) = node;
259 opp->oprom_size = sizeof(int);
260 error = copyout((void *)arg, opp, bufsize + sizeof(int));
262 #endif
264 break;
266 case OPROMPATH2NODE:
267 save_and_cli(flags);
268 node = prom_finddevice(opp->oprom_array);
269 restore_flags(flags);
270 data->current_node = node;
271 *((int *)opp->oprom_array) = node;
272 opp->oprom_size = sizeof(int);
274 error = copyout((void *)arg, opp, bufsize + sizeof(int));
275 break;
277 case OPROMGETBOOTARGS:
278 buf = saved_command_line;
280 len = strlen(buf);
282 if (len > bufsize) {
283 error = -EINVAL;
284 break;
287 strcpy(opp->oprom_array, buf);
288 opp->oprom_size = len;
290 error = copyout((void *)arg, opp, bufsize + sizeof(int));
291 break;
293 case OPROMU2P:
294 case OPROMGETCONS:
295 case OPROMGETFBNAME:
296 if (cnt++ < 10)
297 printk(KERN_INFO "openprom_sunos_ioctl: unimplemented ioctl\n");
298 error = -EINVAL;
299 break;
300 default:
301 if (cnt++ < 10)
302 printk(KERN_INFO "openprom_sunos_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg);
303 error = -EINVAL;
304 break;
307 kfree(opp);
308 return error;
312 /* Return nonzero if a specific node is in the PROM device tree. */
313 static int intree(int root, int node)
315 for (; root != 0; root = prom_getsibling(root))
316 if (root == node || intree(prom_getchild(root),node))
317 return 1;
318 return 0;
321 /* Return nonzero if a specific node is "valid". */
322 static int goodnode(int n, DATA *data)
324 if (n == data->lastnode || n == prom_root_node || n == options_node)
325 return 1;
326 if (n == 0 || n == -1 || !intree(prom_root_node,n))
327 return 0;
328 data->lastnode = n;
329 return 1;
332 /* Copy in a whole string from userspace into kernelspace. */
333 static int copyin_string(char *user, size_t len, char **ptr)
335 char *tmp;
337 tmp = kmalloc(len + 1, GFP_KERNEL);
338 if (!tmp)
339 return -ENOMEM;
341 if(copy_from_user(tmp, user, len)) {
342 kfree(tmp);
343 return -EFAULT;
346 tmp[len] = '\0';
348 *ptr = tmp;
350 return 0;
354 * NetBSD /dev/openprom ioctl calls.
356 static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
357 unsigned int cmd, unsigned long arg)
359 DATA *data = (DATA *) file->private_data;
360 struct opiocdesc op;
361 unsigned long flags;
362 int error, node, len;
363 char *str, *tmp;
364 char buffer[64];
365 static int cnt;
367 switch (cmd) {
368 case OPIOCGET:
369 if (copy_from_user(&op, (void *)arg, sizeof(op)))
370 return -EFAULT;
372 if (!goodnode(op.op_nodeid,data))
373 return -EINVAL;
375 error = copyin_string(op.op_name, op.op_namelen, &str);
376 if (error)
377 return error;
379 save_and_cli(flags);
380 len = prom_getproplen(op.op_nodeid,str);
381 restore_flags(flags);
383 if (len > op.op_buflen) {
384 kfree(str);
385 return -ENOMEM;
388 op.op_buflen = len;
390 if (len <= 0) {
391 kfree(str);
392 /* Verified by the above copy_from_user */
393 if (__copy_to_user((void *)arg, &op,
394 sizeof(op)))
395 return -EFAULT;
396 return 0;
399 tmp = kmalloc(len + 1, GFP_KERNEL);
400 if (!tmp) {
401 kfree(str);
402 return -ENOMEM;
405 save_and_cli(flags);
406 prom_getproperty(op.op_nodeid, str, tmp, len);
407 restore_flags(flags);
409 tmp[len] = '\0';
411 error = __copy_to_user((void *)arg, &op, sizeof(op));
412 if (!error)
413 error = copy_to_user(op.op_buf, tmp, len);
415 kfree(tmp);
416 kfree(str);
418 return error;
420 case OPIOCNEXTPROP:
421 if (copy_from_user(&op, (void *)arg, sizeof(op)))
422 return -EFAULT;
424 if (!goodnode(op.op_nodeid,data))
425 return -EINVAL;
427 error = copyin_string(op.op_name, op.op_namelen, &str);
428 if (error)
429 return error;
431 save_and_cli(flags);
432 tmp = prom_nextprop(op.op_nodeid,str,buffer);
433 restore_flags(flags);
435 if (tmp) {
436 len = strlen(tmp);
437 if (len > op.op_buflen)
438 len = op.op_buflen;
439 else
440 op.op_buflen = len;
441 } else {
442 len = op.op_buflen = 0;
445 error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(op));
446 if (error) {
447 kfree(str);
448 return error;
451 error = verify_area(VERIFY_WRITE, op.op_buf, len);
452 if (error) {
453 kfree(str);
454 return error;
457 error = __copy_to_user((void *)arg, &op, sizeof(op));
458 if (!error) error = __copy_to_user(op.op_buf, tmp, len);
460 kfree(str);
462 return error;
464 case OPIOCSET:
465 if (copy_from_user(&op, (void *)arg, sizeof(op)))
466 return -EFAULT;
468 if (!goodnode(op.op_nodeid,data))
469 return -EINVAL;
471 error = copyin_string(op.op_name, op.op_namelen, &str);
472 if (error)
473 return error;
475 error = copyin_string(op.op_buf, op.op_buflen, &tmp);
476 if (error) {
477 kfree(str);
478 return error;
481 save_and_cli(flags);
482 len = prom_setprop(op.op_nodeid,str,tmp,op.op_buflen+1);
483 restore_flags(flags);
485 if (len != op.op_buflen)
486 return -EINVAL;
488 kfree(str);
489 kfree(tmp);
491 return 0;
493 case OPIOCGETOPTNODE:
494 if (copy_to_user((void *)arg, &options_node, sizeof(int)))
495 return -EFAULT;
496 return 0;
498 case OPIOCGETNEXT:
499 case OPIOCGETCHILD:
500 if (copy_from_user(&node, (void *)arg, sizeof(int)))
501 return -EFAULT;
503 save_and_cli(flags);
504 if (cmd == OPIOCGETNEXT)
505 node = __prom_getsibling(node);
506 else
507 node = __prom_getchild(node);
508 restore_flags(flags);
510 if (__copy_to_user((void *)arg, &node, sizeof(int)))
511 return -EFAULT;
513 return 0;
515 default:
516 if (cnt++ < 10)
517 printk(KERN_INFO "openprom_bsd_ioctl: cmd 0x%X\n", cmd);
518 return -EINVAL;
525 * Handoff control to the correct ioctl handler.
527 static int openprom_ioctl(struct inode * inode, struct file * file,
528 unsigned int cmd, unsigned long arg)
530 DATA *data = (DATA *) file->private_data;
531 static int cnt;
533 switch (cmd) {
534 case OPROMGETOPT:
535 case OPROMNXTOPT:
536 if ((file->f_mode & FMODE_READ) == 0)
537 return -EPERM;
538 return openprom_sunos_ioctl(inode, file, cmd, arg,
539 options_node);
541 case OPROMSETOPT:
542 case OPROMSETOPT2:
543 if ((file->f_mode & FMODE_WRITE) == 0)
544 return -EPERM;
545 return openprom_sunos_ioctl(inode, file, cmd, arg,
546 options_node);
548 case OPROMNEXT:
549 case OPROMCHILD:
550 case OPROMGETPROP:
551 case OPROMNXTPROP:
552 if ((file->f_mode & FMODE_READ) == 0)
553 return -EPERM;
554 return openprom_sunos_ioctl(inode, file, cmd, arg,
555 data->current_node);
557 case OPROMU2P:
558 case OPROMGETCONS:
559 case OPROMGETFBNAME:
560 case OPROMGETBOOTARGS:
561 case OPROMSETCUR:
562 case OPROMPCI2NODE:
563 case OPROMPATH2NODE:
564 if ((file->f_mode & FMODE_READ) == 0)
565 return -EPERM;
566 return openprom_sunos_ioctl(inode, file, cmd, arg, 0);
568 case OPIOCGET:
569 case OPIOCNEXTPROP:
570 case OPIOCGETOPTNODE:
571 case OPIOCGETNEXT:
572 case OPIOCGETCHILD:
573 if ((file->f_mode & FMODE_READ) == 0)
574 return -EBADF;
575 return openprom_bsd_ioctl(inode,file,cmd,arg);
577 case OPIOCSET:
578 if ((file->f_mode & FMODE_WRITE) == 0)
579 return -EBADF;
580 return openprom_bsd_ioctl(inode,file,cmd,arg);
582 default:
583 if (cnt++ < 10)
584 printk("openprom_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg);
585 return -EINVAL;
589 static long long openprom_lseek(struct file * file, long long offset, int origin)
591 return -ESPIPE;
594 static int openprom_open(struct inode * inode, struct file * file)
596 DATA *data;
598 data = (DATA *) kmalloc(sizeof(DATA), GFP_KERNEL);
599 if (!data)
600 return -ENOMEM;
602 data->current_node = prom_root_node;
603 data->lastnode = prom_root_node;
604 file->private_data = (void *)data;
606 return 0;
609 static int openprom_release(struct inode * inode, struct file * file)
611 kfree(file->private_data);
612 return 0;
615 static struct file_operations openprom_fops = {
616 owner: THIS_MODULE,
617 llseek: openprom_lseek,
618 ioctl: openprom_ioctl,
619 open: openprom_open,
620 release: openprom_release,
623 static struct miscdevice openprom_dev = {
624 SUN_OPENPROM_MINOR, "openprom", &openprom_fops
627 EXPORT_NO_SYMBOLS;
629 #ifdef MODULE
630 int init_module(void)
631 #else
632 int __init openprom_init(void)
633 #endif
635 unsigned long flags;
636 int error;
638 error = misc_register(&openprom_dev);
639 if (error) {
640 printk(KERN_ERR "openprom: unable to get misc minor\n");
641 return error;
644 save_and_cli(flags);
645 options_node = prom_getchild(prom_root_node);
646 options_node = prom_searchsiblings(options_node,"options");
647 restore_flags(flags);
649 if (options_node == 0 || options_node == -1) {
650 printk(KERN_ERR "openprom: unable to find options node\n");
651 misc_deregister(&openprom_dev);
652 return -EIO;
655 return 0;
658 #ifdef MODULE
659 void cleanup_module(void)
661 misc_deregister(&openprom_dev);
663 #endif