1 /*
2 * $Id: mtdchar.c,v 1.49 2003/01/24 12:02:58 dwmw2 Exp $
3 *
4 * Character-device access to raw MTD devices.
5 * Pure 2.4 version - compatibility cruft removed to mtdchar-compat.c
6 *
7 */
8
9 #include <linux/config.h>
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/mtd/mtd.h>
13 #include <linux/slab.h>
14
15 #ifdef CONFIG_DEVFS_FS
16 #include <linux/devfs_fs_kernel.h>
17 static void mtd_notify_add(struct mtd_info* mtd);
18 static void mtd_notify_remove(struct mtd_info* mtd);
19
20 static struct mtd_notifier notifier = {
21 add: mtd_notify_add,
22 remove: mtd_notify_remove,
23 };
24
25 static devfs_handle_t devfs_dir_handle;
26 static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES];
27 static devfs_handle_t devfs_ro_handle[MAX_MTD_DEVICES];
28 #endif
29
mtd_lseek(struct file * file,loff_t offset,int orig)30 static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
31 {
32 struct mtd_info *mtd=(struct mtd_info *)file->private_data;
33
34 switch (orig) {
35 case 0:
36 /* SEEK_SET */
37 file->f_pos = offset;
38 break;
39 case 1:
40 /* SEEK_CUR */
41 file->f_pos += offset;
42 break;
43 case 2:
44 /* SEEK_END */
45 file->f_pos =mtd->size + offset;
46 break;
47 default:
48 return -EINVAL;
49 }
50
51 if (file->f_pos < 0)
52 file->f_pos = 0;
53 else if (file->f_pos >= mtd->size)
54 file->f_pos = mtd->size - 1;
55
56 return file->f_pos;
57 }
58
59
60
mtd_open(struct inode * inode,struct file * file)61 static int mtd_open(struct inode *inode, struct file *file)
62 {
63 int minor = minor(inode->i_rdev);
64 int devnum = minor >> 1;
65 struct mtd_info *mtd;
66
67 DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n");
68
69 if (devnum >= MAX_MTD_DEVICES)
70 return -ENODEV;
71
72 /* You can't open the RO devices RW */
73 if ((file->f_mode & 2) && (minor & 1))
74 return -EACCES;
75
76 mtd = get_mtd_device(NULL, devnum);
77
78 if (!mtd)
79 return -ENODEV;
80
81 if (MTD_ABSENT == mtd->type) {
82 put_mtd_device(mtd);
83 return -ENODEV;
84 }
85
86 file->private_data = mtd;
87
88 /* You can't open it RW if it's not a writeable device */
89 if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) {
90 put_mtd_device(mtd);
91 return -EACCES;
92 }
93
94 return 0;
95 } /* mtd_open */
96
97 /*====================================================================*/
98
mtd_close(struct inode * inode,struct file * file)99 static int mtd_close(struct inode *inode, struct file *file)
100 {
101 struct mtd_info *mtd;
102
103 DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
104
105 mtd = (struct mtd_info *)file->private_data;
106
107 if (mtd->sync)
108 mtd->sync(mtd);
109
110 put_mtd_device(mtd);
111
112 return 0;
113 } /* mtd_close */
114
115 /* FIXME: This _really_ needs to die. In 2.5, we should lock the
116 userspace buffer down and use it directly with readv/writev.
117 */
118 #define MAX_KMALLOC_SIZE 0x20000
119
mtd_read(struct file * file,char * buf,size_t count,loff_t * ppos)120 static ssize_t mtd_read(struct file *file, char *buf, size_t count,loff_t *ppos)
121 {
122 struct mtd_info *mtd = (struct mtd_info *)file->private_data;
123 size_t retlen=0;
124 size_t total_retlen=0;
125 int ret=0;
126 int len;
127 char *kbuf;
128 loff_t pos = *ppos;
129
130 DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n");
131
132 if (pos < 0 || pos > mtd->size)
133 return 0;
134
135 if (count > mtd->size - pos)
136 count = mtd->size - pos;
137
138 if (!count)
139 return 0;
140
141 /* FIXME: Use kiovec in 2.5 to lock down the user's buffers
142 and pass them directly to the MTD functions */
143 while (count) {
144 if (count > MAX_KMALLOC_SIZE)
145 len = MAX_KMALLOC_SIZE;
146 else
147 len = count;
148
149 kbuf=kmalloc(len,GFP_KERNEL);
150 if (!kbuf)
151 return -ENOMEM;
152
153 ret = MTD_READ(mtd, pos, len, &retlen, kbuf);
154 if (!ret) {
155 pos += retlen;
156 if (copy_to_user(buf, kbuf, retlen)) {
157 kfree(kbuf);
158 return -EFAULT;
159 }
160 else
161 total_retlen += retlen;
162
163 count -= retlen;
164 buf += retlen;
165 }
166 else {
167 kfree(kbuf);
168 return ret;
169 }
170
171 kfree(kbuf);
172 }
173
174 *ppos = pos;
175
176 return total_retlen;
177 } /* mtd_read */
178
mtd_write(struct file * file,const char * buf,size_t count,loff_t * ppos)179 static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t *ppos)
180 {
181 struct mtd_info *mtd = (struct mtd_info *)file->private_data;
182 char *kbuf;
183 size_t retlen;
184 size_t total_retlen=0;
185 loff_t pos = *ppos;
186 int ret=0;
187 int len;
188
189 DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n");
190
191 if (pos < 0 || pos >= mtd->size)
192 return -ENOSPC;
193
194 if (count > mtd->size - pos)
195 count = mtd->size - pos;
196
197 if (!count)
198 return 0;
199
200 while (count) {
201 if (count > MAX_KMALLOC_SIZE)
202 len = MAX_KMALLOC_SIZE;
203 else
204 len = count;
205
206 kbuf=kmalloc(len,GFP_KERNEL);
207 if (!kbuf) {
208 printk("kmalloc is null\n");
209 return -ENOMEM;
210 }
211
212 if (copy_from_user(kbuf, buf, len)) {
213 kfree(kbuf);
214 return -EFAULT;
215 }
216
217 ret = (*(mtd->write))(mtd, pos, len, &retlen, kbuf);
218 if (!ret) {
219 pos += retlen;
220 total_retlen += retlen;
221 count -= retlen;
222 buf += retlen;
223 }
224 else {
225 kfree(kbuf);
226 return ret;
227 }
228
229 kfree(kbuf);
230 }
231 *ppos = pos;
232
233 return total_retlen;
234 } /* mtd_write */
235
236 /*======================================================================
237
238 IOCTL calls for getting device parameters.
239
240 ======================================================================*/
mtd_erase_callback(struct erase_info * instr)241 static void mtd_erase_callback (struct erase_info *instr)
242 {
243 wake_up((wait_queue_head_t *)instr->priv);
244 }
245
mtd_ioctl(struct inode * inode,struct file * file,u_int cmd,u_long arg)246 static int mtd_ioctl(struct inode *inode, struct file *file,
247 u_int cmd, u_long arg)
248 {
249 struct mtd_info *mtd = (struct mtd_info *)file->private_data;
250 int ret = 0;
251 u_long size;
252
253 DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n");
254
255 size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
256 if (cmd & IOC_IN) {
257 ret = verify_area(VERIFY_READ, (char *)arg, size);
258 if (ret) return ret;
259 }
260 if (cmd & IOC_OUT) {
261 ret = verify_area(VERIFY_WRITE, (char *)arg, size);
262 if (ret) return ret;
263 }
264
265 switch (cmd) {
266 case MEMGETREGIONCOUNT:
267 if (copy_to_user((int *) arg, &(mtd->numeraseregions), sizeof(int)))
268 return -EFAULT;
269 break;
270
271 case MEMGETREGIONINFO:
272 {
273 struct region_info_user ur;
274
275 if (copy_from_user( &ur,
276 (struct region_info_user *)arg,
277 sizeof(struct region_info_user))) {
278 return -EFAULT;
279 }
280
281 if (ur.regionindex >= mtd->numeraseregions)
282 return -EINVAL;
283 if (copy_to_user((struct mtd_erase_region_info *) arg,
284 &(mtd->eraseregions[ur.regionindex]),
285 sizeof(struct mtd_erase_region_info)))
286 return -EFAULT;
287 break;
288 }
289
290 case MEMGETINFO:
291 if (copy_to_user((struct mtd_info *)arg, mtd,
292 sizeof(struct mtd_info_user)))
293 return -EFAULT;
294 break;
295
296 case MEMERASE:
297 {
298 struct erase_info *erase;
299
300 if(!(file->f_mode & 2))
301 return -EPERM;
302
303 erase=kmalloc(sizeof(struct erase_info),GFP_KERNEL);
304 if (!erase)
305 ret = -ENOMEM;
306 else {
307 wait_queue_head_t waitq;
308 DECLARE_WAITQUEUE(wait, current);
309
310 init_waitqueue_head(&waitq);
311
312 memset (erase,0,sizeof(struct erase_info));
313 if (copy_from_user(&erase->addr, (u_long *)arg,
314 2 * sizeof(u_long))) {
315 kfree(erase);
316 return -EFAULT;
317 }
318 erase->mtd = mtd;
319 erase->callback = mtd_erase_callback;
320 erase->priv = (unsigned long)&waitq;
321
322 /*
323 FIXME: Allow INTERRUPTIBLE. Which means
324 not having the wait_queue head on the stack.
325
326 If the wq_head is on the stack, and we
327 leave because we got interrupted, then the
328 wq_head is no longer there when the
329 callback routine tries to wake us up.
330 */
331 ret = mtd->erase(mtd, erase);
332 if (!ret) {
333 set_current_state(TASK_UNINTERRUPTIBLE);
334 add_wait_queue(&waitq, &wait);
335 if (erase->state != MTD_ERASE_DONE &&
336 erase->state != MTD_ERASE_FAILED)
337 schedule();
338 remove_wait_queue(&waitq, &wait);
339 set_current_state(TASK_RUNNING);
340
341 ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0;
342 }
343 kfree(erase);
344 }
345 break;
346 }
347
348 case MEMWRITEOOB:
349 {
350 struct mtd_oob_buf buf;
351 void *databuf;
352 ssize_t retlen;
353
354 if(!(file->f_mode & 2))
355 return -EPERM;
356
357 if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
358 return -EFAULT;
359
360 if (buf.length > 0x4096)
361 return -EINVAL;
362
363 if (!mtd->write_oob)
364 ret = -EOPNOTSUPP;
365 else
366 ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length);
367
368 if (ret)
369 return ret;
370
371 databuf = kmalloc(buf.length, GFP_KERNEL);
372 if (!databuf)
373 return -ENOMEM;
374
375 if (copy_from_user(databuf, buf.ptr, buf.length)) {
376 kfree(databuf);
377 return -EFAULT;
378 }
379
380 ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf);
381
382 if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
383 ret = -EFAULT;
384
385 kfree(databuf);
386 break;
387
388 }
389
390 case MEMREADOOB:
391 {
392 struct mtd_oob_buf buf;
393 void *databuf;
394 ssize_t retlen;
395
396 if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
397 return -EFAULT;
398
399 if (buf.length > 0x4096)
400 return -EINVAL;
401
402 if (!mtd->read_oob)
403 ret = -EOPNOTSUPP;
404 else
405 ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length);
406
407 if (ret)
408 return ret;
409
410 databuf = kmalloc(buf.length, GFP_KERNEL);
411 if (!databuf)
412 return -ENOMEM;
413
414 ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf);
415
416 if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
417 ret = -EFAULT;
418 else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
419 ret = -EFAULT;
420
421 kfree(databuf);
422 break;
423 }
424
425 case MEMLOCK:
426 {
427 unsigned long adrs[2];
428
429 if (copy_from_user(adrs ,(void *)arg, 2* sizeof(unsigned long)))
430 return -EFAULT;
431
432 if (!mtd->lock)
433 ret = -EOPNOTSUPP;
434 else
435 ret = mtd->lock(mtd, adrs[0], adrs[1]);
436 break;
437 }
438
439 case MEMUNLOCK:
440 {
441 unsigned long adrs[2];
442
443 if (copy_from_user(adrs, (void *)arg, 2* sizeof(unsigned long)))
444 return -EFAULT;
445
446 if (!mtd->unlock)
447 ret = -EOPNOTSUPP;
448 else
449 ret = mtd->unlock(mtd, adrs[0], adrs[1]);
450 break;
451 }
452
453 case MEMWRITEDATA:
454 {
455 struct mtd_oob_buf buf;
456 void *databuf;
457 ssize_t retlen;
458
459 if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
460 return -EFAULT;
461
462 if (buf.length > 0x4096)
463 return -EINVAL;
464
465 if (!mtd->write_ecc)
466 ret = -EOPNOTSUPP;
467 else
468 ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length);
469
470 if (ret)
471 return ret;
472
473 databuf = kmalloc(buf.length, GFP_KERNEL);
474 if (!databuf)
475 return -ENOMEM;
476
477 if (copy_from_user(databuf, buf.ptr, buf.length)) {
478 kfree(databuf);
479 return -EFAULT;
480 }
481
482 ret = (mtd->write_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0);
483
484 if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
485 ret = -EFAULT;
486
487 kfree(databuf);
488 break;
489
490 }
491
492 case MEMREADDATA:
493 {
494 struct mtd_oob_buf buf;
495 void *databuf;
496 ssize_t retlen = 0;
497
498 if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
499 return -EFAULT;
500
501 if (buf.length > 0x4096)
502 return -EINVAL;
503
504 if (!mtd->read_ecc)
505 ret = -EOPNOTSUPP;
506 else
507 ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length);
508
509 if (ret)
510 return ret;
511
512 databuf = kmalloc(buf.length, GFP_KERNEL);
513 if (!databuf)
514 return -ENOMEM;
515
516 ret = (mtd->read_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0);
517
518 if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
519 ret = -EFAULT;
520 else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
521 ret = -EFAULT;
522
523 kfree(databuf);
524 break;
525 }
526
527
528 default:
529 DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO);
530 ret = -ENOTTY;
531 }
532
533 return ret;
534 } /* memory_ioctl */
535
536 static struct file_operations mtd_fops = {
537 owner: THIS_MODULE,
538 llseek: mtd_lseek, /* lseek */
539 read: mtd_read, /* read */
540 write: mtd_write, /* write */
541 ioctl: mtd_ioctl, /* ioctl */
542 open: mtd_open, /* open */
543 release: mtd_close, /* release */
544 };
545
546
547 #ifdef CONFIG_DEVFS_FS
548 /* Notification that a new device has been added. Create the devfs entry for
549 * it. */
550
mtd_notify_add(struct mtd_info * mtd)551 static void mtd_notify_add(struct mtd_info* mtd)
552 {
553 char name[8];
554
555 if (!mtd)
556 return;
557
558 sprintf(name, "%d", mtd->index);
559 devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
560 DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2,
561 S_IFCHR | S_IRUGO | S_IWUGO,
562 &mtd_fops, NULL);
563
564 sprintf(name, "%dro", mtd->index);
565 devfs_ro_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
566 DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2+1,
567 S_IFCHR | S_IRUGO,
568 &mtd_fops, NULL);
569 }
570
mtd_notify_remove(struct mtd_info * mtd)571 static void mtd_notify_remove(struct mtd_info* mtd)
572 {
573 if (!mtd)
574 return;
575
576 devfs_unregister(devfs_rw_handle[mtd->index]);
577 devfs_unregister(devfs_ro_handle[mtd->index]);
578 }
579 #endif
580
init_mtdchar(void)581 static int __init init_mtdchar(void)
582 {
583 #ifdef CONFIG_DEVFS_FS
584 if (devfs_register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
585 {
586 printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
587 MTD_CHAR_MAJOR);
588 return -EAGAIN;
589 }
590
591 devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL);
592
593 register_mtd_user(¬ifier);
594 #else
595 if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
596 {
597 printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
598 MTD_CHAR_MAJOR);
599 return -EAGAIN;
600 }
601 #endif
602
603 return 0;
604 }
605
cleanup_mtdchar(void)606 static void __exit cleanup_mtdchar(void)
607 {
608 #ifdef CONFIG_DEVFS_FS
609 unregister_mtd_user(¬ifier);
610 devfs_unregister(devfs_dir_handle);
611 devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
612 #else
613 unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
614 #endif
615 }
616
617 module_init(init_mtdchar);
618 module_exit(cleanup_mtdchar);
619
620
621 MODULE_LICENSE("GPL");
622 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
623 MODULE_DESCRIPTION("Direct character-device access to MTD devices");
624