aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/mtdchar.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/mtdchar.c')
-rw-r--r--drivers/mtd/mtdchar.c176
1 files changed, 143 insertions, 33 deletions
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 510ad78312cc..1ed602a0f24c 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * $Id: mtdchar.c,v 1.66 2005/01/05 18:05:11 dwmw2 Exp $ 2 * $Id: mtdchar.c,v 1.73 2005/07/04 17:36:41 gleixner Exp $
3 * 3 *
4 * Character-device access to raw MTD devices. 4 * Character-device access to raw MTD devices.
5 * 5 *
@@ -15,27 +15,30 @@
15#include <linux/fs.h> 15#include <linux/fs.h>
16#include <asm/uaccess.h> 16#include <asm/uaccess.h>
17 17
18#ifdef CONFIG_DEVFS_FS 18#include <linux/device.h>
19#include <linux/devfs_fs_kernel.h> 19
20static struct class *mtd_class;
20 21
21static void mtd_notify_add(struct mtd_info* mtd) 22static void mtd_notify_add(struct mtd_info* mtd)
22{ 23{
23 if (!mtd) 24 if (!mtd)
24 return; 25 return;
25 26
26 devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2), 27 class_device_create(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
27 S_IFCHR | S_IRUGO | S_IWUGO, "mtd/%d", mtd->index); 28 NULL, "mtd%d", mtd->index);
28 29
29 devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), 30 class_device_create(mtd_class,
30 S_IFCHR | S_IRUGO, "mtd/%dro", mtd->index); 31 MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
32 NULL, "mtd%dro", mtd->index);
31} 33}
32 34
33static void mtd_notify_remove(struct mtd_info* mtd) 35static void mtd_notify_remove(struct mtd_info* mtd)
34{ 36{
35 if (!mtd) 37 if (!mtd)
36 return; 38 return;
37 devfs_remove("mtd/%d", mtd->index); 39
38 devfs_remove("mtd/%dro", mtd->index); 40 class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2));
41 class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1));
39} 42}
40 43
41static struct mtd_notifier notifier = { 44static struct mtd_notifier notifier = {
@@ -43,25 +46,25 @@ static struct mtd_notifier notifier = {
43 .remove = mtd_notify_remove, 46 .remove = mtd_notify_remove,
44}; 47};
45 48
46static inline void mtdchar_devfs_init(void) 49/*
47{ 50 * We use file->private_data to store a pointer to the MTDdevice.
48 devfs_mk_dir("mtd"); 51 * Since alighment is at least 32 bits, we have 2 bits free for OTP
49 register_mtd_user(&notifier); 52 * modes as well.
50} 53 */
51 54
52static inline void mtdchar_devfs_exit(void) 55#define TO_MTD(file) (struct mtd_info *)((long)((file)->private_data) & ~3L)
53{ 56
54 unregister_mtd_user(&notifier); 57#define MTD_MODE_OTP_FACT 1
55 devfs_remove("mtd"); 58#define MTD_MODE_OTP_USER 2
56} 59#define MTD_MODE(file) ((long)((file)->private_data) & 3)
57#else /* !DEVFS */ 60
58#define mtdchar_devfs_init() do { } while(0) 61#define SET_MTD_MODE(file, mode) \
59#define mtdchar_devfs_exit() do { } while(0) 62 do { long __p = (long)((file)->private_data); \
60#endif 63 (file)->private_data = (void *)((__p & ~3L) | mode); } while (0)
61 64
62static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) 65static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
63{ 66{
64 struct mtd_info *mtd = file->private_data; 67 struct mtd_info *mtd = TO_MTD(file);
65 68
66 switch (orig) { 69 switch (orig) {
67 case 0: 70 case 0:
@@ -134,7 +137,7 @@ static int mtd_close(struct inode *inode, struct file *file)
134 137
135 DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); 138 DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
136 139
137 mtd = file->private_data; 140 mtd = TO_MTD(file);
138 141
139 if (mtd->sync) 142 if (mtd->sync)
140 mtd->sync(mtd); 143 mtd->sync(mtd);
@@ -151,7 +154,7 @@ static int mtd_close(struct inode *inode, struct file *file)
151 154
152static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) 155static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
153{ 156{
154 struct mtd_info *mtd = file->private_data; 157 struct mtd_info *mtd = TO_MTD(file);
155 size_t retlen=0; 158 size_t retlen=0;
156 size_t total_retlen=0; 159 size_t total_retlen=0;
157 int ret=0; 160 int ret=0;
@@ -178,7 +181,16 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t
178 if (!kbuf) 181 if (!kbuf)
179 return -ENOMEM; 182 return -ENOMEM;
180 183
181 ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf); 184 switch (MTD_MODE(file)) {
185 case MTD_MODE_OTP_FACT:
186 ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf);
187 break;
188 case MTD_MODE_OTP_USER:
189 ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
190 break;
191 default:
192 ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf);
193 }
182 /* Nand returns -EBADMSG on ecc errors, but it returns 194 /* Nand returns -EBADMSG on ecc errors, but it returns
183 * the data. For our userspace tools it is important 195 * the data. For our userspace tools it is important
184 * to dump areas with ecc errors ! 196 * to dump areas with ecc errors !
@@ -196,6 +208,8 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t
196 208
197 count -= retlen; 209 count -= retlen;
198 buf += retlen; 210 buf += retlen;
211 if (retlen == 0)
212 count = 0;
199 } 213 }
200 else { 214 else {
201 kfree(kbuf); 215 kfree(kbuf);
@@ -210,7 +224,7 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t
210 224
211static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos) 225static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos)
212{ 226{
213 struct mtd_info *mtd = file->private_data; 227 struct mtd_info *mtd = TO_MTD(file);
214 char *kbuf; 228 char *kbuf;
215 size_t retlen; 229 size_t retlen;
216 size_t total_retlen=0; 230 size_t total_retlen=0;
@@ -245,7 +259,20 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count
245 return -EFAULT; 259 return -EFAULT;
246 } 260 }
247 261
248 ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); 262 switch (MTD_MODE(file)) {
263 case MTD_MODE_OTP_FACT:
264 ret = -EROFS;
265 break;
266 case MTD_MODE_OTP_USER:
267 if (!mtd->write_user_prot_reg) {
268 ret = -EOPNOTSUPP;
269 break;
270 }
271 ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
272 break;
273 default:
274 ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);
275 }
249 if (!ret) { 276 if (!ret) {
250 *ppos += retlen; 277 *ppos += retlen;
251 total_retlen += retlen; 278 total_retlen += retlen;
@@ -276,7 +303,7 @@ static void mtdchar_erase_callback (struct erase_info *instr)
276static int mtd_ioctl(struct inode *inode, struct file *file, 303static int mtd_ioctl(struct inode *inode, struct file *file,
277 u_int cmd, u_long arg) 304 u_int cmd, u_long arg)
278{ 305{
279 struct mtd_info *mtd = file->private_data; 306 struct mtd_info *mtd = TO_MTD(file);
280 void __user *argp = (void __user *)arg; 307 void __user *argp = (void __user *)arg;
281 int ret = 0; 308 int ret = 0;
282 u_long size; 309 u_long size;
@@ -518,6 +545,80 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
518 break; 545 break;
519 } 546 }
520 547
548#ifdef CONFIG_MTD_OTP
549 case OTPSELECT:
550 {
551 int mode;
552 if (copy_from_user(&mode, argp, sizeof(int)))
553 return -EFAULT;
554 SET_MTD_MODE(file, 0);
555 switch (mode) {
556 case MTD_OTP_FACTORY:
557 if (!mtd->read_fact_prot_reg)
558 ret = -EOPNOTSUPP;
559 else
560 SET_MTD_MODE(file, MTD_MODE_OTP_FACT);
561 break;
562 case MTD_OTP_USER:
563 if (!mtd->read_fact_prot_reg)
564 ret = -EOPNOTSUPP;
565 else
566 SET_MTD_MODE(file, MTD_MODE_OTP_USER);
567 break;
568 default:
569 ret = -EINVAL;
570 case MTD_OTP_OFF:
571 break;
572 }
573 file->f_pos = 0;
574 break;
575 }
576
577 case OTPGETREGIONCOUNT:
578 case OTPGETREGIONINFO:
579 {
580 struct otp_info *buf = kmalloc(4096, GFP_KERNEL);
581 if (!buf)
582 return -ENOMEM;
583 ret = -EOPNOTSUPP;
584 switch (MTD_MODE(file)) {
585 case MTD_MODE_OTP_FACT:
586 if (mtd->get_fact_prot_info)
587 ret = mtd->get_fact_prot_info(mtd, buf, 4096);
588 break;
589 case MTD_MODE_OTP_USER:
590 if (mtd->get_user_prot_info)
591 ret = mtd->get_user_prot_info(mtd, buf, 4096);
592 break;
593 }
594 if (ret >= 0) {
595 if (cmd == OTPGETREGIONCOUNT) {
596 int nbr = ret / sizeof(struct otp_info);
597 ret = copy_to_user(argp, &nbr, sizeof(int));
598 } else
599 ret = copy_to_user(argp, buf, ret);
600 if (ret)
601 ret = -EFAULT;
602 }
603 kfree(buf);
604 break;
605 }
606
607 case OTPLOCK:
608 {
609 struct otp_info info;
610
611 if (MTD_MODE(file) != MTD_MODE_OTP_USER)
612 return -EINVAL;
613 if (copy_from_user(&info, argp, sizeof(info)))
614 return -EFAULT;
615 if (!mtd->lock_user_prot_reg)
616 return -EOPNOTSUPP;
617 ret = mtd->lock_user_prot_reg(mtd, info.start, info.length);
618 break;
619 }
620#endif
621
521 default: 622 default:
522 ret = -ENOTTY; 623 ret = -ENOTTY;
523 } 624 }
@@ -543,13 +644,22 @@ static int __init init_mtdchar(void)
543 return -EAGAIN; 644 return -EAGAIN;
544 } 645 }
545 646
546 mtdchar_devfs_init(); 647 mtd_class = class_create(THIS_MODULE, "mtd");
648
649 if (IS_ERR(mtd_class)) {
650 printk(KERN_ERR "Error creating mtd class.\n");
651 unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
652 return PTR_ERR(mtd_class);
653 }
654
655 register_mtd_user(&notifier);
547 return 0; 656 return 0;
548} 657}
549 658
550static void __exit cleanup_mtdchar(void) 659static void __exit cleanup_mtdchar(void)
551{ 660{
552 mtdchar_devfs_exit(); 661 unregister_mtd_user(&notifier);
662 class_destroy(mtd_class);
553 unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); 663 unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
554} 664}
555 665