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 ] Architecture: [ i386 ]
  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.