diff options
Diffstat (limited to 'drivers/mtd/mtdchar.c')
-rw-r--r-- | drivers/mtd/mtdchar.c | 86 |
1 files changed, 42 insertions, 44 deletions
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 16df1e4fb0e9..6f044584bdc6 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c | |||
@@ -1,22 +1,23 @@ | |||
1 | /* | 1 | /* |
2 | * $Id: mtdchar.c,v 1.73 2005/07/04 17:36:41 gleixner Exp $ | 2 | * $Id: mtdchar.c,v 1.76 2005/11/07 11:14:20 gleixner Exp $ |
3 | * | 3 | * |
4 | * Character-device access to raw MTD devices. | 4 | * Character-device access to raw MTD devices. |
5 | * | 5 | * |
6 | */ | 6 | */ |
7 | 7 | ||
8 | #include <linux/config.h> | 8 | #include <linux/config.h> |
9 | #include <linux/device.h> | ||
10 | #include <linux/fs.h> | ||
11 | #include <linux/init.h> | ||
9 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
10 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <linux/slab.h> | ||
15 | #include <linux/sched.h> | ||
16 | |||
11 | #include <linux/mtd/mtd.h> | 17 | #include <linux/mtd/mtd.h> |
12 | #include <linux/mtd/compatmac.h> | 18 | #include <linux/mtd/compatmac.h> |
13 | #include <linux/slab.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/fs.h> | ||
16 | #include <linux/sched.h> /* TASK_* */ | ||
17 | #include <asm/uaccess.h> | ||
18 | 19 | ||
19 | #include <linux/device.h> | 20 | #include <asm/uaccess.h> |
20 | 21 | ||
21 | static struct class *mtd_class; | 22 | static struct class *mtd_class; |
22 | 23 | ||
@@ -27,7 +28,7 @@ static void mtd_notify_add(struct mtd_info* mtd) | |||
27 | 28 | ||
28 | class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2), | 29 | class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2), |
29 | NULL, "mtd%d", mtd->index); | 30 | NULL, "mtd%d", mtd->index); |
30 | 31 | ||
31 | class_device_create(mtd_class, NULL, | 32 | class_device_create(mtd_class, NULL, |
32 | MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), | 33 | MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), |
33 | NULL, "mtd%dro", mtd->index); | 34 | NULL, "mtd%dro", mtd->index); |
@@ -70,26 +71,23 @@ static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) | |||
70 | switch (orig) { | 71 | switch (orig) { |
71 | case 0: | 72 | case 0: |
72 | /* SEEK_SET */ | 73 | /* SEEK_SET */ |
73 | file->f_pos = offset; | ||
74 | break; | 74 | break; |
75 | case 1: | 75 | case 1: |
76 | /* SEEK_CUR */ | 76 | /* SEEK_CUR */ |
77 | file->f_pos += offset; | 77 | offset += file->f_pos; |
78 | break; | 78 | break; |
79 | case 2: | 79 | case 2: |
80 | /* SEEK_END */ | 80 | /* SEEK_END */ |
81 | file->f_pos =mtd->size + offset; | 81 | offset += mtd->size; |
82 | break; | 82 | break; |
83 | default: | 83 | default: |
84 | return -EINVAL; | 84 | return -EINVAL; |
85 | } | 85 | } |
86 | 86 | ||
87 | if (file->f_pos < 0) | 87 | if (offset >= 0 && offset < mtd->size) |
88 | file->f_pos = 0; | 88 | return file->f_pos = offset; |
89 | else if (file->f_pos >= mtd->size) | ||
90 | file->f_pos = mtd->size - 1; | ||
91 | 89 | ||
92 | return file->f_pos; | 90 | return -EINVAL; |
93 | } | 91 | } |
94 | 92 | ||
95 | 93 | ||
@@ -110,23 +108,23 @@ static int mtd_open(struct inode *inode, struct file *file) | |||
110 | return -EACCES; | 108 | return -EACCES; |
111 | 109 | ||
112 | mtd = get_mtd_device(NULL, devnum); | 110 | mtd = get_mtd_device(NULL, devnum); |
113 | 111 | ||
114 | if (!mtd) | 112 | if (!mtd) |
115 | return -ENODEV; | 113 | return -ENODEV; |
116 | 114 | ||
117 | if (MTD_ABSENT == mtd->type) { | 115 | if (MTD_ABSENT == mtd->type) { |
118 | put_mtd_device(mtd); | 116 | put_mtd_device(mtd); |
119 | return -ENODEV; | 117 | return -ENODEV; |
120 | } | 118 | } |
121 | 119 | ||
122 | file->private_data = mtd; | 120 | file->private_data = mtd; |
123 | 121 | ||
124 | /* You can't open it RW if it's not a writeable device */ | 122 | /* You can't open it RW if it's not a writeable device */ |
125 | if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) { | 123 | if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) { |
126 | put_mtd_device(mtd); | 124 | put_mtd_device(mtd); |
127 | return -EACCES; | 125 | return -EACCES; |
128 | } | 126 | } |
129 | 127 | ||
130 | return 0; | 128 | return 0; |
131 | } /* mtd_open */ | 129 | } /* mtd_open */ |
132 | 130 | ||
@@ -139,10 +137,10 @@ static int mtd_close(struct inode *inode, struct file *file) | |||
139 | DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); | 137 | DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); |
140 | 138 | ||
141 | mtd = TO_MTD(file); | 139 | mtd = TO_MTD(file); |
142 | 140 | ||
143 | if (mtd->sync) | 141 | if (mtd->sync) |
144 | mtd->sync(mtd); | 142 | mtd->sync(mtd); |
145 | 143 | ||
146 | put_mtd_device(mtd); | 144 | put_mtd_device(mtd); |
147 | 145 | ||
148 | return 0; | 146 | return 0; |
@@ -161,7 +159,7 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t | |||
161 | int ret=0; | 159 | int ret=0; |
162 | int len; | 160 | int len; |
163 | char *kbuf; | 161 | char *kbuf; |
164 | 162 | ||
165 | DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n"); | 163 | DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n"); |
166 | 164 | ||
167 | if (*ppos + count > mtd->size) | 165 | if (*ppos + count > mtd->size) |
@@ -169,11 +167,11 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t | |||
169 | 167 | ||
170 | if (!count) | 168 | if (!count) |
171 | return 0; | 169 | return 0; |
172 | 170 | ||
173 | /* FIXME: Use kiovec in 2.5 to lock down the user's buffers | 171 | /* FIXME: Use kiovec in 2.5 to lock down the user's buffers |
174 | and pass them directly to the MTD functions */ | 172 | and pass them directly to the MTD functions */ |
175 | while (count) { | 173 | while (count) { |
176 | if (count > MAX_KMALLOC_SIZE) | 174 | if (count > MAX_KMALLOC_SIZE) |
177 | len = MAX_KMALLOC_SIZE; | 175 | len = MAX_KMALLOC_SIZE; |
178 | else | 176 | else |
179 | len = count; | 177 | len = count; |
@@ -181,7 +179,7 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t | |||
181 | kbuf=kmalloc(len,GFP_KERNEL); | 179 | kbuf=kmalloc(len,GFP_KERNEL); |
182 | if (!kbuf) | 180 | if (!kbuf) |
183 | return -ENOMEM; | 181 | return -ENOMEM; |
184 | 182 | ||
185 | switch (MTD_MODE(file)) { | 183 | switch (MTD_MODE(file)) { |
186 | case MTD_MODE_OTP_FACT: | 184 | case MTD_MODE_OTP_FACT: |
187 | ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf); | 185 | ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf); |
@@ -194,7 +192,7 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t | |||
194 | } | 192 | } |
195 | /* Nand returns -EBADMSG on ecc errors, but it returns | 193 | /* Nand returns -EBADMSG on ecc errors, but it returns |
196 | * the data. For our userspace tools it is important | 194 | * the data. For our userspace tools it is important |
197 | * to dump areas with ecc errors ! | 195 | * to dump areas with ecc errors ! |
198 | * Userspace software which accesses NAND this way | 196 | * Userspace software which accesses NAND this way |
199 | * must be aware of the fact that it deals with NAND | 197 | * must be aware of the fact that it deals with NAND |
200 | */ | 198 | */ |
@@ -216,7 +214,7 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t | |||
216 | kfree(kbuf); | 214 | kfree(kbuf); |
217 | return ret; | 215 | return ret; |
218 | } | 216 | } |
219 | 217 | ||
220 | kfree(kbuf); | 218 | kfree(kbuf); |
221 | } | 219 | } |
222 | 220 | ||
@@ -233,10 +231,10 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count | |||
233 | int len; | 231 | int len; |
234 | 232 | ||
235 | DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n"); | 233 | DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n"); |
236 | 234 | ||
237 | if (*ppos == mtd->size) | 235 | if (*ppos == mtd->size) |
238 | return -ENOSPC; | 236 | return -ENOSPC; |
239 | 237 | ||
240 | if (*ppos + count > mtd->size) | 238 | if (*ppos + count > mtd->size) |
241 | count = mtd->size - *ppos; | 239 | count = mtd->size - *ppos; |
242 | 240 | ||
@@ -244,7 +242,7 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count | |||
244 | return 0; | 242 | return 0; |
245 | 243 | ||
246 | while (count) { | 244 | while (count) { |
247 | if (count > MAX_KMALLOC_SIZE) | 245 | if (count > MAX_KMALLOC_SIZE) |
248 | len = MAX_KMALLOC_SIZE; | 246 | len = MAX_KMALLOC_SIZE; |
249 | else | 247 | else |
250 | len = count; | 248 | len = count; |
@@ -259,7 +257,7 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count | |||
259 | kfree(kbuf); | 257 | kfree(kbuf); |
260 | return -EFAULT; | 258 | return -EFAULT; |
261 | } | 259 | } |
262 | 260 | ||
263 | switch (MTD_MODE(file)) { | 261 | switch (MTD_MODE(file)) { |
264 | case MTD_MODE_OTP_FACT: | 262 | case MTD_MODE_OTP_FACT: |
265 | ret = -EROFS; | 263 | ret = -EROFS; |
@@ -284,7 +282,7 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count | |||
284 | kfree(kbuf); | 282 | kfree(kbuf); |
285 | return ret; | 283 | return ret; |
286 | } | 284 | } |
287 | 285 | ||
288 | kfree(kbuf); | 286 | kfree(kbuf); |
289 | } | 287 | } |
290 | 288 | ||
@@ -308,7 +306,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
308 | void __user *argp = (void __user *)arg; | 306 | void __user *argp = (void __user *)arg; |
309 | int ret = 0; | 307 | int ret = 0; |
310 | u_long size; | 308 | u_long size; |
311 | 309 | ||
312 | DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n"); | 310 | DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n"); |
313 | 311 | ||
314 | size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; | 312 | size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; |
@@ -320,7 +318,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
320 | if (!access_ok(VERIFY_WRITE, argp, size)) | 318 | if (!access_ok(VERIFY_WRITE, argp, size)) |
321 | return -EFAULT; | 319 | return -EFAULT; |
322 | } | 320 | } |
323 | 321 | ||
324 | switch (cmd) { | 322 | switch (cmd) { |
325 | case MEMGETREGIONCOUNT: | 323 | case MEMGETREGIONCOUNT: |
326 | if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int))) | 324 | if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int))) |
@@ -372,11 +370,11 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
372 | erase->mtd = mtd; | 370 | erase->mtd = mtd; |
373 | erase->callback = mtdchar_erase_callback; | 371 | erase->callback = mtdchar_erase_callback; |
374 | erase->priv = (unsigned long)&waitq; | 372 | erase->priv = (unsigned long)&waitq; |
375 | 373 | ||
376 | /* | 374 | /* |
377 | FIXME: Allow INTERRUPTIBLE. Which means | 375 | FIXME: Allow INTERRUPTIBLE. Which means |
378 | not having the wait_queue head on the stack. | 376 | not having the wait_queue head on the stack. |
379 | 377 | ||
380 | If the wq_head is on the stack, and we | 378 | If the wq_head is on the stack, and we |
381 | leave because we got interrupted, then the | 379 | leave because we got interrupted, then the |
382 | wq_head is no longer there when the | 380 | wq_head is no longer there when the |
@@ -404,13 +402,13 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
404 | struct mtd_oob_buf buf; | 402 | struct mtd_oob_buf buf; |
405 | void *databuf; | 403 | void *databuf; |
406 | ssize_t retlen; | 404 | ssize_t retlen; |
407 | 405 | ||
408 | if(!(file->f_mode & 2)) | 406 | if(!(file->f_mode & 2)) |
409 | return -EPERM; | 407 | return -EPERM; |
410 | 408 | ||
411 | if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) | 409 | if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) |
412 | return -EFAULT; | 410 | return -EFAULT; |
413 | 411 | ||
414 | if (buf.length > 0x4096) | 412 | if (buf.length > 0x4096) |
415 | return -EINVAL; | 413 | return -EINVAL; |
416 | 414 | ||
@@ -426,7 +424,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
426 | databuf = kmalloc(buf.length, GFP_KERNEL); | 424 | databuf = kmalloc(buf.length, GFP_KERNEL); |
427 | if (!databuf) | 425 | if (!databuf) |
428 | return -ENOMEM; | 426 | return -ENOMEM; |
429 | 427 | ||
430 | if (copy_from_user(databuf, buf.ptr, buf.length)) { | 428 | if (copy_from_user(databuf, buf.ptr, buf.length)) { |
431 | kfree(databuf); | 429 | kfree(databuf); |
432 | return -EFAULT; | 430 | return -EFAULT; |
@@ -450,7 +448,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
450 | 448 | ||
451 | if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) | 449 | if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) |
452 | return -EFAULT; | 450 | return -EFAULT; |
453 | 451 | ||
454 | if (buf.length > 0x4096) | 452 | if (buf.length > 0x4096) |
455 | return -EINVAL; | 453 | return -EINVAL; |
456 | 454 | ||
@@ -466,14 +464,14 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
466 | databuf = kmalloc(buf.length, GFP_KERNEL); | 464 | databuf = kmalloc(buf.length, GFP_KERNEL); |
467 | if (!databuf) | 465 | if (!databuf) |
468 | return -ENOMEM; | 466 | return -ENOMEM; |
469 | 467 | ||
470 | ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf); | 468 | ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf); |
471 | 469 | ||
472 | if (put_user(retlen, (uint32_t __user *)argp)) | 470 | if (put_user(retlen, (uint32_t __user *)argp)) |
473 | ret = -EFAULT; | 471 | ret = -EFAULT; |
474 | else if (retlen && copy_to_user(buf.ptr, databuf, retlen)) | 472 | else if (retlen && copy_to_user(buf.ptr, databuf, retlen)) |
475 | ret = -EFAULT; | 473 | ret = -EFAULT; |
476 | 474 | ||
477 | kfree(databuf); | 475 | kfree(databuf); |
478 | break; | 476 | break; |
479 | } | 477 | } |
@@ -523,7 +521,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
523 | case MEMGETBADBLOCK: | 521 | case MEMGETBADBLOCK: |
524 | { | 522 | { |
525 | loff_t offs; | 523 | loff_t offs; |
526 | 524 | ||
527 | if (copy_from_user(&offs, argp, sizeof(loff_t))) | 525 | if (copy_from_user(&offs, argp, sizeof(loff_t))) |
528 | return -EFAULT; | 526 | return -EFAULT; |
529 | if (!mtd->block_isbad) | 527 | if (!mtd->block_isbad) |