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