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