blob: 73627d380f07f44677808b128b8b52322acfae79 [file] [log] [blame]
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -03001/*
2 * helper functions for vmalloc video4linux capture buffers
3 *
4 * The functions expect the hardware being able to scatter gatter
5 * (i.e. the buffers are not linear in physical memory, but fragmented
6 * into PAGE_SIZE chunks). They also assume the driver does not need
7 * to touch the video data.
8 *
9 * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2
14 */
15
16#include <linux/init.h>
17#include <linux/module.h>
18#include <linux/moduleparam.h>
19#include <linux/slab.h>
20#include <linux/interrupt.h>
21
22#include <linux/pci.h>
23#include <linux/vmalloc.h>
24#include <linux/pagemap.h>
25#include <asm/page.h>
26#include <asm/pgtable.h>
27
28#include <media/videobuf-vmalloc.h>
29
30#define MAGIC_DMABUF 0x17760309
31#define MAGIC_VMAL_MEM 0x18221223
32
33#define MAGIC_CHECK(is,should) if (unlikely((is) != (should))) \
34 { printk(KERN_ERR "magic mismatch: %x (expected %x)\n",is,should); BUG(); }
35
Douglas Schilling Landgrafff699e62008-04-22 14:41:48 -030036static int debug;
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -030037module_param(debug, int, 0644);
38
39MODULE_DESCRIPTION("helper module to manage video4linux vmalloc buffers");
40MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
41MODULE_LICENSE("GPL");
42
43#define dprintk(level, fmt, arg...) if (debug >= level) \
Brandon Philips493977f2007-11-30 22:37:28 -030044 printk(KERN_DEBUG "vbuf-vmalloc: " fmt , ## arg)
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -030045
46
47/***************************************************************************/
48
49static void
50videobuf_vm_open(struct vm_area_struct *vma)
51{
52 struct videobuf_mapping *map = vma->vm_private_data;
53
Brandon Philips0b296692007-12-08 23:05:53 -030054 dprintk(2,"vm_open %p [count=%u,vma=%08lx-%08lx]\n",map,
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -030055 map->count,vma->vm_start,vma->vm_end);
56
57 map->count++;
58}
59
Mauro Carvalho Chehabaaea56a2008-04-13 14:58:43 -030060static void videobuf_vm_close(struct vm_area_struct *vma)
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -030061{
62 struct videobuf_mapping *map = vma->vm_private_data;
63 struct videobuf_queue *q = map->q;
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -030064 int i;
65
Mauro Carvalho Chehabaaea56a2008-04-13 14:58:43 -030066 dprintk(2,"vm_close %p [count=%u,vma=%08lx-%08lx]\n", map,
67 map->count, vma->vm_start, vma->vm_end);
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -030068
69 map->count--;
70 if (0 == map->count) {
Mauro Carvalho Chehabaaea56a2008-04-13 14:58:43 -030071 struct videobuf_vmalloc_memory *mem;
72
73 dprintk(1, "munmap %p q=%p\n", map, q);
Mauro Carvalho Chehab64f94772008-01-31 13:57:53 -030074 mutex_lock(&q->vb_lock);
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -030075 for (i = 0; i < VIDEO_MAX_FRAME; i++) {
76 if (NULL == q->bufs[i])
77 continue;
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -030078
Mauro Carvalho Chehab851c0c92007-09-27 18:25:44 -030079 if (q->bufs[i]->map != map)
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -030080 continue;
Mauro Carvalho Chehab123f8ef2007-09-06 20:11:35 -030081
Mauro Carvalho Chehabaaea56a2008-04-13 14:58:43 -030082 mem = q->bufs[i]->priv;
83 if (mem) {
84 /* This callback is called only if kernel has
85 allocated memory and this memory is mmapped.
86 In this case, memory should be freed,
87 in order to do memory unmap.
88 */
89 MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
90 vfree(mem->vmalloc);
91 mem->vmalloc = NULL;
92 }
93
Mauro Carvalho Chehab851c0c92007-09-27 18:25:44 -030094 q->bufs[i]->map = NULL;
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -030095 q->bufs[i]->baddr = 0;
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -030096 }
Mauro Carvalho Chehab64f94772008-01-31 13:57:53 -030097 mutex_unlock(&q->vb_lock);
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -030098 kfree(map);
99 }
100 return;
101}
102
103static struct vm_operations_struct videobuf_vm_ops =
104{
105 .open = videobuf_vm_open,
106 .close = videobuf_vm_close,
107};
108
109/* ---------------------------------------------------------------------
110 * vmalloc handlers for the generic methods
111 */
112
113/* Allocated area consists on 3 parts:
114 struct video_buffer
115 struct <driver>_buffer (cx88_buffer, saa7134_buf, ...)
Guennadi Liakhovetski07051352008-04-22 14:42:13 -0300116 struct videobuf_dma_sg_memory
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300117 */
118
119static void *__videobuf_alloc(size_t size)
120{
Brandon Philips384b8352008-02-04 20:52:21 -0300121 struct videobuf_vmalloc_memory *mem;
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300122 struct videobuf_buffer *vb;
123
124 vb = kzalloc(size+sizeof(*mem),GFP_KERNEL);
125
126 mem = vb->priv = ((char *)vb)+size;
127 mem->magic=MAGIC_VMAL_MEM;
128
129 dprintk(1,"%s: allocated at %p(%ld+%ld) & %p(%ld)\n",
Harvey Harrison7e28adb2008-04-08 23:20:00 -0300130 __func__,vb,(long)sizeof(*vb),(long)size-sizeof(*vb),
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300131 mem,(long)sizeof(*mem));
132
133 return vb;
134}
135
136static int __videobuf_iolock (struct videobuf_queue* q,
137 struct videobuf_buffer *vb,
138 struct v4l2_framebuffer *fbuf)
139{
Mauro Carvalho Chehab968ced72008-04-13 14:58:21 -0300140 struct videobuf_vmalloc_memory *mem = vb->priv;
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300141
142 BUG_ON(!mem);
143
Mauro Carvalho Chehab968ced72008-04-13 14:58:21 -0300144 MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300145
Mauro Carvalho Chehab968ced72008-04-13 14:58:21 -0300146 switch (vb->memory) {
147 case V4L2_MEMORY_MMAP:
148 dprintk(1, "%s memory method MMAP\n", __func__);
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300149
Mauro Carvalho Chehab968ced72008-04-13 14:58:21 -0300150 /* All handling should be done by __videobuf_mmap_mapper() */
151 if (!mem->vmalloc) {
152 printk(KERN_ERR "memory is not alloced/mmapped.\n");
153 return -EINVAL;
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300154 }
Mauro Carvalho Chehab968ced72008-04-13 14:58:21 -0300155 break;
156 case V4L2_MEMORY_USERPTR:
157 {
158 int pages = PAGE_ALIGN(vb->size);
159
160 dprintk(1, "%s memory method USERPTR\n", __func__);
161
162#if 1
163 if (vb->baddr) {
164 printk(KERN_ERR "USERPTR is currently not supported\n");
165 return -EINVAL;
166 }
167#endif
168
169 /* The only USERPTR currently supported is the one needed for
170 read() method.
171 */
172
173 mem->vmalloc = vmalloc_user(pages);
174 if (!mem->vmalloc) {
175 printk(KERN_ERR "vmalloc (%d pages) failed\n", pages);
176 return -ENOMEM;
177 }
178 dprintk(1, "vmalloc is at addr %p (%d pages)\n",
179 mem->vmalloc, pages);
180
181#if 0
182 int rc;
183 /* Kernel userptr is used also by read() method. In this case,
184 there's no need to remap, since data will be copied to user
185 */
186 if (!vb->baddr)
187 return 0;
188
189 /* FIXME: to properly support USERPTR, remap should occur.
190 The code bellow won't work, since mem->vma = NULL
191 */
192 /* Try to remap memory */
193 rc = remap_vmalloc_range(mem->vma, (void *)vb->baddr, 0);
194 if (rc < 0) {
195 printk(KERN_ERR "mmap: remap failed with error %d. ", rc);
196 return -ENOMEM;
197 }
198#endif
199
200 break;
201 }
202 case V4L2_MEMORY_OVERLAY:
203 default:
204 dprintk(1, "%s memory method OVERLAY/unknown\n", __func__);
205
206 /* Currently, doesn't support V4L2_MEMORY_OVERLAY */
207 printk(KERN_ERR "Memory method currently unsupported.\n");
208 return -EINVAL;
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300209 }
210
211 return 0;
212}
213
214static int __videobuf_sync(struct videobuf_queue *q,
215 struct videobuf_buffer *buf)
216{
217 return 0;
218}
219
220static int __videobuf_mmap_free(struct videobuf_queue *q)
221{
222 unsigned int i;
223
Mauro Carvalho Chehab968ced72008-04-13 14:58:21 -0300224 dprintk(1, "%s\n", __func__);
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300225 for (i = 0; i < VIDEO_MAX_FRAME; i++) {
226 if (q->bufs[i]) {
Mauro Carvalho Chehab851c0c92007-09-27 18:25:44 -0300227 if (q->bufs[i]->map)
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300228 return -EBUSY;
229 }
230 }
231
232 return 0;
233}
234
235static int __videobuf_mmap_mapper(struct videobuf_queue *q,
236 struct vm_area_struct *vma)
237{
Brandon Philips384b8352008-02-04 20:52:21 -0300238 struct videobuf_vmalloc_memory *mem;
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300239 struct videobuf_mapping *map;
240 unsigned int first;
Mauro Carvalho Chehab968ced72008-04-13 14:58:21 -0300241 int retval, pages;
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300242 unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
243
Mauro Carvalho Chehab968ced72008-04-13 14:58:21 -0300244 dprintk(1, "%s\n", __func__);
245 if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED))
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300246 return -EINVAL;
247
248 /* look for first buffer to map */
249 for (first = 0; first < VIDEO_MAX_FRAME; first++) {
250 if (NULL == q->bufs[first])
251 continue;
252
253 if (V4L2_MEMORY_MMAP != q->bufs[first]->memory)
254 continue;
255 if (q->bufs[first]->boff == offset)
256 break;
257 }
258 if (VIDEO_MAX_FRAME == first) {
259 dprintk(1,"mmap app bug: offset invalid [offset=0x%lx]\n",
260 (vma->vm_pgoff << PAGE_SHIFT));
261 return -EINVAL;
262 }
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300263
264 /* create mapping + update buffer list */
Mauro Carvalho Chehab968ced72008-04-13 14:58:21 -0300265 map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL);
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300266 if (NULL == map)
267 return -ENOMEM;
268
Mauro Carvalho Chehab968ced72008-04-13 14:58:21 -0300269 q->bufs[first]->map = map;
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300270 map->start = vma->vm_start;
271 map->end = vma->vm_end;
272 map->q = q;
273
274 q->bufs[first]->baddr = vma->vm_start;
275
Mauro Carvalho Chehab968ced72008-04-13 14:58:21 -0300276 mem = q->bufs[first]->priv;
277 BUG_ON(!mem);
278 MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
279
280 pages = PAGE_ALIGN(vma->vm_end - vma->vm_start);
281 mem->vmalloc = vmalloc_user(pages);
282 if (!mem->vmalloc) {
283 printk(KERN_ERR "vmalloc (%d pages) failed\n", pages);
284 goto error;
285 }
286 dprintk(1, "vmalloc is at addr %p (%d pages)\n",
287 mem->vmalloc, pages);
288
289 /* Try to remap memory */
290 retval = remap_vmalloc_range(vma, mem->vmalloc, 0);
291 if (retval < 0) {
292 printk(KERN_ERR "mmap: remap failed with error %d. ", retval);
293 vfree(mem->vmalloc);
294 goto error;
295 }
296
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300297 vma->vm_ops = &videobuf_vm_ops;
298 vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED;
299 vma->vm_private_data = map;
300
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300301 dprintk(1,"mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
Mauro Carvalho Chehab968ced72008-04-13 14:58:21 -0300302 map, q, vma->vm_start, vma->vm_end,
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300303 (long int) q->bufs[first]->bsize,
Mauro Carvalho Chehab968ced72008-04-13 14:58:21 -0300304 vma->vm_pgoff, first);
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300305
306 videobuf_vm_open(vma);
307
Mauro Carvalho Chehab968ced72008-04-13 14:58:21 -0300308 return 0;
309
310error:
311 mem = NULL;
312 kfree(map);
313 return -ENOMEM;
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300314}
315
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300316static int __videobuf_copy_to_user ( struct videobuf_queue *q,
317 char __user *data, size_t count,
318 int nonblocking )
319{
Brandon Philips384b8352008-02-04 20:52:21 -0300320 struct videobuf_vmalloc_memory *mem=q->read_buf->priv;
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300321 BUG_ON (!mem);
322 MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM);
323
324 BUG_ON (!mem->vmalloc);
325
326 /* copy to userspace */
327 if (count > q->read_buf->size - q->read_off)
328 count = q->read_buf->size - q->read_off;
329
330 if (copy_to_user(data, mem->vmalloc+q->read_off, count))
331 return -EFAULT;
332
333 return count;
334}
335
336static int __videobuf_copy_stream ( struct videobuf_queue *q,
337 char __user *data, size_t count, size_t pos,
338 int vbihack, int nonblocking )
339{
340 unsigned int *fc;
Brandon Philips384b8352008-02-04 20:52:21 -0300341 struct videobuf_vmalloc_memory *mem=q->read_buf->priv;
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300342 BUG_ON (!mem);
343 MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM);
344
345 if (vbihack) {
346 /* dirty, undocumented hack -- pass the frame counter
347 * within the last four bytes of each vbi data block.
348 * We need that one to maintain backward compatibility
349 * to all vbi decoding software out there ... */
350 fc = (unsigned int*)mem->vmalloc;
351 fc += (q->read_buf->size>>2) -1;
352 *fc = q->read_buf->field_count >> 1;
353 dprintk(1,"vbihack: %d\n",*fc);
354 }
355
356 /* copy stuff using the common method */
357 count = __videobuf_copy_to_user (q,data,count,nonblocking);
358
359 if ( (count==-EFAULT) && (0 == pos) )
360 return -EFAULT;
361
362 return count;
363}
364
365static struct videobuf_qtype_ops qops = {
366 .magic = MAGIC_QTYPE_OPS,
367
368 .alloc = __videobuf_alloc,
369 .iolock = __videobuf_iolock,
370 .sync = __videobuf_sync,
371 .mmap_free = __videobuf_mmap_free,
372 .mmap_mapper = __videobuf_mmap_mapper,
Al Viro13bcd5d2007-10-13 08:25:24 +0100373 .video_copy_to_user = __videobuf_copy_to_user,
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300374 .copy_stream = __videobuf_copy_stream,
375};
376
377void videobuf_queue_vmalloc_init(struct videobuf_queue* q,
378 struct videobuf_queue_ops *ops,
379 void *dev,
380 spinlock_t *irqlock,
381 enum v4l2_buf_type type,
382 enum v4l2_field field,
383 unsigned int msize,
384 void *priv)
385{
Mauro Carvalho Chehabd4cae5a2007-10-08 12:20:02 -0300386 videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
387 priv, &qops);
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300388}
389
390EXPORT_SYMBOL_GPL(videobuf_queue_vmalloc_init);
391
392void *videobuf_to_vmalloc (struct videobuf_buffer *buf)
393{
Brandon Philips384b8352008-02-04 20:52:21 -0300394 struct videobuf_vmalloc_memory *mem=buf->priv;
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300395 BUG_ON (!mem);
396 MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM);
397
398 return mem->vmalloc;
399}
400EXPORT_SYMBOL_GPL(videobuf_to_vmalloc);
401
402void videobuf_vmalloc_free (struct videobuf_buffer *buf)
403{
Mauro Carvalho Chehab968ced72008-04-13 14:58:21 -0300404 struct videobuf_vmalloc_memory *mem = buf->priv;
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300405
Mauro Carvalho Chehabaaea56a2008-04-13 14:58:43 -0300406 /* mmapped memory can't be freed here, otherwise mmapped region
407 would be released, while still needed. In this case, the memory
408 release should happen inside videobuf_vm_close().
409 So, it should free memory only if the memory were allocated for
410 read() operation.
411 */
412 if ((buf->memory != V4L2_MEMORY_USERPTR) || (buf->baddr == 0))
413 return;
414
Mauro Carvalho Chehab968ced72008-04-13 14:58:21 -0300415 if (!mem)
416 return;
417
418 MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM);
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300419
420 vfree(mem->vmalloc);
Mauro Carvalho Chehab968ced72008-04-13 14:58:21 -0300421 mem->vmalloc = NULL;
422
Mauro Carvalho Chehab87b9ad02007-08-02 23:31:33 -0300423 return;
424}
425EXPORT_SYMBOL_GPL(videobuf_vmalloc_free);
426
427/*
428 * Local variables:
429 * c-basic-offset: 8
430 * End:
431 */