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