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_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
  38static 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
  47static 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 */
  56struct mtd_file_info {
  57        struct mtd_info *mtd;
  58        enum mtd_file_modes mode;
  59};
  60
  61static 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
  87static 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
 134out:
 135        unlock_kernel();
 136        return ret;
 137} /* mtd_open */
 138
 139/*====================================================================*/
 140
 141static 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
 164static 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
 257static 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======================================================================*/
 348static 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)
 354static 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
 383static 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
 782static 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
 792static 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(&notifier);
 809        return 0;
 810}
 811
 812static void __exit cleanup_mtdchar(void)
 813{
 814        unregister_mtd_user(&notifier);
 815        class_destroy(mtd_class);
 816        unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
 817}
 818
 819module_init(init_mtdchar);
 820module_exit(cleanup_mtdchar);
 821
 822
 823MODULE_LICENSE("GPL");
 824MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
 825MODULE_DESCRIPTION("Direct character-device access to MTD devices");
 826