Blame view
drivers/media/video/videobuf-vmalloc.c
8.33 KB
87b9ad070 V4L/DVB (6254): A... |
1 2 3 |
/* * helper functions for vmalloc video4linux capture buffers * |
5d6aaf50e V4L/DVB (8340): v... |
4 |
* The functions expect the hardware being able to scatter gather |
87b9ad070 V4L/DVB (6254): A... |
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
* (i.e. the buffers are not linear in physical memory, but fragmented * into PAGE_SIZE chunks). They also assume the driver does not need * to touch the video data. * * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 */ #include <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/pci.h> #include <linux/vmalloc.h> #include <linux/pagemap.h> #include <asm/page.h> #include <asm/pgtable.h> #include <media/videobuf-vmalloc.h> #define MAGIC_DMABUF 0x17760309 #define MAGIC_VMAL_MEM 0x18221223 |
7a02264ca V4L/DVB: v4l: vid... |
32 33 34 35 36 37 38 |
#define MAGIC_CHECK(is, should) \ if (unlikely((is) != (should))) { \ printk(KERN_ERR "magic mismatch: %x (expected %x) ", \ is, should); \ BUG(); \ } |
87b9ad070 V4L/DVB (6254): A... |
39 |
|
ff699e6bd V4L/DVB (7094): ... |
40 |
static int debug; |
87b9ad070 V4L/DVB (6254): A... |
41 42 43 44 45 |
module_param(debug, int, 0644); MODULE_DESCRIPTION("helper module to manage video4linux vmalloc buffers"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); MODULE_LICENSE("GPL"); |
7a02264ca V4L/DVB: v4l: vid... |
46 47 48 |
#define dprintk(level, fmt, arg...) \ if (debug >= level) \ printk(KERN_DEBUG "vbuf-vmalloc: " fmt , ## arg) |
87b9ad070 V4L/DVB (6254): A... |
49 50 51 |
/***************************************************************************/ |
7a02264ca V4L/DVB: v4l: vid... |
52 |
static void videobuf_vm_open(struct vm_area_struct *vma) |
87b9ad070 V4L/DVB (6254): A... |
53 54 |
{ struct videobuf_mapping *map = vma->vm_private_data; |
7a02264ca V4L/DVB: v4l: vid... |
55 56 57 |
dprintk(2, "vm_open %p [count=%u,vma=%08lx-%08lx] ", map, map->count, vma->vm_start, vma->vm_end); |
87b9ad070 V4L/DVB (6254): A... |
58 59 60 |
map->count++; } |
aaea56afc V4L/DVB (7553): v... |
61 |
static void videobuf_vm_close(struct vm_area_struct *vma) |
87b9ad070 V4L/DVB (6254): A... |
62 63 64 |
{ struct videobuf_mapping *map = vma->vm_private_data; struct videobuf_queue *q = map->q; |
87b9ad070 V4L/DVB (6254): A... |
65 |
int i; |
7a02264ca V4L/DVB: v4l: vid... |
66 67 |
dprintk(2, "vm_close %p [count=%u,vma=%08lx-%08lx] ", map, |
aaea56afc V4L/DVB (7553): v... |
68 |
map->count, vma->vm_start, vma->vm_end); |
87b9ad070 V4L/DVB (6254): A... |
69 70 71 |
map->count--; if (0 == map->count) { |
aaea56afc V4L/DVB (7553): v... |
72 73 74 75 |
struct videobuf_vmalloc_memory *mem; dprintk(1, "munmap %p q=%p ", map, q); |
973976878 V4L/DVB: videobuf... |
76 |
videobuf_queue_lock(q); |
aa9479ed5 V4L/DVB (7561): v... |
77 78 79 80 |
/* We need first to cancel streams, before unmapping */ if (q->streaming) videobuf_queue_cancel(q); |
87b9ad070 V4L/DVB (6254): A... |
81 82 83 |
for (i = 0; i < VIDEO_MAX_FRAME; i++) { if (NULL == q->bufs[i]) continue; |
87b9ad070 V4L/DVB (6254): A... |
84 |
|
851c0c96b V4L/DVB (6266): v... |
85 |
if (q->bufs[i]->map != map) |
87b9ad070 V4L/DVB (6254): A... |
86 |
continue; |
123f8ef64 V4L/DVB (6263): F... |
87 |
|
aaea56afc V4L/DVB (7553): v... |
88 89 90 91 92 93 94 |
mem = q->bufs[i]->priv; if (mem) { /* This callback is called only if kernel has allocated memory and this memory is mmapped. In this case, memory should be freed, in order to do memory unmap. */ |
aa9479ed5 V4L/DVB (7561): v... |
95 |
|
aaea56afc V4L/DVB (7553): v... |
96 |
MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); |
aa9479ed5 V4L/DVB (7561): v... |
97 98 99 100 101 102 |
/* vfree is not atomic - can't be called with IRQ's disabled */ dprintk(1, "%s: buf[%d] freeing (%p) ", |
bb6dbe748 V4L/DVB: videobuf... |
103 |
__func__, i, mem->vaddr); |
aa9479ed5 V4L/DVB (7561): v... |
104 |
|
bb6dbe748 V4L/DVB: videobuf... |
105 106 |
vfree(mem->vaddr); mem->vaddr = NULL; |
aaea56afc V4L/DVB (7553): v... |
107 |
} |
851c0c96b V4L/DVB (6266): v... |
108 |
q->bufs[i]->map = NULL; |
87b9ad070 V4L/DVB (6254): A... |
109 |
q->bufs[i]->baddr = 0; |
87b9ad070 V4L/DVB (6254): A... |
110 |
} |
aa9479ed5 V4L/DVB (7561): v... |
111 |
|
87b9ad070 V4L/DVB (6254): A... |
112 |
kfree(map); |
aa9479ed5 V4L/DVB (7561): v... |
113 |
|
973976878 V4L/DVB: videobuf... |
114 |
videobuf_queue_unlock(q); |
87b9ad070 V4L/DVB (6254): A... |
115 |
} |
aa9479ed5 V4L/DVB (7561): v... |
116 |
|
87b9ad070 V4L/DVB (6254): A... |
117 118 |
return; } |
7a02264ca V4L/DVB: v4l: vid... |
119 |
static const struct vm_operations_struct videobuf_vm_ops = { |
87b9ad070 V4L/DVB (6254): A... |
120 121 122 123 124 125 126 127 128 129 130 |
.open = videobuf_vm_open, .close = videobuf_vm_close, }; /* --------------------------------------------------------------------- * vmalloc handlers for the generic methods */ /* Allocated area consists on 3 parts: struct video_buffer struct <driver>_buffer (cx88_buffer, saa7134_buf, ...) |
0705135e5 V4L/DVB (7237): C... |
131 |
struct videobuf_dma_sg_memory |
87b9ad070 V4L/DVB (6254): A... |
132 |
*/ |
33c38283f V4L/DVB: videobuf... |
133 |
static struct videobuf_buffer *__videobuf_alloc_vb(size_t size) |
87b9ad070 V4L/DVB (6254): A... |
134 |
{ |
384b835ae V4L/DVB (7150): [... |
135 |
struct videobuf_vmalloc_memory *mem; |
87b9ad070 V4L/DVB (6254): A... |
136 |
struct videobuf_buffer *vb; |
7a02264ca V4L/DVB: v4l: vid... |
137 |
vb = kzalloc(size + sizeof(*mem), GFP_KERNEL); |
bee527f97 V4L/DVB: videobuf... |
138 139 |
if (!vb) return vb; |
87b9ad070 V4L/DVB (6254): A... |
140 |
|
7a02264ca V4L/DVB: v4l: vid... |
141 142 |
mem = vb->priv = ((char *)vb) + size; mem->magic = MAGIC_VMAL_MEM; |
87b9ad070 V4L/DVB (6254): A... |
143 |
|
7a02264ca V4L/DVB: v4l: vid... |
144 145 146 147 |
dprintk(1, "%s: allocated at %p(%ld+%ld) & %p(%ld) ", __func__, vb, (long)sizeof(*vb), (long)size - sizeof(*vb), mem, (long)sizeof(*mem)); |
87b9ad070 V4L/DVB (6254): A... |
148 149 150 |
return vb; } |
7a02264ca V4L/DVB: v4l: vid... |
151 152 153 |
static int __videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb, struct v4l2_framebuffer *fbuf) |
87b9ad070 V4L/DVB (6254): A... |
154 |
{ |
968ced78a V4L/DVB (7552): v... |
155 |
struct videobuf_vmalloc_memory *mem = vb->priv; |
aa9479ed5 V4L/DVB (7561): v... |
156 |
int pages; |
87b9ad070 V4L/DVB (6254): A... |
157 158 |
BUG_ON(!mem); |
968ced78a V4L/DVB (7552): v... |
159 |
MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); |
87b9ad070 V4L/DVB (6254): A... |
160 |
|
968ced78a V4L/DVB (7552): v... |
161 162 163 164 |
switch (vb->memory) { case V4L2_MEMORY_MMAP: dprintk(1, "%s memory method MMAP ", __func__); |
87b9ad070 V4L/DVB (6254): A... |
165 |
|
968ced78a V4L/DVB (7552): v... |
166 |
/* All handling should be done by __videobuf_mmap_mapper() */ |
bb6dbe748 V4L/DVB: videobuf... |
167 |
if (!mem->vaddr) { |
968ced78a V4L/DVB (7552): v... |
168 169 170 171 172 173 |
printk(KERN_ERR "memory is not alloced/mmapped. "); return -EINVAL; } break; case V4L2_MEMORY_USERPTR: |
aa9479ed5 V4L/DVB (7561): v... |
174 |
pages = PAGE_ALIGN(vb->size); |
87b9ad070 V4L/DVB (6254): A... |
175 |
|
968ced78a V4L/DVB (7552): v... |
176 177 |
dprintk(1, "%s memory method USERPTR ", __func__); |
968ced78a V4L/DVB (7552): v... |
178 179 180 181 182 |
if (vb->baddr) { printk(KERN_ERR "USERPTR is currently not supported "); return -EINVAL; } |
87b9ad070 V4L/DVB (6254): A... |
183 |
|
968ced78a V4L/DVB (7552): v... |
184 |
/* The only USERPTR currently supported is the one needed for |
7a02264ca V4L/DVB: v4l: vid... |
185 |
* read() method. |
968ced78a V4L/DVB (7552): v... |
186 |
*/ |
bb6dbe748 V4L/DVB: videobuf... |
187 188 |
mem->vaddr = vmalloc_user(pages); if (!mem->vaddr) { |
968ced78a V4L/DVB (7552): v... |
189 190 191 |
printk(KERN_ERR "vmalloc (%d pages) failed ", pages); return -ENOMEM; |
87b9ad070 V4L/DVB (6254): A... |
192 |
} |
968ced78a V4L/DVB (7552): v... |
193 194 |
dprintk(1, "vmalloc is at addr %p (%d pages) ", |
bb6dbe748 V4L/DVB: videobuf... |
195 |
mem->vaddr, pages); |
968ced78a V4L/DVB (7552): v... |
196 197 198 199 200 201 202 203 204 205 |
#if 0 int rc; /* Kernel userptr is used also by read() method. In this case, there's no need to remap, since data will be copied to user */ if (!vb->baddr) return 0; /* FIXME: to properly support USERPTR, remap should occur. |
de1e575db V4L/DVB (8525): f... |
206 |
The code below won't work, since mem->vma = NULL |
968ced78a V4L/DVB (7552): v... |
207 208 209 210 |
*/ /* Try to remap memory */ rc = remap_vmalloc_range(mem->vma, (void *)vb->baddr, 0); if (rc < 0) { |
7a02264ca V4L/DVB: v4l: vid... |
211 |
printk(KERN_ERR "mmap: remap failed with error %d", rc); |
968ced78a V4L/DVB (7552): v... |
212 213 214 215 216 |
return -ENOMEM; } #endif break; |
968ced78a V4L/DVB (7552): v... |
217 218 219 220 221 222 223 224 225 |
case V4L2_MEMORY_OVERLAY: default: dprintk(1, "%s memory method OVERLAY/unknown ", __func__); /* Currently, doesn't support V4L2_MEMORY_OVERLAY */ printk(KERN_ERR "Memory method currently unsupported. "); return -EINVAL; |
87b9ad070 V4L/DVB (6254): A... |
226 227 228 229 |
} return 0; } |
87b9ad070 V4L/DVB (6254): A... |
230 |
static int __videobuf_mmap_mapper(struct videobuf_queue *q, |
0b62b7377 V4L/DVB: v4l vide... |
231 232 |
struct videobuf_buffer *buf, struct vm_area_struct *vma) |
87b9ad070 V4L/DVB (6254): A... |
233 |
{ |
384b835ae V4L/DVB (7150): [... |
234 |
struct videobuf_vmalloc_memory *mem; |
87b9ad070 V4L/DVB (6254): A... |
235 |
struct videobuf_mapping *map; |
968ced78a V4L/DVB (7552): v... |
236 |
int retval, pages; |
87b9ad070 V4L/DVB (6254): A... |
237 |
|
968ced78a V4L/DVB (7552): v... |
238 239 |
dprintk(1, "%s ", __func__); |
87b9ad070 V4L/DVB (6254): A... |
240 241 |
/* create mapping + update buffer list */ |
968ced78a V4L/DVB (7552): v... |
242 |
map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL); |
87b9ad070 V4L/DVB (6254): A... |
243 244 |
if (NULL == map) return -ENOMEM; |
0b62b7377 V4L/DVB: v4l vide... |
245 |
buf->map = map; |
87b9ad070 V4L/DVB (6254): A... |
246 |
map->q = q; |
0b62b7377 V4L/DVB: v4l vide... |
247 |
buf->baddr = vma->vm_start; |
87b9ad070 V4L/DVB (6254): A... |
248 |
|
0b62b7377 V4L/DVB: v4l vide... |
249 |
mem = buf->priv; |
968ced78a V4L/DVB (7552): v... |
250 251 |
BUG_ON(!mem); MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); |
87b9ad070 V4L/DVB (6254): A... |
252 |
|
968ced78a V4L/DVB (7552): v... |
253 |
pages = PAGE_ALIGN(vma->vm_end - vma->vm_start); |
bb6dbe748 V4L/DVB: videobuf... |
254 255 |
mem->vaddr = vmalloc_user(pages); if (!mem->vaddr) { |
968ced78a V4L/DVB (7552): v... |
256 257 258 259 |
printk(KERN_ERR "vmalloc (%d pages) failed ", pages); goto error; } |
bb6dbe748 V4L/DVB: videobuf... |
260 261 |
dprintk(1, "vmalloc is at addr %p (%d pages) ", mem->vaddr, pages); |
851c0c96b V4L/DVB (6266): v... |
262 |
|
87b9ad070 V4L/DVB (6254): A... |
263 |
/* Try to remap memory */ |
bb6dbe748 V4L/DVB: videobuf... |
264 |
retval = remap_vmalloc_range(vma, mem->vaddr, 0); |
968ced78a V4L/DVB (7552): v... |
265 266 |
if (retval < 0) { printk(KERN_ERR "mmap: remap failed with error %d. ", retval); |
bb6dbe748 V4L/DVB: videobuf... |
267 |
vfree(mem->vaddr); |
968ced78a V4L/DVB (7552): v... |
268 |
goto error; |
87b9ad070 V4L/DVB (6254): A... |
269 |
} |
968ced78a V4L/DVB (7552): v... |
270 271 272 |
vma->vm_ops = &videobuf_vm_ops; vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED; vma->vm_private_data = map; |
7a02264ca V4L/DVB: v4l: vid... |
273 274 |
dprintk(1, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d ", |
968ced78a V4L/DVB (7552): v... |
275 |
map, q, vma->vm_start, vma->vm_end, |
0b62b7377 V4L/DVB: v4l vide... |
276 277 |
(long int)buf->bsize, vma->vm_pgoff, buf->i); |
87b9ad070 V4L/DVB (6254): A... |
278 279 |
videobuf_vm_open(vma); |
968ced78a V4L/DVB (7552): v... |
280 281 282 283 284 285 |
return 0; error: mem = NULL; kfree(map); return -ENOMEM; |
87b9ad070 V4L/DVB (6254): A... |
286 |
} |
87b9ad070 V4L/DVB (6254): A... |
287 288 |
static struct videobuf_qtype_ops qops = { .magic = MAGIC_QTYPE_OPS, |
33c38283f V4L/DVB: videobuf... |
289 |
.alloc_vb = __videobuf_alloc_vb, |
87b9ad070 V4L/DVB (6254): A... |
290 |
.iolock = __videobuf_iolock, |
87b9ad070 V4L/DVB (6254): A... |
291 |
.mmap_mapper = __videobuf_mmap_mapper, |
037c75eb1 V4L/DVB: v4l vide... |
292 |
.vaddr = videobuf_to_vmalloc, |
87b9ad070 V4L/DVB (6254): A... |
293 |
}; |
7a02264ca V4L/DVB: v4l: vid... |
294 |
void videobuf_queue_vmalloc_init(struct videobuf_queue *q, |
38a54f35a V4L/DVB (13377): ... |
295 |
const struct videobuf_queue_ops *ops, |
f8b0bca1a V4L/DVB (13417): ... |
296 |
struct device *dev, |
87b9ad070 V4L/DVB (6254): A... |
297 298 299 300 |
spinlock_t *irqlock, enum v4l2_buf_type type, enum v4l2_field field, unsigned int msize, |
08bff03ed V4L/DVB: videobuf... |
301 302 |
void *priv, struct mutex *ext_lock) |
87b9ad070 V4L/DVB (6254): A... |
303 |
{ |
d4cae5a50 V4L/DVB (6292): v... |
304 |
videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize, |
08bff03ed V4L/DVB: videobuf... |
305 |
priv, &qops, ext_lock); |
87b9ad070 V4L/DVB (6254): A... |
306 |
} |
87b9ad070 V4L/DVB (6254): A... |
307 |
EXPORT_SYMBOL_GPL(videobuf_queue_vmalloc_init); |
7a02264ca V4L/DVB: v4l: vid... |
308 |
void *videobuf_to_vmalloc(struct videobuf_buffer *buf) |
87b9ad070 V4L/DVB (6254): A... |
309 |
{ |
7a02264ca V4L/DVB: v4l: vid... |
310 311 312 |
struct videobuf_vmalloc_memory *mem = buf->priv; BUG_ON(!mem); MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); |
87b9ad070 V4L/DVB (6254): A... |
313 |
|
bb6dbe748 V4L/DVB: videobuf... |
314 |
return mem->vaddr; |
87b9ad070 V4L/DVB (6254): A... |
315 316 |
} EXPORT_SYMBOL_GPL(videobuf_to_vmalloc); |
7a02264ca V4L/DVB: v4l: vid... |
317 |
void videobuf_vmalloc_free(struct videobuf_buffer *buf) |
87b9ad070 V4L/DVB (6254): A... |
318 |
{ |
968ced78a V4L/DVB (7552): v... |
319 |
struct videobuf_vmalloc_memory *mem = buf->priv; |
87b9ad070 V4L/DVB (6254): A... |
320 |
|
aaea56afc V4L/DVB (7553): v... |
321 322 323 324 325 326 |
/* mmapped memory can't be freed here, otherwise mmapped region would be released, while still needed. In this case, the memory release should happen inside videobuf_vm_close(). So, it should free memory only if the memory were allocated for read() operation. */ |
5993a663a V4L/DVB (10305): ... |
327 |
if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr) |
aaea56afc V4L/DVB (7553): v... |
328 |
return; |
968ced78a V4L/DVB (7552): v... |
329 330 331 332 |
if (!mem) return; MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); |
87b9ad070 V4L/DVB (6254): A... |
333 |
|
bb6dbe748 V4L/DVB: videobuf... |
334 335 |
vfree(mem->vaddr); mem->vaddr = NULL; |
968ced78a V4L/DVB (7552): v... |
336 |
|
87b9ad070 V4L/DVB (6254): A... |
337 338 339 |
return; } EXPORT_SYMBOL_GPL(videobuf_vmalloc_free); |