1 /* -*- C -*-
2 * main.c -- the bare sculld 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/module.h>
19 #include <linux/moduleparam.h>
20 #include <linux/init.h>
21 #include <linux/kernel.h> /* printk() */
22 #include <linux/slab.h> /* kmalloc() */
23 #include <linux/fs.h> /* everything... */
24 #include <linux/errno.h> /* error codes */
25 #include <linux/types.h> /* size_t */
26 #include <linux/proc_fs.h>
27 #include <linux/fcntl.h> /* O_ACCMODE */
28 #include <linux/aio.h>
29 #include <asm/uaccess.h>
30 #include "sculld.h" /* local definitions */
31
32
33 int sculld_major = SCULLD_MAJOR;
34 int sculld_devs = SCULLD_DEVS; /* number of bare sculld devices */
35 int sculld_qset = SCULLD_QSET;
36 int sculld_order = SCULLD_ORDER;
37
38 module_param(sculld_major, int, 0);
39 module_param(sculld_devs, int, 0);
40 module_param(sculld_qset, int, 0);
41 module_param(sculld_order, int, 0);
42 MODULE_AUTHOR("Alessandro Rubini");
43 MODULE_LICENSE("Dual BSD/GPL");
44
45 struct sculld_dev *sculld_devices; /* allocated in sculld_init */
46
47 int sculld_trim(struct sculld_dev *dev);
48 void sculld_cleanup(void);
49
50
51
52 /* Device model stuff */
53
54 static struct ldd_driver sculld_driver = {
55 .version = "$Revision: 1.21 $",
56 .module = THIS_MODULE,
57 .driver = {
58 .name = "sculld",
59 },
60 };
61
62
63
64 #ifdef SCULLD_USE_PROC /* don't waste space if unused */
65 /*
66 * The proc filesystem: function to read and entry
67 */
68
69 void sculld_proc_offset(char *buf, char **start, off_t *offset, int *len)
70 {
71 if (*offset == 0)
72 return;
73 if (*offset >= *len) {
74 /* Not there yet */
75 *offset -= *len;
76 *len = 0;
77 } else {
78 /* We're into the interesting stuff now */
79 *start = buf + *offset;
80 *offset = 0;
81 }
82 }
83
84 /* FIXME: Do we need this here?? It be ugly */
85 int sculld_read_procmem(char *buf, char **start, off_t offset,
86 int count, int *eof, void *data)
87 {
88 int i, j, order, qset, len = 0;
89 int limit = count - 80; /* Don't print more than this */
90 struct sculld_dev *d;
91
92 *start = buf;
93 for(i = 0; i < sculld_devs; i++) {
94 d = &sculld_devices[i];
95 if (down_interruptible (&d->sem))
96 return -ERESTARTSYS;
97 qset = d->qset; /* retrieve the features of each device */
98 order = d->order;
99 len += sprintf(buf+len,"\nDevice %i: qset %i, order %i, sz %li\n",
100 i, qset, order, (long)(d->size));
101 for (; d; d = d->next) { /* scan the list */
102 len += sprintf(buf+len," item at %p, qset at %p\n",d,d->data);
103 sculld_proc_offset (buf, start, &offset, &len);
104 if (len > limit)
105 goto out;
106 if (d->data && !d->next) /* dump only the last item - save space */
107 for (j = 0; j < qset; j++) {
108 if (d->data[j])
109 len += sprintf(buf+len," % 4i:%8p\n",j,d->data[j]);
110 sculld_proc_offset (buf, start, &offset, &len);
111 if (len > limit)
112 goto out;
113 }
114 }
115 out:
116 up (&sculld_devices[i].sem);
117 if (len > limit)
118 break;
119 }
120 *eof = 1;
121 return len;
122 }
123
124 #endif /* SCULLD_USE_PROC */
125
126 /*
127 * Open and close
128 */
129
130 int sculld_open (struct inode *inode, struct file *filp)
131 {
132 struct sculld_dev *dev; /* device information */
133
134 /* Find the device */
135 dev = container_of(inode->i_cdev, struct sculld_dev, cdev);
136
137 /* now trim to 0 the length of the device if open was write-only */
138 if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {
139 if (down_interruptible (&dev->sem))
140 return -ERESTARTSYS;
141 sculld_trim(dev); /* ignore errors */
142 up (&dev->sem);
143 }
144
145 /* and use filp->private_data to point to the device data */
146 filp->private_data = dev;
147
148 return 0; /* success */
149 }
150
151 int sculld_release (struct inode *inode, struct file *filp)
152 {
153 return 0;
154 }
155
156 /*
157 * Follow the list
158 */
159 struct sculld_dev *sculld_follow(struct sculld_dev *dev, int n)
160 {
161 while (n--) {
162 if (!dev->next) {
163 dev->next = kmalloc(sizeof(struct sculld_dev), GFP_KERNEL);
164 memset(dev->next, 0, sizeof(struct sculld_dev));
165 }
166 dev = dev->next;
167 continue;
168 }
169 return dev;
170 }
171
172 /*
173 * Data management: read and write
174 */
175
176 ssize_t sculld_read (struct file *filp, char __user *buf, size_t count,
177 loff_t *f_pos)
178 {
179 struct sculld_dev *dev = filp->private_data; /* the first listitem */
180 struct sculld_dev *dptr;
181 int quantum = PAGE_SIZE << dev->order;
182 int qset = dev->qset;
183 int itemsize = quantum * qset; /* how many bytes in the listitem */
184 int item, s_pos, q_pos, rest;
185 ssize_t retval = 0;
186
187 if (down_interruptible (&dev->sem))
188 return -ERESTARTSYS;
189 if (*f_pos > dev->size)
190 goto nothing;
191 if (*f_pos + count > dev->size)
192 count = dev->size - *f_pos;
193 /* find listitem, qset index, and offset in the quantum */
194 item = ((long) *f_pos) / itemsize;
195 rest = ((long) *f_pos) % itemsize;
196 s_pos = rest / quantum; q_pos = rest % quantum;
197
198 /* follow the list up to the right position (defined elsewhere) */
199 dptr = sculld_follow(dev, item);
200
201 if (!dptr->data)
202 goto nothing; /* don't fill holes */
203 if (!dptr->data[s_pos])
204 goto nothing;
205 if (count > quantum - q_pos)
206 count = quantum - q_pos; /* read only up to the end of this quantum */
207
208 if (copy_to_user (buf, dptr->data[s_pos]+q_pos, count)) {
209 retval = -EFAULT;
210 goto nothing;
211 }
212 up (&dev->sem);
213
214 *f_pos += count;
215 return count;
216
217 nothing:
218 up (&dev->sem);
219 return retval;
220 }
221
222
223
224 ssize_t sculld_write (struct file *filp, const char __user *buf, size_t count,
225 loff_t *f_pos)
226 {
227 struct sculld_dev *dev = filp->private_data;
228 struct sculld_dev *dptr;
229 int quantum = PAGE_SIZE << dev->order;
230 int qset = dev->qset;
231 int itemsize = quantum * qset;
232 int item, s_pos, q_pos, rest;
233 ssize_t retval = -ENOMEM; /* our most likely error */
234
235 if (down_interruptible (&dev->sem))
236 return -ERESTARTSYS;
237
238 /* find listitem, qset index and offset in the quantum */
239 item = ((long) *f_pos) / itemsize;
240 rest = ((long) *f_pos) % itemsize;
241 s_pos = rest / quantum; q_pos = rest % quantum;
242
243 /* follow the list up to the right position */
244 dptr = sculld_follow(dev, item);
245 if (!dptr->data) {
246 dptr->data = kmalloc(qset * sizeof(void *), GFP_KERNEL);
247 if (!dptr->data)
248 goto nomem;
249 memset(dptr->data, 0, qset * sizeof(char *));
250 }
251 /* Here's the allocation of a single quantum */
252 if (!dptr->data[s_pos]) {
253 dptr->data[s_pos] =
254 (void *)__get_free_pages(GFP_KERNEL, dptr->order);
255 if (!dptr->data[s_pos])
256 goto nomem;
257 memset(dptr->data[s_pos], 0, PAGE_SIZE << dptr->order);
258 }
259 if (count > quantum - q_pos)
260 count = quantum - q_pos; /* write only up to the end of this quantum */
261 if (copy_from_user (dptr->data[s_pos]+q_pos, buf, count)) {
262 retval = -EFAULT;
263 goto nomem;
264 }
265 *f_pos += count;
266
267 /* update the size */
268 if (dev->size < *f_pos)
269 dev->size = *f_pos;
270 up (&dev->sem);
271 return count;
272
273 nomem:
274 up (&dev->sem);
275 return retval;
276 }
277
278 /*
279 * The ioctl() implementation
280 */
281
282 int sculld_ioctl (struct inode *inode, struct file *filp,
283 unsigned int cmd, unsigned long arg)
284 {
285
286 int err = 0, ret = 0, tmp;
287
288 /* don't even decode wrong cmds: better returning ENOTTY than EFAULT */
289 if (_IOC_TYPE(cmd) != SCULLD_IOC_MAGIC) return -ENOTTY;
290 if (_IOC_NR(cmd) > SCULLD_IOC_MAXNR) return -ENOTTY;
291
292 /*
293 * the type is a bitmask, and VERIFY_WRITE catches R/W
294 * transfers. Note that the type is user-oriented, while
295 * verify_area is kernel-oriented, so the concept of "read" and
296 * "write" is reversed
297 */
298 if (_IOC_DIR(cmd) & _IOC_READ)
299 err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
300 else if (_IOC_DIR(cmd) & _IOC_WRITE)
301 err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
302 if (err)
303 return -EFAULT;
304
305 switch(cmd) {
306
307 case SCULLD_IOCRESET:
308 sculld_qset = SCULLD_QSET;
309 sculld_order = SCULLD_ORDER;
310 break;
311
312 case SCULLD_IOCSORDER: /* Set: arg points to the value */
313 ret = __get_user(sculld_order, (int __user *) arg);
314 break;
315
316 case SCULLD_IOCTORDER: /* Tell: arg is the value */
317 sculld_order = arg;
318 break;
319
320 case SCULLD_IOCGORDER: /* Get: arg is pointer to result */
321 ret = __put_user (sculld_order, (int __user *) arg);
322 break;
323
324 case SCULLD_IOCQORDER: /* Query: return it (it's positive) */
325 return sculld_order;
326
327 case SCULLD_IOCXORDER: /* eXchange: use arg as pointer */
328 tmp = sculld_order;
329 ret = __get_user(sculld_order, (int __user *) arg);
330 if (ret == 0)
331 ret = __put_user(tmp, (int __user *) arg);
332 break;
333
334 case SCULLD_IOCHORDER: /* sHift: like Tell + Query */
335 tmp = sculld_order;
336 sculld_order = arg;
337 return tmp;
338
339 case SCULLD_IOCSQSET:
340 ret = __get_user(sculld_qset, (int __user *) arg);
341 break;
342
343 case SCULLD_IOCTQSET:
344 sculld_qset = arg;
345 break;
346
347 case SCULLD_IOCGQSET:
348 ret = __put_user(sculld_qset, (int __user *)arg);
349 break;
350
351 case SCULLD_IOCQQSET:
352 return sculld_qset;
353
354 case SCULLD_IOCXQSET:
355 tmp = sculld_qset;
356 ret = __get_user(sculld_qset, (int __user *)arg);
357 if (ret == 0)
358 ret = __put_user(tmp, (int __user *)arg);
359 break;
360
361 case SCULLD_IOCHQSET:
362 tmp = sculld_qset;
363 sculld_qset = arg;
364 return tmp;
365
366 default: /* redundant, as cmd was checked against MAXNR */
367 return -ENOTTY;
368 }
369
370 return ret;
371 }
372
373 /*
374 * The "extended" operations
375 */
376
377 loff_t sculld_llseek (struct file *filp, loff_t off, int whence)
378 {
379 struct sculld_dev *dev = filp->private_data;
380 long newpos;
381
382 switch(whence) {
383 case 0: /* SEEK_SET */
384 newpos = off;
385 break;
386
387 case 1: /* SEEK_CUR */
388 newpos = filp->f_pos + off;
389 break;
390
391 case 2: /* SEEK_END */
392 newpos = dev->size + off;
393 break;
394
395 default: /* can't happen */
396 return -EINVAL;
397 }
398 if (newpos<0) return -EINVAL;
399 filp->f_pos = newpos;
400 return newpos;
401 }
402
403
404 /*
405 * A simple asynchronous I/O implementation.
406 */
407
408 struct async_work {
409 struct kiocb *iocb;
410 int result;
411 struct delayed_work work;
412 };
413
414 /*
415 * "Complete" an asynchronous operation.
416 */
417 static void sculld_do_deferred_op(struct work_struct *work)
418 {
419 struct async_work *stuff = container_of(work, struct async_work, work.work);
420 aio_complete(stuff->iocb, stuff->result, 0);
421 kfree(stuff);
422 }
423
424 static ssize_t sculld_defer_op(int write, struct kiocb *iocb, const struct iovec *iov,
425 unsigned long nr_segs, loff_t pos)
426 {
427 struct async_work *stuff;
428 ssize_t result, len = 0;
429 int i;
430
431 for (i = 0; i < nr_segs; i++) {
432 if (write) {
433 result = sculld_write (iocb->ki_filp, iov[i].iov_base, iov[i].iov_len, &pos);
434 } else { /* read */
435 result = sculld_read (iocb->ki_filp, iov[i].iov_base, iov[i].iov_len, &pos);
436 }
437 if (result < 0)
438 return result;
439 len += result;
440 }
441 result = len;
442
443 /* If this is a synchronous IOCB, we return our status now. */
444 if (is_sync_kiocb(iocb))
445 return result;
446
447 /* Otherwise defer the completion for a few milliseconds. */
448 stuff = kmalloc (sizeof (*stuff), GFP_KERNEL);
449 if (stuff == NULL)
450 return result; /* No memory, just complete now */
451 stuff->iocb = iocb;
452 stuff->result = result;
453 INIT_DELAYED_WORK(&stuff->work, sculld_do_deferred_op);
454 schedule_delayed_work(&stuff->work, HZ/100);
455 return -EIOCBQUEUED;
456 }
457
458
459 static ssize_t sculld_aio_read(struct kiocb *iocb,
460 const struct iovec *iovec,
461 unsigned long nr_segs,
462 loff_t pos)
463 {
464 return sculld_defer_op(0, iocb, iovec, nr_segs, pos);
465 }
466
467 static ssize_t sculld_aio_write(struct kiocb *iocb,
468 const struct iovec *iovec,
469 unsigned long nr_segs,
470 loff_t pos)
471 {
472 return sculld_defer_op(1, iocb, iovec, nr_segs, pos);
473 }
474
475 /*
476 * Mmap *is* available, but confined in a different file
477 */
478 extern int sculld_mmap(struct file *filp, struct vm_area_struct *vma);
479
480
481 /*
482 * The fops
483 */
484
485 struct file_operations sculld_fops = {
486 .owner = THIS_MODULE,
487 .llseek = sculld_llseek,
488 .read = sculld_read,
489 .write = sculld_write,
490 .ioctl = sculld_ioctl,
491 .mmap = sculld_mmap,
492 .open = sculld_open,
493 .release = sculld_release,
494 .aio_read = sculld_aio_read,
495 .aio_write = sculld_aio_write,
496 };
497
498 int sculld_trim(struct sculld_dev *dev)
499 {
500 struct sculld_dev *next, *dptr;
501 int qset = dev->qset; /* "dev" is not-null */
502 int i;
503
504 if (dev->vmas) /* don't trim: there are active mappings */
505 return -EBUSY;
506
507 for (dptr = dev; dptr; dptr = next) { /* all the list items */
508 if (dptr->data) {
509 /* This code frees a whole quantum-set */
510 for (i = 0; i < qset; i++)
511 if (dptr->data[i])
512 free_pages((unsigned long)(dptr->data[i]),
513 dptr->order);
514
515 kfree(dptr->data);
516 dptr->data=NULL;
517 }
518 next=dptr->next;
519 if (dptr != dev) kfree(dptr); /* all of them but the first */
520 }
521 dev->size = 0;
522 dev->qset = sculld_qset;
523 dev->order = sculld_order;
524 dev->next = NULL;
525 return 0;
526 }
527
528
529 static void sculld_setup_cdev(struct sculld_dev *dev, int index)
530 {
531 int err, devno = MKDEV(sculld_major, index);
532
533 cdev_init(&dev->cdev, &sculld_fops);
534 dev->cdev.owner = THIS_MODULE;
535 dev->cdev.ops = &sculld_fops;
536 err = cdev_add (&dev->cdev, devno, 1);
537 /* Fail gracefully if need be */
538 if (err)
539 printk(KERN_NOTICE "Error %d adding scull%d", err, index);
540 }
541
542 static ssize_t sculld_show_dev(struct device *ddev,
543 struct device_attribute *attr,
544 char *buf)
545 {
546 /* no longer a good example. should use attr parameter. */
547 struct sculld_dev *dev = ddev->driver_data;
548 return print_dev_t(buf, dev->cdev.dev);
549 }
550
551 static DEVICE_ATTR(dev, S_IRUGO, sculld_show_dev, NULL);
552
553 static int sculld_register_dev(struct sculld_dev *dev, int index)
554 {
555 int result;
556 sprintf(dev->devname, "sculld%d", index);
557 dev->ldev.name = dev->devname;
558 dev->ldev.driver = &sculld_driver;
559 dev->ldev.dev.driver_data = dev;
560 register_ldd_device(&dev->ldev);
561 if ((result = device_create_file(&dev->ldev.dev, &dev_attr_dev))) {
562 unregister_ldd_device(&dev->ldev);
563 return result;
564 } else return 0;
565 }
566
567 /*
568 * Finally, the module stuff
569 */
570
571 int sculld_init(void)
572 {
573 int result, i;
574 dev_t dev = MKDEV(sculld_major, 0);
575
576 /*
577 * Register your major, and accept a dynamic number.
578 */
579 if (sculld_major)
580 result = register_chrdev_region(dev, sculld_devs, "sculld");
581 else {
582 result = alloc_chrdev_region(&dev, 0, sculld_devs, "sculld");
583 sculld_major = MAJOR(dev);
584 }
585 if (result < 0)
586 return result;
587
588 /*
589 * Register with the driver core.
590 */
591 result = register_ldd_driver(&sculld_driver);
592 if (result)
593 goto fail_malloc;
594
595 /*
596 * allocate the devices -- we can't have them static, as the number
597 * can be specified at load time
598 */
599 sculld_devices = kmalloc(sculld_devs*sizeof (struct sculld_dev), GFP_KERNEL);
600 if (!sculld_devices) {
601 result = -ENOMEM;
602 goto fail_malloc;
603 }
604 memset(sculld_devices, 0, sculld_devs*sizeof (struct sculld_dev));
605 for (i = 0; i < sculld_devs; i++) {
606 sculld_devices[i].order = sculld_order;
607 sculld_devices[i].qset = sculld_qset;
608 sema_init (&sculld_devices[i].sem, 1);
609 sculld_setup_cdev(sculld_devices + i, i);
610 if (sculld_register_dev(sculld_devices + i, i))
611 goto fail_register_dev;
612
613 }
614
615 #ifdef SCULLD_USE_PROC /* only when available */
616 create_proc_read_entry("sculldmem", 0, NULL, sculld_read_procmem, NULL);
617 #endif
618 return 0; /* succeed */
619
620 /* Consider modifying sculld_cleanup() so that it can be called here,
621 to avoid the following duplicate code. */
622
623 fail_register_dev:
624 for (i-- ; i >= 0; i--) {
625 unregister_ldd_device(&sculld_devices[i].ldev);
626 cdev_del(&sculld_devices[i].cdev);
627 sculld_trim(sculld_devices + i);
628 }
629 kfree(sculld_devices);
630 unregister_ldd_driver(&sculld_driver);
631 fail_malloc:
632 unregister_chrdev_region(dev, sculld_devs);
633 return result;
634 }
635
636
637
638 void sculld_cleanup(void)
639 {
640 int i;
641
642 #ifdef SCULLD_USE_PROC
643 remove_proc_entry("sculldmem", NULL);
644 #endif
645
646 for (i = 0; i < sculld_devs; i++) {
647 unregister_ldd_device(&sculld_devices[i].ldev);
648 cdev_del(&sculld_devices[i].cdev);
649 sculld_trim(sculld_devices + i);
650 }
651 kfree(sculld_devices);
652 unregister_ldd_driver(&sculld_driver);
653 unregister_chrdev_region(MKDEV (sculld_major, 0), sculld_devs);
654 }
655
656
657 module_init(sculld_init);
658 module_exit(sculld_cleanup);
659
|
This page was automatically generated by the
LXR engine.
|