added mtd driver
[linux-2.4.git] / drivers / mtd / mtdchar.c
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
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         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
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 ======================================================================*/
241 static void mtd_erase_callback (struct erase_info *instr)
242 {
243         wake_up((wait_queue_head_t *)instr->priv);
244 }
245
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
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
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
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
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");