aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolas Pitre <nico@cam.org>2005-02-08 12:45:55 -0500
committerThomas Gleixner <tglx@mtd.linutronix.de>2005-05-23 06:26:04 -0400
commit31f4233baeaaeb7c563d2766781c6592ad259b6a (patch)
tree475ffcfcc1fe91a47d6b8c413d8f25b3fdfcc1c0
parentf77814dd5728edaf1239d19755d2aa0d8c33d861 (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>
-rw-r--r--drivers/mtd/mtdchar.c113
-rw-r--r--include/mtd/mtd-abi.h11
2 files changed, 120 insertions, 4 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
62static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) 68static 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 }
diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h
index 091eb571e993..c984cb2c9413 100644
--- a/include/mtd/mtd-abi.h
+++ b/include/mtd/mtd-abi.h
@@ -1,5 +1,5 @@
1/* 1/*
2 * $Id: mtd-abi.h,v 1.8 2005/02/08 17:11:16 nico Exp $ 2 * $Id: mtd-abi.h,v 1.9 2005/02/08 17:45:52 nico Exp $
3 * 3 *
4 * Portions of MTD ABI definition which are shared by kernel and user space 4 * Portions of MTD ABI definition which are shared by kernel and user space
5 */ 5 */
@@ -61,6 +61,11 @@ struct mtd_oob_buf {
61#define MTD_NANDECC_AUTOPLACE 2 // Use the default placement scheme 61#define MTD_NANDECC_AUTOPLACE 2 // Use the default placement scheme
62#define MTD_NANDECC_PLACEONLY 3 // Use the given placement in the structure (Do not store ecc result on read) 62#define MTD_NANDECC_PLACEONLY 3 // Use the given placement in the structure (Do not store ecc result on read)
63 63
64/* OTP mode selection */
65#define MTD_OTP_OFF 0
66#define MTD_OTP_FACTORY 1
67#define MTD_OTP_USER 2
68
64struct mtd_info_user { 69struct mtd_info_user {
65 uint8_t type; 70 uint8_t type;
66 uint32_t flags; 71 uint32_t flags;
@@ -98,6 +103,10 @@ struct otp_info {
98#define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo) 103#define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo)
99#define MEMGETBADBLOCK _IOW('M', 11, loff_t) 104#define MEMGETBADBLOCK _IOW('M', 11, loff_t)
100#define MEMSETBADBLOCK _IOW('M', 12, loff_t) 105#define MEMSETBADBLOCK _IOW('M', 12, loff_t)
106#define OTPSELECT _IOR('M', 13, int)
107#define OTPGETREGIONCOUNT _IOW('M', 14, int)
108#define OTPGETREGIONINFO _IOW('M', 15, struct otp_info)
109#define OTPLOCK _IOR('M', 16, struct otp_info)
101 110
102struct nand_oobinfo { 111struct nand_oobinfo {
103 uint32_t useecc; 112 uint32_t useecc;