diff options
Diffstat (limited to 'drivers/mtd/mtdchar.c')
-rw-r--r-- | drivers/mtd/mtdchar.c | 327 |
1 files changed, 233 insertions, 94 deletions
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 6f044584bdc6..aa18d45b264b 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; |
@@ -170,36 +173,58 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t | |||
170 | 173 | ||
171 | /* FIXME: Use kiovec in 2.5 to lock down the user's buffers | 174 | /* FIXME: Use kiovec in 2.5 to lock down the user's buffers |
172 | and pass them directly to the MTD functions */ | 175 | and pass them directly to the MTD functions */ |
176 | |||
177 | if (count > MAX_KMALLOC_SIZE) | ||
178 | kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL); | ||
179 | else | ||
180 | kbuf=kmalloc(count, GFP_KERNEL); | ||
181 | |||
182 | if (!kbuf) | ||
183 | return -ENOMEM; | ||
184 | |||
173 | while (count) { | 185 | while (count) { |
186 | |||
174 | if (count > MAX_KMALLOC_SIZE) | 187 | if (count > MAX_KMALLOC_SIZE) |
175 | len = MAX_KMALLOC_SIZE; | 188 | len = MAX_KMALLOC_SIZE; |
176 | else | 189 | else |
177 | len = count; | 190 | len = count; |
178 | 191 | ||
179 | kbuf=kmalloc(len,GFP_KERNEL); | 192 | switch (mfi->mode) { |
180 | if (!kbuf) | 193 | case MTD_MODE_OTP_FACTORY: |
181 | return -ENOMEM; | ||
182 | |||
183 | switch (MTD_MODE(file)) { | ||
184 | case MTD_MODE_OTP_FACT: | ||
185 | ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf); | 194 | ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf); |
186 | break; | 195 | break; |
187 | case MTD_MODE_OTP_USER: | 196 | case MTD_MODE_OTP_USER: |
188 | ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); | 197 | ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); |
189 | 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 | } | ||
190 | default: | 212 | default: |
191 | ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf); | 213 | ret = mtd->read(mtd, *ppos, len, &retlen, kbuf); |
192 | } | 214 | } |
193 | /* Nand returns -EBADMSG on ecc errors, but it returns | 215 | /* Nand returns -EBADMSG on ecc errors, but it returns |
194 | * the data. For our userspace tools it is important | 216 | * the data. For our userspace tools it is important |
195 | * to dump areas with ecc errors ! | 217 | * to dump areas with ecc errors ! |
218 | * For kernel internal usage it also might return -EUCLEAN | ||
219 | * to signal the caller that a bitflip has occured and has | ||
220 | * been corrected by the ECC algorithm. | ||
196 | * Userspace software which accesses NAND this way | 221 | * Userspace software which accesses NAND this way |
197 | * must be aware of the fact that it deals with NAND | 222 | * must be aware of the fact that it deals with NAND |
198 | */ | 223 | */ |
199 | if (!ret || (ret == -EBADMSG)) { | 224 | if (!ret || (ret == -EUCLEAN) || (ret == -EBADMSG)) { |
200 | *ppos += retlen; | 225 | *ppos += retlen; |
201 | if (copy_to_user(buf, kbuf, retlen)) { | 226 | if (copy_to_user(buf, kbuf, retlen)) { |
202 | kfree(kbuf); | 227 | kfree(kbuf); |
203 | return -EFAULT; | 228 | return -EFAULT; |
204 | } | 229 | } |
205 | else | 230 | else |
@@ -215,15 +240,16 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t | |||
215 | return ret; | 240 | return ret; |
216 | } | 241 | } |
217 | 242 | ||
218 | kfree(kbuf); | ||
219 | } | 243 | } |
220 | 244 | ||
245 | kfree(kbuf); | ||
221 | return total_retlen; | 246 | return total_retlen; |
222 | } /* mtd_read */ | 247 | } /* mtd_read */ |
223 | 248 | ||
224 | 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) |
225 | { | 250 | { |
226 | struct mtd_info *mtd = TO_MTD(file); | 251 | struct mtd_file_info *mfi = file->private_data; |
252 | struct mtd_info *mtd = mfi->mtd; | ||
227 | char *kbuf; | 253 | char *kbuf; |
228 | size_t retlen; | 254 | size_t retlen; |
229 | size_t total_retlen=0; | 255 | size_t total_retlen=0; |
@@ -241,25 +267,28 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count | |||
241 | if (!count) | 267 | if (!count) |
242 | return 0; | 268 | return 0; |
243 | 269 | ||
270 | if (count > MAX_KMALLOC_SIZE) | ||
271 | kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL); | ||
272 | else | ||
273 | kbuf=kmalloc(count, GFP_KERNEL); | ||
274 | |||
275 | if (!kbuf) | ||
276 | return -ENOMEM; | ||
277 | |||
244 | while (count) { | 278 | while (count) { |
279 | |||
245 | if (count > MAX_KMALLOC_SIZE) | 280 | if (count > MAX_KMALLOC_SIZE) |
246 | len = MAX_KMALLOC_SIZE; | 281 | len = MAX_KMALLOC_SIZE; |
247 | else | 282 | else |
248 | len = count; | 283 | len = count; |
249 | 284 | ||
250 | kbuf=kmalloc(len,GFP_KERNEL); | ||
251 | if (!kbuf) { | ||
252 | printk("kmalloc is null\n"); | ||
253 | return -ENOMEM; | ||
254 | } | ||
255 | |||
256 | if (copy_from_user(kbuf, buf, len)) { | 285 | if (copy_from_user(kbuf, buf, len)) { |
257 | kfree(kbuf); | 286 | kfree(kbuf); |
258 | return -EFAULT; | 287 | return -EFAULT; |
259 | } | 288 | } |
260 | 289 | ||
261 | switch (MTD_MODE(file)) { | 290 | switch (mfi->mode) { |
262 | case MTD_MODE_OTP_FACT: | 291 | case MTD_MODE_OTP_FACTORY: |
263 | ret = -EROFS; | 292 | ret = -EROFS; |
264 | break; | 293 | break; |
265 | case MTD_MODE_OTP_USER: | 294 | case MTD_MODE_OTP_USER: |
@@ -269,6 +298,21 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count | |||
269 | } | 298 | } |
270 | ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); | 299 | ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); |
271 | 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 | |||
272 | default: | 316 | default: |
273 | ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); | 317 | ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); |
274 | } | 318 | } |
@@ -282,10 +326,9 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count | |||
282 | kfree(kbuf); | 326 | kfree(kbuf); |
283 | return ret; | 327 | return ret; |
284 | } | 328 | } |
285 | |||
286 | kfree(kbuf); | ||
287 | } | 329 | } |
288 | 330 | ||
331 | kfree(kbuf); | ||
289 | return total_retlen; | 332 | return total_retlen; |
290 | } /* mtd_write */ | 333 | } /* mtd_write */ |
291 | 334 | ||
@@ -299,13 +342,45 @@ static void mtdchar_erase_callback (struct erase_info *instr) | |||
299 | wake_up((wait_queue_head_t *)instr->priv); | 342 | wake_up((wait_queue_head_t *)instr->priv); |
300 | } | 343 | } |
301 | 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 | |||
302 | static int mtd_ioctl(struct inode *inode, struct file *file, | 375 | static int mtd_ioctl(struct inode *inode, struct file *file, |
303 | u_int cmd, u_long arg) | 376 | u_int cmd, u_long arg) |
304 | { | 377 | { |
305 | struct mtd_info *mtd = TO_MTD(file); | 378 | struct mtd_file_info *mfi = file->private_data; |
379 | struct mtd_info *mtd = mfi->mtd; | ||
306 | void __user *argp = (void __user *)arg; | 380 | void __user *argp = (void __user *)arg; |
307 | int ret = 0; | 381 | int ret = 0; |
308 | u_long size; | 382 | u_long size; |
383 | struct mtd_info_user info; | ||
309 | 384 | ||
310 | DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n"); | 385 | DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n"); |
311 | 386 | ||
@@ -341,7 +416,15 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
341 | } | 416 | } |
342 | 417 | ||
343 | case MEMGETINFO: | 418 | case MEMGETINFO: |
344 | if (copy_to_user(argp, mtd, sizeof(struct mtd_info_user))) | 419 | info.type = mtd->type; |
420 | info.flags = mtd->flags; | ||
421 | info.size = mtd->size; | ||
422 | info.erasesize = mtd->erasesize; | ||
423 | info.writesize = mtd->writesize; | ||
424 | info.oobsize = mtd->oobsize; | ||
425 | info.ecctype = mtd->ecctype; | ||
426 | info.eccsize = mtd->eccsize; | ||
427 | if (copy_to_user(argp, &info, sizeof(struct mtd_info_user))) | ||
345 | return -EFAULT; | 428 | return -EFAULT; |
346 | break; | 429 | break; |
347 | 430 | ||
@@ -400,8 +483,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
400 | case MEMWRITEOOB: | 483 | case MEMWRITEOOB: |
401 | { | 484 | { |
402 | struct mtd_oob_buf buf; | 485 | struct mtd_oob_buf buf; |
403 | void *databuf; | 486 | struct mtd_oob_ops ops; |
404 | ssize_t retlen; | ||
405 | 487 | ||
406 | if(!(file->f_mode & 2)) | 488 | if(!(file->f_mode & 2)) |
407 | return -EPERM; | 489 | return -EPERM; |
@@ -409,7 +491,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
409 | if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) | 491 | if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) |
410 | return -EFAULT; | 492 | return -EFAULT; |
411 | 493 | ||
412 | if (buf.length > 0x4096) | 494 | if (buf.length > 4096) |
413 | return -EINVAL; | 495 | return -EINVAL; |
414 | 496 | ||
415 | if (!mtd->write_oob) | 497 | if (!mtd->write_oob) |
@@ -421,21 +503,32 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
421 | if (ret) | 503 | if (ret) |
422 | return ret; | 504 | return ret; |
423 | 505 | ||
424 | databuf = kmalloc(buf.length, GFP_KERNEL); | 506 | ops.len = buf.length; |
425 | if (!databuf) | 507 | ops.ooblen = buf.length; |
508 | ops.ooboffs = buf.start & (mtd->oobsize - 1); | ||
509 | ops.datbuf = NULL; | ||
510 | ops.mode = MTD_OOB_PLACE; | ||
511 | |||
512 | if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs)) | ||
513 | return -EINVAL; | ||
514 | |||
515 | ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); | ||
516 | if (!ops.oobbuf) | ||
426 | return -ENOMEM; | 517 | return -ENOMEM; |
427 | 518 | ||
428 | if (copy_from_user(databuf, buf.ptr, buf.length)) { | 519 | if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) { |
429 | kfree(databuf); | 520 | kfree(ops.oobbuf); |
430 | return -EFAULT; | 521 | return -EFAULT; |
431 | } | 522 | } |
432 | 523 | ||
433 | ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf); | 524 | buf.start &= ~(mtd->oobsize - 1); |
525 | ret = mtd->write_oob(mtd, buf.start, &ops); | ||
434 | 526 | ||
435 | if (copy_to_user(argp + sizeof(uint32_t), &retlen, sizeof(uint32_t))) | 527 | if (copy_to_user(argp + sizeof(uint32_t), &ops.retlen, |
528 | sizeof(uint32_t))) | ||
436 | ret = -EFAULT; | 529 | ret = -EFAULT; |
437 | 530 | ||
438 | kfree(databuf); | 531 | kfree(ops.oobbuf); |
439 | break; | 532 | break; |
440 | 533 | ||
441 | } | 534 | } |
@@ -443,13 +536,12 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
443 | case MEMREADOOB: | 536 | case MEMREADOOB: |
444 | { | 537 | { |
445 | struct mtd_oob_buf buf; | 538 | struct mtd_oob_buf buf; |
446 | void *databuf; | 539 | struct mtd_oob_ops ops; |
447 | ssize_t retlen; | ||
448 | 540 | ||
449 | if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) | 541 | if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) |
450 | return -EFAULT; | 542 | return -EFAULT; |
451 | 543 | ||
452 | if (buf.length > 0x4096) | 544 | if (buf.length > 4096) |
453 | return -EINVAL; | 545 | return -EINVAL; |
454 | 546 | ||
455 | if (!mtd->read_oob) | 547 | if (!mtd->read_oob) |
@@ -457,22 +549,32 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
457 | else | 549 | else |
458 | ret = access_ok(VERIFY_WRITE, buf.ptr, | 550 | ret = access_ok(VERIFY_WRITE, buf.ptr, |
459 | buf.length) ? 0 : -EFAULT; | 551 | buf.length) ? 0 : -EFAULT; |
460 | |||
461 | if (ret) | 552 | if (ret) |
462 | return ret; | 553 | return ret; |
463 | 554 | ||
464 | databuf = kmalloc(buf.length, GFP_KERNEL); | 555 | ops.len = buf.length; |
465 | if (!databuf) | 556 | ops.ooblen = buf.length; |
557 | ops.ooboffs = buf.start & (mtd->oobsize - 1); | ||
558 | ops.datbuf = NULL; | ||
559 | ops.mode = MTD_OOB_PLACE; | ||
560 | |||
561 | if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs)) | ||
562 | return -EINVAL; | ||
563 | |||
564 | ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); | ||
565 | if (!ops.oobbuf) | ||
466 | return -ENOMEM; | 566 | return -ENOMEM; |
467 | 567 | ||
468 | ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf); | 568 | buf.start &= ~(mtd->oobsize - 1); |
569 | ret = mtd->read_oob(mtd, buf.start, &ops); | ||
469 | 570 | ||
470 | if (put_user(retlen, (uint32_t __user *)argp)) | 571 | if (put_user(ops.retlen, (uint32_t __user *)argp)) |
471 | ret = -EFAULT; | 572 | ret = -EFAULT; |
472 | else if (retlen && copy_to_user(buf.ptr, databuf, retlen)) | 573 | else if (ops.retlen && copy_to_user(buf.ptr, ops.oobbuf, |
574 | ops.retlen)) | ||
473 | ret = -EFAULT; | 575 | ret = -EFAULT; |
474 | 576 | ||
475 | kfree(databuf); | 577 | kfree(ops.oobbuf); |
476 | break; | 578 | break; |
477 | } | 579 | } |
478 | 580 | ||
@@ -504,16 +606,22 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
504 | break; | 606 | break; |
505 | } | 607 | } |
506 | 608 | ||
507 | case MEMSETOOBSEL: | 609 | /* Legacy interface */ |
508 | { | ||
509 | if (copy_from_user(&mtd->oobinfo, argp, sizeof(struct nand_oobinfo))) | ||
510 | return -EFAULT; | ||
511 | break; | ||
512 | } | ||
513 | |||
514 | case MEMGETOOBSEL: | 610 | case MEMGETOOBSEL: |
515 | { | 611 | { |
516 | if (copy_to_user(argp, &(mtd->oobinfo), sizeof(struct nand_oobinfo))) | 612 | struct nand_oobinfo oi; |
613 | |||
614 | if (!mtd->ecclayout) | ||
615 | return -EOPNOTSUPP; | ||
616 | if (mtd->ecclayout->eccbytes > ARRAY_SIZE(oi.eccpos)) | ||
617 | return -EINVAL; | ||
618 | |||
619 | oi.useecc = MTD_NANDECC_AUTOPLACE; | ||
620 | memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos)); | ||
621 | memcpy(&oi.oobfree, mtd->ecclayout->oobfree, | ||
622 | sizeof(oi.oobfree)); | ||
623 | |||
624 | if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo))) | ||
517 | return -EFAULT; | 625 | return -EFAULT; |
518 | break; | 626 | break; |
519 | } | 627 | } |
@@ -544,31 +652,17 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
544 | break; | 652 | break; |
545 | } | 653 | } |
546 | 654 | ||
547 | #ifdef CONFIG_MTD_OTP | 655 | #if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP) |
548 | case OTPSELECT: | 656 | case OTPSELECT: |
549 | { | 657 | { |
550 | int mode; | 658 | int mode; |
551 | if (copy_from_user(&mode, argp, sizeof(int))) | 659 | if (copy_from_user(&mode, argp, sizeof(int))) |
552 | return -EFAULT; | 660 | return -EFAULT; |
553 | SET_MTD_MODE(file, 0); | 661 | |
554 | switch (mode) { | 662 | mfi->mode = MTD_MODE_NORMAL; |
555 | case MTD_OTP_FACTORY: | 663 | |
556 | if (!mtd->read_fact_prot_reg) | 664 | ret = otp_select_filemode(mfi, mode); |
557 | ret = -EOPNOTSUPP; | 665 | |
558 | else | ||
559 | SET_MTD_MODE(file, MTD_MODE_OTP_FACT); | ||
560 | break; | ||
561 | case MTD_OTP_USER: | ||
562 | if (!mtd->read_fact_prot_reg) | ||
563 | ret = -EOPNOTSUPP; | ||
564 | else | ||
565 | SET_MTD_MODE(file, MTD_MODE_OTP_USER); | ||
566 | break; | ||
567 | default: | ||
568 | ret = -EINVAL; | ||
569 | case MTD_OTP_OFF: | ||
570 | break; | ||
571 | } | ||
572 | file->f_pos = 0; | 666 | file->f_pos = 0; |
573 | break; | 667 | break; |
574 | } | 668 | } |
@@ -580,8 +674,8 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
580 | if (!buf) | 674 | if (!buf) |
581 | return -ENOMEM; | 675 | return -ENOMEM; |
582 | ret = -EOPNOTSUPP; | 676 | ret = -EOPNOTSUPP; |
583 | switch (MTD_MODE(file)) { | 677 | switch (mfi->mode) { |
584 | case MTD_MODE_OTP_FACT: | 678 | case MTD_MODE_OTP_FACTORY: |
585 | if (mtd->get_fact_prot_info) | 679 | if (mtd->get_fact_prot_info) |
586 | ret = mtd->get_fact_prot_info(mtd, buf, 4096); | 680 | ret = mtd->get_fact_prot_info(mtd, buf, 4096); |
587 | break; | 681 | break; |
@@ -589,6 +683,8 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
589 | if (mtd->get_user_prot_info) | 683 | if (mtd->get_user_prot_info) |
590 | ret = mtd->get_user_prot_info(mtd, buf, 4096); | 684 | ret = mtd->get_user_prot_info(mtd, buf, 4096); |
591 | break; | 685 | break; |
686 | default: | ||
687 | break; | ||
592 | } | 688 | } |
593 | if (ret >= 0) { | 689 | if (ret >= 0) { |
594 | if (cmd == OTPGETREGIONCOUNT) { | 690 | if (cmd == OTPGETREGIONCOUNT) { |
@@ -607,7 +703,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
607 | { | 703 | { |
608 | struct otp_info info; | 704 | struct otp_info info; |
609 | 705 | ||
610 | if (MTD_MODE(file) != MTD_MODE_OTP_USER) | 706 | if (mfi->mode != MTD_MODE_OTP_USER) |
611 | return -EINVAL; | 707 | return -EINVAL; |
612 | if (copy_from_user(&info, argp, sizeof(info))) | 708 | if (copy_from_user(&info, argp, sizeof(info))) |
613 | return -EFAULT; | 709 | return -EFAULT; |
@@ -618,6 +714,49 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
618 | } | 714 | } |
619 | #endif | 715 | #endif |
620 | 716 | ||
717 | case ECCGETLAYOUT: | ||
718 | { | ||
719 | if (!mtd->ecclayout) | ||
720 | return -EOPNOTSUPP; | ||
721 | |||
722 | if (copy_to_user(argp, &mtd->ecclayout, | ||
723 | sizeof(struct nand_ecclayout))) | ||
724 | return -EFAULT; | ||
725 | break; | ||
726 | } | ||
727 | |||
728 | case ECCGETSTATS: | ||
729 | { | ||
730 | if (copy_to_user(argp, &mtd->ecc_stats, | ||
731 | sizeof(struct mtd_ecc_stats))) | ||
732 | return -EFAULT; | ||
733 | break; | ||
734 | } | ||
735 | |||
736 | case MTDFILEMODE: | ||
737 | { | ||
738 | mfi->mode = 0; | ||
739 | |||
740 | switch(arg) { | ||
741 | case MTD_MODE_OTP_FACTORY: | ||
742 | case MTD_MODE_OTP_USER: | ||
743 | ret = otp_select_filemode(mfi, arg); | ||
744 | break; | ||
745 | |||
746 | case MTD_MODE_RAW: | ||
747 | if (!mtd->read_oob || !mtd->write_oob) | ||
748 | return -EOPNOTSUPP; | ||
749 | mfi->mode = arg; | ||
750 | |||
751 | case MTD_MODE_NORMAL: | ||
752 | break; | ||
753 | default: | ||
754 | ret = -EINVAL; | ||
755 | } | ||
756 | file->f_pos = 0; | ||
757 | break; | ||
758 | } | ||
759 | |||
621 | default: | 760 | default: |
622 | ret = -ENOTTY; | 761 | ret = -ENOTTY; |
623 | } | 762 | } |