diff options
author | Aidan Thornton <makosoft@googlemail.com> | 2008-04-13 14:09:36 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2008-04-24 13:09:39 -0400 |
commit | 3b5fa928a6b2971ec65571745defc5d9758b4bc1 (patch) | |
tree | 80854c270cec4154a9b86e4ed02b4970897104b5 /drivers/media | |
parent | b4916f8ca1da71bb97fb6dcf1e8da3f9c64cf80e (diff) |
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>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/video/em28xx/em28xx-video.c | 84 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx.h | 3 |
2 files changed, 42 insertions, 45 deletions
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index d5728c2cd360..f8bbf397d3a7 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c | |||
@@ -266,7 +266,7 @@ static inline void print_err_status(struct em28xx *dev, | |||
266 | /* | 266 | /* |
267 | * video-buf generic routine to get the next available buffer | 267 | * video-buf generic routine to get the next available buffer |
268 | */ | 268 | */ |
269 | static inline int get_next_buf(struct em28xx_dmaqueue *dma_q, | 269 | static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, |
270 | struct em28xx_buffer **buf) | 270 | struct em28xx_buffer **buf) |
271 | { | 271 | { |
272 | struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); | 272 | struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); |
@@ -275,38 +275,20 @@ static inline int get_next_buf(struct em28xx_dmaqueue *dma_q, | |||
275 | if (list_empty(&dma_q->active)) { | 275 | if (list_empty(&dma_q->active)) { |
276 | em28xx_isocdbg("No active queue to serve\n"); | 276 | em28xx_isocdbg("No active queue to serve\n"); |
277 | dev->isoc_ctl.buf = NULL; | 277 | dev->isoc_ctl.buf = NULL; |
278 | return 0; | ||
279 | } | ||
280 | |||
281 | /* Check if the last buffer were fully filled */ | ||
282 | *buf = dev->isoc_ctl.buf; | ||
283 | |||
284 | /* Nobody is waiting on this buffer - discards */ | ||
285 | if (*buf && !waitqueue_active(&(*buf)->vb.done)) { | ||
286 | dev->isoc_ctl.buf = NULL; | ||
287 | *buf = NULL; | 278 | *buf = NULL; |
279 | return; | ||
288 | } | 280 | } |
289 | 281 | ||
290 | /* Returns the last buffer, to be filled with remaining data */ | ||
291 | if (*buf) | ||
292 | return 1; | ||
293 | |||
294 | /* Get the next buffer */ | 282 | /* Get the next buffer */ |
295 | *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); | 283 | *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); |
296 | 284 | ||
297 | /* Nobody is waiting on the next buffer. returns */ | ||
298 | if (!*buf || !waitqueue_active(&(*buf)->vb.done)) { | ||
299 | em28xx_isocdbg("No active queue to serve\n"); | ||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | /* Cleans up buffer - Usefull for testing for frame/URB loss */ | 285 | /* Cleans up buffer - Usefull for testing for frame/URB loss */ |
304 | outp = videobuf_to_vmalloc(&(*buf)->vb); | 286 | outp = videobuf_to_vmalloc(&(*buf)->vb); |
305 | memset(outp, 0, (*buf)->vb.size); | 287 | memset(outp, 0, (*buf)->vb.size); |
306 | 288 | ||
307 | dev->isoc_ctl.buf = *buf; | 289 | dev->isoc_ctl.buf = *buf; |
308 | 290 | ||
309 | return 1; | 291 | return; |
310 | } | 292 | } |
311 | 293 | ||
312 | /* | 294 | /* |
@@ -317,7 +299,7 @@ static inline int em28xx_isoc_copy(struct urb *urb) | |||
317 | struct em28xx_buffer *buf; | 299 | struct em28xx_buffer *buf; |
318 | struct em28xx_dmaqueue *dma_q = urb->context; | 300 | struct em28xx_dmaqueue *dma_q = urb->context; |
319 | struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); | 301 | struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); |
320 | unsigned char *outp; | 302 | unsigned char *outp = NULL; |
321 | int i, len = 0, rc = 1; | 303 | int i, len = 0, rc = 1; |
322 | unsigned char *p; | 304 | unsigned char *p; |
323 | 305 | ||
@@ -333,11 +315,9 @@ static inline int em28xx_isoc_copy(struct urb *urb) | |||
333 | return 0; | 315 | return 0; |
334 | } | 316 | } |
335 | 317 | ||
336 | rc = get_next_buf(dma_q, &buf); | 318 | buf = dev->isoc_ctl.buf; |
337 | if (rc <= 0) | 319 | if (buf != NULL) |
338 | return rc; | 320 | outp = videobuf_to_vmalloc(&buf->vb); |
339 | |||
340 | outp = videobuf_to_vmalloc(&buf->vb); | ||
341 | 321 | ||
342 | for (i = 0; i < urb->number_of_packets; i++) { | 322 | for (i = 0; i < urb->number_of_packets; i++) { |
343 | int status = urb->iso_frame_desc[i].status; | 323 | int status = urb->iso_frame_desc[i].status; |
@@ -374,24 +354,27 @@ static inline int em28xx_isoc_copy(struct urb *urb) | |||
374 | em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], | 354 | em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], |
375 | len, (p[2] & 1)? "odd" : "even"); | 355 | len, (p[2] & 1)? "odd" : "even"); |
376 | 356 | ||
377 | if (p[2] & 1) | 357 | if (!(p[2] & 1)) { |
378 | buf->top_field = 0; | 358 | if (buf != NULL) |
379 | else | 359 | buffer_filled(dev, dma_q, buf); |
380 | buf->top_field = 1; | 360 | get_next_buf(dma_q, &buf); |
381 | 361 | if (buf == NULL) | |
382 | // if (dev->isoc_ctl.last_field && !buf->top_field) { | 362 | outp = NULL; |
383 | if (dev->isoc_ctl.last_field != buf->top_field) { | 363 | else |
384 | buffer_filled(dev, dma_q, buf); | 364 | outp = videobuf_to_vmalloc(&buf->vb); |
385 | rc = get_next_buf(dma_q, &buf); | 365 | } |
386 | if (rc <= 0) | 366 | |
387 | return rc; | 367 | if (buf != NULL) { |
388 | outp = videobuf_to_vmalloc(&buf->vb); | 368 | if (p[2] & 1) |
369 | buf->top_field = 0; | ||
370 | else | ||
371 | buf->top_field = 1; | ||
389 | } | 372 | } |
390 | dev->isoc_ctl.last_field = buf->top_field; | ||
391 | 373 | ||
392 | dma_q->pos = 0; | 374 | dma_q->pos = 0; |
393 | } | 375 | } |
394 | em28xx_copy_video(dev, dma_q, buf, p, outp, len); | 376 | if (buf != NULL) |
377 | em28xx_copy_video(dev, dma_q, buf, p, outp, len); | ||
395 | } | 378 | } |
396 | return rc; | 379 | return rc; |
397 | } | 380 | } |
@@ -597,12 +580,29 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) | |||
597 | return 0; | 580 | return 0; |
598 | } | 581 | } |
599 | 582 | ||
583 | /* This is called *without* dev->slock held; please keep it that way */ | ||
600 | static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) | 584 | static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) |
601 | { | 585 | { |
586 | struct em28xx_fh *fh = vq->priv_data; | ||
587 | struct em28xx *dev = fh->dev; | ||
588 | unsigned long flags = 0; | ||
602 | if (in_interrupt()) | 589 | if (in_interrupt()) |
603 | BUG(); | 590 | BUG(); |
604 | 591 | ||
605 | videobuf_waiton(&buf->vb, 0, 0); | 592 | /* We used to wait for the buffer to finish here, but this didn't work |
593 | because, as we were keeping the state as VIDEOBUF_QUEUED, | ||
594 | videobuf_queue_cancel marked it as finished for us. | ||
595 | (Also, it could wedge forever if the hardware was misconfigured.) | ||
596 | |||
597 | This should be safe; by the time we get here, the buffer isn't | ||
598 | queued anymore. If we ever start marking the buffers as | ||
599 | VIDEOBUF_ACTIVE, it won't be, though. | ||
600 | */ | ||
601 | spin_lock_irqsave(&dev->slock, flags); | ||
602 | if (dev->isoc_ctl.buf == buf) | ||
603 | dev->isoc_ctl.buf = NULL; | ||
604 | spin_unlock_irqrestore(&dev->slock, flags); | ||
605 | |||
606 | videobuf_vmalloc_free(&buf->vb); | 606 | videobuf_vmalloc_free(&buf->vb); |
607 | buf->vb.state = VIDEOBUF_NEEDS_INIT; | 607 | buf->vb.state = VIDEOBUF_NEEDS_INIT; |
608 | } | 608 | } |
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 993c1ed05cc3..6d62357a038f 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h | |||
@@ -114,9 +114,6 @@ struct em28xx_usb_isoc_ctl { | |||
114 | /* Stores already requested buffers */ | 114 | /* Stores already requested buffers */ |
115 | struct em28xx_buffer *buf; | 115 | struct em28xx_buffer *buf; |
116 | 116 | ||
117 | /* Store last filled frame */ | ||
118 | int last_field; | ||
119 | |||
120 | /* Stores the number of received fields */ | 117 | /* Stores the number of received fields */ |
121 | int nfields; | 118 | int nfields; |
122 | }; | 119 | }; |