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(&notifier);
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(&notifier);
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