OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [mtd/] [mtdchar.c] - Blame information for rev 62

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.