* better
[mascara-docs.git] / i386 / linux-2.3.21 / drivers / sbus / char / openprom.c
blob2e67dd4af8692de2ef551471ad0c4fcf55981a97
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/module.h>
35 #include <linux/kernel.h>
36 #include <linux/sched.h>
37 #include <linux/errno.h>
38 #include <linux/malloc.h>
39 #include <linux/string.h>
40 #include <linux/miscdevice.h>
41 #include <linux/init.h>
42 #include <asm/oplib.h>
43 #include <asm/system.h>
44 #include <asm/uaccess.h>
45 #include <asm/openpromio.h>
48 /* Private data kept by the driver for each descriptor. */
49 typedef struct openprom_private_data
51 int current_node; /* Current node for SunOS ioctls. */
52 int lastnode; /* Last valid node used by BSD ioctls. */
53 } DATA;
55 /* ID of the PROM node containing all of the EEPROM options. */
56 static int options_node = 0;
59 * Copy an openpromio structure into kernel space from user space.
60 * This routine does error checking to make sure that all memory
61 * accesses are within bounds. A pointer to the allocated openpromio
62 * structure will be placed in "*opp_p". Return value is the length
63 * of the user supplied buffer.
65 static int copyin(struct openpromio *info, struct openpromio **opp_p)
67 int bufsize;
69 if (!info || !opp_p)
70 return -EFAULT;
72 get_user_ret(bufsize, &info->oprom_size, -EFAULT);
74 if (bufsize == 0 || bufsize > OPROMMAXPARAM)
75 return -EINVAL;
77 if (!(*opp_p = kmalloc(sizeof(int) + bufsize + 1, GFP_KERNEL)))
78 return -ENOMEM;
79 memset(*opp_p, 0, sizeof(int) + bufsize + 1);
81 if (copy_from_user(&(*opp_p)->oprom_array,
82 &info->oprom_array, bufsize)) {
83 kfree(*opp_p);
84 return -EFAULT;
86 return bufsize;
89 static int getstrings(struct openpromio *info, struct openpromio **opp_p)
91 int n, bufsize;
92 char c;
94 if (!info || !opp_p)
95 return -EFAULT;
97 if (!(*opp_p = kmalloc(sizeof(int) + OPROMMAXPARAM + 1, GFP_KERNEL)))
98 return -ENOMEM;
100 memset(*opp_p, 0, sizeof(int) + OPROMMAXPARAM + 1);
101 (*opp_p)->oprom_size = 0;
103 n = bufsize = 0;
104 while ((n < 2) && (bufsize < OPROMMAXPARAM)) {
105 if (get_user(c, &info->oprom_array[bufsize])) {
106 kfree(*opp_p);
107 return -EFAULT;
109 if (c == '\0')
110 n++;
111 (*opp_p)->oprom_array[bufsize++] = c;
113 if (!n) {
114 kfree(*opp_p);
115 return -EINVAL;
117 return bufsize;
121 * Copy an openpromio structure in kernel space back to user space.
123 static int copyout(void *info, struct openpromio *opp, int len)
125 copy_to_user_ret(info, opp, len, -EFAULT);
126 return 0;
130 * SunOS and Solaris /dev/openprom ioctl calls.
132 static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
133 unsigned int cmd, unsigned long arg, int node)
135 DATA *data = (DATA *) file->private_data;
136 char buffer[OPROMMAXPARAM+1], *buf;
137 struct openpromio *opp;
138 unsigned long flags;
139 int bufsize, len, error = 0;
140 extern char saved_command_line[];
142 if (cmd == OPROMSETOPT)
143 bufsize = getstrings((void *)arg, &opp);
144 else
145 bufsize = copyin((void *)arg, &opp);
147 if (bufsize < 0)
148 return bufsize;
150 switch (cmd) {
151 case OPROMGETOPT:
152 case OPROMGETPROP:
153 save_and_cli(flags);
154 len = prom_getproplen(node, opp->oprom_array);
155 restore_flags(flags);
157 if (len <= 0 || len > bufsize) {
158 error = copyout((void *)arg, opp, sizeof(int));
159 break;
162 save_and_cli(flags);
163 len = prom_getproperty(node, opp->oprom_array, buffer, bufsize);
164 restore_flags(flags);
166 memcpy(opp->oprom_array, buffer, len);
167 opp->oprom_array[len] = '\0';
168 opp->oprom_size = len;
170 error = copyout((void *)arg, opp, sizeof(int) + bufsize);
171 break;
173 case OPROMNXTOPT:
174 case OPROMNXTPROP:
175 save_and_cli(flags);
176 buf = prom_nextprop(node, opp->oprom_array, buffer);
177 restore_flags(flags);
179 len = strlen(buf);
180 if (len == 0 || len + 1 > bufsize) {
181 error = copyout((void *)arg, opp, sizeof(int));
182 break;
185 memcpy(opp->oprom_array, buf, len);
186 opp->oprom_array[len] = '\0';
187 opp->oprom_size = ++len;
189 error = copyout((void *)arg, opp, sizeof(int) + bufsize);
190 break;
192 case OPROMSETOPT:
193 case OPROMSETOPT2:
194 buf = opp->oprom_array + strlen(opp->oprom_array) + 1;
195 len = opp->oprom_array + bufsize - buf;
197 printk(KERN_DEBUG "OPROMSETOPT%s %s='%s'\n",
198 (cmd == OPROMSETOPT) ? "" : "2", opp->oprom_array, buf);
200 save_and_cli(flags);
201 error = prom_setprop(options_node, opp->oprom_array,
202 buf, len);
203 restore_flags(flags);
205 if (error <= 0)
206 error = -EINVAL;
207 break;
209 case OPROMNEXT:
210 case OPROMCHILD:
211 if (bufsize < sizeof(int)) {
212 error = -EINVAL;
213 break;
216 node = *((int *) opp->oprom_array);
218 save_and_cli(flags);
219 if (cmd == OPROMNEXT)
220 node = __prom_getsibling(node);
221 else
222 node = __prom_getchild(node);
223 restore_flags(flags);
225 data->current_node = node;
226 *((int *)opp->oprom_array) = node;
227 opp->oprom_size = sizeof(int);
229 error = copyout((void *)arg, opp, bufsize + sizeof(int));
230 break;
232 case OPROMGETBOOTARGS:
233 buf = saved_command_line;
235 len = strlen(buf);
237 if (len > bufsize) {
238 error = -EINVAL;
239 break;
242 strcpy(opp->oprom_array, buf);
243 opp->oprom_size = len;
245 error = copyout((void *)arg, opp, bufsize + sizeof(int));
246 break;
248 case OPROMU2P:
249 case OPROMGETCONS:
250 case OPROMGETFBNAME:
251 printk(KERN_INFO "openprom_sunos_ioctl: unimplemented ioctl\n");
252 error = -EINVAL;
253 break;
254 default:
255 printk(KERN_INFO "openprom_sunos_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg);
256 error = -EINVAL;
257 break;
260 kfree(opp);
261 return error;
265 /* Return nonzero if a specific node is in the PROM device tree. */
266 static int intree(int root, int node)
268 for (; root != 0; root = prom_getsibling(root))
269 if (root == node || intree(prom_getchild(root),node))
270 return 1;
271 return 0;
274 /* Return nonzero if a specific node is "valid". */
275 static int goodnode(int n, DATA *data)
277 if (n == data->lastnode || n == prom_root_node || n == options_node)
278 return 1;
279 if (n == 0 || n == -1 || !intree(prom_root_node,n))
280 return 0;
281 data->lastnode = n;
282 return 1;
285 /* Copy in a whole string from userspace into kernelspace. */
286 static int copyin_string(char *user, size_t len, char **ptr)
288 char *tmp;
290 tmp = kmalloc(len + 1, GFP_KERNEL);
291 if (!tmp)
292 return -ENOMEM;
294 if(copy_from_user(tmp, user, len)) {
295 kfree(tmp);
296 return -EFAULT;
299 tmp[len] = '\0';
301 *ptr = tmp;
303 return 0;
307 * NetBSD /dev/openprom ioctl calls.
309 static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
310 unsigned int cmd, unsigned long arg)
312 DATA *data = (DATA *) file->private_data;
313 struct opiocdesc op;
314 unsigned long flags;
315 int error, node, len;
316 char *str, *tmp;
317 char buffer[64];
319 switch (cmd) {
320 case OPIOCGET:
321 copy_from_user_ret(&op, (void *)arg, sizeof(op), -EFAULT);
323 if (!goodnode(op.op_nodeid,data))
324 return -EINVAL;
326 error = copyin_string(op.op_name, op.op_namelen, &str);
327 if (error)
328 return error;
330 save_and_cli(flags);
331 len = prom_getproplen(op.op_nodeid,str);
332 restore_flags(flags);
334 if (len > op.op_buflen) {
335 kfree(str);
336 return -ENOMEM;
339 op.op_buflen = len;
341 if (len <= 0) {
342 kfree(str);
343 /* Verified by the above copy_from_user_ret */
344 __copy_to_user_ret((void *)arg, &op,
345 sizeof(op), -EFAULT);
346 return 0;
349 tmp = kmalloc(len + 1, GFP_KERNEL);
350 if (!tmp) {
351 kfree(str);
352 return -ENOMEM;
355 save_and_cli(flags);
356 prom_getproperty(op.op_nodeid, str, tmp, len);
357 restore_flags(flags);
359 tmp[len] = '\0';
361 error = __copy_to_user((void *)arg, &op, sizeof(op));
362 if (!error)
363 error = copy_to_user(op.op_buf, tmp, len);
365 kfree(tmp);
366 kfree(str);
368 return error;
370 case OPIOCNEXTPROP:
371 copy_from_user_ret(&op, (void *)arg, sizeof(op), -EFAULT);
373 if (!goodnode(op.op_nodeid,data))
374 return -EINVAL;
376 error = copyin_string(op.op_name, op.op_namelen, &str);
377 if (error)
378 return error;
380 save_and_cli(flags);
381 tmp = prom_nextprop(op.op_nodeid,str,buffer);
382 restore_flags(flags);
384 if (tmp) {
385 len = strlen(tmp);
386 if (len > op.op_buflen)
387 len = op.op_buflen;
388 else
389 op.op_buflen = len;
390 } else {
391 len = op.op_buflen = 0;
394 error = verify_area(VERIFY_WRITE, (void *)arg, sizeof(op));
395 if (error) {
396 kfree(str);
397 return error;
400 error = verify_area(VERIFY_WRITE, op.op_buf, len);
401 if (error) {
402 kfree(str);
403 return error;
406 error = __copy_to_user((void *)arg, &op, sizeof(op));
407 if (!error) error = __copy_to_user(op.op_buf, tmp, len);
409 kfree(str);
411 return error;
413 case OPIOCSET:
414 copy_from_user_ret(&op, (void *)arg, sizeof(op), -EFAULT);
416 if (!goodnode(op.op_nodeid,data))
417 return -EINVAL;
419 error = copyin_string(op.op_name, op.op_namelen, &str);
420 if (error)
421 return error;
423 error = copyin_string(op.op_buf, op.op_buflen, &tmp);
424 if (error) {
425 kfree(str);
426 return error;
429 save_and_cli(flags);
430 len = prom_setprop(op.op_nodeid,str,tmp,op.op_buflen+1);
431 restore_flags(flags);
433 if (len != op.op_buflen)
434 return -EINVAL;
436 kfree(str);
437 kfree(tmp);
439 return 0;
441 case OPIOCGETOPTNODE:
442 copy_to_user_ret((void *)arg, &options_node,
443 sizeof(int), -EFAULT);
444 return 0;
446 case OPIOCGETNEXT:
447 case OPIOCGETCHILD:
448 copy_from_user_ret(&node, (void *)arg, sizeof(int), -EFAULT);
450 save_and_cli(flags);
451 if (cmd == OPIOCGETNEXT)
452 node = __prom_getsibling(node);
453 else
454 node = __prom_getchild(node);
455 restore_flags(flags);
457 __copy_to_user_ret((void *)arg, &node, sizeof(int), -EFAULT);
459 return 0;
461 default:
462 printk(KERN_INFO "openprom_bsd_ioctl: cmd 0x%X\n", cmd);
463 return -EINVAL;
470 * Handoff control to the correct ioctl handler.
472 static int openprom_ioctl(struct inode * inode, struct file * file,
473 unsigned int cmd, unsigned long arg)
475 DATA *data = (DATA *) file->private_data;
477 switch (cmd) {
478 case OPROMGETOPT:
479 case OPROMNXTOPT:
480 if ((file->f_mode & FMODE_READ) == 0)
481 return -EPERM;
482 return openprom_sunos_ioctl(inode, file, cmd, arg,
483 options_node);
485 case OPROMSETOPT:
486 case OPROMSETOPT2:
487 if ((file->f_mode & FMODE_WRITE) == 0)
488 return -EPERM;
489 return openprom_sunos_ioctl(inode, file, cmd, arg,
490 options_node);
492 case OPROMNEXT:
493 case OPROMCHILD:
494 case OPROMGETPROP:
495 case OPROMNXTPROP:
496 if ((file->f_mode & FMODE_READ) == 0)
497 return -EPERM;
498 return openprom_sunos_ioctl(inode, file, cmd, arg,
499 data->current_node);
501 case OPROMU2P:
502 case OPROMGETCONS:
503 case OPROMGETFBNAME:
504 case OPROMGETBOOTARGS:
505 if ((file->f_mode & FMODE_READ) == 0)
506 return -EPERM;
507 return openprom_sunos_ioctl(inode, file, cmd, arg, 0);
509 case OPIOCGET:
510 case OPIOCNEXTPROP:
511 case OPIOCGETOPTNODE:
512 case OPIOCGETNEXT:
513 case OPIOCGETCHILD:
514 if ((file->f_mode & FMODE_READ) == 0)
515 return -EBADF;
516 return openprom_bsd_ioctl(inode,file,cmd,arg);
518 case OPIOCSET:
519 if ((file->f_mode & FMODE_WRITE) == 0)
520 return -EBADF;
521 return openprom_bsd_ioctl(inode,file,cmd,arg);
523 default:
524 printk("openprom_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg);
525 return -EINVAL;
529 static long long openprom_lseek(struct file * file, long long offset, int origin)
531 return -ESPIPE;
534 static int openprom_open(struct inode * inode, struct file * file)
536 DATA *data;
538 data = (DATA *) kmalloc(sizeof(DATA), GFP_KERNEL);
539 if (!data)
540 return -ENOMEM;
542 data->current_node = prom_root_node;
543 data->lastnode = prom_root_node;
544 file->private_data = (void *)data;
546 MOD_INC_USE_COUNT;
548 return 0;
551 static int openprom_release(struct inode * inode, struct file * file)
553 kfree_s(file->private_data, sizeof(DATA));
554 MOD_DEC_USE_COUNT;
555 return 0;
558 static struct file_operations openprom_fops = {
559 openprom_lseek,
560 NULL, /* openprom_read */
561 NULL, /* openprom_write */
562 NULL, /* openprom_readdir */
563 NULL, /* openprom_poll */
564 openprom_ioctl,
565 NULL, /* openprom_mmap */
566 openprom_open,
567 NULL, /* flush */
568 openprom_release
571 static struct miscdevice openprom_dev = {
572 SUN_OPENPROM_MINOR, "openprom", &openprom_fops
575 EXPORT_NO_SYMBOLS;
577 #ifdef MODULE
578 int init_module(void)
579 #else
580 int __init openprom_init(void)
581 #endif
583 unsigned long flags;
584 int error;
586 error = misc_register(&openprom_dev);
587 if (error) {
588 printk(KERN_ERR "openprom: unable to get misc minor\n");
589 return error;
592 save_and_cli(flags);
593 options_node = prom_getchild(prom_root_node);
594 options_node = prom_searchsiblings(options_node,"options");
595 restore_flags(flags);
597 if (options_node == 0 || options_node == -1) {
598 printk(KERN_ERR "openprom: unable to find options node\n");
599 misc_deregister(&openprom_dev);
600 return -EIO;
603 return 0;
606 #ifdef MODULE
607 void cleanup_module(void)
609 misc_deregister(&openprom_dev);
611 #endif