2002-10-31 15:24:02

by Gerd Knorr

[permalink] [raw]
Subject: [patch] videobuf update

Hi Linus,

This patch updates the for the video-buf.c module (helper module for
video buffer management). Some memory management fixes, also some
adaptions to the final v4l2 api.

Gerd

--- linux-2.5.45/drivers/media/video/video-buf.c 2002-10-31 14:03:59.000000000 +0100
+++ linux/drivers/media/video/video-buf.c 2002-10-31 14:20:27.000000000 +0100
@@ -26,6 +26,11 @@
#include <asm/page.h>
#include <asm/pgtable.h>

+#ifndef TryLockPage
+# include "linux/page-flags.h"
+# define TryLockPage TestSetPageLocked
+#endif
+
#include "video-buf.h"

static int debug = 0;
@@ -65,12 +70,12 @@
return NULL;
}

-struct scatterlist *
+struct scatterlist*
videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset)
{
struct scatterlist *sglist;
int i = 0;
-
+
if (NULL == pages[0])
return NULL;
sglist = kmalloc(sizeof(*sglist) * nr_pages, GFP_KERNEL);
@@ -80,21 +85,27 @@

if (PageHighMem(pages[0]))
/* DMA to highmem pages might not work */
- goto err;
+ goto highmem;
sglist[0].page = pages[0];
sglist[0].offset = offset;
sglist[0].length = PAGE_SIZE - offset;
for (i = 1; i < nr_pages; i++) {
if (NULL == pages[i])
- goto err;
+ goto nopage;
if (PageHighMem(pages[i]))
- goto err;
+ goto highmem;
sglist[i].page = pages[i];
sglist[i].length = PAGE_SIZE;
}
return sglist;

- err:
+ nopage:
+ dprintk(2,"sgl: oops - no page\n");
+ kfree(sglist);
+ return NULL;
+
+ highmem:
+ dprintk(2,"sgl: oops - highmem page\n");
kfree(sglist);
return NULL;
}
@@ -103,14 +114,18 @@
{
int i;

+ dprintk(2,"lock start ...\n");
for (i = 0; i < nr_pages; i++)
- if (TestSetPageLocked(pages[i]))
+ if (TryLockPage(pages[i]))
goto err;
+ dprintk(2,"lock ok\n");
return 0;

err:
+ dprintk(2,"lock failed, unlock ...\n");
while (i > 0)
unlock_page(pages[--i]);
+ dprintk(2,"lock quit\n");
return -EINVAL;
}

@@ -118,8 +133,10 @@
{
int i;

+ dprintk(2,"unlock start ...\n");
for (i = 0; i < nr_pages; i++)
unlock_page(pages[i]);
+ dprintk(2,"unlock ok\n");
return 0;
}

@@ -128,6 +145,7 @@
int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction,
unsigned long data, unsigned long size)
{
+ unsigned long first,last;
int err, rw = 0;

dma->direction = direction;
@@ -137,25 +155,35 @@
default: BUG();
}

- dma->offset = data & PAGE_MASK;
- dma->nr_pages = ((((data+size) & ~PAGE_MASK) -
- (data & ~PAGE_MASK)) >> PAGE_SHIFT) +1;
- dma->pages = kmalloc(dma->nr_pages * sizeof(struct page*),
- GFP_KERNEL);
+ first = (data & PAGE_MASK) >> PAGE_SHIFT;
+ last = ((data+size-1) & PAGE_MASK) >> PAGE_SHIFT;
+ dma->offset = data & ~PAGE_MASK;
+ dma->nr_pages = last-first+1;
+ dma->pages = kmalloc(dma->nr_pages * sizeof(struct page*),
+ GFP_KERNEL);
if (NULL == dma->pages)
return -ENOMEM;
+ dprintk(1,"init user [0x%lx+0x%lx => %d pages]\n",
+ data,size,dma->nr_pages);
+
down_read(&current->mm->mmap_sem);
err = get_user_pages(current,current->mm,
- data, dma->nr_pages,
- rw == READ, 0, /* don't force */
+ data & PAGE_MASK, dma->nr_pages,
+ rw == READ, 1, /* force */
dma->pages, NULL);
up_read(&current->mm->mmap_sem);
- return err;
+ if (err != dma->nr_pages) {
+ dma->nr_pages = (err >= 0) ? err : 0;
+ dprintk(1,"get_user_pages: err=%d [%d]\n",err,dma->nr_pages);
+ return err < 0 ? err : -EINVAL;
+ }
+ return 0;
}

