File: /usr/src/linux/drivers/mtd/mtdchar.c

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