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