root/drivers/sbus/char/openprom.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. copyin
  2. getstrings
  3. copyout
  4. opromgetprop
  5. opromnxtprop
  6. opromsetopt
  7. opromnext
  8. oprompci2node
  9. oprompath2node
  10. opromgetbootargs
  11. openprom_sunos_ioctl
  12. get_node
  13. copyin_string
  14. opiocget
  15. opiocnextprop
  16. opiocset
  17. opiocgetnext
  18. openprom_bsd_ioctl
  19. openprom_ioctl
  20. openprom_compat_ioctl
  21. openprom_open
  22. openprom_release
  23. openprom_init
  24. openprom_cleanup

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Linux/SPARC PROM Configuration Driver
   4  * Copyright (C) 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu)
   5  * Copyright (C) 1996 Eddie C. Dost  (ecd@skynet.be)
   6  *
   7  * This character device driver allows user programs to access the
   8  * PROM device tree. It is compatible with the SunOS /dev/openprom
   9  * driver and the NetBSD /dev/openprom driver. The SunOS eeprom
  10  * utility works without any modifications.
  11  *
  12  * The driver uses a minor number under the misc device major. The
  13  * file read/write mode determines the type of access to the PROM.
  14  * Interrupts are disabled whenever the driver calls into the PROM for
  15  * sanity's sake.
  16  */
  17 
  18 
  19 #include <linux/module.h>
  20 #include <linux/kernel.h>
  21 #include <linux/errno.h>
  22 #include <linux/slab.h>
  23 #include <linux/mutex.h>
  24 #include <linux/string.h>
  25 #include <linux/miscdevice.h>
  26 #include <linux/init.h>
  27 #include <linux/fs.h>
  28 #include <asm/oplib.h>
  29 #include <asm/prom.h>
  30 #include <linux/uaccess.h>
  31 #include <asm/openpromio.h>
  32 #ifdef CONFIG_PCI
  33 #include <linux/pci.h>
  34 #endif
  35 
  36 MODULE_AUTHOR("Thomas K. Dyas (tdyas@noc.rutgers.edu) and Eddie C. Dost  (ecd@skynet.be)");
  37 MODULE_DESCRIPTION("OPENPROM Configuration Driver");
  38 MODULE_LICENSE("GPL");
  39 MODULE_VERSION("1.0");
  40 MODULE_ALIAS_MISCDEV(SUN_OPENPROM_MINOR);
  41 
  42 /* Private data kept by the driver for each descriptor. */
  43 typedef struct openprom_private_data
  44 {
  45         struct device_node *current_node; /* Current node for SunOS ioctls. */
  46         struct device_node *lastnode; /* Last valid node used by BSD ioctls. */
  47 } DATA;
  48 
  49 /* ID of the PROM node containing all of the EEPROM options. */
  50 static DEFINE_MUTEX(openprom_mutex);
  51 static struct device_node *options_node;
  52 
  53 /*
  54  * Copy an openpromio structure into kernel space from user space.
  55  * This routine does error checking to make sure that all memory
  56  * accesses are within bounds. A pointer to the allocated openpromio
  57  * structure will be placed in "*opp_p". Return value is the length
  58  * of the user supplied buffer.
  59  */
  60 static int copyin(struct openpromio __user *info, struct openpromio **opp_p)
  61 {
  62         unsigned int bufsize;
  63 
  64         if (!info || !opp_p)
  65                 return -EFAULT;
  66 
  67         if (get_user(bufsize, &info->oprom_size))
  68                 return -EFAULT;
  69 
  70         if (bufsize == 0)
  71                 return -EINVAL;
  72 
  73         /* If the bufsize is too large, just limit it.
  74          * Fix from Jason Rappleye.
  75          */
  76         if (bufsize > OPROMMAXPARAM)
  77                 bufsize = OPROMMAXPARAM;
  78 
  79         if (!(*opp_p = kzalloc(sizeof(int) + bufsize + 1, GFP_KERNEL)))
  80                 return -ENOMEM;
  81 
  82         if (copy_from_user(&(*opp_p)->oprom_array,
  83                            &info->oprom_array, bufsize)) {
  84                 kfree(*opp_p);
  85                 return -EFAULT;
  86         }
  87         return bufsize;
  88 }
  89 
  90 static int getstrings(struct openpromio __user *info, struct openpromio **opp_p)
  91 {
  92         int n, bufsize;
  93         char c;
  94 
  95         if (!info || !opp_p)
  96                 return -EFAULT;
  97 
  98         if (!(*opp_p = kzalloc(sizeof(int) + OPROMMAXPARAM + 1, GFP_KERNEL)))
  99                 return -ENOMEM;
 100 
 101         (*opp_p)->oprom_size = 0;
 102 
 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;
 108                 }
 109                 if (c == '\0')
 110                         n++;
 111                 (*opp_p)->oprom_array[bufsize++] = c;
 112         }
 113         if (!n) {
 114                 kfree(*opp_p);
 115                 return -EINVAL;
 116         }
 117         return bufsize;
 118 }
 119 
 120 /*
 121  * Copy an openpromio structure in kernel space back to user space.
 122  */
 123 static int copyout(void __user *info, struct openpromio *opp, int len)
 124 {
 125         if (copy_to_user(info, opp, len))
 126                 return -EFAULT;
 127         return 0;
 128 }
 129 
 130 static int opromgetprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize)
 131 {
 132         const void *pval;
 133         int len;
 134 
 135         if (!dp ||
 136             !(pval = of_get_property(dp, op->oprom_array, &len)) ||
 137             len <= 0 || len > bufsize)
 138                 return copyout(argp, op, sizeof(int));
 139 
 140         memcpy(op->oprom_array, pval, len);
 141         op->oprom_array[len] = '\0';
 142         op->oprom_size = len;
 143 
 144         return copyout(argp, op, sizeof(int) + bufsize);
 145 }
 146 
 147 static int opromnxtprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize)
 148 {
 149         struct property *prop;
 150         int len;
 151 
 152         if (!dp)
 153                 return copyout(argp, op, sizeof(int));
 154         if (op->oprom_array[0] == '\0') {
 155                 prop = dp->properties;
 156                 if (!prop)
 157                         return copyout(argp, op, sizeof(int));
 158                 len = strlen(prop->name);
 159         } else {
 160                 prop = of_find_property(dp, op->oprom_array, NULL);
 161 
 162                 if (!prop ||
 163                     !prop->next ||
 164                     (len = strlen(prop->next->name)) + 1 > bufsize)
 165                         return copyout(argp, op, sizeof(int));
 166 
 167                 prop = prop->next;
 168         }
 169 
 170         memcpy(op->oprom_array, prop->name, len);
 171         op->oprom_array[len] = '\0';
 172         op->oprom_size = ++len;
 173 
 174         return copyout(argp, op, sizeof(int) + bufsize);
 175 }
 176 
 177 static int opromsetopt(struct device_node *dp, struct openpromio *op, int bufsize)
 178 {
 179         char *buf = op->oprom_array + strlen(op->oprom_array) + 1;
 180         int len = op->oprom_array + bufsize - buf;
 181 
 182         return of_set_property(options_node, op->oprom_array, buf, len);
 183 }
 184 
 185 static int opromnext(void __user *argp, unsigned int cmd, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data)
 186 {
 187         phandle ph;
 188 
 189         BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
 190 
 191         if (bufsize < sizeof(phandle))
 192                 return -EINVAL;
 193 
 194         ph = *((int *) op->oprom_array);
 195         if (ph) {
 196                 dp = of_find_node_by_phandle(ph);
 197                 if (!dp)
 198                         return -EINVAL;
 199 
 200                 switch (cmd) {
 201                 case OPROMNEXT:
 202                         dp = dp->sibling;
 203                         break;
 204 
 205                 case OPROMCHILD:
 206                         dp = dp->child;
 207                         break;
 208 
 209                 case OPROMSETCUR:
 210                 default:
 211                         break;
 212                 }
 213         } else {
 214                 /* Sibling of node zero is the root node.  */
 215                 if (cmd != OPROMNEXT)
 216                         return -EINVAL;
 217 
 218                 dp = of_find_node_by_path("/");
 219         }
 220 
 221         ph = 0;
 222         if (dp)
 223                 ph = dp->phandle;
 224 
 225         data->current_node = dp;
 226         *((int *) op->oprom_array) = ph;
 227         op->oprom_size = sizeof(phandle);
 228 
 229         return copyout(argp, op, bufsize + sizeof(int));
 230 }
 231 
 232 static int oprompci2node(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data)
 233 {
 234         int err = -EINVAL;
 235 
 236         if (bufsize >= 2*sizeof(int)) {
 237 #ifdef CONFIG_PCI
 238                 struct pci_dev *pdev;
 239                 struct device_node *dp;
 240 
 241                 pdev = pci_get_domain_bus_and_slot(0,
 242                                                 ((int *) op->oprom_array)[0],
 243                                                 ((int *) op->oprom_array)[1]);
 244 
 245                 dp = pci_device_to_OF_node(pdev);
 246                 data->current_node = dp;
 247                 *((int *)op->oprom_array) = dp->phandle;
 248                 op->oprom_size = sizeof(int);
 249                 err = copyout(argp, op, bufsize + sizeof(int));
 250 
 251                 pci_dev_put(pdev);
 252 #endif
 253         }
 254 
 255         return err;
 256 }
 257 
 258 static int oprompath2node(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data)
 259 {
 260         phandle ph = 0;
 261 
 262         dp = of_find_node_by_path(op->oprom_array);
 263         if (dp)
 264                 ph = dp->phandle;
 265         data->current_node = dp;
 266         *((int *)op->oprom_array) = ph;
 267         op->oprom_size = sizeof(int);
 268 
 269         return copyout(argp, op, bufsize + sizeof(int));
 270 }
 271 
 272 static int opromgetbootargs(void __user *argp, struct openpromio *op, int bufsize)
 273 {
 274         char *buf = saved_command_line;
 275         int len = strlen(buf);
 276 
 277         if (len > bufsize)
 278                 return -EINVAL;
 279 
 280         strcpy(op->oprom_array, buf);
 281         op->oprom_size = len;
 282 
 283         return copyout(argp, op, bufsize + sizeof(int));
 284 }
 285 
 286 /*
 287  *      SunOS and Solaris /dev/openprom ioctl calls.
 288  */
 289 static long openprom_sunos_ioctl(struct file * file,
 290                                  unsigned int cmd, unsigned long arg,
 291                                  struct device_node *dp)
 292 {
 293         DATA *data = file->private_data;
 294         struct openpromio *opp = NULL;
 295         int bufsize, error = 0;
 296         static int cnt;
 297         void __user *argp = (void __user *)arg;
 298 
 299         if (cmd == OPROMSETOPT)
 300                 bufsize = getstrings(argp, &opp);
 301         else
 302                 bufsize = copyin(argp, &opp);
 303 
 304         if (bufsize < 0)
 305                 return bufsize;
 306 
 307         mutex_lock(&openprom_mutex);
 308 
 309         switch (cmd) {
 310         case OPROMGETOPT:
 311         case OPROMGETPROP:
 312                 error = opromgetprop(argp, dp, opp, bufsize);
 313                 break;
 314 
 315         case OPROMNXTOPT:
 316         case OPROMNXTPROP:
 317                 error = opromnxtprop(argp, dp, opp, bufsize);
 318                 break;
 319 
 320         case OPROMSETOPT:
 321         case OPROMSETOPT2:
 322                 error = opromsetopt(dp, opp, bufsize);
 323                 break;
 324 
 325         case OPROMNEXT:
 326         case OPROMCHILD:
 327         case OPROMSETCUR:
 328                 error = opromnext(argp, cmd, dp, opp, bufsize, data);
 329                 break;
 330 
 331         case OPROMPCI2NODE:
 332                 error = oprompci2node(argp, dp, opp, bufsize, data);
 333                 break;
 334 
 335         case OPROMPATH2NODE:
 336                 error = oprompath2node(argp, dp, opp, bufsize, data);
 337                 break;
 338 
 339         case OPROMGETBOOTARGS:
 340                 error = opromgetbootargs(argp, opp, bufsize);
 341                 break;
 342 
 343         case OPROMU2P:
 344         case OPROMGETCONS:
 345         case OPROMGETFBNAME:
 346                 if (cnt++ < 10)
 347                         printk(KERN_INFO "openprom_sunos_ioctl: unimplemented ioctl\n");
 348                 error = -EINVAL;
 349                 break;
 350         default:
 351                 if (cnt++ < 10)
 352                         printk(KERN_INFO "openprom_sunos_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg);
 353                 error = -EINVAL;
 354                 break;
 355         }
 356 
 357         kfree(opp);
 358         mutex_unlock(&openprom_mutex);
 359 
 360         return error;
 361 }
 362 
 363 static struct device_node *get_node(phandle n, DATA *data)
 364 {
 365         struct device_node *dp = of_find_node_by_phandle(n);
 366 
 367         if (dp)
 368                 data->lastnode = dp;
 369 
 370         return dp;
 371 }
 372 
 373 /* Copy in a whole string from userspace into kernelspace. */
 374 static char * copyin_string(char __user *user, size_t len)
 375 {
 376         if ((ssize_t)len < 0 || (ssize_t)(len + 1) < 0)
 377                 return ERR_PTR(-EINVAL);
 378 
 379         return memdup_user_nul(user, len);
 380 }
 381 
 382 /*
 383  *      NetBSD /dev/openprom ioctl calls.
 384  */
 385 static int opiocget(void __user *argp, DATA *data)
 386 {
 387         struct opiocdesc op;
 388         struct device_node *dp;
 389         char *str;
 390         const void *pval;
 391         int err, len;
 392 
 393         if (copy_from_user(&op, argp, sizeof(op)))
 394                 return -EFAULT;
 395 
 396         dp = get_node(op.op_nodeid, data);
 397 
 398         str = copyin_string(op.op_name, op.op_namelen);
 399         if (IS_ERR(str))
 400                 return PTR_ERR(str);
 401 
 402         pval = of_get_property(dp, str, &len);
 403         err = 0;
 404         if (!pval || len > op.op_buflen) {
 405                 err = -EINVAL;
 406         } else {
 407                 op.op_buflen = len;
 408                 if (copy_to_user(argp, &op, sizeof(op)) ||
 409                     copy_to_user(op.op_buf, pval, len))
 410                         err = -EFAULT;
 411         }
 412         kfree(str);
 413 
 414         return err;
 415 }
 416 
 417 static int opiocnextprop(void __user *argp, DATA *data)
 418 {
 419         struct opiocdesc op;
 420         struct device_node *dp;
 421         struct property *prop;
 422         char *str;
 423         int len;
 424 
 425         if (copy_from_user(&op, argp, sizeof(op)))
 426                 return -EFAULT;
 427 
 428         dp = get_node(op.op_nodeid, data);
 429         if (!dp)
 430                 return -EINVAL;
 431 
 432         str = copyin_string(op.op_name, op.op_namelen);
 433         if (IS_ERR(str))
 434                 return PTR_ERR(str);
 435 
 436         if (str[0] == '\0') {
 437                 prop = dp->properties;
 438         } else {
 439                 prop = of_find_property(dp, str, NULL);
 440                 if (prop)
 441                         prop = prop->next;
 442         }
 443         kfree(str);
 444 
 445         if (!prop)
 446                 len = 0;
 447         else
 448                 len = prop->length;
 449 
 450         if (len > op.op_buflen)
 451                 len = op.op_buflen;
 452 
 453         if (copy_to_user(argp, &op, sizeof(op)))
 454                 return -EFAULT;
 455 
 456         if (len &&
 457             copy_to_user(op.op_buf, prop->value, len))
 458                 return -EFAULT;
 459 
 460         return 0;
 461 }
 462 
 463 static int opiocset(void __user *argp, DATA *data)
 464 {
 465         struct opiocdesc op;
 466         struct device_node *dp;
 467         char *str, *tmp;
 468         int err;
 469 
 470         if (copy_from_user(&op, argp, sizeof(op)))
 471                 return -EFAULT;
 472 
 473         dp = get_node(op.op_nodeid, data);
 474         if (!dp)
 475                 return -EINVAL;
 476 
 477         str = copyin_string(op.op_name, op.op_namelen);
 478         if (IS_ERR(str))
 479                 return PTR_ERR(str);
 480 
 481         tmp = copyin_string(op.op_buf, op.op_buflen);
 482         if (IS_ERR(tmp)) {
 483                 kfree(str);
 484                 return PTR_ERR(tmp);
 485         }
 486 
 487         err = of_set_property(dp, str, tmp, op.op_buflen);
 488 
 489         kfree(str);
 490         kfree(tmp);
 491 
 492         return err;
 493 }
 494 
 495 static int opiocgetnext(unsigned int cmd, void __user *argp)
 496 {
 497         struct device_node *dp;
 498         phandle nd;
 499 
 500         BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
 501 
 502         if (copy_from_user(&nd, argp, sizeof(phandle)))
 503                 return -EFAULT;
 504 
 505         if (nd == 0) {
 506                 if (cmd != OPIOCGETNEXT)
 507                         return -EINVAL;
 508                 dp = of_find_node_by_path("/");
 509         } else {
 510                 dp = of_find_node_by_phandle(nd);
 511                 nd = 0;
 512                 if (dp) {
 513                         if (cmd == OPIOCGETNEXT)
 514                                 dp = dp->sibling;
 515                         else
 516                                 dp = dp->child;
 517                 }
 518         }
 519         if (dp)
 520                 nd = dp->phandle;
 521         if (copy_to_user(argp, &nd, sizeof(phandle)))
 522                 return -EFAULT;
 523 
 524         return 0;
 525 }
 526 
 527 static int openprom_bsd_ioctl(struct file * file,
 528                               unsigned int cmd, unsigned long arg)
 529 {
 530         DATA *data = file->private_data;
 531         void __user *argp = (void __user *)arg;
 532         int err;
 533 
 534         mutex_lock(&openprom_mutex);
 535         switch (cmd) {
 536         case OPIOCGET:
 537                 err = opiocget(argp, data);
 538                 break;
 539 
 540         case OPIOCNEXTPROP:
 541                 err = opiocnextprop(argp, data);
 542                 break;
 543 
 544         case OPIOCSET:
 545                 err = opiocset(argp, data);
 546                 break;
 547 
 548         case OPIOCGETOPTNODE:
 549                 BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
 550 
 551                 err = 0;
 552                 if (copy_to_user(argp, &options_node->phandle, sizeof(phandle)))
 553                         err = -EFAULT;
 554                 break;
 555 
 556         case OPIOCGETNEXT:
 557         case OPIOCGETCHILD:
 558                 err = opiocgetnext(cmd, argp);
 559                 break;
 560 
 561         default:
 562                 err = -EINVAL;
 563                 break;
 564         }
 565         mutex_unlock(&openprom_mutex);
 566 
 567         return err;
 568 }
 569 
 570 
 571 /*
 572  *      Handoff control to the correct ioctl handler.
 573  */
 574 static long openprom_ioctl(struct file * file,
 575                            unsigned int cmd, unsigned long arg)
 576 {
 577         DATA *data = file->private_data;
 578 
 579         switch (cmd) {
 580         case OPROMGETOPT:
 581         case OPROMNXTOPT:
 582                 if ((file->f_mode & FMODE_READ) == 0)
 583                         return -EPERM;
 584                 return openprom_sunos_ioctl(file, cmd, arg,
 585                                             options_node);
 586 
 587         case OPROMSETOPT:
 588         case OPROMSETOPT2:
 589                 if ((file->f_mode & FMODE_WRITE) == 0)
 590                         return -EPERM;
 591                 return openprom_sunos_ioctl(file, cmd, arg,
 592                                             options_node);
 593 
 594         case OPROMNEXT:
 595         case OPROMCHILD:
 596         case OPROMGETPROP:
 597         case OPROMNXTPROP:
 598                 if ((file->f_mode & FMODE_READ) == 0)
 599                         return -EPERM;
 600                 return openprom_sunos_ioctl(file, cmd, arg,
 601                                             data->current_node);
 602 
 603         case OPROMU2P:
 604         case OPROMGETCONS:
 605         case OPROMGETFBNAME:
 606         case OPROMGETBOOTARGS:
 607         case OPROMSETCUR:
 608         case OPROMPCI2NODE:
 609         case OPROMPATH2NODE:
 610                 if ((file->f_mode & FMODE_READ) == 0)
 611                         return -EPERM;
 612                 return openprom_sunos_ioctl(file, cmd, arg, NULL);
 613 
 614         case OPIOCGET:
 615         case OPIOCNEXTPROP:
 616         case OPIOCGETOPTNODE:
 617         case OPIOCGETNEXT:
 618         case OPIOCGETCHILD:
 619                 if ((file->f_mode & FMODE_READ) == 0)
 620                         return -EBADF;
 621                 return openprom_bsd_ioctl(file,cmd,arg);
 622 
 623         case OPIOCSET:
 624                 if ((file->f_mode & FMODE_WRITE) == 0)
 625                         return -EBADF;
 626                 return openprom_bsd_ioctl(file,cmd,arg);
 627 
 628         default:
 629                 return -EINVAL;
 630         };
 631 }
 632 
 633 static long openprom_compat_ioctl(struct file *file, unsigned int cmd,
 634                 unsigned long arg)
 635 {
 636         long rval = -ENOTTY;
 637 
 638         /*
 639          * SunOS/Solaris only, the NetBSD one's have embedded pointers in
 640          * the arg which we'd need to clean up...
 641          */
 642         switch (cmd) {
 643         case OPROMGETOPT:
 644         case OPROMSETOPT:
 645         case OPROMNXTOPT:
 646         case OPROMSETOPT2:
 647         case OPROMNEXT:
 648         case OPROMCHILD:
 649         case OPROMGETPROP:
 650         case OPROMNXTPROP:
 651         case OPROMU2P:
 652         case OPROMGETCONS:
 653         case OPROMGETFBNAME:
 654         case OPROMGETBOOTARGS:
 655         case OPROMSETCUR:
 656         case OPROMPCI2NODE:
 657         case OPROMPATH2NODE:
 658                 rval = openprom_ioctl(file, cmd, arg);
 659                 break;
 660         }
 661 
 662         return rval;
 663 }
 664 
 665 static int openprom_open(struct inode * inode, struct file * file)
 666 {
 667         DATA *data;
 668 
 669         data = kmalloc(sizeof(DATA), GFP_KERNEL);
 670         if (!data)
 671                 return -ENOMEM;
 672 
 673         mutex_lock(&openprom_mutex);
 674         data->current_node = of_find_node_by_path("/");
 675         data->lastnode = data->current_node;
 676         file->private_data = (void *) data;
 677         mutex_unlock(&openprom_mutex);
 678 
 679         return 0;
 680 }
 681 
 682 static int openprom_release(struct inode * inode, struct file * file)
 683 {
 684         kfree(file->private_data);
 685         return 0;
 686 }
 687 
 688 static const struct file_operations openprom_fops = {
 689         .owner =        THIS_MODULE,
 690         .llseek =       no_llseek,
 691         .unlocked_ioctl = openprom_ioctl,
 692         .compat_ioctl = openprom_compat_ioctl,
 693         .open =         openprom_open,
 694         .release =      openprom_release,
 695 };
 696 
 697 static struct miscdevice openprom_dev = {
 698         .minor          = SUN_OPENPROM_MINOR,
 699         .name           = "openprom",
 700         .fops           = &openprom_fops,
 701 };
 702 
 703 static int __init openprom_init(void)
 704 {
 705         int err;
 706 
 707         err = misc_register(&openprom_dev);
 708         if (err)
 709                 return err;
 710 
 711         options_node = of_get_child_by_name(of_find_node_by_path("/"), "options");
 712         if (!options_node) {
 713                 misc_deregister(&openprom_dev);
 714                 return -EIO;
 715         }
 716 
 717         return 0;
 718 }
 719 
 720 static void __exit openprom_cleanup(void)
 721 {
 722         misc_deregister(&openprom_dev);
 723 }
 724 
 725 module_init(openprom_init);
 726 module_exit(openprom_cleanup);

/* [<][>][^][v][top][bottom][index][help] */