import of ftp.dlink.com/GPL/DSMG-600_reB/ppclinux.tar.gz
[linux-2.4.21-pre4.git] / drivers / mtd / mtdchar.c
1 /*
2  * $Id: mtdchar.c,v 1.1.1.1 2005/04/11 02:50:25 jack 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
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
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
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
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         
129         DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n");
130
131         if (*ppos + count > mtd->size)
132                 count = mtd->size - *ppos;
133
134         if (!count)
135                 return 0;
136         
137         /* FIXME: Use kiovec in 2.5 to lock down the user's buffers
138            and pass them directly to the MTD functions */
139         while (count) {
140                 if (count > MAX_KMALLOC_SIZE) 
141                         len = MAX_KMALLOC_SIZE;
142                 else
143                         len = count;
144
145                 kbuf=kmalloc(len,GFP_KERNEL);
146                 if (!kbuf)
147                         return -ENOMEM;
148                 
149                 ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf);
150                 if (!ret) {
151                         *ppos += retlen;
152                         if (copy_to_user(buf, kbuf, retlen)) {
153                                 kfree(kbuf);
154                                 return -EFAULT;
155                         }
156                         else
157                                 total_retlen += retlen;
158
159                         count -= retlen;
160                         buf += retlen;
161                 }
162                 else {
163                         kfree(kbuf);
164                         return ret;
165                 }
166                 
167                 kfree(kbuf);
168         }
169         
170         return total_retlen;
171 } /* mtd_read */
172
173 static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t *ppos)
174 {
175         struct mtd_info *mtd = (struct mtd_info *)file->private_data;
176         char *kbuf;
177         size_t retlen;
178         size_t total_retlen=0;
179         int ret=0;
180         int len;
181
182         DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n");
183         
184         if (*ppos == mtd->size)
185                 return -ENOSPC;
186         
187         if (*ppos + count > mtd->size)
188                 count = mtd->size - *ppos;
189
190         if (!count)
191                 return 0;
192
193         while (count) {
194                 if (count > MAX_KMALLOC_SIZE) 
195                         len = MAX_KMALLOC_SIZE;
196                 else
197                         len = count;
198
199                 kbuf=kmalloc(len,GFP_KERNEL);
200                 if (!kbuf) {
201                         printk("kmalloc is null\n");
202                         return -ENOMEM;
203                 }
204
205                 if (copy_from_user(kbuf, buf, len)) {
206                         kfree(kbuf);
207                         return -EFAULT;
208                 }
209                 
210                 ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);
211                 if (!ret) {
212                         *ppos += retlen;
213                         total_retlen += retlen;
214                         count -= retlen;
215                         buf += retlen;
216                 }
217                 else {
218                         kfree(kbuf);
219                         return ret;
220                 }
221                 
222                 kfree(kbuf);
223         }
224
225         return total_retlen;
226 } /* mtd_write */
227
228 /*======================================================================
229
230     IOCTL calls for getting device parameters.
231
232 ======================================================================*/
233 static void mtd_erase_callback (struct erase_info *instr)
234 {
235         wake_up((wait_queue_head_t *)instr->priv);
236 }
237
238 static int mtd_ioctl(struct inode *inode, struct file *file,
239                      u_int cmd, u_long arg)
240 {
241         struct mtd_info *mtd = (struct mtd_info *)file->private_data;
242         int ret = 0;
243         u_long size;
244         
245         DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n");
246
247         size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
248         if (cmd & IOC_IN) {
249                 ret = verify_area(VERIFY_READ, (char *)arg, size);
250                 if (ret) return ret;
251         }
252         if (cmd & IOC_OUT) {
253                 ret = verify_area(VERIFY_WRITE, (char *)arg, size);
254                 if (ret) return ret;
255         }
256         
257         switch (cmd) {
258         case MEMGETREGIONCOUNT:
259                 if (copy_to_user((int *) arg, &(mtd->numeraseregions), sizeof(int)))
260                         return -EFAULT;
261                 break;
262
263         case MEMGETREGIONINFO:
264         {
265                 struct region_info_user ur;
266
267                 if (copy_from_user(     &ur, 
268                                         (struct region_info_user *)arg, 
269                                         sizeof(struct region_info_user))) {
270                         return -EFAULT;
271                 }
272
273                 if (ur.regionindex >= mtd->numeraseregions)
274                         return -EINVAL;
275                 if (copy_to_user((struct mtd_erase_region_info *) arg, 
276                                 &(mtd->eraseregions[ur.regionindex]),
277                                 sizeof(struct mtd_erase_region_info)))
278                         return -EFAULT;
279                 break;
280         }
281
282         case MEMGETINFO:
283                 if (copy_to_user((struct mtd_info *)arg, mtd,
284                                  sizeof(struct mtd_info_user)))
285                         return -EFAULT;
286                 break;
287
288         case MEMERASE:
289         {
290                 struct erase_info *erase;
291
292                 if(!(file->f_mode & 2))
293                         return -EPERM;
294
295                 erase=kmalloc(sizeof(struct erase_info),GFP_KERNEL);
296                 if (!erase)
297                         ret = -ENOMEM;
298                 else {
299                         wait_queue_head_t waitq;
300                         DECLARE_WAITQUEUE(wait, current);
301
302                         init_waitqueue_head(&waitq);
303
304                         memset (erase,0,sizeof(struct erase_info));
305                         if (copy_from_user(&erase->addr, (u_long *)arg,
306                                            2 * sizeof(u_long))) {
307                                 kfree(erase);
308                                 return -EFAULT;
309                         }
310                         erase->mtd = mtd;
311                         erase->callback = mtd_erase_callback;
312                         erase->priv = (unsigned long)&waitq;
313                         
314                         /*
315                           FIXME: Allow INTERRUPTIBLE. Which means
316                           not having the wait_queue head on the stack.
317                           
318                           If the wq_head is on the stack, and we
319                           leave because we got interrupted, then the
320                           wq_head is no longer there when the
321                           callback routine tries to wake us up.
322                         */
323                         ret = mtd->erase(mtd, erase);
324                         if (!ret) {
325                                 set_current_state(TASK_UNINTERRUPTIBLE);
326                                 add_wait_queue(&waitq, &wait);
327                                 if (erase->state != MTD_ERASE_DONE &&
328                                     erase->state != MTD_ERASE_FAILED)
329                                         schedule();
330                                 remove_wait_queue(&waitq, &wait);
331                                 set_current_state(TASK_RUNNING);
332
333                                 ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0;
334                         }
335                         kfree(erase);
336                 }
337                 break;
338         }
339
340         case MEMWRITEOOB:
341         {
342                 struct mtd_oob_buf buf;
343                 void *databuf;
344                 ssize_t retlen;
345                 
346                 if(!(file->f_mode & 2))
347                         return -EPERM;
348
349                 if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
350                         return -EFAULT;
351                 
352                 if (buf.length > 0x4096)
353                         return -EINVAL;
354
355                 if (!mtd->write_oob)
356                         ret = -EOPNOTSUPP;
357                 else
358                         ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length);
359
360                 if (ret)
361                         return ret;
362
363                 databuf = kmalloc(buf.length, GFP_KERNEL);
364                 if (!databuf)
365                         return -ENOMEM;
366                 
367                 if (copy_from_user(databuf, buf.ptr, buf.length)) {
368                         kfree(databuf);
369                         return -EFAULT;
370                 }
371
372                 ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf);
373
374                 if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
375                         ret = -EFAULT;
376
377                 kfree(databuf);
378                 break;
379
380         }
381
382         case MEMREADOOB:
383         {
384                 struct mtd_oob_buf buf;
385                 void *databuf;
386                 ssize_t retlen;
387
388                 if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
389                         return -EFAULT;
390                 
391                 if (buf.length > 0x4096)
392                         return -EINVAL;
393
394                 if (!mtd->read_oob)
395                         ret = -EOPNOTSUPP;
396                 else
397                         ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length);
398
399                 if (ret)
400                         return ret;
401
402                 databuf = kmalloc(buf.length, GFP_KERNEL);
403                 if (!databuf)
404                         return -ENOMEM;
405                 
406                 ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf);
407
408                 if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
409                         ret = -EFAULT;
410                 else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
411                         ret = -EFAULT;
412                 
413                 kfree(databuf);
414                 break;
415         }
416
417         case MEMLOCK:
418         {
419                 unsigned long adrs[2];
420
421                 if (copy_from_user(adrs ,(void *)arg, 2* sizeof(unsigned long)))
422                         return -EFAULT;
423
424                 if (!mtd->lock)
425                         ret = -EOPNOTSUPP;
426                 else
427                         ret = mtd->lock(mtd, adrs[0], adrs[1]);
428                 break;
429         }
430
431         case MEMUNLOCK:
432         {
433                 unsigned long adrs[2];
434
435                 if (copy_from_user(adrs, (void *)arg, 2* sizeof(unsigned long)))
436                         return -EFAULT;
437
438                 if (!mtd->unlock)
439                         ret = -EOPNOTSUPP;
440                 else
441                         ret = mtd->unlock(mtd, adrs[0], adrs[1]);
442                 break;
443         }
444
445         case MEMWRITEDATA:
446         {
447                 struct mtd_oob_buf buf;
448                 void *databuf;
449                 ssize_t retlen;
450                 
451                 if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
452                         return -EFAULT;
453                 
454                 if (buf.length > 0x4096)
455                         return -EINVAL;
456
457                 if (!mtd->write_ecc)
458                         ret = -EOPNOTSUPP;
459                 else
460                         ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length);
461
462                 if (ret)
463                         return ret;
464
465                 databuf = kmalloc(buf.length, GFP_KERNEL);
466                 if (!databuf)
467                         return -ENOMEM;
468                 
469                 if (copy_from_user(databuf, buf.ptr, buf.length)) {
470                         kfree(databuf);
471                         return -EFAULT;
472                 }
473
474                 ret = (mtd->write_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0);
475
476                 if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
477                         ret = -EFAULT;
478
479                 kfree(databuf);
480                 break;
481
482         }
483
484         case MEMREADDATA:
485         {
486                 struct mtd_oob_buf buf;
487                 void *databuf;
488                 ssize_t retlen = 0;
489
490                 if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
491                         return -EFAULT;
492                 
493                 if (buf.length > 0x4096)
494                         return -EINVAL;
495
496                 if (!mtd->read_ecc)
497                         ret = -EOPNOTSUPP;
498                 else
499                         ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length);
500
501                 if (ret)
502                         return ret;
503
504                 databuf = kmalloc(buf.length, GFP_KERNEL);
505                 if (!databuf)
506                         return -ENOMEM;
507                 
508                 ret = (mtd->read_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0);
509
510                 if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
511                         ret = -EFAULT;
512                 else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
513                         ret = -EFAULT;
514                 
515                 kfree(databuf);
516                 break;
517         }
518
519                 
520         default:
521                 DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO);
522                 ret = -ENOTTY;
523         }
524
525         return ret;
526 } /* memory_ioctl */
527
528 static struct file_operations mtd_fops = {
529         owner:          THIS_MODULE,
530         llseek:         mtd_lseek,      /* lseek */
531         read:           mtd_read,       /* read */
532         write:          mtd_write,      /* write */
533         ioctl:          mtd_ioctl,      /* ioctl */
534         open:           mtd_open,       /* open */
535         release:        mtd_close,      /* release */
536 };
537
538
539 #ifdef CONFIG_DEVFS_FS
540 /* Notification that a new device has been added. Create the devfs entry for
541  * it. */
542
543 static void mtd_notify_add(struct mtd_info* mtd)
544 {
545         char name[8];
546
547         if (!mtd)
548                 return;
549
550         sprintf(name, "%d", mtd->index);
551         devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
552                         DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2,
553                         S_IFCHR | S_IRUGO | S_IWUGO,
554                         &mtd_fops, NULL);
555
556         sprintf(name, "%dro", mtd->index);
557         devfs_ro_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
558                         DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2+1,
559                         S_IFCHR | S_IRUGO,
560                         &mtd_fops, NULL);
561 }
562
563 static void mtd_notify_remove(struct mtd_info* mtd)
564 {
565         if (!mtd)
566                 return;
567
568         devfs_unregister(devfs_rw_handle[mtd->index]);
569         devfs_unregister(devfs_ro_handle[mtd->index]);
570 }
571 #endif
572
573 static int __init init_mtdchar(void)
574 {
575 #ifdef CONFIG_DEVFS_FS
576         if (devfs_register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
577         {
578                 printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
579                        MTD_CHAR_MAJOR);
580                 return -EAGAIN;
581         }
582
583         devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL);
584
585         register_mtd_user(&notifier);
586 #else
587         if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
588         {
589                 printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
590                        MTD_CHAR_MAJOR);
591                 return -EAGAIN;
592         }
593 #endif
594
595         return 0;
596 }
597
598 static void __exit cleanup_mtdchar(void)
599 {
600 #ifdef CONFIG_DEVFS_FS
601         unregister_mtd_user(&notifier);
602         devfs_unregister(devfs_dir_handle);
603         devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
604 #else
605         unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
606 #endif
607 }
608
609 module_init(init_mtdchar);
610 module_exit(cleanup_mtdchar);
611
612
613 MODULE_LICENSE("GPL");
614 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
615 MODULE_DESCRIPTION("Direct character-device access to MTD devices");