Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  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.