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