int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
int nr_pages)
{
+ dprintk(1,"init kernel [%d pages]\n",nr_pages);
dma->direction = direction;
dma->vmalloc = vmalloc_32(nr_pages << PAGE_SHIFT);
if (NULL == dma->vmalloc) {
@@ -176,13 +204,14 @@

if (dma->pages) {
if (0 != (err = videobuf_lock(dma->pages, dma->nr_pages))) {
- dprintk(1,"videobuf_lock_pages: %d\n",err);
+ dprintk(1,"videobuf_lock: %d\n",err);
return err;
}
dma->sglist = videobuf_pages_to_sg(dma->pages, dma->nr_pages,
dma->offset);
+ if (NULL == dma->sglist)
+ videobuf_unlock(dma->pages, dma->nr_pages);
}
-
if (dma->vmalloc) {
dma->sglist = videobuf_vmalloc_to_sg
(dma->vmalloc,dma->nr_pages);
@@ -215,7 +244,7 @@
dma->sglist = NULL;
dma->sglen = 0;
if (dma->pages)
- videobuf_lock(dma->pages, dma->nr_pages);
+ videobuf_unlock(dma->pages, dma->nr_pages);
return 0;
}

@@ -231,7 +260,6 @@
kfree(dma->pages);
dma->pages = NULL;
}
-
if (dma->vmalloc) {
vfree(dma->vmalloc);
dma->vmalloc = NULL;
@@ -286,16 +314,12 @@
if (0 == vb->baddr) {
/* no userspace addr -- kernel bounce buffer */
pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT;
- dprintk(1,"kernel buf size=%ld (%d pages)\n",
- vb->size,pages);
err = videobuf_dma_init_kernel(&vb->dma,PCI_DMA_FROMDEVICE,
pages);
if (0 != err)
return err;
} else {
/* dma directly to userspace */
- dprintk(1,"user buf addr=%08lx size=%ld\n",
- vb->baddr,vb->bsize);
err = videobuf_dma_init_user(&vb->dma,PCI_DMA_FROMDEVICE,
vb->baddr,vb->bsize);
if (0 != err)
@@ -314,7 +338,7 @@
struct videobuf_queue_ops *ops,
struct pci_dev *pci,
spinlock_t *irqlock,
- int type,
+ enum v4l2_buf_type type,
int msize)
{
memset(q,0,sizeof(*q));
@@ -329,6 +353,30 @@
INIT_LIST_HEAD(&q->stream);
}

+int
+videobuf_queue_is_busy(struct videobuf_queue *q)
+{
+ int i;
+
+ if (q->reading)
+ return 1;
+ if (q->streaming)
+ return 1;
+ if (q->read_buf)
+ return 1;
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == q->bufs[i])
+ continue;
+ if (q->bufs[i]->map)
+ return 1;
+ if (q->bufs[i]->state == STATE_QUEUED)
+ return 1;
+ if (q->bufs[i]->state == STATE_ACTIVE)
+ return 1;
+ }
+ return 0;
+}
+
void
videobuf_queue_cancel(struct file *file, struct videobuf_queue *q)
{
@@ -358,15 +406,15 @@

/* --------------------------------------------------------------------- */

-#ifdef HAVE_V4L2
void
-videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb, int type)
+videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb,
+ enum v4l2_buf_type type)
{
- b->index = vb->i;
- b->type = type;
- b->offset = vb->boff;
- b->length = vb->bsize;
- b->flags = 0;
+ b->index = vb->i;
+ b->type = type;
+ b->m.offset = vb->boff;
+ b->length = vb->bsize;
+ b->flags = 0;
if (vb->map)
b->flags |= V4L2_BUF_FLAG_MAPPED;
switch (vb->state) {
@@ -384,12 +432,7 @@
/* nothing */
break;
}
- if (!(vb->field & VBUF_FIELD_INTER)) {
- if (vb->field & VBUF_FIELD_ODD)
- b->flags |= V4L2_BUF_FLAG_TOPFIELD;
- if (vb->field & VBUF_FIELD_EVEN)
- b->flags |= V4L2_BUF_FLAG_BOTFIELD;
- }
+ b->field = vb->field;
b->timestamp = vb->ts;
b->bytesused = vb->size;
b->sequence = vb->field_count >> 1;
@@ -401,7 +444,7 @@
{
int size,count,retval;

- if ((req->type & V4L2_BUF_TYPE_field) != q->type)
+ if (req->type != q->type)
return -EINVAL;
if (req->count < 1)
return -EINVAL;
@@ -428,11 +471,11 @@
int
videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b)
{
- if ((b->type & V4L2_BUF_TYPE_field) != q->type)
+ if (unlikely(b->type != q->type))
return -EINVAL;
- if (b->index < 0 || b->index >= VIDEO_MAX_FRAME)
+ if (unlikely(b->index < 0 || b->index >= VIDEO_MAX_FRAME))
return -EINVAL;
- if (NULL == q->bufs[b->index])
+ if (unlikely(NULL == q->bufs[b->index]))
return -EINVAL;
videobuf_status(b,q->bufs[b->index],q->type);
return 0;
@@ -444,7 +487,6 @@
{
struct videobuf_buffer *buf;
unsigned long flags;
- int field = 0;
int retval;

down(&q->lock);
@@ -452,7 +494,7 @@
if (q->reading)
goto done;
retval = -EINVAL;
- if ((b->type & V4L2_BUF_TYPE_field) != q->type)
+ if (b->type != q->type)
goto done;
if (b->index < 0 || b->index >= VIDEO_MAX_FRAME)
goto done;
@@ -465,11 +507,7 @@
buf->state == STATE_ACTIVE)
goto done;

- if (b->flags & V4L2_BUF_FLAG_TOPFIELD)
- field |= VBUF_FIELD_ODD;
- if (b->flags & V4L2_BUF_FLAG_BOTFIELD)
- field |= VBUF_FIELD_EVEN;
- retval = q->ops->buf_prepare(file,buf,field);
+ retval = q->ops->buf_prepare(file,buf);
if (0 != retval)
goto done;

@@ -498,7 +536,7 @@
if (q->reading)
goto done;
retval = -EINVAL;
- if ((b->type & V4L2_BUF_TYPE_field) != q->type)
+ if (b->type != q->type)
goto done;
if (list_empty(&q->stream))
goto done;
@@ -526,7 +564,6 @@
up(&q->lock);
return retval;
}
-#endif /* HAVE_V4L2 */

int videobuf_streamon(struct file *file, struct videobuf_queue *q)
{
@@ -586,7 +623,7 @@

q->read_buf->baddr = (unsigned long)data;
q->read_buf->bsize = count;
- retval = q->ops->buf_prepare(file,q->read_buf,0);
+ retval = q->ops->buf_prepare(file,q->read_buf);
if (0 != retval)
goto done;

@@ -631,7 +668,7 @@
q->read_buf = videobuf_alloc(q->msize);
if (NULL == q->read_buf)
goto done;
- retval = q->ops->buf_prepare(file,q->read_buf,0);
+ retval = q->ops->buf_prepare(file,q->read_buf);
if (0 != retval)
goto done;
q->ops->buf_queue(file,q->read_buf);
@@ -683,7 +720,7 @@
if (err)
return err;
for (i = 0; i < count; i++) {
- err = q->ops->buf_prepare(file,q->bufs[i],0);
+ err = q->ops->buf_prepare(file,q->bufs[i]);
if (err)
return err;
list_add_tail(&q->bufs[i]->stream, &q->stream);
@@ -1008,6 +1045,8 @@
/* --------------------------------------------------------------------- */

EXPORT_SYMBOL_GPL(videobuf_vmalloc_to_sg);
+EXPORT_SYMBOL_GPL(videobuf_lock);
+EXPORT_SYMBOL_GPL(videobuf_unlock);

EXPORT_SYMBOL_GPL(videobuf_dma_init_user);
EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel);
@@ -1022,14 +1061,13 @@

EXPORT_SYMBOL_GPL(videobuf_queue_init);
EXPORT_SYMBOL_GPL(videobuf_queue_cancel);
+EXPORT_SYMBOL_GPL(videobuf_queue_is_busy);

-#ifdef HAVE_V4L2
EXPORT_SYMBOL_GPL(videobuf_status);
EXPORT_SYMBOL_GPL(videobuf_reqbufs);
EXPORT_SYMBOL_GPL(videobuf_querybuf);
EXPORT_SYMBOL_GPL(videobuf_qbuf);
EXPORT_SYMBOL_GPL(videobuf_dqbuf);
-#endif
EXPORT_SYMBOL_GPL(videobuf_streamon);
EXPORT_SYMBOL_GPL(videobuf_streamoff);

--- linux-2.5.45/drivers/media/video/video-buf.h 2002-10-31 14:04:05.000000000 +0100
+++ linux/drivers/media/video/video-buf.h 2002-10-31 14:20:27.000000000 +0100
@@ -28,12 +28,14 @@
struct scatterlist* videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages);

/*
- * Return a scatterlist for a an array of userpages (NULL on errors). Memory
- * for the scatterlist is allocated using kmalloc. The caller must
- * free the memory.
+ * Return a scatterlist for a an array of userpages (NULL on errors).
+ * Memory for the scatterlist is allocated using kmalloc. The caller
+ * must free the memory.
*/
-struct scatterlist *videobuf_pages_to_sg(struct page **pages, int nr_pages,
+struct scatterlist* videobuf_pages_to_sg(struct page **pages, int nr_pages,
int offset);
+int videobuf_lock(struct page **pages, int nr_pages);
+int videobuf_unlock(struct page **pages, int nr_pages);

/* --------------------------------------------------------------------- */

@@ -58,8 +60,8 @@

struct videobuf_dmabuf {
/* for userland buffer */
- struct page **pages;
int offset;
+ struct page **pages;

/* for kernel buffers */
void *vmalloc;
@@ -115,10 +117,6 @@
struct videobuf_queue *q;
};

-#define VBUF_FIELD_EVEN 1
-#define VBUF_FIELD_ODD 2
-#define VBUF_FIELD_INTER 4
-
enum videobuf_state {
STATE_NEEDS_INIT = 0,
STATE_PREPARED = 1,
@@ -136,30 +134,27 @@
int width;
int height;
long size;
- int field;
+ enum v4l2_field field;
enum videobuf_state state;
struct videobuf_dmabuf dma;
struct list_head stream; /* QBUF/DQBUF list */

/* for mmap'ed buffers */
- unsigned long boff; /* buffer offset (mmap) */
- unsigned long bsize; /* buffer size */
- unsigned long baddr; /* buffer addr (userland ptr!) */
+ off_t boff; /* buffer offset (mmap) */
+ size_t bsize; /* buffer size */
+ unsigned long baddr; /* buffer addr (userland ptr!) */
struct videobuf_mapping *map;

/* touched by irq handler */
struct list_head queue;
wait_queue_head_t done;
int field_count;
-#ifdef HAVE_V4L2
- stamp_t ts;
-#endif
+ struct timeval ts;
};

struct videobuf_queue_ops {
int (*buf_setup)(struct file *file, int *count, int *size);
- int (*buf_prepare)(struct file *file,struct videobuf_buffer *vb,
- int field);
+ int (*buf_prepare)(struct file *file,struct videobuf_buffer *vb);
void (*buf_queue)(struct file *file,struct videobuf_buffer *vb);
void (*buf_release)(struct file *file,struct videobuf_buffer *vb);
};
@@ -169,7 +164,7 @@
spinlock_t *irqlock;
struct pci_dev *pci;

- int type;
+ enum v4l2_buf_type type;
int msize;
struct videobuf_buffer *bufs[VIDEO_MAX_FRAME];
struct videobuf_queue_ops *ops;
@@ -191,12 +186,12 @@
void videobuf_queue_init(struct videobuf_queue *q,
struct videobuf_queue_ops *ops,
struct pci_dev *pci, spinlock_t *irqlock,
- int type, int msize);
+ enum v4l2_buf_type type, int msize);
+int videobuf_queue_is_busy(struct videobuf_queue *q);
void videobuf_queue_cancel(struct file *file, struct videobuf_queue *q);

-#ifdef HAVE_V4L2
void videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb,
- int type);
+ enum v4l2_buf_type type);
int videobuf_reqbufs(struct file *file, struct videobuf_queue *q,
struct v4l2_requestbuffers *req);
int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b);
@@ -204,7 +199,6 @@
struct v4l2_buffer *b);
int videobuf_dqbuf(struct file *file, struct videobuf_queue *q,
struct v4l2_buffer *b);
-#endif
int videobuf_streamon(struct file *file, struct videobuf_queue *q);
int videobuf_streamoff(struct file *file, struct videobuf_queue *q);


--
You can't please everybody. And usually if you _try_ to please
everybody, the end result is one big mess.
-- Linus Torvalds, 2002-04-20