diff options
author | Marek Szyprowski <m.szyprowski@samsung.com> | 2010-12-09 08:20:47 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-03-21 19:31:34 -0400 |
commit | 004cc3781c40a577b0349eef825efaaebc42dd43 (patch) | |
tree | 81c066bffe8bd906a8707f6654eb9395e39dab8f | |
parent | e23ccc0ad9258634e6d52cedf473b35dc34416c7 (diff) |
[media] v4l: videobuf2: add generic memory handling routines
Add generic memory handling routines for userspace pointer handling,
contiguous memory verification and mapping.
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Pawel Osciak <p.osciak@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
CC: Pawel Osciak <pawel@osciak.com>
Reviewed-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | drivers/media/video/Kconfig | 3 | ||||
-rw-r--r-- | drivers/media/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/video/videobuf2-memops.c | 232 | ||||
-rw-r--r-- | include/media/videobuf2-memops.h | 45 |
4 files changed, 281 insertions, 0 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index a4a6aa703c26..aae5005cf86d 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig | |||
@@ -47,6 +47,9 @@ config V4L2_MEM2MEM_DEV | |||
47 | config VIDEOBUF2_CORE | 47 | config VIDEOBUF2_CORE |
48 | tristate | 48 | tristate |
49 | 49 | ||
50 | config VIDEOBUF2_MEMOPS | ||
51 | tristate | ||
52 | |||
50 | # | 53 | # |
51 | # Multimedia Video device configuration | 54 | # Multimedia Video device configuration |
52 | # | 55 | # |
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 6a1feba87869..fca0444b66b1 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile | |||
@@ -112,6 +112,7 @@ obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o | |||
112 | obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o | 112 | obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o |
113 | 113 | ||
114 | obj-$(CONFIG_VIDEOBUF2_CORE) += videobuf2-core.o | 114 | obj-$(CONFIG_VIDEOBUF2_CORE) += videobuf2-core.o |
115 | obj-$(CONFIG_VIDEOBUF2_MEMOPS) += videobuf2-memops.o | ||
115 | 116 | ||
116 | obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o | 117 | obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o |
117 | 118 | ||
diff --git a/drivers/media/video/videobuf2-memops.c b/drivers/media/video/videobuf2-memops.c new file mode 100644 index 000000000000..053c15794b8d --- /dev/null +++ b/drivers/media/video/videobuf2-memops.c | |||
@@ -0,0 +1,232 @@ | |||
1 | /* | ||
2 | * videobuf2-memops.c - generic memory handling routines for videobuf2 | ||
3 | * | ||
4 | * Copyright (C) 2010 Samsung Electronics | ||
5 | * | ||
6 | * Author: Pawel Osciak <p.osciak@samsung.com> | ||
7 | * Marek Szyprowski <m.szyprowski@samsung.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <linux/slab.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/dma-mapping.h> | ||
17 | #include <linux/vmalloc.h> | ||
18 | #include <linux/mm.h> | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/file.h> | ||
21 | #include <linux/slab.h> | ||
22 | |||
23 | #include <media/videobuf2-core.h> | ||
24 | #include <media/videobuf2-memops.h> | ||
25 | |||
26 | /** | ||
27 | * vb2_get_vma() - acquire and lock the virtual memory area | ||
28 | * @vma: given virtual memory area | ||
29 | * | ||
30 | * This function attempts to acquire an area mapped in the userspace for | ||
31 | * the duration of a hardware operation. The area is "locked" by performing | ||
32 | * the same set of operation that are done when process calls fork() and | ||
33 | * memory areas are duplicated. | ||
34 | * | ||
35 | * Returns a copy of a virtual memory region on success or NULL. | ||
36 | */ | ||
37 | struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma) | ||
38 | { | ||
39 | struct vm_area_struct *vma_copy; | ||
40 | |||
41 | vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL); | ||
42 | if (vma_copy == NULL) | ||
43 | return NULL; | ||
44 | |||
45 | if (vma->vm_ops && vma->vm_ops->open) | ||
46 | vma->vm_ops->open(vma); | ||
47 | |||
48 | if (vma->vm_file) | ||
49 | get_file(vma->vm_file); | ||
50 | |||
51 | memcpy(vma_copy, vma, sizeof(*vma)); | ||
52 | |||
53 | vma_copy->vm_mm = NULL; | ||
54 | vma_copy->vm_next = NULL; | ||
55 | vma_copy->vm_prev = NULL; | ||
56 | |||
57 | return vma_copy; | ||
58 | } | ||
59 | |||
60 | /** | ||
61 | * vb2_put_userptr() - release a userspace virtual memory area | ||
62 | * @vma: virtual memory region associated with the area to be released | ||
63 | * | ||
64 | * This function releases the previously acquired memory area after a hardware | ||
65 | * operation. | ||
66 | */ | ||
67 | void vb2_put_vma(struct vm_area_struct *vma) | ||
68 | { | ||
69 | if (!vma) | ||
70 | return; | ||
71 | |||
72 | if (vma->vm_file) | ||
73 | fput(vma->vm_file); | ||
74 | |||
75 | if (vma->vm_ops && vma->vm_ops->close) | ||
76 | vma->vm_ops->close(vma); | ||
77 | |||
78 | kfree(vma); | ||
79 | } | ||
80 | |||
81 | /** | ||
82 | * vb2_get_contig_userptr() - lock physically contiguous userspace mapped memory | ||
83 | * @vaddr: starting virtual address of the area to be verified | ||
84 | * @size: size of the area | ||
85 | * @res_paddr: will return physical address for the given vaddr | ||
86 | * @res_vma: will return locked copy of struct vm_area for the given area | ||
87 | * | ||
88 | * This function will go through memory area of size @size mapped at @vaddr and | ||
89 | * verify that the underlying physical pages are contiguous. If they are | ||
90 | * contiguous the virtual memory area is locked and a @res_vma is filled with | ||
91 | * the copy and @res_pa set to the physical address of the buffer. | ||
92 | * | ||
93 | * Returns 0 on success. | ||
94 | */ | ||
95 | int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, | ||
96 | struct vm_area_struct **res_vma, dma_addr_t *res_pa) | ||
97 | { | ||
98 | struct mm_struct *mm = current->mm; | ||
99 | struct vm_area_struct *vma; | ||
100 | unsigned long offset, start, end; | ||
101 | unsigned long this_pfn, prev_pfn; | ||
102 | dma_addr_t pa = 0; | ||
103 | int ret = -EFAULT; | ||
104 | |||
105 | start = vaddr; | ||
106 | offset = start & ~PAGE_MASK; | ||
107 | end = start + size; | ||
108 | |||
109 | down_read(&mm->mmap_sem); | ||
110 | vma = find_vma(mm, start); | ||
111 | |||
112 | if (vma == NULL || vma->vm_end < end) | ||
113 | goto done; | ||
114 | |||
115 | for (prev_pfn = 0; start < end; start += PAGE_SIZE) { | ||
116 | ret = follow_pfn(vma, start, &this_pfn); | ||
117 | if (ret) | ||
118 | goto done; | ||
119 | |||
120 | if (prev_pfn == 0) | ||
121 | pa = this_pfn << PAGE_SHIFT; | ||
122 | else if (this_pfn != prev_pfn + 1) { | ||
123 | ret = -EFAULT; | ||
124 | goto done; | ||
125 | } | ||
126 | prev_pfn = this_pfn; | ||
127 | } | ||
128 | |||
129 | /* | ||
130 | * Memory is contigous, lock vma and return to the caller | ||
131 | */ | ||
132 | *res_vma = vb2_get_vma(vma); | ||
133 | if (*res_vma == NULL) { | ||
134 | ret = -ENOMEM; | ||
135 | goto done; | ||
136 | } | ||
137 | *res_pa = pa + offset; | ||
138 | ret = 0; | ||
139 | |||
140 | done: | ||
141 | up_read(&mm->mmap_sem); | ||
142 | return ret; | ||
143 | } | ||
144 | |||
145 | /** | ||
146 | * vb2_mmap_pfn_range() - map physical pages to userspace | ||
147 | * @vma: virtual memory region for the mapping | ||
148 | * @paddr: starting physical address of the memory to be mapped | ||
149 | * @size: size of the memory to be mapped | ||
150 | * @vm_ops: vm operations to be assigned to the created area | ||
151 | * @priv: private data to be associated with the area | ||
152 | * | ||
153 | * Returns 0 on success. | ||
154 | */ | ||
155 | int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr, | ||
156 | unsigned long size, | ||
157 | const struct vm_operations_struct *vm_ops, | ||
158 | void *priv) | ||
159 | { | ||
160 | int ret; | ||
161 | |||
162 | size = min_t(unsigned long, vma->vm_end - vma->vm_start, size); | ||
163 | |||
164 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | ||
165 | ret = remap_pfn_range(vma, vma->vm_start, paddr >> PAGE_SHIFT, | ||
166 | size, vma->vm_page_prot); | ||
167 | if (ret) { | ||
168 | printk(KERN_ERR "Remapping memory failed, error: %d\n", ret); | ||
169 | return ret; | ||
170 | } | ||
171 | |||
172 | vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED; | ||
173 | vma->vm_private_data = priv; | ||
174 | vma->vm_ops = vm_ops; | ||
175 | |||
176 | vma->vm_ops->open(vma); | ||
177 | |||
178 | printk(KERN_DEBUG "%s: mapped paddr 0x%08lx at 0x%08lx, size %ld\n", | ||
179 | __func__, paddr, vma->vm_start, size); | ||
180 | |||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | /** | ||
185 | * vb2_common_vm_open() - increase refcount of the vma | ||
186 | * @vma: virtual memory region for the mapping | ||
187 | * | ||
188 | * This function adds another user to the provided vma. It expects | ||
189 | * struct vb2_vmarea_handler pointer in vma->vm_private_data. | ||
190 | */ | ||
191 | static void vb2_common_vm_open(struct vm_area_struct *vma) | ||
192 | { | ||
193 | struct vb2_vmarea_handler *h = vma->vm_private_data; | ||
194 | |||
195 | printk(KERN_DEBUG "%s: %p, refcount: %d, vma: %08lx-%08lx\n", | ||
196 | __func__, h, atomic_read(h->refcount), vma->vm_start, | ||
197 | vma->vm_end); | ||
198 | |||
199 | atomic_inc(h->refcount); | ||
200 | } | ||
201 | |||
202 | /** | ||
203 | * vb2_common_vm_close() - decrease refcount of the vma | ||
204 | * @vma: virtual memory region for the mapping | ||
205 | * | ||
206 | * This function releases the user from the provided vma. It expects | ||
207 | * struct vb2_vmarea_handler pointer in vma->vm_private_data. | ||
208 | */ | ||
209 | static void vb2_common_vm_close(struct vm_area_struct *vma) | ||
210 | { | ||
211 | struct vb2_vmarea_handler *h = vma->vm_private_data; | ||
212 | |||
213 | printk(KERN_DEBUG "%s: %p, refcount: %d, vma: %08lx-%08lx\n", | ||
214 | __func__, h, atomic_read(h->refcount), vma->vm_start, | ||
215 | vma->vm_end); | ||
216 | |||
217 | h->put(h->arg); | ||
218 | } | ||
219 | |||
220 | /** | ||
221 | * vb2_common_vm_ops - common vm_ops used for tracking refcount of mmaped | ||
222 | * video buffers | ||
223 | */ | ||
224 | const struct vm_operations_struct vb2_common_vm_ops = { | ||
225 | .open = vb2_common_vm_open, | ||
226 | .close = vb2_common_vm_close, | ||
227 | }; | ||
228 | EXPORT_SYMBOL_GPL(vb2_common_vm_ops); | ||
229 | |||
230 | MODULE_DESCRIPTION("common memory handling routines for videobuf2"); | ||
231 | MODULE_AUTHOR("Pawel Osciak"); | ||
232 | MODULE_LICENSE("GPL"); | ||
diff --git a/include/media/videobuf2-memops.h b/include/media/videobuf2-memops.h new file mode 100644 index 000000000000..fee17033a728 --- /dev/null +++ b/include/media/videobuf2-memops.h | |||
@@ -0,0 +1,45 @@ | |||
1 | /* | ||
2 | * videobuf2-memops.h - generic memory handling routines for videobuf2 | ||
3 | * | ||
4 | * Copyright (C) 2010 Samsung Electronics | ||
5 | * | ||
6 | * Author: Pawel Osciak <p.osciak@samsung.com> | ||
7 | * Marek Szyprowski <m.szyprowski@samsung.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #ifndef _MEDIA_VIDEOBUF2_MEMOPS_H | ||
15 | #define _MEDIA_VIDEOBUF2_MEMOPS_H | ||
16 | |||
17 | #include <media/videobuf2-core.h> | ||
18 | |||
19 | /** | ||
20 | * vb2_vmarea_handler - common vma refcount tracking handler | ||
21 | * @refcount: pointer to refcount entry in the buffer | ||
22 | * @put: callback to function that decreases buffer refcount | ||
23 | * @arg: argument for @put callback | ||
24 | */ | ||
25 | struct vb2_vmarea_handler { | ||
26 | atomic_t *refcount; | ||
27 | void (*put)(void *arg); | ||
28 | void *arg; | ||
29 | }; | ||
30 | |||
31 | extern const struct vm_operations_struct vb2_common_vm_ops; | ||
32 | |||
33 | int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, | ||
34 | struct vm_area_struct **res_vma, dma_addr_t *res_pa); | ||
35 | |||
36 | int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr, | ||
37 | unsigned long size, | ||
38 | const struct vm_operations_struct *vm_ops, | ||
39 | void *priv); | ||
40 | |||
41 | struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma); | ||
42 | void vb2_put_vma(struct vm_area_struct *vma); | ||
43 | |||
44 | |||
45 | #endif | ||