diff options
Diffstat (limited to 'drivers/mtd/mtdchar.c')
-rw-r--r-- | drivers/mtd/mtdchar.c | 200 |
1 files changed, 142 insertions, 58 deletions
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index a48210d58b92..fdc535b22e39 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c | |||
@@ -49,24 +49,18 @@ static struct mtd_notifier notifier = { | |||
49 | }; | 49 | }; |
50 | 50 | ||
51 | /* | 51 | /* |
52 | * We use file->private_data to store a pointer to the MTDdevice. | 52 | * Data structure to hold the pointer to the mtd device as well |
53 | * Since alighment is at least 32 bits, we have 2 bits free for OTP | 53 | * as mode information ofr various use cases. |
54 | * modes as well. | ||
55 | */ | 54 | */ |
56 | 55 | struct mtd_file_info { | |
57 | #define TO_MTD(file) (struct mtd_info *)((long)((file)->private_data) & ~3L) | 56 | struct mtd_info *mtd; |
58 | 57 | enum mtd_file_modes mode; | |
59 | #define MTD_MODE_OTP_FACT 1 | 58 | }; |
60 | #define MTD_MODE_OTP_USER 2 | ||
61 | #define MTD_MODE(file) ((long)((file)->private_data) & 3) | ||
62 | |||
63 | #define SET_MTD_MODE(file, mode) \ | ||
64 | do { long __p = (long)((file)->private_data); \ | ||
65 | (file)->private_data = (void *)((__p & ~3L) | mode); } while (0) | ||
66 | 59 | ||
67 | static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) | 60 | static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) |
68 | { | 61 | { |
69 | struct mtd_info *mtd = TO_MTD(file); | 62 | struct mtd_file_info *mfi = file->private_data; |
63 | struct mtd_info *mtd = mfi->mtd; | ||
70 | 64 | ||
71 | switch (orig) { | 65 | switch (orig) { |
72 | case 0: | 66 | case 0: |
@@ -97,6 +91,7 @@ static int mtd_open(struct inode *inode, struct file *file) | |||
97 | int minor = iminor(inode); | 91 | int minor = iminor(inode); |
98 | int devnum = minor >> 1; | 92 | int devnum = minor >> 1; |
99 | struct mtd_info *mtd; | 93 | struct mtd_info *mtd; |
94 | struct mtd_file_info *mfi; | ||
100 | 95 | ||
101 | DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n"); | 96 | DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n"); |
102 | 97 | ||
@@ -117,14 +112,20 @@ static int mtd_open(struct inode *inode, struct file *file) | |||
117 | return -ENODEV; | 112 | return -ENODEV; |
118 | } | 113 | } |
119 | 114 | ||
120 | file->private_data = mtd; | ||
121 | |||
122 | /* You can't open it RW if it's not a writeable device */ | 115 | /* You can't open it RW if it's not a writeable device */ |
123 | if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) { | 116 | if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) { |
124 | put_mtd_device(mtd); | 117 | put_mtd_device(mtd); |
125 | return -EACCES; | 118 | return -EACCES; |
126 | } | 119 | } |
127 | 120 | ||
121 | mfi = kzalloc(sizeof(*mfi), GFP_KERNEL); | ||
122 | if (!mfi) { | ||
123 | put_mtd_device(mtd); | ||
124 | return -ENOMEM; | ||
125 | } | ||
126 | mfi->mtd = mtd; | ||
127 | file->private_data = mfi; | ||
128 | |||
128 | return 0; | 129 | return 0; |
129 | } /* mtd_open */ | 130 | } /* mtd_open */ |
130 | 131 | ||
@@ -132,16 +133,17 @@ static int mtd_open(struct inode *inode, struct file *file) | |||
132 | 133 | ||
133 | static int mtd_close(struct inode *inode, struct file *file) | 134 | static int mtd_close(struct inode *inode, struct file *file) |
134 | { | 135 | { |
135 | struct mtd_info *mtd; | 136 | struct mtd_file_info *mfi = file->private_data; |
137 | struct mtd_info *mtd = mfi->mtd; | ||
136 | 138 | ||
137 | DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); | 139 | DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); |
138 | 140 | ||
139 | mtd = TO_MTD(file); | ||
140 | |||
141 | if (mtd->sync) | 141 | if (mtd->sync) |
142 | mtd->sync(mtd); | 142 | mtd->sync(mtd); |
143 | 143 | ||
144 | put_mtd_device(mtd); | 144 | put_mtd_device(mtd); |
145 | file->private_data = NULL; | ||
146 | kfree(mfi); | ||
145 | 147 | ||
146 | return 0; | 148 | return 0; |
147 | } /* mtd_close */ | 149 | } /* mtd_close */ |
@@ -153,7 +155,8 @@ static int mtd_close(struct inode *inode, struct file *file) | |||
153 | 155 | ||
154 | static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) | 156 | static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) |
155 | { | 157 | { |
156 | struct mtd_info *mtd = TO_MTD(file); | 158 | struct mtd_file_info *mfi = file->private_data; |
159 | struct mtd_info *mtd = mfi->mtd; | ||
157 | size_t retlen=0; | 160 | size_t retlen=0; |
158 | size_t total_retlen=0; | 161 | size_t total_retlen=0; |
159 | int ret=0; | 162 | int ret=0; |
@@ -186,13 +189,26 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t | |||
186 | else | 189 | else |
187 | len = count; | 190 | len = count; |
188 | 191 | ||
189 | switch (MTD_MODE(file)) { | 192 | switch (mfi->mode) { |
190 | case MTD_MODE_OTP_FACT: | 193 | case MTD_MODE_OTP_FACTORY: |
191 | ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf); | 194 | ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf); |
192 | break; | 195 | break; |
193 | case MTD_MODE_OTP_USER: | 196 | case MTD_MODE_OTP_USER: |
194 | ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); | 197 | ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); |
195 | break; | 198 | break; |
199 | case MTD_MODE_RAW: | ||
200 | { | ||
201 | struct mtd_oob_ops ops; | ||
202 | |||
203 | ops.mode = MTD_OOB_RAW; | ||
204 | ops.datbuf = kbuf; | ||
205 | ops.oobbuf = NULL; | ||
206 | ops.len = len; | ||
207 | |||
208 | ret = mtd->read_oob(mtd, *ppos, &ops); | ||
209 | retlen = ops.retlen; | ||
210 | break; | ||
211 | } | ||
196 | default: | 212 | default: |
197 | ret = mtd->read(mtd, *ppos, len, &retlen, kbuf); | 213 | ret = mtd->read(mtd, *ppos, len, &retlen, kbuf); |
198 | } | 214 | } |
@@ -232,7 +248,8 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t | |||
232 | 248 | ||
233 | static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos) | 249 | static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos) |
234 | { | 250 | { |
235 | struct mtd_info *mtd = TO_MTD(file); | 251 | struct mtd_file_info *mfi = file->private_data; |
252 | struct mtd_info *mtd = mfi->mtd; | ||
236 | char *kbuf; | 253 | char *kbuf; |
237 | size_t retlen; | 254 | size_t retlen; |
238 | size_t total_retlen=0; | 255 | size_t total_retlen=0; |
@@ -270,8 +287,8 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count | |||
270 | return -EFAULT; | 287 | return -EFAULT; |
271 | } | 288 | } |
272 | 289 | ||
273 | switch (MTD_MODE(file)) { | 290 | switch (mfi->mode) { |
274 | case MTD_MODE_OTP_FACT: | 291 | case MTD_MODE_OTP_FACTORY: |
275 | ret = -EROFS; | 292 | ret = -EROFS; |
276 | break; | 293 | break; |
277 | case MTD_MODE_OTP_USER: | 294 | case MTD_MODE_OTP_USER: |
@@ -281,6 +298,21 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count | |||
281 | } | 298 | } |
282 | ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); | 299 | ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); |
283 | break; | 300 | break; |
301 | |||
302 | case MTD_MODE_RAW: | ||
303 | { | ||
304 | struct mtd_oob_ops ops; | ||
305 | |||
306 | ops.mode = MTD_OOB_RAW; | ||
307 | ops.datbuf = kbuf; | ||
308 | ops.oobbuf = NULL; | ||
309 | ops.len = len; | ||
310 | |||
311 | ret = mtd->write_oob(mtd, *ppos, &ops); | ||
312 | retlen = ops.retlen; | ||
313 | break; | ||
314 | } | ||
315 | |||
284 | default: | 316 | default: |
285 | ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); | 317 | ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); |
286 | } | 318 | } |
@@ -310,10 +342,41 @@ static void mtdchar_erase_callback (struct erase_info *instr) | |||
310 | wake_up((wait_queue_head_t *)instr->priv); | 342 | wake_up((wait_queue_head_t *)instr->priv); |
311 | } | 343 | } |
312 | 344 | ||
345 | #if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP) | ||
346 | static int otp_select_filemode(struct mtd_file_info *mfi, int mode) | ||
347 | { | ||
348 | struct mtd_info *mtd = mfi->mtd; | ||
349 | int ret = 0; | ||
350 | |||
351 | switch (mode) { | ||
352 | case MTD_OTP_FACTORY: | ||
353 | if (!mtd->read_fact_prot_reg) | ||
354 | ret = -EOPNOTSUPP; | ||
355 | else | ||
356 | mfi->mode = MTD_MODE_OTP_FACTORY; | ||
357 | break; | ||
358 | case MTD_OTP_USER: | ||
359 | if (!mtd->read_fact_prot_reg) | ||
360 | ret = -EOPNOTSUPP; | ||
361 | else | ||
362 | mfi->mode = MTD_MODE_OTP_USER; | ||
363 | break; | ||
364 | default: | ||
365 | ret = -EINVAL; | ||
366 | case MTD_OTP_OFF: | ||
367 | break; | ||
368 | } | ||
369 | return ret; | ||
370 | } | ||
371 | #else | ||
372 | # define otp_select_filemode(f,m) -EOPNOTSUPP | ||
373 | #endif | ||
374 | |||
313 | static int mtd_ioctl(struct inode *inode, struct file *file, | 375 | static int mtd_ioctl(struct inode *inode, struct file *file, |
314 | u_int cmd, u_long arg) | 376 | u_int cmd, u_long arg) |
315 | { | 377 | { |
316 | struct mtd_info *mtd = TO_MTD(file); | 378 | struct mtd_file_info *mfi = file->private_data; |
379 | struct mtd_info *mtd = mfi->mtd; | ||
317 | void __user *argp = (void __user *)arg; | 380 | void __user *argp = (void __user *)arg; |
318 | int ret = 0; | 381 | int ret = 0; |
319 | u_long size; | 382 | u_long size; |
@@ -554,16 +617,6 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
554 | break; | 617 | break; |
555 | } | 618 | } |
556 | 619 | ||
557 | case ECCGETLAYOUT: | ||
558 | |||
559 | if (!mtd->ecclayout) | ||
560 | return -EOPNOTSUPP; | ||
561 | |||
562 | if (copy_to_user(argp, &mtd->ecclayout, | ||
563 | sizeof(struct nand_ecclayout))) | ||
564 | return -EFAULT; | ||
565 | break; | ||
566 | |||
567 | case MEMGETBADBLOCK: | 620 | case MEMGETBADBLOCK: |
568 | { | 621 | { |
569 | loff_t offs; | 622 | loff_t offs; |
@@ -596,25 +649,11 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
596 | int mode; | 649 | int mode; |
597 | if (copy_from_user(&mode, argp, sizeof(int))) | 650 | if (copy_from_user(&mode, argp, sizeof(int))) |
598 | return -EFAULT; | 651 | return -EFAULT; |
599 | SET_MTD_MODE(file, 0); | 652 | |
600 | switch (mode) { | 653 | mfi->mode = MTD_MODE_NORMAL; |
601 | case MTD_OTP_FACTORY: | 654 | |
602 | if (!mtd->read_fact_prot_reg) | 655 | ret = otp_select_filemode(mfi, mode); |
603 | ret = -EOPNOTSUPP; | 656 | |
604 | else | ||
605 | SET_MTD_MODE(file, MTD_MODE_OTP_FACT); | ||
606 | break; | ||
607 | case MTD_OTP_USER: | ||
608 | if (!mtd->read_fact_prot_reg) | ||
609 | ret = -EOPNOTSUPP; | ||
610 | else | ||
611 | SET_MTD_MODE(file, MTD_MODE_OTP_USER); | ||
612 | break; | ||
613 | default: | ||
614 | ret = -EINVAL; | ||
615 | case MTD_OTP_OFF: | ||
616 | break; | ||
617 | } | ||
618 | file->f_pos = 0; | 657 | file->f_pos = 0; |
619 | break; | 658 | break; |
620 | } | 659 | } |
@@ -626,8 +665,8 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
626 | if (!buf) | 665 | if (!buf) |
627 | return -ENOMEM; | 666 | return -ENOMEM; |
628 | ret = -EOPNOTSUPP; | 667 | ret = -EOPNOTSUPP; |
629 | switch (MTD_MODE(file)) { | 668 | switch (mfi->mode) { |
630 | case MTD_MODE_OTP_FACT: | 669 | case MTD_MODE_OTP_FACTORY: |
631 | if (mtd->get_fact_prot_info) | 670 | if (mtd->get_fact_prot_info) |
632 | ret = mtd->get_fact_prot_info(mtd, buf, 4096); | 671 | ret = mtd->get_fact_prot_info(mtd, buf, 4096); |
633 | break; | 672 | break; |
@@ -635,6 +674,8 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
635 | if (mtd->get_user_prot_info) | 674 | if (mtd->get_user_prot_info) |
636 | ret = mtd->get_user_prot_info(mtd, buf, 4096); | 675 | ret = mtd->get_user_prot_info(mtd, buf, 4096); |
637 | break; | 676 | break; |
677 | default: | ||
678 | break; | ||
638 | } | 679 | } |
639 | if (ret >= 0) { | 680 | if (ret >= 0) { |
640 | if (cmd == OTPGETREGIONCOUNT) { | 681 | if (cmd == OTPGETREGIONCOUNT) { |
@@ -653,7 +694,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
653 | { | 694 | { |
654 | struct otp_info info; | 695 | struct otp_info info; |
655 | 696 | ||
656 | if (MTD_MODE(file) != MTD_MODE_OTP_USER) | 697 | if (mfi->mode != MTD_MODE_OTP_USER) |
657 | return -EINVAL; | 698 | return -EINVAL; |
658 | if (copy_from_user(&info, argp, sizeof(info))) | 699 | if (copy_from_user(&info, argp, sizeof(info))) |
659 | return -EFAULT; | 700 | return -EFAULT; |
@@ -664,6 +705,49 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
664 | } | 705 | } |
665 | #endif | 706 | #endif |
666 | 707 | ||
708 | case ECCGETLAYOUT: | ||
709 | { | ||
710 | if (!mtd->ecclayout) | ||
711 | return -EOPNOTSUPP; | ||
712 | |||
713 | if (copy_to_user(argp, &mtd->ecclayout, | ||
714 | sizeof(struct nand_ecclayout))) | ||
715 | return -EFAULT; | ||
716 | break; | ||
717 | } | ||
718 | |||
719 | case ECCGETSTATS: | ||
720 | { | ||
721 | if (copy_to_user(argp, &mtd->ecc_stats, | ||
722 | sizeof(struct mtd_ecc_stats))) | ||
723 | return -EFAULT; | ||
724 | break; | ||
725 | } | ||
726 | |||
727 | case MTDFILEMODE: | ||
728 | { | ||
729 | mfi->mode = 0; | ||
730 | |||
731 | switch(arg) { | ||
732 | case MTD_MODE_OTP_FACTORY: | ||
733 | case MTD_MODE_OTP_USER: | ||
734 | ret = otp_select_filemode(mfi, arg); | ||
735 | break; | ||
736 | |||
737 | case MTD_MODE_RAW: | ||
738 | if (!mtd->read_oob || !mtd->write_oob) | ||
739 | return -EOPNOTSUPP; | ||
740 | mfi->mode = arg; | ||
741 | |||
742 | case MTD_MODE_NORMAL: | ||
743 | break; | ||
744 | default: | ||
745 | ret = -EINVAL; | ||
746 | } | ||
747 | file->f_pos = 0; | ||
748 | break; | ||
749 | } | ||
750 | |||
667 | default: | 751 | default: |
668 | ret = -ENOTTY; | 752 | ret = -ENOTTY; |
669 | } | 753 | } |