1 /* -*- C -*-
2 * main.c -- the bare scullv char module
3 *
4 * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
5 * Copyright (C) 2001 O'Reilly & Associates
6 *
7 * The source code in this file can be freely used, adapted,
8 * and redistributed in source or binary form, so long as an
9 * acknowledgment appears in derived source files. The citation
10 * should list that the code comes from the book "Linux Device
11 * Drivers" by Alessandro Rubini and Jonathan Corbet, published
12 * by O'Reilly & Associates. No warranty is attached;
13 * we cannot take responsibility for errors or fitness for use.
14 *
15 * $Id: _main.c.in,v 1.21 2004/10/14 20:11:39 corbet Exp $
16 */
17
18 #include <linux/config.h>
19 #include <linux/module.h>
20 #include <linux/moduleparam.h>
21 #include <linux/init.h>
22 #include <linux/kernel.h> /* printk() */
23 #include <linux/slab.h> /* kmalloc() */
24 #include <linux/fs.h> /* everything... */
25 #include <linux/errno.h> /* error codes */
26 #include <linux/types.h> /* size_t */
27 #include <linux/proc_fs.h>
28 #include <linux/fcntl.h> /* O_ACCMODE */
29 #include <linux/aio.h>
30 #include <asm/uaccess.h>
31 #include <linux/vmalloc.h>
32 #include "scullv.h" /* local definitions */
33
34
35 int scullv_major = SCULLV_MAJOR;
36 int scullv_devs = SCULLV_DEVS; /* number of bare scullv devices */
37 int scullv_qset = SCULLV_QSET;
38 int scullv_order = SCULLV_ORDER;
39
40 module_param(scullv_major, int, 0);
41 module_param(scullv_devs, int, 0);
42 module_param(scullv_qset, int, 0);
43 module_param(scullv_order, int, 0);
44 MODULE_AUTHOR("Alessandro Rubini");
45 MODULE_LICENSE("Dual BSD/GPL");
46
47 struct scullv_dev *scullv_devices; /* allocated in scullv_init */
48
49 int scullv_trim(struct scullv_dev *dev);
50 void scullv_cleanup(void);
51
52
53
54
55
56
57 #ifdef SCULLV_USE_PROC /* don't waste space if unused */
58 /*
59 * The proc filesystem: function to read and entry
60 */
61
62 void scullv_proc_offset(char *buf, char **start, off_t *offset, int *len)
63 {
64 if (*offset == 0)
65 return;
66 if (*offset >= *len) {
67 /* Not there yet */
68 *offset -= *len;
69 *len = 0;
70 } else {
71 /* We're into the interesting stuff now */
72 *start = buf + *offset;
73 *offset = 0;
74 }
75 }
76
77 /* FIXME: Do we need this here?? It be ugly */
78 int scullv_read_procmem(char *buf, char **start, off_t offset,
79 int count, int *eof, void *data)
80 {
81 int i, j, order, qset, len = 0;
82 int limit = count - 80; /* Don't print more than this */
83 struct scullv_dev *d;
84
85 *start = buf;
86 for(i = 0; i < scullv_devs; i++) {
87 d = &scullv_devices[i];
88 if (down_interruptible (&d->sem))
89 return -ERESTARTSYS;
90 qset = d->qset; /* retrieve the features of each device */
91 order = d->order;
92 len += sprintf(buf+len,"\nDevice %i: qset %i, order %i, sz %li\n",
93 i, qset, order, (long)(d->size));
94 for (; d; d = d->next) { /* scan the list */
95 len += sprintf(buf+len," item at %p, qset at %p\n",d,d->data);
96 scullv_proc_offset (buf, start, &offset, &len);
97 if (len > limit)
98 goto out;
99 if (d->data && !d->next) /* dump only the last item - save space */
100 for (j = 0; j < qset; j++) {
101 if (d->data[j])
102 len += sprintf(buf+len," % 4i:%8p\n",j,d->data[j]);
103 scullv_proc_offset (buf, start, &offset, &len);
104 if (len > limit)
105 goto out;
106 }
107 }
108 out:
109 up (&scullv_devices[i].sem);
110 if (len > limit)
111 break;
112 }
113 *eof = 1;
114 return len;
115 }
116
117 #endif /* SCULLV_USE_PROC */
118
119 /*
120 * Open and close
121 */
122
123 int scullv_open (struct inode *inode, struct file *filp)
124 {
125 struct scullv_dev *dev; /* device information */
126
127 /* Find the device */
128 dev = container_of(inode->i_cdev, struct scullv_dev, cdev);
129
130 /* now trim to 0 the length of the device if open was write-only */
131 if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {
132 if (down_interruptible (&dev->sem))
133 return -ERESTARTSYS;
134 scullv_trim(dev); /* ignore errors */
135 up (&dev->sem);
136 }
137
138 /* and use filp->private_data to point to the device data */
139 filp->private_data = dev;
140
141 return 0; /* success */
142 }
143
144 int scullv_release (struct inode *inode, struct file *filp)
145 {
146 return 0;
147 }
148
149 /*
150 * Follow the list
151 */
152 struct scullv_dev *scullv_follow(struct scullv_dev *dev, int n)
153 {
154 while (n--) {
155 if (!dev->next) {
156 dev->next = kmalloc(sizeof(struct scullv_dev), GFP_KERNEL);
157 memset(dev->next, 0, sizeof(struct scullv_dev));
158 }
159 dev = dev->next;
160 continue;
161 }
162 return dev;
163 }
164
165 /*
166 * Data management: read and write
167 */
168
169 ssize_t scullv_read (struct file *filp, char __user *buf, size_t count,
170 loff_t *f_pos)
171 {
172 struct scullv_dev *dev = filp->private_data; /* the first listitem */
173 struct scullv_dev *dptr;
174 int quantum = PAGE_SIZE << dev->order;
175 int qset = dev->qset;
176 int itemsize = quantum * qset; /* how many bytes in the listitem */
177 int item, s_pos, q_pos, rest;
178 ssize_t retval = 0;
179
180 if (down_interruptible (&dev->sem))
181 return -ERESTARTSYS;
182 if (*f_pos > dev->size)
183 goto nothing;
184 if (*f_pos + count > dev->size)
185 count = dev->size - *f_pos;
186 /* find listitem, qset index, and offset in the quantum */
187 item = ((long) *f_pos) / itemsize;
188 rest = ((long) *f_pos) % itemsize;
189 s_pos = rest / quantum; q_pos = rest % quantum;
190
191 /* follow the list up to the right position (defined elsewhere) */
192 dptr = scullv_follow(dev, item);
193
194 if (!dptr->data)
195 goto nothing; /* don't fill holes */
196 if (!dptr->data[s_pos])
197 goto nothing;
198 if (count > quantum - q_pos)
199 count = quantum - q_pos; /* read only up to the end of this quantum */
200
201 if (copy_to_user (buf, dptr->data[s_pos]+q_pos, count)) {
202 retval = -EFAULT;
203 goto nothing;
204 }
205 up (&dev->sem);
206
207 *f_pos += count;
208 return count;
209
210 nothing:
211 up (&dev->sem);
212 return retval;
213 }
214
215
216
217 ssize_t scullv_write (struct file *filp, const char __user *buf, size_t count,
218 loff_t *f_pos)
219 {
220 struct scullv_dev *dev = filp->private_data;
221 struct scullv_dev *dptr;
222 int quantum = PAGE_SIZE << dev->order;
223 int qset = dev->qset;
224 int itemsize = quantum * qset;
225 int item, s_pos, q_pos, rest;
226 ssize_t retval = -ENOMEM; /* our most likely error */
227
228 if (down_interruptible (&dev->sem))
229 return -ERESTARTSYS;
230
231 /* find listitem, qset index and offset in the quantum */
232 item = ((long) *f_pos) / itemsize;
233 rest = ((long) *f_pos) % itemsize;
234 s_pos = rest / quantum; q_pos = rest % quantum;
235
236 /* follow the list up to the right position */
237 dptr = scullv_follow(dev, item);
238 if (!dptr->data) {
239 dptr->data = kmalloc(qset * sizeof(void *), GFP_KERNEL);
240 if (!dptr->data)
241 goto nomem;
242 memset(dptr->data, 0, qset * sizeof(char *));
243 }
244 /* Allocate a quantum using virtual addresses */
245 if (!dptr->data[s_pos]) {
246 dptr->data[s_pos] = (void *)vmalloc(PAGE_SIZE << dptr->order);
247 if (!dptr->data[s_pos])
248 goto nomem;
249 memset(dptr->data[s_pos], 0, PAGE_SIZE << dptr->order);
250 }
251 if (count > quantum - q_pos)
252 count = quantum - q_pos; /* write only up to the end of this quantum */
253 if (copy_from_user (dptr->data[s_pos]+q_pos, buf, count)) {
254 retval = -EFAULT;
255 goto nomem;
256 }
257 *f_pos += count;
258
259 /* update the size */
260 if (dev->size < *f_pos)
261 dev->size = *f_pos;
262 up (&dev->sem);
263 return count;
264
265 nomem:
266 up (&dev->sem);
267 return retval;
268 }
269
270 /*
271 * The ioctl() implementation
272 */
273
274 int scullv_ioctl (struct inode *inode, struct file *filp,
275 unsigned int cmd, unsigned long arg)
276 {
277
278 int err = 0, ret = 0, tmp;
279
280 /* don't even decode wrong cmds: better returning ENOTTY than EFAULT */
281 if (_IOC_TYPE(cmd) != SCULLV_IOC_MAGIC) return -ENOTTY;
282 if (_IOC_NR(cmd) > SCULLV_IOC_MAXNR) return -ENOTTY;
283
284 /*
285 * the type is a bitmask, and VERIFY_WRITE catches R/W
286 * transfers. Note that the type is user-oriented, while
287 * verify_area is kernel-oriented, so the concept of "read" and
288 * "write" is reversed
289 */
290 if (_IOC_DIR(cmd) & _IOC_READ)
291 err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
292 else if (_IOC_DIR(cmd) & _IOC_WRITE)
293 err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
294 if (err)
295 return -EFAULT;
296
297 switch(cmd) {
298
299 case SCULLV_IOCRESET:
300 scullv_qset = SCULLV_QSET;
301 scullv_order = SCULLV_ORDER;
302 break;
303
304 case SCULLV_IOCSORDER: /* Set: arg points to the value */
305 ret = __get_user(scullv_order, (int __user *) arg);
306 break;
307
308 case SCULLV_IOCTORDER: /* Tell: arg is the value */
309 scullv_order = arg;
310 break;
311
312 case SCULLV_IOCGORDER: /* Get: arg is pointer to result */
313 ret = __put_user (scullv_order, (int __user *) arg);
314 break;
315
316 case SCULLV_IOCQORDER: /* Query: return it (it's positive) */
317 return scullv_order;
318
319 case SCULLV_IOCXORDER: /* eXchange: use arg as pointer */
320 tmp = scullv_order;
321 ret = __get_user(scullv_order, (int __user *) arg);
322 if (ret == 0)
323 ret = __put_user(tmp, (int __user *) arg);
324 break;
325
326 case SCULLV_IOCHORDER: /* sHift: like Tell + Query */
327 tmp = scullv_order;
328 scullv_order = arg;
329 return tmp;
330
331 case SCULLV_IOCSQSET:
332 ret = __get_user(scullv_qset, (int __user *) arg);
333 break;
334
335 case SCULLV_IOCTQSET:
336 scullv_qset = arg;
337 break;
338
339 case SCULLV_IOCGQSET:
340 ret = __put_user(scullv_qset, (int __user *)arg);
341 break;
342
343 case SCULLV_IOCQQSET:
344 return scullv_qset;
345
346 case SCULLV_IOCXQSET:
347 tmp = scullv_qset;
348 ret = __get_user(scullv_qset, (int __user *)arg);
349 if (ret == 0)
350 ret = __put_user(tmp, (int __user *)arg);
351 break;
352
353 case SCULLV_IOCHQSET:
354 tmp = scullv_qset;
355 scullv_qset = arg;
356 return tmp;
357
358 default: /* redundant, as cmd was checked against MAXNR */
359 return -ENOTTY;
360 }
361
362 return ret;
363 }
364
365 /*
366 * The "extended" operations
367 */
368
369 loff_t scullv_llseek (struct file *filp, loff_t off, int whence)
370 {
371 struct scullv_dev *dev = filp->private_data;
372 long newpos;
373
374 switch(whence) {
375 case 0: /* SEEK_SET */
376 newpos = off;
377 break;
378
379 case 1: /* SEEK_CUR */
380 newpos = filp->f_pos + off;
381 break;
382
383 case 2: /* SEEK_END */
384 newpos = dev->size + off;
385 break;
386
387 default: /* can't happen */
388 return -EINVAL;
389 }
390 if (newpos<0) return -EINVAL;
391 filp->f_pos = newpos;
392 return newpos;
393 }
394
395
396 /*
397 * A simple asynchronous I/O implementation.
398 */
399
400 struct async_work {
401 struct kiocb *iocb;
402 int result;
403 struct work_struct work;
404 };
405
406 /*
407 * "Complete" an asynchronous operation.
408 */
409 static void scullv_do_deferred_op(void *p)
410 {
411 struct async_work *stuff = (struct async_work *) p;
412 aio_complete(stuff->iocb, stuff->result, 0);
413 kfree(stuff);
414 }
415
416
417 static int scullv_defer_op(int write, struct kiocb *iocb, char __user *buf,
418 size_t count, loff_t pos)
419 {
420 struct async_work *stuff;
421 int result;
422
423 /* Copy now while we can access the buffer */
424 if (write)
425 result = scullv_write(iocb->ki_filp, buf, count, &pos);
426 else
427 result = scullv_read(iocb->ki_filp, buf, count, &pos);
428
429 /* If this is a synchronous IOCB, we return our status now. */
430 if (is_sync_kiocb(iocb))
431 return result;
432
433 /* Otherwise defer the completion for a few milliseconds. */
434 stuff = kmalloc (sizeof (*stuff), GFP_KERNEL);
435 if (stuff == NULL)
436 return result; /* No memory, just complete now */
437 stuff->iocb = iocb;
438 stuff->result = result;
439 INIT_WORK(&stuff->work, scullv_do_deferred_op, stuff);
440 schedule_delayed_work(&stuff->work, HZ/100);
441 return -EIOCBQUEUED;
442 }
443
444
445 static ssize_t scullv_aio_read(struct kiocb *iocb, char __user *buf, size_t count,
446 loff_t pos)
447 {
448 return scullv_defer_op(0, iocb, buf, count, pos);
449 }
450
451 static ssize_t scullv_aio_write(struct kiocb *iocb, const char __user *buf,
452 size_t count, loff_t pos)
453 {
454 return scullv_defer_op(1, iocb, (char __user *) buf, count, pos);
455 }
456
457
458
459 /*
460 * Mmap *is* available, but confined in a different file
461 */
462 extern int scullv_mmap(struct file *filp, struct vm_area_struct *vma);
463
464
465 /*
466 * The fops
467 */
468
469 struct file_operations scullv_fops = {
470 .owner = THIS_MODULE,
471 .llseek = scullv_llseek,
472 .read = scullv_read,
473 .write = scullv_write,
474 .ioctl = scullv_ioctl,
475 .mmap = scullv_mmap,
476 .open = scullv_open,
477 .release = scullv_release,
478 .aio_read = scullv_aio_read,
479 .aio_write = scullv_aio_write,
480 };
481
482 int scullv_trim(struct scullv_dev *dev)
483 {
484 struct scullv_dev *next, *dptr;
485 int qset = dev->qset; /* "dev" is not-null */
486 int i;
487
488 if (dev->vmas) /* don't trim: there are active mappings */
489 return -EBUSY;
490
491 for (dptr = dev; dptr; dptr = next) { /* all the list items */
492 if (dptr->data) {
493 /* Release the quantum-set */
494 for (i = 0; i < qset; i++)
495 if (dptr->data[i])
496 vfree(dptr->data[i]);
497
498 kfree(dptr->data);
499 dptr->data=NULL;
500 }
501 next=dptr->next;
502 if (dptr != dev) kfree(dptr); /* all of them but the first */
503 }
504 dev->size = 0;
505 dev->qset = scullv_qset;
506 dev->order = scullv_order;
507 dev->next = NULL;
508 return 0;
509 }
510
511
512 static void scullv_setup_cdev(struct scullv_dev *dev, int index)
513 {
514 int err, devno = MKDEV(scullv_major, index);
515
516 cdev_init(&dev->cdev, &scullv_fops);
517 dev->cdev.owner = THIS_MODULE;
518 dev->cdev.ops = &scullv_fops;
519 err = cdev_add (&dev->cdev, devno, 1);
520 /* Fail gracefully if need be */
521 if (err)
522 printk(KERN_NOTICE "Error %d adding scull%d", err, index);
523 }
524
525
526
527 /*
528 * Finally, the module stuff
529 */
530
531 int scullv_init(void)
532 {
533 int result, i;
534 dev_t dev = MKDEV(scullv_major, 0);
535
536 /*
537 * Register your major, and accept a dynamic number.
538 */
539 if (scullv_major)
540 result = register_chrdev_region(dev, scullv_devs, "scullv");
541 else {
542 result = alloc_chrdev_region(&dev, 0, scullv_devs, "scullv");
543 scullv_major = MAJOR(dev);
544 }
545 if (result < 0)
546 return result;
547
548
549 /*
550 * allocate the devices -- we can't have them static, as the number
551 * can be specified at load time
552 */
553 scullv_devices = kmalloc(scullv_devs*sizeof (struct scullv_dev), GFP_KERNEL);
554 if (!scullv_devices) {
555 result = -ENOMEM;
556 goto fail_malloc;
557 }
558 memset(scullv_devices, 0, scullv_devs*sizeof (struct scullv_dev));
559 for (i = 0; i < scullv_devs; i++) {
560 scullv_devices[i].order = scullv_order;
561 scullv_devices[i].qset = scullv_qset;
562 sema_init (&scullv_devices[i].sem, 1);
563 scullv_setup_cdev(scullv_devices + i, i);
564 }
565
566
567 #ifdef SCULLV_USE_PROC /* only when available */
568 create_proc_read_entry("scullvmem", 0, NULL, scullv_read_procmem, NULL);
569 #endif
570 return 0; /* succeed */
571
572 fail_malloc:
573 unregister_chrdev_region(dev, scullv_devs);
574 return result;
575 }
576
577
578
579 void scullv_cleanup(void)
580 {
581 int i;
582
583 #ifdef SCULLV_USE_PROC
584 remove_proc_entry("scullvmem", NULL);
585 #endif
586
587 for (i = 0; i < scullv_devs; i++) {
588 cdev_del(&scullv_devices[i].cdev);
589 scullv_trim(scullv_devices + i);
590 }
591 kfree(scullv_devices);
592 unregister_chrdev_region(MKDEV (scullv_major, 0), scullv_devs);
593 }
594
595
596 module_init(scullv_init);
597 module_exit(scullv_cleanup);
598
|
This page was automatically generated by the
LXR engine.
|