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 /*
  2  * Simple - REALLY simple memory mapping demonstration.
  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: simple.c,v 1.12 2005/01/31 16:15:31 rubini Exp $
 16  */
 17 
 18 #include <linux/config.h>
 19 #include <linux/module.h>
 20 #include <linux/moduleparam.h>
 21 #include <linux/init.h>
 22 
 23 #include <linux/kernel.h>   /* printk() */
 24 #include <linux/slab.h>   /* kmalloc() */
 25 #include <linux/fs.h>       /* everything... */
 26 #include <linux/errno.h>    /* error codes */
 27 #include <linux/types.h>    /* size_t */
 28 #include <linux/mm.h>
 29 #include <linux/kdev_t.h>
 30 #include <asm/page.h>
 31 #include <linux/cdev.h>
 32 
 33 #include <linux/device.h>
 34 
 35 static int simple_major = 0;
 36 module_param(simple_major, int, 0);
 37 MODULE_AUTHOR("Jonathan Corbet");
 38 MODULE_LICENSE("Dual BSD/GPL");
 39 
 40 /*
 41  * Open the device; in fact, there's nothing to do here.
 42  */
 43 static int simple_open (struct inode *inode, struct file *filp)
 44 {
 45         return 0;
 46 }
 47 
 48 
 49 /*
 50  * Closing is just as simpler.
 51  */
 52 static int simple_release(struct inode *inode, struct file *filp)
 53 {
 54         return 0;
 55 }
 56 
 57 
 58 
 59 /*
 60  * Common VMA ops.
 61  */
 62 
 63 void simple_vma_open(struct vm_area_struct *vma)
 64 {
 65         printk(KERN_NOTICE "Simple VMA open, virt %lx, phys %lx\n",
 66                         vma->vm_start, vma->vm_pgoff << PAGE_SHIFT);
 67 }
 68 
 69 void simple_vma_close(struct vm_area_struct *vma)
 70 {
 71         printk(KERN_NOTICE "Simple VMA close.\n");
 72 }
 73 
 74 
 75 /*
 76  * The remap_pfn_range version of mmap.  This one is heavily borrowed
 77  * from drivers/char/mem.c.
 78  */
 79 
 80 static struct vm_operations_struct simple_remap_vm_ops = {
 81         .open =  simple_vma_open,
 82         .close = simple_vma_close,
 83 };
 84 
 85 static int simple_remap_mmap(struct file *filp, struct vm_area_struct *vma)
 86 {
 87         if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
 88                             vma->vm_end - vma->vm_start,
 89                             vma->vm_page_prot))
 90                 return -EAGAIN;
 91 
 92         vma->vm_ops = &simple_remap_vm_ops;
 93         simple_vma_open(vma);
 94         return 0;
 95 }
 96 
 97 
 98 
 99 /*
100  * The nopage version.
101  */
102 struct page *simple_vma_nopage(struct vm_area_struct *vma,
103                 unsigned long address, int *type)
104 {
105         struct page *pageptr;
106         unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
107         unsigned long physaddr = address - vma->vm_start + offset;
108         unsigned long pageframe = physaddr >> PAGE_SHIFT;
109 
110 // Eventually remove these printks
111         printk (KERN_NOTICE "---- Nopage, off %lx phys %lx\n", offset, physaddr);
112         printk (KERN_NOTICE "VA is %p\n", __va (physaddr));
113         printk (KERN_NOTICE "Page at %p\n", virt_to_page (__va (physaddr)));
114         if (!pfn_valid(pageframe))
115                 return NOPAGE_SIGBUS;
116         pageptr = pfn_to_page(pageframe);
117         printk (KERN_NOTICE "page->index = %ld mapping %p\n", pageptr->index, pageptr->mapping);
118         printk (KERN_NOTICE "Page frame %ld\n", pageframe);
119         get_page(pageptr);
120         if (type)
121                 *type = VM_FAULT_MINOR;
122         return pageptr;
123 }
124 
125 static struct vm_operations_struct simple_nopage_vm_ops = {
126         .open =   simple_vma_open,
127         .close =  simple_vma_close,
128         .nopage = simple_vma_nopage,
129 };
130 
131 static int simple_nopage_mmap(struct file *filp, struct vm_area_struct *vma)
132 {
133         unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
134 
135         if (offset >= __pa(high_memory) || (filp->f_flags & O_SYNC))
136                 vma->vm_flags |= VM_IO;
137         vma->vm_flags |= VM_RESERVED;
138 
139         vma->vm_ops = &simple_nopage_vm_ops;
140         simple_vma_open(vma);
141         return 0;
142 }
143 
144 
145 /*
146  * Set up the cdev structure for a device.
147  */
148 static void simple_setup_cdev(struct cdev *dev, int minor,
149                 struct file_operations *fops)
150 {
151         int err, devno = MKDEV(simple_major, minor);
152     
153         cdev_init(dev, fops);
154         dev->owner = THIS_MODULE;
155         dev->ops = fops;
156         err = cdev_add (dev, devno, 1);
157         /* Fail gracefully if need be */
158         if (err)
159                 printk (KERN_NOTICE "Error %d adding simple%d", err, minor);
160 }
161 
162 
163 /*
164  * Our various sub-devices.
165  */
166 /* Device 0 uses remap_pfn_range */
167 static struct file_operations simple_remap_ops = {
168         .owner   = THIS_MODULE,
169         .open    = simple_open,
170         .release = simple_release,
171         .mmap    = simple_remap_mmap,
172 };
173 
174 /* Device 1 uses nopage */
175 static struct file_operations simple_nopage_ops = {
176         .owner   = THIS_MODULE,
177         .open    = simple_open,
178         .release = simple_release,
179         .mmap    = simple_nopage_mmap,
180 };
181 
182 #define MAX_SIMPLE_DEV 2
183 
184 #if 0
185 static struct file_operations *simple_fops[MAX_SIMPLE_DEV] = {
186         &simple_remap_ops,
187         &simple_nopage_ops,
188 };
189 #endif
190 
191 /*
192  * We export two simple devices.  There's no need for us to maintain any
193  * special housekeeping info, so we just deal with raw cdevs.
194  */
195 static struct cdev SimpleDevs[MAX_SIMPLE_DEV];
196 
197 /*
198  * Module housekeeping.
199  */
200 static int simple_init(void)
201 {
202         int result;
203         dev_t dev = MKDEV(simple_major, 0);
204 
205         /* Figure out our device number. */
206         if (simple_major)
207                 result = register_chrdev_region(dev, 2, "simple");
208         else {
209                 result = alloc_chrdev_region(&dev, 0, 2, "simple");
210                 simple_major = MAJOR(dev);
211         }
212         if (result < 0) {
213                 printk(KERN_WARNING "simple: unable to get major %d\n", simple_major);
214                 return result;
215         }
216         if (simple_major == 0)
217                 simple_major = result;
218 
219         /* Now set up two cdevs. */
220         simple_setup_cdev(SimpleDevs, 0, &simple_remap_ops);
221         simple_setup_cdev(SimpleDevs + 1, 1, &simple_nopage_ops);
222         return 0;
223 }
224 
225 
226 static void simple_cleanup(void)
227 {
228         cdev_del(SimpleDevs);
229         cdev_del(SimpleDevs + 1);
230         unregister_chrdev_region(MKDEV(simple_major, 0), 2);
231 }
232 
233 
234 module_init(simple_init);
235 module_exit(simple_cleanup);
236 
  This page was automatically generated by the LXR engine.