linux-old/drivers/mtd/mtdchar.c
<<
>>
Prefs
   1/*
   2 * $Id: mtdchar.c,v 1.49 2003/01/24 12:02:58 dwmw2 Exp $
   3 *
   4 * Character-device access to raw MTD devices.
   5 * Pure 2.4 version - compatibility cruft removed to mtdchar-compat.c
   6 *
   7 */
   8
   9#include <linux/config.h>
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/mtd/mtd.h>
  13#include <linux/slab.h>
  14
  15#ifdef CONFIG_DEVFS_FS
  16#include <linux/devfs_fs_kernel.h>
  17static void mtd_notify_add(struct mtd_info* mtd);
  18static void mtd_notify_remove(struct mtd_info* mtd);
  19
  20static struct mtd_notifier notifier = {
  21        add:    mtd_notify_add,
  22        remove: mtd_notify_remove,
  23};
  24
  25static devfs_handle_t devfs_dir_handle;
  26static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES];
  27static devfs_handle_t devfs_ro_handle[MAX_MTD_DEVICES];
  28#endif
  29
  30static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
  31{
  32        struct mtd_info *mtd=(struct mtd_info *)file->private_data;
  33
  34        switch (orig) {
  35        case 0:
  36                /* SEEK_SET */
  37                file->f_pos = offset;
  38                break;
  39        case 1:
  40                /* SEEK_CUR */
  41                file->f_pos += offset;
  42                break;
  43        case 2:
  44                /* SEEK_END */
  45                file->f_pos =mtd->size + offset;
  46                break;
  47        default:
  48                return -EINVAL;
  49        }
  50
  51        if (file->f_pos < 0)
  52                file->f_pos = 0;
  53        else if (file->f_pos >= mtd->size)
  54                file->f_pos = mtd->size - 1;
  55
  56        return file->f_pos;
  57}
  58
  59
  60
  61static int mtd_open(struct inode *inode, struct file *file)
  62{
  63        int minor = minor(inode->i_rdev);
  64        int devnum = minor >> 1;
  65        struct mtd_info *mtd;
  66
  67        DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n");
  68
  69        if (devnum >= MAX_MTD_DEVICES)
  70                return -ENODEV;
  71
  72        /* You can't open the RO devices RW */
  73        if ((file->f_mode & 2) && (minor & 1))
  74                return -EACCES;
  75
  76        mtd = get_mtd_device(NULL, devnum);
  77        
  78        if (!mtd)
  79                return -ENODEV;
  80        
  81        if (MTD_ABSENT == mtd->type) {
  82                put_mtd_device(mtd);
  83                return -ENODEV;
  84        }
  85
  86        file->private_data = mtd;
  87                
  88        /* You can't open it RW if it's not a writeable device */
  89        if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) {
  90                put_mtd_device(mtd);
  91                return -EACCES;
  92        }
  93                
  94        return 0;
  95} /* mtd_open */
  96
  97/*====================================================================*/
  98
  99static int mtd_close(struct inode *inode, struct file *file)
 100{
 101        struct mtd_info *mtd;
 102
 103        DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
 104
 105        mtd = (struct mtd_info *)file->private_data;
 106        
 107        if (mtd->sync)
 108                mtd->sync(mtd);
 109        
 110        put_mtd_device(mtd);
 111
 112        return 0;
 113} /* mtd_close */
 114
 115/* FIXME: This _really_ needs to die. In 2.5, we should lock the
 116   userspace buffer down and use it directly with readv/writev.
 117*/
 118#define MAX_KMALLOC_SIZE 0x20000
 119
 120static ssize_t mtd_read(struct file *file, char *buf, size_t count,loff_t *ppos)
 121{
 122        struct mtd_info *mtd = (struct mtd_info *)file->private_data;
 123        size_t retlen=0;
 124        size_t total_retlen=0;
 125        int ret=0;
 126        int len;
 127        char *kbuf;
 128        loff_t pos = *ppos;
 129        
 130        DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n");
 131
 132        if (pos < 0 || pos > mtd->size)
 133                return 0;
 134
 135        if (count > mtd->size - pos)
 136                count = mtd->size - pos;
 137
 138        if (!count)
 139                return 0;
 140        
 141        /* FIXME: Use kiovec in 2.5 to lock down the user's buffers
 142           and pass them directly to the MTD functions */
 143        while (count) {
 144                if (count > MAX_KMALLOC_SIZE) 
 145                        len = MAX_KMALLOC_SIZE;
 146                else
 147                        len = count;
 148
 149                kbuf=kmalloc(len,GFP_KERNEL);
 150                if (!kbuf)
 151                        return -ENOMEM;
 152                
 153                ret = MTD_READ(mtd, pos, len, &retlen, kbuf);
 154                if (!ret) {
 155                        pos += retlen;
 156                        if (copy_to_user(buf, kbuf, retlen)) {
 157                                kfree(kbuf);
 158                                return -EFAULT;
 159                        }
 160                        else
 161                                total_retlen += retlen;
 162
 163                        count -= retlen;
 164                        buf += retlen;
 165                }
 166                else {
 167                        kfree(kbuf);
 168                        return ret;
 169                }
 170                
 171                kfree(kbuf);
 172        }
 173        
 174        *ppos = pos;
 175        
 176        return total_retlen;
 177} /* mtd_read */
 178
 179static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t *ppos)
 180{
 181        struct mtd_info *mtd = (struct mtd_info *)file->private_data;
 182        char *kbuf;
 183        size_t retlen;
 184        size_t total_retlen=0;
 185        loff_t pos = *ppos;
 186        int ret=0;
 187        int len;
 188
 189        DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n");
 190        
 191        if (pos < 0 || pos >= mtd->size)
 192                return -ENOSPC;
 193
 194        if (count > mtd->size - pos)
 195                count = mtd->size - pos;
 196        
 197        if (!count)
 198                return 0;
 199
 200        while (count) {
 201                if (count > MAX_KMALLOC_SIZE) 
 202                        len = MAX_KMALLOC_SIZE;
 203                else
 204                        len = count;
 205
 206                kbuf=kmalloc(len,GFP_KERNEL);
 207                if (!kbuf) {
 208                        printk("kmalloc is null\n");
 209                        return -ENOMEM;
 210                }
 211
 212                if (copy_from_user(kbuf, buf, len)) {
 213                        kfree(kbuf);
 214                        return -EFAULT;
 215                }
 216                
 217                ret = (*(mtd->write))(mtd, pos, len, &retlen, kbuf);
 218                if (!ret) {
 219                        pos += retlen;
 220                        total_retlen += retlen;
 221                        count -= retlen;
 222                        buf += retlen;
 223                }
 224                else {
 225                        kfree(kbuf);
 226                        return ret;
 227                }
 228                
 229                kfree(kbuf);
 230        }
 231        *ppos = pos;
 232
 233        return total_retlen;
 234} /* mtd_write */
 235
 236/*======================================================================
 237
 238    IOCTL calls for getting device parameters.
 239
 240======================================================================*/
 241static void mtd_erase_callback (struct erase_info *instr)
 242{
 243        wake_up((wait_queue_head_t *)instr->priv);
 244}
 245
 246static int mtd_ioctl(struct inode *inode, struct file *file,
 247                     u_int cmd, u_long arg)
 248{
 249        struct mtd_info *mtd = (struct mtd_info *)file->private_data;
 250        int ret = 0;
 251        u_long size;
 252        
 253        DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n");
 254
 255        size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
 256        if (cmd & IOC_IN) {
 257                ret = verify_area(VERIFY_READ, (char *)arg, size);
 258                if (ret) return ret;
 259        }
 260        if (cmd & IOC_OUT) {
 261                ret = verify_area(VERIFY_WRITE, (char *)arg, size);
 262                if (ret) return ret;
 263        }
 264        
 265        switch (cmd) {
 266        case MEMGETREGIONCOUNT:
 267                if (copy_to_user((int *) arg, &(mtd->numeraseregions), sizeof(int)))
 268                        return -EFAULT;
 269                break;
 270
 271        case MEMGETREGIONINFO:
 272        {
 273                struct region_info_user ur;
 274
 275                if (copy_from_user(     &ur, 
 276                                        (struct region_info_user *)arg, 
 277                                        sizeof(struct region_info_user))) {
 278                        return -EFAULT;
 279                }
 280
 281                if (ur.regionindex >= mtd->numeraseregions)
 282                        return -EINVAL;
 283                if (copy_to_user((struct mtd_erase_region_info *) arg, 
 284                                &(mtd->eraseregions[ur.regionindex]),
 285                                sizeof(struct mtd_erase_region_info)))
 286                        return -EFAULT;
 287                break;
 288        }
 289
 290        case MEMGETINFO:
 291                if (copy_to_user((struct mtd_info *)arg, mtd,
 292                                 sizeof(struct mtd_info_user)))
 293                        return -EFAULT;
 294                break;
 295
 296        case MEMERASE:
 297        {
 298                struct erase_info *erase;
 299
 300                if(!(file->f_mode & 2))
 301                        return -EPERM;
 302
 303                erase=kmalloc(sizeof(struct erase_info),GFP_KERNEL);
 304                if (!erase)
 305                        ret = -ENOMEM;
 306                else {
 307                        wait_queue_head_t waitq;
 308                        DECLARE_WAITQUEUE(wait, current);
 309
 310                        init_waitqueue_head(&waitq);
 311
 312                        memset (erase,0,sizeof(struct erase_info));
 313                        if (copy_from_user(&erase->addr, (u_long *)arg,
 314                                           2 * sizeof(u_long))) {
 315                                kfree(erase);
 316                                return -EFAULT;
 317                        }
 318                        erase->mtd = mtd;
 319                        erase->callback = mtd_erase_callback;
 320                        erase->priv = (unsigned long)&waitq;
 321                        
 322                        /*
 323                          FIXME: Allow INTERRUPTIBLE. Which means
 324                          not having the wait_queue head on the stack.
 325                          
 326                          If the wq_head is on the stack, and we
 327                          leave because we got interrupted, then the
 328                          wq_head is no longer there when the
 329                          callback routine tries to wake us up.
 330                        */
 331                        ret = mtd->erase(mtd, erase);
 332                        if (!ret) {
 333                                set_current_state(TASK_UNINTERRUPTIBLE);
 334                                add_wait_queue(&waitq, &wait);
 335                                if (erase->state != MTD_ERASE_DONE &&
 336                                    erase->state != MTD_ERASE_FAILED)
 337                                        schedule();
 338                                remove_wait_queue(&waitq, &wait);
 339                                set_current_state(TASK_RUNNING);
 340
 341                                ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0;
 342                        }
 343                        kfree(erase);
 344                }
 345                break;
 346        }
 347
 348        case MEMWRITEOOB:
 349        {
 350                struct mtd_oob_buf buf;
 351                void *databuf;
 352                ssize_t retlen;
 353                
 354                if(!(file->f_mode & 2))
 355                        return -EPERM;
 356
 357                if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
 358                        return -EFAULT;
 359                
 360                if (buf.length > 0x4096)
 361                        return -EINVAL;
 362
 363                if (!mtd->write_oob)
 364                        ret = -EOPNOTSUPP;
 365                else
 366                        ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length);
 367
 368                if (ret)
 369                        return ret;
 370
 371                databuf = kmalloc(buf.length, GFP_KERNEL);
 372                if (!databuf)
 373                        return -ENOMEM;
 374                
 375                if (copy_from_user(databuf, buf.ptr, buf.length)) {
 376                        kfree(databuf);
 377                        return -EFAULT;
 378                }
 379
 380                ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf);
 381
 382                if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
 383                        ret = -EFAULT;
 384
 385                kfree(databuf);
 386                break;
 387
 388        }
 389
 390        case MEMREADOOB:
 391        {
 392                struct mtd_oob_buf buf;
 393                void *databuf;
 394                ssize_t retlen;
 395
 396                if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
 397                        return -EFAULT;
 398                
 399                if (buf.length > 0x4096)
 400                        return -EINVAL;
 401
 402                if (!mtd->read_oob)
 403                        ret = -EOPNOTSUPP;
 404                else
 405                        ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length);
 406
 407                if (ret)
 408                        return ret;
 409
 410                databuf = kmalloc(buf.length, GFP_KERNEL);
 411                if (!databuf)
 412                        return -ENOMEM;
 413                
 414                ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf);
 415
 416                if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
 417                        ret = -EFAULT;
 418                else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
 419                        ret = -EFAULT;
 420                
 421                kfree(databuf);
 422                break;
 423        }
 424
 425        case MEMLOCK:
 426        {
 427                unsigned long adrs[2];
 428
 429                if (copy_from_user(adrs ,(void *)arg, 2* sizeof(unsigned long)))
 430                        return -EFAULT;
 431
 432                if (!mtd->lock)
 433                        ret = -EOPNOTSUPP;
 434                else
 435                        ret = mtd->lock(mtd, adrs[0], adrs[1]);
 436                break;
 437        }
 438
 439        case MEMUNLOCK:
 440        {
 441                unsigned long adrs[2];
 442
 443                if (copy_from_user(adrs, (void *)arg, 2* sizeof(unsigned long)))
 444                        return -EFAULT;
 445
 446                if (!mtd->unlock)
 447                        ret = -EOPNOTSUPP;
 448                else
 449                        ret = mtd->unlock(mtd, adrs[0], adrs[1]);
 450                break;
 451        }
 452
 453        case MEMWRITEDATA:
 454        {
 455                struct mtd_oob_buf buf;
 456                void *databuf;
 457                ssize_t retlen;
 458                
 459                if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
 460                        return -EFAULT;
 461                
 462                if (buf.length > 0x4096)
 463                        return -EINVAL;
 464
 465                if (!mtd->write_ecc)
 466                        ret = -EOPNOTSUPP;
 467                else
 468                        ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length);
 469
 470                if (ret)
 471                        return ret;
 472
 473                databuf = kmalloc(buf.length, GFP_KERNEL);
 474                if (!databuf)
 475                        return -ENOMEM;
 476                
 477                if (copy_from_user(databuf, buf.ptr, buf.length)) {
 478                        kfree(databuf);
 479                        return -EFAULT;
 480                }
 481
 482                ret = (mtd->write_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0);
 483
 484                if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
 485                        ret = -EFAULT;
 486
 487                kfree(databuf);
 488                break;
 489
 490        }
 491
 492        case MEMREADDATA:
 493        {
 494                struct mtd_oob_buf buf;
 495                void *databuf;
 496                ssize_t retlen = 0;
 497
 498                if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
 499                        return -EFAULT;
 500                
 501                if (buf.length > 0x4096)
 502                        return -EINVAL;
 503
 504                if (!mtd->read_ecc)
 505                        ret = -EOPNOTSUPP;
 506                else
 507                        ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length);
 508
 509                if (ret)
 510                        return ret;
 511
 512                databuf = kmalloc(buf.length, GFP_KERNEL);
 513                if (!databuf)
 514                        return -ENOMEM;
 515                
 516                ret = (mtd->read_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0);
 517
 518                if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
 519                        ret = -EFAULT;
 520                else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
 521                        ret = -EFAULT;
 522                
 523                kfree(databuf);
 524                break;
 525        }
 526
 527                
 528        default:
 529                DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO);
 530                ret = -ENOTTY;
 531        }
 532
 533        return ret;
 534} /* memory_ioctl */
 535
 536static struct file_operations mtd_fops = {
 537        owner:          THIS_MODULE,
 538        llseek:         mtd_lseek,      /* lseek */
 539        read:           mtd_read,       /* read */
 540        write:          mtd_write,      /* write */
 541        ioctl:          mtd_ioctl,      /* ioctl */
 542        open:           mtd_open,       /* open */
 543        release:        mtd_close,      /* release */
 544};
 545
 546
 547#ifdef CONFIG_DEVFS_FS
 548/* Notification that a new device has been added. Create the devfs entry for
 549 * it. */
 550
 551static void mtd_notify_add(struct mtd_info* mtd)
 552{
 553        char name[8];
 554
 555        if (!mtd)
 556                return;
 557
 558        sprintf(name, "%d", mtd->index);
 559        devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
 560                        DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2,
 561                        S_IFCHR | S_IRUGO | S_IWUGO,
 562                        &mtd_fops, NULL);
 563
 564        sprintf(name, "%dro", mtd->index);
 565        devfs_ro_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
 566                        DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2+1,
 567                        S_IFCHR | S_IRUGO,
 568                        &mtd_fops, NULL);
 569}
 570
 571static void mtd_notify_remove(struct mtd_info* mtd)
 572{
 573        if (!mtd)
 574                return;
 575
 576        devfs_unregister(devfs_rw_handle[mtd->index]);
 577        devfs_unregister(devfs_ro_handle[mtd->index]);
 578}
 579#endif
 580
 581static int __init init_mtdchar(void)
 582{
 583#ifdef CONFIG_DEVFS_FS
 584        if (devfs_register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
 585        {
 586                printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
 587                       MTD_CHAR_MAJOR);
 588                return -EAGAIN;
 589        }
 590
 591        devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL);
 592
 593        register_mtd_user(&notifier);
 594#else
 595        if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
 596        {
 597                printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
 598                       MTD_CHAR_MAJOR);
 599                return -EAGAIN;
 600        }
 601#endif
 602
 603        return 0;
 604}
 605
 606static void __exit cleanup_mtdchar(void)
 607{
 608#ifdef CONFIG_DEVFS_FS
 609        unregister_mtd_user(&notifier);
 610        devfs_unregister(devfs_dir_handle);
 611        devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
 612#else
 613        unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
 614#endif
 615}
 616
 617module_init(init_mtdchar);
 618module_exit(cleanup_mtdchar);
 619
 620
 621MODULE_LICENSE("GPL");
 622MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
 623MODULE_DESCRIPTION("Direct character-device access to MTD devices");
 624