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