1 /* -*- C -*-
2 * mmap.c -- memory mapping for the 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: _mmap.c.in,v 1.13 2004/10/18 18:07:36 corbet Exp $
16 */
17
18 #include <linux/config.h>
19 #include <linux/module.h>
20
21 #include <linux/mm.h> /* everything */
22 #include <linux/errno.h> /* error codes */
23 #include <asm/pgtable.h>
24
25 #include "sculld.h" /* local definitions */
26
27
28 /*
29 * open and close: just keep track of how many times the device is
30 * mapped, to avoid releasing it.
31 */
32
33 void sculld_vma_open(struct vm_area_struct *vma)
34 {
35 struct sculld_dev *dev = vma->vm_private_data;
36
37 dev->vmas++;
38 }
39
40 void sculld_vma_close(struct vm_area_struct *vma)
41 {
42 struct sculld_dev *dev = vma->vm_private_data;
43
44 dev->vmas--;
45 }
46
47 /*
48 * The nopage method: the core of the file. It retrieves the
49 * page required from the sculld device and returns it to the
50 * user. The count for the page must be incremented, because
51 * it is automatically decremented at page unmap.
52 *
53 * For this reason, "order" must be zero. Otherwise, only the first
54 * page has its count incremented, and the allocating module must
55 * release it as a whole block. Therefore, it isn't possible to map
56 * pages from a multipage block: when they are unmapped, their count
57 * is individually decreased, and would drop to 0.
58 */
59
60 struct page *sculld_vma_nopage(struct vm_area_struct *vma,
61 unsigned long address, int *type)
62 {
63 unsigned long offset;
64 struct sculld_dev *ptr, *dev = vma->vm_private_data;
65 struct page *page = NOPAGE_SIGBUS;
66 void *pageptr = NULL; /* default to "missing" */
67
68 down(&dev->sem);
69 offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
70 if (offset >= dev->size) goto out; /* out of range */
71
72 /*
73 * Now retrieve the sculld device from the list,then the page.
74 * If the device has holes, the process receives a SIGBUS when
75 * accessing the hole.
76 */
77 offset >>= PAGE_SHIFT; /* offset is a number of pages */
78 for (ptr = dev; ptr && offset >= dev->qset;) {
79 ptr = ptr->next;
80 offset -= dev->qset;
81 }
82 if (ptr && ptr->data) pageptr = ptr->data[offset];
83 if (!pageptr) goto out; /* hole or end-of-file */
84
85 /* got it, now increment the count */
86 get_page(page);
87 if (type)
88 *type = VM_FAULT_MINOR;
89 out:
90 up(&dev->sem);
91 return page;
92 }
93
94
95
96 struct vm_operations_struct sculld_vm_ops = {
97 .open = sculld_vma_open,
98 .close = sculld_vma_close,
99 .nopage = sculld_vma_nopage,
100 };
101
102
103 int sculld_mmap(struct file *filp, struct vm_area_struct *vma)
104 {
105 struct inode *inode = filp->f_dentry->d_inode;
106
107 /* refuse to map if order is not 0 */
108 if (sculld_devices[iminor(inode)].order)
109 return -ENODEV;
110
111 /* don't do anything here: "nopage" will set up page table entries */
112 vma->vm_ops = &sculld_vm_ops;
113 vma->vm_flags |= VM_RESERVED;
114 vma->vm_private_data = filp->private_data;
115 sculld_vma_open(vma);
116 return 0;
117 }
118
119
|
This page was automatically generated by the
LXR engine.
|