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