diff options
Diffstat (limited to 'drivers/media/video/videobuf-vmalloc.c')
-rw-r--r-- | drivers/media/video/videobuf-vmalloc.c | 211 |
1 files changed, 145 insertions, 66 deletions
diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c index 5266ecc91dab..c91e1d8e3802 100644 --- a/drivers/media/video/videobuf-vmalloc.c +++ b/drivers/media/video/videobuf-vmalloc.c | |||
@@ -33,7 +33,7 @@ | |||
33 | #define MAGIC_CHECK(is,should) if (unlikely((is) != (should))) \ | 33 | #define MAGIC_CHECK(is,should) if (unlikely((is) != (should))) \ |
34 | { printk(KERN_ERR "magic mismatch: %x (expected %x)\n",is,should); BUG(); } | 34 | { printk(KERN_ERR "magic mismatch: %x (expected %x)\n",is,should); BUG(); } |
35 | 35 | ||
36 | static int debug = 0; | 36 | static int debug; |
37 | module_param(debug, int, 0644); | 37 | module_param(debug, int, 0644); |
38 | 38 | ||
39 | MODULE_DESCRIPTION("helper module to manage video4linux vmalloc buffers"); | 39 | MODULE_DESCRIPTION("helper module to manage video4linux vmalloc buffers"); |
@@ -57,20 +57,26 @@ videobuf_vm_open(struct vm_area_struct *vma) | |||
57 | map->count++; | 57 | map->count++; |
58 | } | 58 | } |
59 | 59 | ||
60 | static void | 60 | static void videobuf_vm_close(struct vm_area_struct *vma) |
61 | videobuf_vm_close(struct vm_area_struct *vma) | ||
62 | { | 61 | { |
63 | struct videobuf_mapping *map = vma->vm_private_data; | 62 | struct videobuf_mapping *map = vma->vm_private_data; |
64 | struct videobuf_queue *q = map->q; | 63 | struct videobuf_queue *q = map->q; |
65 | int i; | 64 | int i; |
66 | 65 | ||
67 | dprintk(2,"vm_close %p [count=%u,vma=%08lx-%08lx]\n",map, | 66 | dprintk(2,"vm_close %p [count=%u,vma=%08lx-%08lx]\n", map, |
68 | map->count,vma->vm_start,vma->vm_end); | 67 | map->count, vma->vm_start, vma->vm_end); |
69 | 68 | ||
70 | map->count--; | 69 | map->count--; |
71 | if (0 == map->count) { | 70 | if (0 == map->count) { |
72 | dprintk(1,"munmap %p q=%p\n",map,q); | 71 | struct videobuf_vmalloc_memory *mem; |
72 | |||
73 | dprintk(1, "munmap %p q=%p\n", map, q); | ||
73 | mutex_lock(&q->vb_lock); | 74 | mutex_lock(&q->vb_lock); |
75 | |||
76 | /* We need first to cancel streams, before unmapping */ | ||
77 | if (q->streaming) | ||
78 | videobuf_queue_cancel(q); | ||
79 | |||
74 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { | 80 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { |
75 | if (NULL == q->bufs[i]) | 81 | if (NULL == q->bufs[i]) |
76 | continue; | 82 | continue; |
@@ -78,14 +84,35 @@ videobuf_vm_close(struct vm_area_struct *vma) | |||
78 | if (q->bufs[i]->map != map) | 84 | if (q->bufs[i]->map != map) |
79 | continue; | 85 | continue; |
80 | 86 | ||
81 | q->ops->buf_release(q,q->bufs[i]); | 87 | mem = q->bufs[i]->priv; |
88 | if (mem) { | ||
89 | /* This callback is called only if kernel has | ||
90 | allocated memory and this memory is mmapped. | ||
91 | In this case, memory should be freed, | ||
92 | in order to do memory unmap. | ||
93 | */ | ||
94 | |||
95 | MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); | ||
96 | |||
97 | /* vfree is not atomic - can't be | ||
98 | called with IRQ's disabled | ||
99 | */ | ||
100 | dprintk(1, "%s: buf[%d] freeing (%p)\n", | ||
101 | __func__, i, mem->vmalloc); | ||
102 | |||
103 | vfree(mem->vmalloc); | ||
104 | mem->vmalloc = NULL; | ||
105 | } | ||
82 | 106 | ||
83 | q->bufs[i]->map = NULL; | 107 | q->bufs[i]->map = NULL; |
84 | q->bufs[i]->baddr = 0; | 108 | q->bufs[i]->baddr = 0; |
85 | } | 109 | } |
86 | mutex_unlock(&q->vb_lock); | 110 | |
87 | kfree(map); | 111 | kfree(map); |
112 | |||
113 | mutex_unlock(&q->vb_lock); | ||
88 | } | 114 | } |
115 | |||
89 | return; | 116 | return; |
90 | } | 117 | } |
91 | 118 | ||
@@ -102,7 +129,7 @@ static struct vm_operations_struct videobuf_vm_ops = | |||
102 | /* Allocated area consists on 3 parts: | 129 | /* Allocated area consists on 3 parts: |
103 | struct video_buffer | 130 | struct video_buffer |
104 | struct <driver>_buffer (cx88_buffer, saa7134_buf, ...) | 131 | struct <driver>_buffer (cx88_buffer, saa7134_buf, ...) |
105 | struct videobuf_pci_sg_memory | 132 | struct videobuf_dma_sg_memory |
106 | */ | 133 | */ |
107 | 134 | ||
108 | static void *__videobuf_alloc(size_t size) | 135 | static void *__videobuf_alloc(size_t size) |
@@ -116,7 +143,7 @@ static void *__videobuf_alloc(size_t size) | |||
116 | mem->magic=MAGIC_VMAL_MEM; | 143 | mem->magic=MAGIC_VMAL_MEM; |
117 | 144 | ||
118 | dprintk(1,"%s: allocated at %p(%ld+%ld) & %p(%ld)\n", | 145 | dprintk(1,"%s: allocated at %p(%ld+%ld) & %p(%ld)\n", |
119 | __FUNCTION__,vb,(long)sizeof(*vb),(long)size-sizeof(*vb), | 146 | __func__,vb,(long)sizeof(*vb),(long)size-sizeof(*vb), |
120 | mem,(long)sizeof(*mem)); | 147 | mem,(long)sizeof(*mem)); |
121 | 148 | ||
122 | return vb; | 149 | return vb; |
@@ -126,45 +153,74 @@ static int __videobuf_iolock (struct videobuf_queue* q, | |||
126 | struct videobuf_buffer *vb, | 153 | struct videobuf_buffer *vb, |
127 | struct v4l2_framebuffer *fbuf) | 154 | struct v4l2_framebuffer *fbuf) |
128 | { | 155 | { |
156 | struct videobuf_vmalloc_memory *mem = vb->priv; | ||
129 | int pages; | 157 | int pages; |
130 | struct videobuf_vmalloc_memory *mem=vb->priv; | ||
131 | 158 | ||
132 | BUG_ON(!mem); | 159 | BUG_ON(!mem); |
133 | 160 | ||
134 | MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM); | 161 | MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); |
135 | 162 | ||
136 | pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT; | 163 | switch (vb->memory) { |
164 | case V4L2_MEMORY_MMAP: | ||
165 | dprintk(1, "%s memory method MMAP\n", __func__); | ||
137 | 166 | ||
138 | /* Currently, doesn't support V4L2_MEMORY_OVERLAY */ | 167 | /* All handling should be done by __videobuf_mmap_mapper() */ |
139 | if ((vb->memory != V4L2_MEMORY_MMAP) && | 168 | if (!mem->vmalloc) { |
140 | (vb->memory != V4L2_MEMORY_USERPTR) ) { | 169 | printk(KERN_ERR "memory is not alloced/mmapped.\n"); |
141 | printk(KERN_ERR "Method currently unsupported.\n"); | 170 | return -EINVAL; |
142 | return -EINVAL; | 171 | } |
143 | } | 172 | break; |
173 | case V4L2_MEMORY_USERPTR: | ||
174 | pages = PAGE_ALIGN(vb->size); | ||
144 | 175 | ||
145 | /* FIXME: should be tested with kernel mmap mem */ | 176 | dprintk(1, "%s memory method USERPTR\n", __func__); |
146 | mem->vmalloc=vmalloc_user (PAGE_ALIGN(vb->size)); | ||
147 | if (NULL == mem->vmalloc) { | ||
148 | printk(KERN_ERR "vmalloc (%d pages) failed\n",pages); | ||
149 | return -ENOMEM; | ||
150 | } | ||
151 | 177 | ||
152 | dprintk(1,"vmalloc is at addr 0x%08lx, size=%d\n", | 178 | #if 1 |
153 | (unsigned long)mem->vmalloc, | 179 | if (vb->baddr) { |
154 | pages << PAGE_SHIFT); | 180 | printk(KERN_ERR "USERPTR is currently not supported\n"); |
181 | return -EINVAL; | ||
182 | } | ||
183 | #endif | ||
155 | 184 | ||
156 | /* It seems that some kernel versions need to do remap *after* | 185 | /* The only USERPTR currently supported is the one needed for |
157 | the mmap() call | 186 | read() method. |
158 | */ | 187 | */ |
159 | if (mem->vma) { | 188 | |
160 | int retval=remap_vmalloc_range(mem->vma, mem->vmalloc,0); | 189 | mem->vmalloc = vmalloc_user(pages); |
161 | kfree(mem->vma); | 190 | if (!mem->vmalloc) { |
162 | mem->vma=NULL; | 191 | printk(KERN_ERR "vmalloc (%d pages) failed\n", pages); |
163 | if (retval<0) { | 192 | return -ENOMEM; |
164 | dprintk(1,"mmap app bug: remap_vmalloc_range area %p error %d\n", | 193 | } |
165 | mem->vmalloc,retval); | 194 | dprintk(1, "vmalloc is at addr %p (%d pages)\n", |
166 | return retval; | 195 | mem->vmalloc, pages); |
196 | |||
197 | #if 0 | ||
198 | int rc; | ||
199 | /* Kernel userptr is used also by read() method. In this case, | ||
200 | there's no need to remap, since data will be copied to user | ||
201 | */ | ||
202 | if (!vb->baddr) | ||
203 | return 0; | ||
204 | |||
205 | /* FIXME: to properly support USERPTR, remap should occur. | ||
206 | The code bellow won't work, since mem->vma = NULL | ||
207 | */ | ||
208 | /* Try to remap memory */ | ||
209 | rc = remap_vmalloc_range(mem->vma, (void *)vb->baddr, 0); | ||
210 | if (rc < 0) { | ||
211 | printk(KERN_ERR "mmap: remap failed with error %d. ", rc); | ||
212 | return -ENOMEM; | ||
167 | } | 213 | } |
214 | #endif | ||
215 | |||
216 | break; | ||
217 | case V4L2_MEMORY_OVERLAY: | ||
218 | default: | ||
219 | dprintk(1, "%s memory method OVERLAY/unknown\n", __func__); | ||
220 | |||
221 | /* Currently, doesn't support V4L2_MEMORY_OVERLAY */ | ||
222 | printk(KERN_ERR "Memory method currently unsupported.\n"); | ||
223 | return -EINVAL; | ||
168 | } | 224 | } |
169 | 225 | ||
170 | return 0; | 226 | return 0; |
@@ -180,6 +236,7 @@ static int __videobuf_mmap_free(struct videobuf_queue *q) | |||
180 | { | 236 | { |
181 | unsigned int i; | 237 | unsigned int i; |
182 | 238 | ||
239 | dprintk(1, "%s\n", __func__); | ||
183 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { | 240 | for (i = 0; i < VIDEO_MAX_FRAME; i++) { |
184 | if (q->bufs[i]) { | 241 | if (q->bufs[i]) { |
185 | if (q->bufs[i]->map) | 242 | if (q->bufs[i]->map) |
@@ -196,10 +253,11 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, | |||
196 | struct videobuf_vmalloc_memory *mem; | 253 | struct videobuf_vmalloc_memory *mem; |
197 | struct videobuf_mapping *map; | 254 | struct videobuf_mapping *map; |
198 | unsigned int first; | 255 | unsigned int first; |
199 | int retval; | 256 | int retval, pages; |
200 | unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; | 257 | unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; |
201 | 258 | ||
202 | if (! (vma->vm_flags & VM_WRITE) || ! (vma->vm_flags & VM_SHARED)) | 259 | dprintk(1, "%s\n", __func__); |
260 | if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) | ||
203 | return -EINVAL; | 261 | return -EINVAL; |
204 | 262 | ||
205 | /* look for first buffer to map */ | 263 | /* look for first buffer to map */ |
@@ -219,46 +277,55 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, | |||
219 | } | 277 | } |
220 | 278 | ||
221 | /* create mapping + update buffer list */ | 279 | /* create mapping + update buffer list */ |
222 | map = q->bufs[first]->map = kzalloc(sizeof(struct videobuf_mapping),GFP_KERNEL); | 280 | map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL); |
223 | if (NULL == map) | 281 | if (NULL == map) |
224 | return -ENOMEM; | 282 | return -ENOMEM; |
225 | 283 | ||
284 | q->bufs[first]->map = map; | ||
226 | map->start = vma->vm_start; | 285 | map->start = vma->vm_start; |
227 | map->end = vma->vm_end; | 286 | map->end = vma->vm_end; |
228 | map->q = q; | 287 | map->q = q; |
229 | 288 | ||
230 | q->bufs[first]->baddr = vma->vm_start; | 289 | q->bufs[first]->baddr = vma->vm_start; |
231 | 290 | ||
232 | vma->vm_ops = &videobuf_vm_ops; | 291 | mem = q->bufs[first]->priv; |
233 | vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED; | 292 | BUG_ON(!mem); |
234 | vma->vm_private_data = map; | 293 | MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); |
235 | 294 | ||
236 | mem=q->bufs[first]->priv; | 295 | pages = PAGE_ALIGN(vma->vm_end - vma->vm_start); |
237 | BUG_ON (!mem); | 296 | mem->vmalloc = vmalloc_user(pages); |
238 | MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM); | 297 | if (!mem->vmalloc) { |
298 | printk(KERN_ERR "vmalloc (%d pages) failed\n", pages); | ||
299 | goto error; | ||
300 | } | ||
301 | dprintk(1, "vmalloc is at addr %p (%d pages)\n", | ||
302 | mem->vmalloc, pages); | ||
239 | 303 | ||
240 | /* Try to remap memory */ | 304 | /* Try to remap memory */ |
241 | retval=remap_vmalloc_range(vma, mem->vmalloc,0); | 305 | retval = remap_vmalloc_range(vma, mem->vmalloc, 0); |
242 | if (retval<0) { | 306 | if (retval < 0) { |
243 | dprintk(1,"mmap: postponing remap_vmalloc_range\n"); | 307 | printk(KERN_ERR "mmap: remap failed with error %d. ", retval); |
244 | 308 | vfree(mem->vmalloc); | |
245 | mem->vma=kmalloc(sizeof(*vma),GFP_KERNEL); | 309 | goto error; |
246 | if (!mem->vma) { | ||
247 | kfree(map); | ||
248 | q->bufs[first]->map=NULL; | ||
249 | return -ENOMEM; | ||
250 | } | ||
251 | memcpy(mem->vma,vma,sizeof(*vma)); | ||
252 | } | 310 | } |
253 | 311 | ||
312 | vma->vm_ops = &videobuf_vm_ops; | ||
313 | vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED; | ||
314 | vma->vm_private_data = map; | ||
315 | |||
254 | dprintk(1,"mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n", | 316 | dprintk(1,"mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n", |
255 | map,q,vma->vm_start,vma->vm_end, | 317 | map, q, vma->vm_start, vma->vm_end, |
256 | (long int) q->bufs[first]->bsize, | 318 | (long int) q->bufs[first]->bsize, |
257 | vma->vm_pgoff,first); | 319 | vma->vm_pgoff, first); |
258 | 320 | ||
259 | videobuf_vm_open(vma); | 321 | videobuf_vm_open(vma); |
260 | 322 | ||
261 | return (0); | 323 | return 0; |
324 | |||
325 | error: | ||
326 | mem = NULL; | ||
327 | kfree(map); | ||
328 | return -ENOMEM; | ||
262 | } | 329 | } |
263 | 330 | ||
264 | static int __videobuf_copy_to_user ( struct videobuf_queue *q, | 331 | static int __videobuf_copy_to_user ( struct videobuf_queue *q, |
@@ -320,6 +387,7 @@ static struct videobuf_qtype_ops qops = { | |||
320 | .mmap_mapper = __videobuf_mmap_mapper, | 387 | .mmap_mapper = __videobuf_mmap_mapper, |
321 | .video_copy_to_user = __videobuf_copy_to_user, | 388 | .video_copy_to_user = __videobuf_copy_to_user, |
322 | .copy_stream = __videobuf_copy_stream, | 389 | .copy_stream = __videobuf_copy_stream, |
390 | .vmalloc = videobuf_to_vmalloc, | ||
323 | }; | 391 | }; |
324 | 392 | ||
325 | void videobuf_queue_vmalloc_init(struct videobuf_queue* q, | 393 | void videobuf_queue_vmalloc_init(struct videobuf_queue* q, |
@@ -349,13 +417,24 @@ EXPORT_SYMBOL_GPL(videobuf_to_vmalloc); | |||
349 | 417 | ||
350 | void videobuf_vmalloc_free (struct videobuf_buffer *buf) | 418 | void videobuf_vmalloc_free (struct videobuf_buffer *buf) |
351 | { | 419 | { |
352 | struct videobuf_vmalloc_memory *mem=buf->priv; | 420 | struct videobuf_vmalloc_memory *mem = buf->priv; |
353 | BUG_ON (!mem); | ||
354 | 421 | ||
355 | MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM); | 422 | /* mmapped memory can't be freed here, otherwise mmapped region |
423 | would be released, while still needed. In this case, the memory | ||
424 | release should happen inside videobuf_vm_close(). | ||
425 | So, it should free memory only if the memory were allocated for | ||
426 | read() operation. | ||
427 | */ | ||
428 | if ((buf->memory != V4L2_MEMORY_USERPTR) || (buf->baddr == 0)) | ||
429 | return; | ||
430 | |||
431 | if (!mem) | ||
432 | return; | ||
433 | |||
434 | MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); | ||
356 | 435 | ||
357 | vfree(mem->vmalloc); | 436 | vfree(mem->vmalloc); |
358 | mem->vmalloc=NULL; | 437 | mem->vmalloc = NULL; |
359 | 438 | ||
360 | return; | 439 | return; |
361 | } | 440 | } |