Topic Text   Topic Comments (0)   Topic Properties   Topic Information 44@55....
Topic title: ??C?? Wednesday June 17, 2009 01:55:00

Download topic text | View in monospace font | Tab width set to 4 (change to 8)

Files in topic: (view all files)  
mtdchar.c   {+533,-0}

[Add General Comment] to topic.

File mtdchar.c (Revision 1.0) [Add File Comment] [Top]
 
1 /*
2 * $Id: mtdchar.c,v 1.44 2001/10/02 15:05:11 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
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=kmalloc(sizeof(struct erase_info),GFP_KERNEL);
291 if (!erase)
292 ret = -ENOMEM;
293 else {
294 wait_queue_head_t waitq;
295 DECLARE_WAITQUEUE(wait, current);
296
297 init_waitqueue_head(&waitq);
298
299 memset (erase,0,sizeof(struct erase_info));
300 if (copy_from_user(&erase->addr, (u_long *)arg,
301 2 * sizeof(u_long))) {
302 kfree(erase);
303 return -EFAULT;
304 }
305 erase->mtd = mtd;
306 erase->callback = mtd_erase_callback;
307 erase->priv = (unsigned long)&waitq;
308
309 /*
310 FIXME: Allow INTERRUPTIBLE. Which means
311 not having the wait_queue head on the stack.
312
313 If the wq_head is on the stack, and we
314 leave because we got interrupted, then the
315 wq_head is no longer there when the
316 callback routine tries to wake us up.
317 */
318 ret = mtd->erase(mtd, erase);
319 if (!ret) {
320 set_current_state(TASK_UNINTERRUPTIBLE);
321 add_wait_queue(&waitq, &wait);
322 if (erase->state != MTD_ERASE_DONE &&
323 erase->state != MTD_ERASE_FAILED)
324 schedule();
325 remove_wait_queue(&waitq, &wait);
326 set_current_state(TASK_RUNNING);
327
328 ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0;
329 }
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 static int __init init_mtdchar(void)
492 {
493 #ifdef CONFIG_DEVFS_FS
494 if (devfs_register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
495 {
496 printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
497 MTD_CHAR_MAJOR);
498 return -EAGAIN;
499 }
500
501 devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL);
502
503 register_mtd_user(&notifier);
504 #else
505 if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
506 {
507 printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
508 MTD_CHAR_MAJOR);
509 return -EAGAIN;
510 }
511 #endif
512
513 return 0;
514 }
515
516 static void __exit cleanup_mtdchar(void)
517 {
518 #ifdef CONFIG_DEVFS_FS
519 unregister_mtd_user(&notifier);
520 devfs_unregister(devfs_dir_handle);
521 devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
522 #else
523 unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
524 #endif
525 }
526
527 module_init(init_mtdchar);
528 module_exit(cleanup_mtdchar);
529
530
531 MODULE_LICENSE("GPL");
532 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
533 MODULE_DESCRIPTION("Direct character-device access to MTD devices");
 
File mtdchar.c (Revision 1.0) [Add File Comment] [Top]
  
Legend:
Removed 
Changed
 Added

[Add General Comment] to topic.