diff options
author | Nicolas Pitre <nico@cam.org> | 2005-02-08 12:45:55 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@mtd.linutronix.de> | 2005-05-23 06:26:04 -0400 |
commit | 31f4233baeaaeb7c563d2766781c6592ad259b6a (patch) | |
tree | 475ffcfcc1fe91a47d6b8c413d8f25b3fdfcc1c0 /drivers | |
parent | f77814dd5728edaf1239d19755d2aa0d8c33d861 (diff) |
[MTD] User interface to Protection Registers
This is implemented using a ioctl to switch the MTD char device into
one of the different OTP "modes", at which point read/write/seek can
operate on the selected OTP area. Also some extra ioctls to query
for size and lock protection segments or groups. Some example user
space utilities are provided.
Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mtd/mtdchar.c | 113 |
1 files changed, 110 insertions, 3 deletions
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 510ad78312cc..6ea2d8058a4a 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.67 2005/02/08 17:45:51 nico Exp $ |
3 | * | 3 | * |
4 | * Character-device access to raw MTD devices. | 4 | * Character-device access to raw MTD devices. |
5 | * | 5 | * |
@@ -59,6 +59,12 @@ static inline void mtdchar_devfs_exit(void) | |||
59 | #define mtdchar_devfs_exit() do { } while(0) | 59 | #define mtdchar_devfs_exit() do { } while(0) |
60 | #endif | 60 | #endif |
61 | 61 | ||
62 | |||
63 | /* Well... let's abuse the unused bits in file->f_mode for those */ | ||
64 | #define MTD_MODE_OTP_FACT 0x1000 | ||
65 | #define MTD_MODE_OTP_USER 0x2000 | ||
66 | #define MTD_MODE_MASK 0xf000 | ||
67 | |||
62 | static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) | 68 | static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) |
63 | { | 69 | { |
64 | struct mtd_info *mtd = file->private_data; | 70 | struct mtd_info *mtd = file->private_data; |
@@ -105,6 +111,10 @@ static int mtd_open(struct inode *inode, struct file *file) | |||
105 | if ((file->f_mode & 2) && (minor & 1)) | 111 | if ((file->f_mode & 2) && (minor & 1)) |
106 | return -EACCES; | 112 | return -EACCES; |
107 | 113 | ||
114 | /* make sure the locally abused bits are initialy clear */ | ||
115 | if (file->f_mode & MTD_MODE_MASK) | ||
116 | return -EWOULDBLOCK; | ||
117 | |||
108 | mtd = get_mtd_device(NULL, devnum); | 118 | mtd = get_mtd_device(NULL, devnum); |
109 | 119 | ||
110 | if (!mtd) | 120 | if (!mtd) |
@@ -178,7 +188,16 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t | |||
178 | if (!kbuf) | 188 | if (!kbuf) |
179 | return -ENOMEM; | 189 | return -ENOMEM; |
180 | 190 | ||
181 | ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf); | 191 | switch (file->f_mode & MTD_MODE_MASK) { |
192 | case MTD_MODE_OTP_FACT: | ||
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 | default: | ||
199 | ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf); | ||
200 | } | ||
182 | /* Nand returns -EBADMSG on ecc errors, but it returns | 201 | /* Nand returns -EBADMSG on ecc errors, but it returns |
183 | * the data. For our userspace tools it is important | 202 | * the data. For our userspace tools it is important |
184 | * to dump areas with ecc errors ! | 203 | * to dump areas with ecc errors ! |
@@ -196,6 +215,8 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t | |||
196 | 215 | ||
197 | count -= retlen; | 216 | count -= retlen; |
198 | buf += retlen; | 217 | buf += retlen; |
218 | if (retlen == 0) | ||
219 | count = 0; | ||
199 | } | 220 | } |
200 | else { | 221 | else { |
201 | kfree(kbuf); | 222 | kfree(kbuf); |
@@ -245,7 +266,20 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count | |||
245 | return -EFAULT; | 266 | return -EFAULT; |
246 | } | 267 | } |
247 | 268 | ||
248 | ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); | 269 | switch (file->f_mode & MTD_MODE_MASK) { |
270 | case MTD_MODE_OTP_FACT: | ||
271 | ret = -EROFS; | ||
272 | break; | ||
273 | case MTD_MODE_OTP_USER: | ||
274 | if (!mtd->write_user_prot_reg) { | ||
275 | ret = -EOPNOTSUPP; | ||
276 | break; | ||
277 | } | ||
278 | ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); | ||
279 | break; | ||
280 | default: | ||
281 | ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); | ||
282 | } | ||
249 | if (!ret) { | 283 | if (!ret) { |
250 | *ppos += retlen; | 284 | *ppos += retlen; |
251 | total_retlen += retlen; | 285 | total_retlen += retlen; |
@@ -518,6 +552,79 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
518 | break; | 552 | break; |
519 | } | 553 | } |
520 | 554 | ||
555 | #ifdef CONFIG_MTD_OTP | ||
556 | case OTPSELECT: | ||
557 | { | ||
558 | int mode; | ||
559 | if (copy_from_user(&mode, argp, sizeof(int))) | ||
560 | return -EFAULT; | ||
561 | file->f_mode &= ~MTD_MODE_MASK; | ||
562 | switch (mode) { | ||
563 | case MTD_OTP_FACTORY: | ||
564 | if (!mtd->read_fact_prot_reg) | ||
565 | ret = -EOPNOTSUPP; | ||
566 | else | ||
567 | file->f_mode |= MTD_MODE_OTP_FACT; | ||
568 | break; | ||
569 | case MTD_OTP_USER: | ||
570 | if (!mtd->read_fact_prot_reg) | ||
571 | ret = -EOPNOTSUPP; | ||
572 | else | ||
573 | file->f_mode |= MTD_MODE_OTP_USER; | ||
574 | break; | ||
575 | default: | ||
576 | ret = -EINVAL; | ||
577 | case MTD_OTP_OFF: | ||
578 | break; | ||
579 | } | ||
580 | break; | ||
581 | } | ||
582 | |||
583 | case OTPGETREGIONCOUNT: | ||
584 | case OTPGETREGIONINFO: | ||
585 | { | ||
586 | struct otp_info *buf = kmalloc(4096, GFP_KERNEL); | ||
587 | if (!buf) | ||
588 | return -ENOMEM; | ||
589 | ret = -EOPNOTSUPP; | ||
590 | switch (file->f_mode & MTD_MODE_MASK) { | ||
591 | case MTD_MODE_OTP_FACT: | ||
592 | if (mtd->get_fact_prot_info) | ||
593 | ret = mtd->get_fact_prot_info(mtd, buf, 4096); | ||
594 | break; | ||
595 | case MTD_MODE_OTP_USER: | ||
596 | if (mtd->get_user_prot_info) | ||
597 | ret = mtd->get_user_prot_info(mtd, buf, 4096); | ||
598 | break; | ||
599 | } | ||
600 | if (ret >= 0) { | ||
601 | if (cmd == OTPGETREGIONCOUNT) { | ||
602 | int nbr = ret / sizeof(struct otp_info); | ||
603 | ret = copy_to_user(argp, &nbr, sizeof(int)); | ||
604 | } else | ||
605 | ret = copy_to_user(argp, buf, ret); | ||
606 | if (ret) | ||
607 | ret = -EFAULT; | ||
608 | } | ||
609 | kfree(buf); | ||
610 | break; | ||
611 | } | ||
612 | |||
613 | case OTPLOCK: | ||
614 | { | ||
615 | struct otp_info info; | ||
616 | |||
617 | if ((file->f_mode & MTD_MODE_MASK) != MTD_MODE_OTP_USER) | ||
618 | return -EINVAL; | ||
619 | if (copy_from_user(&info, argp, sizeof(info))) | ||
620 | return -EFAULT; | ||
621 | if (!mtd->lock_user_prot_reg) | ||
622 | return -EOPNOTSUPP; | ||
623 | ret = mtd->lock_user_prot_reg(mtd, info.start, info.length); | ||
624 | break; | ||
625 | } | ||
626 | #endif | ||
627 | |||
521 | default: | 628 | default: |
522 | ret = -ENOTTY; | 629 | ret = -ENOTTY; |
523 | } | 630 | } |