dect
/
linux-2.6
Archived
13
0
Fork 0

V4L/DVB (7565): em28xx: fix buffer underrun handling

This patch fixes three related issues and a fourth trivial one:

- Use buffers even if no-one's currently waiting for them (fixes
  underrun issues);

- Don't return incomplete/mangled frames at the start of streaming and
  in the case of buffer underruns;

- Fix an issue which could cause the driver to write to a buffer that's
  been freed after videobuf_queue_cancel is called (exposed by the
  previous two fixes - for some reason, ignoring buffers that weren't
  being waited on worked around the issue);

- Fix a bug which could cause only one field to be filled in the first
  buffer (or first few buffers) after streaming is started.

Signed-off-by: Aidan Thornton <makosoft@googlemail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
This commit is contained in:
Aidan Thornton 2008-04-13 15:09:36 -03:00 committed by Mauro Carvalho Chehab
parent b4916f8ca1
commit 3b5fa928a6
2 changed files with 42 additions and 45 deletions

View File

@ -266,7 +266,7 @@ static inline void print_err_status(struct em28xx *dev,
/* /*
* video-buf generic routine to get the next available buffer * video-buf generic routine to get the next available buffer
*/ */
static inline int get_next_buf(struct em28xx_dmaqueue *dma_q, static inline void get_next_buf(struct em28xx_dmaqueue *dma_q,
struct em28xx_buffer **buf) struct em28xx_buffer **buf)
{ {
struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); struct em28xx *dev = container_of(dma_q, struct em28xx, vidq);
@ -274,39 +274,21 @@ static inline int get_next_buf(struct em28xx_dmaqueue *dma_q,
if (list_empty(&dma_q->active)) { if (list_empty(&dma_q->active)) {
em28xx_isocdbg("No active queue to serve\n"); em28xx_isocdbg("No active queue to serve\n");
dev->isoc_ctl.buf = NULL;
return 0;
}
/* Check if the last buffer were fully filled */
*buf = dev->isoc_ctl.buf;
/* Nobody is waiting on this buffer - discards */
if (*buf && !waitqueue_active(&(*buf)->vb.done)) {
dev->isoc_ctl.buf = NULL; dev->isoc_ctl.buf = NULL;
*buf = NULL; *buf = NULL;
return;
} }
/* Returns the last buffer, to be filled with remaining data */
if (*buf)
return 1;
/* Get the next buffer */ /* Get the next buffer */
*buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue);
/* Nobody is waiting on the next buffer. returns */
if (!*buf || !waitqueue_active(&(*buf)->vb.done)) {
em28xx_isocdbg("No active queue to serve\n");
return 0;
}
/* Cleans up buffer - Usefull for testing for frame/URB loss */ /* Cleans up buffer - Usefull for testing for frame/URB loss */
outp = videobuf_to_vmalloc(&(*buf)->vb); outp = videobuf_to_vmalloc(&(*buf)->vb);
memset(outp, 0, (*buf)->vb.size); memset(outp, 0, (*buf)->vb.size);
dev->isoc_ctl.buf = *buf; dev->isoc_ctl.buf = *buf;
return 1; return;
} }
/* /*
@ -317,7 +299,7 @@ static inline int em28xx_isoc_copy(struct urb *urb)
struct em28xx_buffer *buf; struct em28xx_buffer *buf;
struct em28xx_dmaqueue *dma_q = urb->context; struct em28xx_dmaqueue *dma_q = urb->context;
struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); struct em28xx *dev = container_of(dma_q, struct em28xx, vidq);
unsigned char *outp; unsigned char *outp = NULL;
int i, len = 0, rc = 1; int i, len = 0, rc = 1;
unsigned char *p; unsigned char *p;
@ -333,11 +315,9 @@ static inline int em28xx_isoc_copy(struct urb *urb)
return 0; return 0;
} }
rc = get_next_buf(dma_q, &buf); buf = dev->isoc_ctl.buf;
if (rc <= 0) if (buf != NULL)
return rc; outp = videobuf_to_vmalloc(&buf->vb);
outp = videobuf_to_vmalloc(&buf->vb);
for (i = 0; i < urb->number_of_packets; i++) { for (i = 0; i < urb->number_of_packets; i++) {
int status = urb->iso_frame_desc[i].status; int status = urb->iso_frame_desc[i].status;
@ -374,24 +354,27 @@ static inline int em28xx_isoc_copy(struct urb *urb)
em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2],
len, (p[2] & 1)? "odd" : "even"); len, (p[2] & 1)? "odd" : "even");
if (p[2] & 1) if (!(p[2] & 1)) {
buf->top_field = 0; if (buf != NULL)
else buffer_filled(dev, dma_q, buf);
buf->top_field = 1; get_next_buf(dma_q, &buf);
if (buf == NULL)
// if (dev->isoc_ctl.last_field && !buf->top_field) { outp = NULL;
if (dev->isoc_ctl.last_field != buf->top_field) { else
buffer_filled(dev, dma_q, buf); outp = videobuf_to_vmalloc(&buf->vb);
rc = get_next_buf(dma_q, &buf); }
if (rc <= 0)
return rc; if (buf != NULL) {
outp = videobuf_to_vmalloc(&buf->vb); if (p[2] & 1)
buf->top_field = 0;
else
buf->top_field = 1;
} }
dev->isoc_ctl.last_field = buf->top_field;
dma_q->pos = 0; dma_q->pos = 0;
} }
em28xx_copy_video(dev, dma_q, buf, p, outp, len); if (buf != NULL)
em28xx_copy_video(dev, dma_q, buf, p, outp, len);
} }
return rc; return rc;
} }
@ -597,12 +580,29 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
return 0; return 0;
} }
/* This is called *without* dev->slock held; please keep it that way */
static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf)
{ {
struct em28xx_fh *fh = vq->priv_data;
struct em28xx *dev = fh->dev;
unsigned long flags = 0;
if (in_interrupt()) if (in_interrupt())
BUG(); BUG();
videobuf_waiton(&buf->vb, 0, 0); /* We used to wait for the buffer to finish here, but this didn't work
because, as we were keeping the state as VIDEOBUF_QUEUED,
videobuf_queue_cancel marked it as finished for us.
(Also, it could wedge forever if the hardware was misconfigured.)
This should be safe; by the time we get here, the buffer isn't
queued anymore. If we ever start marking the buffers as
VIDEOBUF_ACTIVE, it won't be, though.
*/
spin_lock_irqsave(&dev->slock, flags);
if (dev->isoc_ctl.buf == buf)
dev->isoc_ctl.buf = NULL;
spin_unlock_irqrestore(&dev->slock, flags);
videobuf_vmalloc_free(&buf->vb); videobuf_vmalloc_free(&buf->vb);
buf->vb.state = VIDEOBUF_NEEDS_INIT; buf->vb.state = VIDEOBUF_NEEDS_INIT;
} }

View File

@ -114,9 +114,6 @@ struct em28xx_usb_isoc_ctl {
/* Stores already requested buffers */ /* Stores already requested buffers */
struct em28xx_buffer *buf; struct em28xx_buffer *buf;
/* Store last filled frame */
int last_field;
/* Stores the number of received fields */ /* Stores the number of received fields */
int nfields; int nfields;
}; };