1 /* 2 * $Id: mtdchar.c,v 1.76 2005/11/07 11:14:20 gleixner Exp $ 3 * 4 * Character-device access to raw MTD devices. 5 * 6 */ 7 8 #include <linux/device.h> 9 #include <linux/fs.h> 10 #include <linux/mm.h> 11 #include <linux/err.h> 12 #include <linux/init.h> 13 #include <linux/kernel.h> 14 #include <linux/module.h> 15 #include <linux/slab.h> 16 #include <linux/sched.h> 17 18 #include <linux/mtd/mtd.h> 19 #include <linux/mtd/compatmac.h> 20 21 #include <asm/uaccess.h> 22 23 static struct class *mtd_class; 24 25 static void mtd_notify_add(struct mtd_info* mtd) 26 { 27 if (!mtd) 28 return; 29 30 class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2), 31 NULL, "mtd%d", mtd->index); 32 33 class_device_create(mtd_class, NULL, 34 MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), 35 NULL, "mtd%dro", mtd->index); 36 } 37 38 static void mtd_notify_remove(struct mtd_info* mtd) 39 { 40 if (!mtd) 41 return; 42 43 class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2)); 44 class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1)); 45 } 46 47 static struct mtd_notifier notifier = { 48 .add = mtd_notify_add, 49 .remove = mtd_notify_remove, 50 }; 51 52 /* 53 * Data structure to hold the pointer to the mtd device as well 54 * as mode information ofr various use cases. 55 */ 56 struct mtd_file_info { 57 struct mtd_info *mtd; 58 enum mtd_file_modes mode; 59 }; 60 61 static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) 62 { 63 struct mtd_file_info *mfi = file->private_data; 64 struct mtd_info *mtd = mfi->mtd; 65 66 switch (orig) { 67 case SEEK_SET: 68 break; 69 case SEEK_CUR: 70 offset += file->f_pos; 71 break; 72 case SEEK_END: 73 offset += mtd->size; 74 break; 75 default: 76 return -EINVAL; 77 } 78 79 if (offset >= 0 && offset <= mtd->size) 80 return file->f_pos = offset; 81 82 return -EINVAL; 83 } 84 85 86 87 static int mtd_open(struct inode *inode, struct file *file) 88 { 89 int minor = iminor(inode); 90 int devnum = minor >> 1; 91 struct mtd_info *mtd; 92 struct mtd_file_info *mfi; 93 94 DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n"); 95 96 if (devnum >= MAX_MTD_DEVICES) 97 return -ENODEV; 98 99 /* You can't open the RO devices RW */ 100 if ((file->f_mode & 2) && (minor & 1)) 101 return -EACCES; 102 103 mtd = get_mtd_device(NULL, devnum); 104 105 if (IS_ERR(mtd)) 106 return PTR_ERR(mtd); 107 108 if (MTD_ABSENT == mtd->type) { 109 put_mtd_device(mtd); 110 return -ENODEV; 111 } 112 113 /* You can't open it RW if it's not a writeable device */ 114 if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) { 115 put_mtd_device(mtd); 116 return -EACCES; 117 } 118 119 mfi = kzalloc(sizeof(*mfi), GFP_KERNEL); 120 if (!mfi) { 121 put_mtd_device(mtd); 122 return -ENOMEM; 123 } 124 mfi->mtd = mtd; 125 file->private_data = mfi; 126 127 return 0; 128 } /* mtd_open */ 129 130 /*====================================================================*/ 131 132 static int mtd_close(struct inode *inode, struct file *file) 133 { 134 struct mtd_file_info *mfi = file->private_data; 135 struct mtd_info *mtd = mfi->mtd; 136 137 DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); 138 139 /* Only sync if opened RW */ 140 if ((file->f_mode & 2) && mtd->sync) 141 mtd->sync(mtd); 142 143 put_mtd_device(mtd); 144 file->private_data = NULL; 145 kfree(mfi); 146 147 return 0; 148 } /* mtd_close */ 149 150 /* FIXME: This _really_ needs to die. In 2.5, we should lock the 151 userspace buffer down and use it directly with readv/writev. 152 */ 153 #define MAX_KMALLOC_SIZE 0x20000 154 155 static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) 156 { 157 struct mtd_file_info *mfi = file->private_data; 158 struct mtd_info *mtd = mfi->mtd; 159 size_t retlen=0; 160 size_t total_retlen=0; 161 int ret=0; 162 int len; 163 char *kbuf; 164 165 DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n"); 166 167 if (*ppos + count > mtd->size) 168 count = mtd->size - *ppos; 169 170 if (!count) 171 return 0; 172 173 /* FIXME: Use kiovec in 2.5 to lock down the user's buffers 174 and pass them directly to the MTD functions */ 175 176 if (count > MAX_KMALLOC_SIZE) 177 kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL); 178 else 179 kbuf=kmalloc(count, GFP_KERNEL); 180 181 if (!kbuf) 182 return -ENOMEM; 183 184 while (count) { 185 186 if (count > MAX_KMALLOC_SIZE) 187 len = MAX_KMALLOC_SIZE; 188 else 189 len = count; 190 191 switch (mfi->mode) { 192 case MTD_MODE_OTP_FACTORY: 193 ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf); 194 break; 195 case MTD_MODE_OTP_USER: 196 ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); 197 break; 198 case MTD_MODE_RAW: 199 { 200 struct mtd_oob_ops ops; 201 202 ops.mode = MTD_OOB_RAW; 203 ops.datbuf = kbuf; 204 ops.oobbuf = NULL; 205 ops.len = len; 206 207 ret = mtd->read_oob(mtd, *ppos, &ops); 208 retlen = ops.retlen; 209 break; 210 } 211 default: 212 ret = mtd->read(mtd, *ppos, len, &retlen, kbuf); 213 } 214 /* Nand returns -EBADMSG on ecc errors, but it returns 215 * the data. For our userspace tools it is important 216 * to dump areas with ecc errors ! 217 * For kernel internal usage it also might return -EUCLEAN 218 * to signal the caller that a bitflip has occured and has 219 * been corrected by the ECC algorithm. 220 * Userspace software which accesses NAND this way 221 * must be aware of the fact that it deals with NAND 222 */ 223 if (!ret || (ret == -EUCLEAN) || (ret == -EBADMSG)) { 224 *ppos += retlen; 225 if (copy_to_user(buf, kbuf, retlen)) { 226 kfree(kbuf); 227 return -EFAULT; 228 } 229 else 230 total_retlen += retlen; 231 232 count -= retlen; 233 buf += retlen; 234 if (retlen == 0) 235 count = 0; 236 } 237 else { 238 kfree(kbuf); 239 return ret; 240 } 241 242 } 243 244 kfree(kbuf); 245 return total_retlen; 246 } /* mtd_read */ 247 248 static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos) 249 { 250 struct mtd_file_info *mfi = file->private_data; 251 struct mtd_info *mtd = mfi->mtd; 252 char *kbuf; 253 size_t retlen; 254 size_t total_retlen=0; 255 int ret=0; 256 int len; 257 258 DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n"); 259 260 if (*ppos == mtd->size) 261 return -ENOSPC; 262 263 if (*ppos + count > mtd->size) 264 count = mtd->size - *ppos; 265 266 if (!count) 267 return 0; 268 269 if (count > MAX_KMALLOC_SIZE) 270 kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL); 271 else 272 kbuf=kmalloc(count, GFP_KERNEL); 273 274 if (!kbuf) 275 return -ENOMEM; 276 277 while (count) { 278 279 if (count > MAX_KMALLOC_SIZE) 280 len = MAX_KMALLOC_SIZE; 281 else 282 len = count; 283 284 if (copy_from_user(kbuf, buf, len)) { 285 kfree(kbuf); 286 return -EFAULT; 287 } 288 289 switch (mfi->mode) { 290 case MTD_MODE_OTP_FACTORY: 291 ret = -EROFS; 292 break; 293 case MTD_MODE_OTP_USER: 294 if (!mtd->write_user_prot_reg) { 295 ret = -EOPNOTSUPP; 296 break; 297 } 298 ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); 299 break; 300 301 case MTD_MODE_RAW: 302 { 303 struct mtd_oob_ops ops; 304 305 ops.mode = MTD_OOB_RAW; 306 ops.datbuf = kbuf; 307 ops.oobbuf = NULL; 308 ops.len = len; 309 310 ret = mtd->write_oob(mtd, *ppos, &ops); 311 retlen = ops.retlen; 312 break; 313 } 314 315 default: 316 ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); 317 } 318 if (!ret) { 319 *ppos += retlen; 320 total_retlen += retlen; 321 count -= retlen; 322 buf += retlen; 323 } 324 else { 325 kfree(kbuf); 326 return ret; 327 } 328 } 329 330 kfree(kbuf); 331 return total_retlen; 332 } /* mtd_write */ 333 334 /*====================================================================== 335 336 IOCTL calls for getting device parameters. 337 338 ======================================================================*/ 339 static void mtdchar_erase_callback (struct erase_info *instr) 340 { 341 wake_up((wait_queue_head_t *)instr->priv); 342 } 343 344 #if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP) 345 static int otp_select_filemode(struct mtd_file_info *mfi, int mode) 346 { 347 struct mtd_info *mtd = mfi->mtd; 348 int ret = 0; 349 350 switch (mode) { 351 case MTD_OTP_FACTORY: 352 if (!mtd->read_fact_prot_reg) 353 ret = -EOPNOTSUPP; 354 else 355 mfi->mode = MTD_MODE_OTP_FACTORY; 356 break; 357 case MTD_OTP_USER: 358 if (!mtd->read_fact_prot_reg) 359 ret = -EOPNOTSUPP; 360 else 361 mfi->mode = MTD_MODE_OTP_USER; 362 break; 363 default: 364 ret = -EINVAL; 365 case MTD_OTP_OFF: 366 break; 367 } 368 return ret; 369 } 370 #else 371 # define otp_select_filemode(f,m) -EOPNOTSUPP 372 #endif 373 374 static int mtd_ioctl(struct inode *inode, struct file *file, 375 u_int cmd, u_long arg) 376 { 377 struct mtd_file_info *mfi = file->private_data; 378 struct mtd_info *mtd = mfi->mtd; 379 void __user *argp = (void __user *)arg; 380 int ret = 0; 381 u_long size; 382 struct mtd_info_user info; 383 384 DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n"); 385 386 size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; 387 if (cmd & IOC_IN) { 388 if (!access_ok(VERIFY_READ, argp, size)) 389 return -EFAULT; 390 } 391 if (cmd & IOC_OUT) { 392 if (!access_ok(VERIFY_WRITE, argp, size)) 393 return -EFAULT; 394 } 395 396 switch (cmd) { 397 case MEMGETREGIONCOUNT: 398 if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int))) 399 return -EFAULT; 400 break; 401 402 case MEMGETREGIONINFO: 403 { 404 struct region_info_user ur; 405 406 if (copy_from_user(&ur, argp, sizeof(struct region_info_user))) 407 return -EFAULT; 408 409 if (ur.regionindex >= mtd->numeraseregions) 410 return -EINVAL; 411 if (copy_to_user(argp, &(mtd->eraseregions[ur.regionindex]), 412 sizeof(struct mtd_erase_region_info))) 413 return -EFAULT; 414 break; 415 } 416 417 case MEMGETINFO: 418 info.type = mtd->type; 419 info.flags = mtd->flags; 420 info.size = mtd->size; 421 info.erasesize = mtd->erasesize; 422 info.writesize = mtd->writesize; 423 info.oobsize = mtd->oobsize; 424 /* The below fields are obsolete */ 425 info.ecctype = -1; 426 info.eccsize = 0; 427 if (copy_to_user(argp, &info, sizeof(struct mtd_info_user))) 428 return -EFAULT; 429 break; 430 431 case MEMERASE: 432 { 433 struct erase_info *erase; 434 435 if(!(file->f_mode & 2)) 436 return -EPERM; 437 438 erase=kzalloc(sizeof(struct erase_info),GFP_KERNEL); 439 if (!erase) 440 ret = -ENOMEM; 441 else { 442 wait_queue_head_t waitq; 443 DECLARE_WAITQUEUE(wait, current); 444 445 init_waitqueue_head(&waitq); 446 447 if (copy_from_user(&erase->addr, argp, 448 sizeof(struct erase_info_user))) { 449 kfree(erase); 450 return -EFAULT; 451 } 452 erase->mtd = mtd; 453 erase->callback = mtdchar_erase_callback; 454 erase->priv = (unsigned long)&waitq; 455 456 /* 457 FIXME: Allow INTERRUPTIBLE. Which means 458 not having the wait_queue head on the stack. 459 460 If the wq_head is on the stack, and we 461 leave because we got interrupted, then the 462 wq_head is no longer there when the 463 callback routine tries to wake us up. 464 */ 465 ret = mtd->erase(mtd, erase); 466 if (!ret) { 467 set_current_state(TASK_UNINTERRUPTIBLE); 468 add_wait_queue(&waitq, &wait); 469 if (erase->state != MTD_ERASE_DONE && 470 erase->state != MTD_ERASE_FAILED) 471 schedule(); 472 remove_wait_queue(&waitq, &wait); 473 set_current_state(TASK_RUNNING); 474 475 ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0; 476 } 477 kfree(erase); 478 } 479 break; 480 } 481 482 case MEMWRITEOOB: 483 { 484 struct mtd_oob_buf buf; 485 struct mtd_oob_ops ops; 486 487 if(!(file->f_mode & 2)) 488 return -EPERM; 489 490 if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) 491 return -EFAULT; 492 493 if (buf.length > 4096) 494 return -EINVAL; 495 496 if (!mtd->write_oob) 497 ret = -EOPNOTSUPP; 498 else 499 ret = access_ok(VERIFY_READ, buf.ptr, 500 buf.length) ? 0 : EFAULT; 501 502 if (ret) 503 return ret; 504 505 ops.ooblen = buf.length; 506 ops.ooboffs = buf.start & (mtd->oobsize - 1); 507 ops.datbuf = NULL; 508 ops.mode = MTD_OOB_PLACE; 509 510 if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) 511 return -EINVAL; 512 513 ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); 514 if (!ops.oobbuf) 515 return -ENOMEM; 516 517 if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) { 518 kfree(ops.oobbuf); 519 return -EFAULT; 520 } 521 522 buf.start &= ~(mtd->oobsize - 1); 523 ret = mtd->write_oob(mtd, buf.start, &ops); 524 525 if (copy_to_user(argp + sizeof(uint32_t), &ops.oobretlen, 526 sizeof(uint32_t))) 527 ret = -EFAULT; 528 529 kfree(ops.oobbuf); 530 break; 531 532 } 533 534 case MEMREADOOB: 535 { 536 struct mtd_oob_buf buf; 537 struct mtd_oob_ops ops; 538 539 if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) 540 return -EFAULT; 541 542 if (buf.length > 4096) 543 return -EINVAL; 544 545 if (!mtd->read_oob) 546 ret = -EOPNOTSUPP; 547 else 548 ret = access_ok(VERIFY_WRITE, buf.ptr, 549 buf.length) ? 0 : -EFAULT; 550 if (ret) 551 return ret; 552 553 ops.ooblen = buf.length; 554 ops.ooboffs = buf.start & (mtd->oobsize - 1); 555 ops.datbuf = NULL; 556 ops.mode = MTD_OOB_PLACE; 557 558 if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) 559 return -EINVAL; 560 561 ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); 562 if (!ops.oobbuf) 563 return -ENOMEM; 564 565 buf.start &= ~(mtd->oobsize - 1); 566 ret = mtd->read_oob(mtd, buf.start, &ops); 567 568 if (put_user(ops.oobretlen, (uint32_t __user *)argp)) 569 ret = -EFAULT; 570 else if (ops.oobretlen && copy_to_user(buf.ptr, ops.oobbuf, 571 ops.oobretlen)) 572 ret = -EFAULT; 573 574 kfree(ops.oobbuf); 575 break; 576 } 577 578 case MEMLOCK: 579 { 580 struct erase_info_user info; 581 582 if (copy_from_user(&info, argp, sizeof(info))) 583 return -EFAULT; 584 585 if (!mtd->lock) 586 ret = -EOPNOTSUPP; 587 else 588 ret = mtd->lock(mtd, info.start, info.length); 589 break; 590 } 591 592 case MEMUNLOCK: 593 { 594 struct erase_info_user info; 595 596 if (copy_from_user(&info, argp, sizeof(info))) 597 return -EFAULT; 598 599 if (!mtd->unlock) 600 ret = -EOPNOTSUPP; 601 else 602 ret = mtd->unlock(mtd, info.start, info.length); 603 break; 604 } 605 606 /* Legacy interface */ 607 case MEMGETOOBSEL: 608 { 609 struct nand_oobinfo oi; 610 611 if (!mtd->ecclayout) 612 return -EOPNOTSUPP; 613 if (mtd->ecclayout->eccbytes > ARRAY_SIZE(oi.eccpos)) 614 return -EINVAL; 615 616 oi.useecc = MTD_NANDECC_AUTOPLACE; 617 memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos)); 618 memcpy(&oi.oobfree, mtd->ecclayout->oobfree, 619 sizeof(oi.oobfree)); 620 oi.eccbytes = mtd->ecclayout->eccbytes; 621 622 if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo))) 623 return -EFAULT; 624 break; 625 } 626 627 case MEMGETBADBLOCK: 628 { 629 loff_t offs; 630 631 if (copy_from_user(&offs, argp, sizeof(loff_t))) 632 return -EFAULT; 633 if (!mtd->block_isbad) 634 ret = -EOPNOTSUPP; 635 else 636 return mtd->block_isbad(mtd, offs); 637 break; 638 } 639 640 case MEMSETBADBLOCK: 641 { 642 loff_t offs; 643 644 if (copy_from_user(&offs, argp, sizeof(loff_t))) 645 return -EFAULT; 646 if (!mtd->block_markbad) 647 ret = -EOPNOTSUPP; 648 else 649 return mtd->block_markbad(mtd, offs); 650 break; 651 } 652 653 #if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP) 654 case OTPSELECT: 655 { 656 int mode; 657 if (copy_from_user(&mode, argp, sizeof(int))) 658 return -EFAULT; 659 660 mfi->mode = MTD_MODE_NORMAL; 661 662 ret = otp_select_filemode(mfi, mode); 663 664 file->f_pos = 0; 665 break; 666 } 667 668 case OTPGETREGIONCOUNT: 669 case OTPGETREGIONINFO: 670 { 671 struct otp_info *buf = kmalloc(4096, GFP_KERNEL); 672 if (!buf) 673 return -ENOMEM; 674 ret = -EOPNOTSUPP; 675 switch (mfi->mode) { 676 case MTD_MODE_OTP_FACTORY: 677 if (mtd->get_fact_prot_info) 678 ret = mtd->get_fact_prot_info(mtd, buf, 4096); 679 break; 680 case MTD_MODE_OTP_USER: 681 if (mtd->get_user_prot_info) 682 ret = mtd->get_user_prot_info(mtd, buf, 4096); 683 break; 684 default: 685 break; 686 } 687 if (ret >= 0) { 688 if (cmd == OTPGETREGIONCOUNT) { 689 int nbr = ret / sizeof(struct otp_info); 690 ret = copy_to_user(argp, &nbr, sizeof(int)); 691 } else 692 ret = copy_to_user(argp, buf, ret); 693 if (ret) 694 ret = -EFAULT; 695 } 696 kfree(buf); 697 break; 698 } 699 700 case OTPLOCK: 701 { 702 struct otp_info info; 703 704 if (mfi->mode != MTD_MODE_OTP_USER) 705 return -EINVAL; 706 if (copy_from_user(&info, argp, sizeof(info))) 707 return -EFAULT; 708 if (!mtd->lock_user_prot_reg) 709 return -EOPNOTSUPP; 710 ret = mtd->lock_user_prot_reg(mtd, info.start, info.length); 711 break; 712 } 713 #endif 714 715 case ECCGETLAYOUT: 716 { 717 if (!mtd->ecclayout) 718 return -EOPNOTSUPP; 719 720 if (copy_to_user(argp, mtd->ecclayout, 721 sizeof(struct nand_ecclayout))) 722 return -EFAULT; 723 break; 724 } 725 726 case ECCGETSTATS: 727 { 728 if (copy_to_user(argp, &mtd->ecc_stats, 729 sizeof(struct mtd_ecc_stats))) 730 return -EFAULT; 731 break; 732 } 733 734 case MTDFILEMODE: 735 { 736 mfi->mode = 0; 737 738 switch(arg) { 739 case MTD_MODE_OTP_FACTORY: 740 case MTD_MODE_OTP_USER: 741 ret = otp_select_filemode(mfi, arg); 742 break; 743 744 case MTD_MODE_RAW: 745 if (!mtd->read_oob || !mtd->write_oob) 746 return -EOPNOTSUPP; 747 mfi->mode = arg; 748 749 case MTD_MODE_NORMAL: 750 break; 751 default: 752 ret = -EINVAL; 753 } 754 file->f_pos = 0; 755 break; 756 } 757 758 default: 759 ret = -ENOTTY; 760 } 761 762 return ret; 763 } /* memory_ioctl */ 764 765 static const struct file_operations mtd_fops = { 766 .owner = THIS_MODULE, 767 .llseek = mtd_lseek, 768 .read = mtd_read, 769 .write = mtd_write, 770 .ioctl = mtd_ioctl, 771 .open = mtd_open, 772 .release = mtd_close, 773 }; 774 775 static int __init init_mtdchar(void) 776 { 777 if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) { 778 printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", 779 MTD_CHAR_MAJOR); 780 return -EAGAIN; 781 } 782 783 mtd_class = class_create(THIS_MODULE, "mtd"); 784 785 if (IS_ERR(mtd_class)) { 786 printk(KERN_ERR "Error creating mtd class.\n"); 787 unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); 788 return PTR_ERR(mtd_class); 789 } 790 791 register_mtd_user(¬ifier); 792 return 0; 793 } 794 795 static void __exit cleanup_mtdchar(void) 796 { 797 unregister_mtd_user(¬ifier); 798 class_destroy(mtd_class); 799 unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); 800 } 801 802 module_init(init_mtdchar); 803 module_exit(cleanup_mtdchar); 804 805 806 MODULE_LICENSE("GPL"); 807 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 808 MODULE_DESCRIPTION("Direct character-device access to MTD devices"); 809