mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-14 06:24:53 +08:00
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:
parent
b4916f8ca1
commit
3b5fa928a6
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user