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.
|