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(¤t->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(¤t->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