Commit 885fe18f authored by Hans de Goede's avatar Hans de Goede Committed by Mauro Carvalho Chehab

[media] pwc: Replace private buffer management code with videobuf2

Looking at the pwc buffer management code has made it clear to me it needed
some serious fixing. Not only was there a ton of code duplication even
internally to pwc (read and mmap wait for frame code was duplicated), the
code also was outright buggy. With the worst offender being dqbuf, which
just round robin returned all the mmap buffers, without paying any attention
to them being queued by the app with qbuf or not. And qbuf itself was a noop.

So I set out to fix this and already had some cleanups in place when
I read Jonathan Corbet's lwn article on videobuf2, this inspired me to just
rip out the buffer management code and replace it with videobuf2, greatly
reducing the amount of code, and fixing all bugs in one go:

Many thanks to Jonathan for the timely article on this !
Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 5f40d915
config USB_PWC config USB_PWC
tristate "USB Philips Cameras" tristate "USB Philips Cameras"
depends on VIDEO_V4L2 depends on VIDEO_V4L2
select VIDEOBUF2_VMALLOC
---help--- ---help---
Say Y or M here if you want to use one of these Philips & OEM Say Y or M here if you want to use one of these Philips & OEM
webcams: webcams:
......
...@@ -511,13 +511,9 @@ unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned i ...@@ -511,13 +511,9 @@ unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned i
return ret; return ret;
} }
#define BLACK_Y 0
#define BLACK_U 128
#define BLACK_V 128
static void pwc_set_image_buffer_size(struct pwc_device *pdev) static void pwc_set_image_buffer_size(struct pwc_device *pdev)
{ {
int i, factor = 0; int factor = 0;
/* for V4L2_PIX_FMT_YUV420 */ /* for V4L2_PIX_FMT_YUV420 */
switch (pdev->pixfmt) { switch (pdev->pixfmt) {
...@@ -541,22 +537,9 @@ static void pwc_set_image_buffer_size(struct pwc_device *pdev) ...@@ -541,22 +537,9 @@ static void pwc_set_image_buffer_size(struct pwc_device *pdev)
*/ */
pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC; pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC;
pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE; pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE;
/* Fill buffers with black colors */
for (i = 0; i < pwc_mbufs; i++) {
unsigned char *p = pdev->image_data + pdev->images[i].offset;
memset(p, BLACK_Y, pdev->view.x * pdev->view.y);
p += pdev->view.x * pdev->view.y;
memset(p, BLACK_U, pdev->view.x * pdev->view.y/4);
p += pdev->view.x * pdev->view.y/4;
memset(p, BLACK_V, pdev->view.x * pdev->view.y/4);
}
} }
/* BRIGHTNESS */ /* BRIGHTNESS */
int pwc_get_brightness(struct pwc_device *pdev) int pwc_get_brightness(struct pwc_device *pdev)
{ {
char buf; char buf;
......
...@@ -116,6 +116,7 @@ MODULE_DEVICE_TABLE(usb, pwc_device_table); ...@@ -116,6 +116,7 @@ MODULE_DEVICE_TABLE(usb, pwc_device_table);
static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id); static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id);
static void usb_pwc_disconnect(struct usb_interface *intf); static void usb_pwc_disconnect(struct usb_interface *intf);
static void pwc_isoc_cleanup(struct pwc_device *pdev);
static struct usb_driver pwc_driver = { static struct usb_driver pwc_driver = {
.name = "Philips webcam", /* name */ .name = "Philips webcam", /* name */
...@@ -129,8 +130,6 @@ static struct usb_driver pwc_driver = { ...@@ -129,8 +130,6 @@ static struct usb_driver pwc_driver = {
static int default_size = PSZ_QCIF; static int default_size = PSZ_QCIF;
static int default_fps = 10; static int default_fps = 10;
static int default_fbufs = 3; /* Default number of frame buffers */
int pwc_mbufs = 2; /* Default number of mmap() buffers */
#ifdef CONFIG_USB_PWC_DEBUG #ifdef CONFIG_USB_PWC_DEBUG
int pwc_trace = PWC_DEBUG_LEVEL; int pwc_trace = PWC_DEBUG_LEVEL;
#endif #endif
...@@ -173,52 +172,6 @@ static struct video_device pwc_template = { ...@@ -173,52 +172,6 @@ static struct video_device pwc_template = {
/***************************************************************************/ /***************************************************************************/
/* Private functions */ /* Private functions */
/* Here we want the physical address of the memory.
* This is used when initializing the contents of the area.
*/
static void *pwc_rvmalloc(unsigned long size)
{
void * mem;
unsigned long adr;
mem=vmalloc_32(size);
if (!mem)
return NULL;
memset(mem, 0, size); /* Clear the ram out, no junk to the user */
adr=(unsigned long) mem;
while (size > 0)
{
SetPageReserved(vmalloc_to_page((void *)adr));
adr += PAGE_SIZE;
size -= PAGE_SIZE;
}
return mem;
}
static void pwc_rvfree(void * mem, unsigned long size)
{
unsigned long adr;
if (!mem)
return;
adr=(unsigned long) mem;
while ((long) size > 0)
{
ClearPageReserved(vmalloc_to_page((void *)adr));
adr += PAGE_SIZE;
size -= PAGE_SIZE;
}
vfree(mem);
}
static int pwc_allocate_buffers(struct pwc_device *pdev) static int pwc_allocate_buffers(struct pwc_device *pdev)
{ {
int i, err; int i, err;
...@@ -239,30 +192,6 @@ static int pwc_allocate_buffers(struct pwc_device *pdev) ...@@ -239,30 +192,6 @@ static int pwc_allocate_buffers(struct pwc_device *pdev)
} }
} }
/* Allocate frame buffer structure */
if (pdev->fbuf == NULL) {
kbuf = kzalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL);
if (kbuf == NULL) {
PWC_ERROR("Failed to allocate frame buffer structure.\n");
return -ENOMEM;
}
PWC_DEBUG_MEMORY("Allocated frame buffer structure at %p.\n", kbuf);
pdev->fbuf = kbuf;
}
/* create frame buffers, and make circular ring */
for (i = 0; i < default_fbufs; i++) {
if (pdev->fbuf[i].data == NULL) {
kbuf = vzalloc(PWC_FRAME_SIZE); /* need vmalloc since frame buffer > 128K */
if (kbuf == NULL) {
PWC_ERROR("Failed to allocate frame buffer %d.\n", i);
return -ENOMEM;
}
PWC_DEBUG_MEMORY("Allocated frame buffer %d at %p.\n", i, kbuf);
pdev->fbuf[i].data = kbuf;
}
}
/* Allocate decompressor table space */ /* Allocate decompressor table space */
if (DEVICE_USE_CODEC1(pdev->type)) if (DEVICE_USE_CODEC1(pdev->type))
err = pwc_dec1_alloc(pdev); err = pwc_dec1_alloc(pdev);
...@@ -274,25 +203,6 @@ static int pwc_allocate_buffers(struct pwc_device *pdev) ...@@ -274,25 +203,6 @@ static int pwc_allocate_buffers(struct pwc_device *pdev)
return err; return err;
} }
/* Allocate image buffer; double buffer for mmap() */
kbuf = pwc_rvmalloc(pwc_mbufs * pdev->len_per_image);
if (kbuf == NULL) {
PWC_ERROR("Failed to allocate image buffer(s). needed (%d)\n",
pwc_mbufs * pdev->len_per_image);
return -ENOMEM;
}
PWC_DEBUG_MEMORY("Allocated image buffer at %p.\n", kbuf);
pdev->image_data = kbuf;
for (i = 0; i < pwc_mbufs; i++) {
pdev->images[i].offset = i * pdev->len_per_image;
pdev->images[i].vma_use_count = 0;
}
for (; i < MAX_IMAGES; i++) {
pdev->images[i].offset = 0;
}
kbuf = NULL;
PWC_DEBUG_MEMORY("<< pwc_allocate_buffers()\n"); PWC_DEBUG_MEMORY("<< pwc_allocate_buffers()\n");
return 0; return 0;
} }
...@@ -311,19 +221,6 @@ static void pwc_free_buffers(struct pwc_device *pdev) ...@@ -311,19 +221,6 @@ static void pwc_free_buffers(struct pwc_device *pdev)
pdev->sbuf[i].data = NULL; pdev->sbuf[i].data = NULL;
} }
/* The same for frame buffers */
if (pdev->fbuf != NULL) {
for (i = 0; i < default_fbufs; i++) {
if (pdev->fbuf[i].data != NULL) {
PWC_DEBUG_MEMORY("Freeing frame buffer %d at %p.\n", i, pdev->fbuf[i].data);
vfree(pdev->fbuf[i].data);
pdev->fbuf[i].data = NULL;
}
}
kfree(pdev->fbuf);
pdev->fbuf = NULL;
}
/* Intermediate decompression buffer & tables */ /* Intermediate decompression buffer & tables */
if (pdev->decompress_data != NULL) { if (pdev->decompress_data != NULL) {
PWC_DEBUG_MEMORY("Freeing decompression buffer at %p.\n", pdev->decompress_data); PWC_DEBUG_MEMORY("Freeing decompression buffer at %p.\n", pdev->decompress_data);
...@@ -331,226 +228,23 @@ static void pwc_free_buffers(struct pwc_device *pdev) ...@@ -331,226 +228,23 @@ static void pwc_free_buffers(struct pwc_device *pdev)
pdev->decompress_data = NULL; pdev->decompress_data = NULL;
} }
/* Release image buffers */
if (pdev->image_data != NULL) {
PWC_DEBUG_MEMORY("Freeing image buffer at %p.\n", pdev->image_data);
pwc_rvfree(pdev->image_data, pwc_mbufs * pdev->len_per_image);
}
pdev->image_data = NULL;
PWC_DEBUG_MEMORY("Leaving free_buffers().\n"); PWC_DEBUG_MEMORY("Leaving free_buffers().\n");
} }
/* The frame & image buffer mess. struct pwc_frame_buf *pwc_get_next_fill_buf(struct pwc_device *pdev)
Yes, this is a mess. Well, it used to be simple, but alas... In this
module, 3 buffers schemes are used to get the data from the USB bus to
the user program. The first scheme involves the ISO buffers (called thus
since they transport ISO data from the USB controller), and not really
interesting. Suffices to say the data from this buffer is quickly
gathered in an interrupt handler (pwc_isoc_handler) and placed into the
frame buffer.
The frame buffer is the second scheme, and is the central element here.
It collects the data from a single frame from the camera (hence, the
name). Frames are delimited by the USB camera with a short USB packet,
so that's easy to detect. The frame buffers form a list that is filled
by the camera+USB controller and drained by the user process through
either read() or mmap().
The image buffer is the third scheme, in which frames are decompressed
and converted into planar format. For mmap() there is more than
one image buffer available.
The frame buffers provide the image buffering. In case the user process
is a bit slow, this introduces lag and some undesired side-effects.
The problem arises when the frame buffer is full. I used to drop the last
frame, which makes the data in the queue stale very quickly. But dropping
the frame at the head of the queue proved to be a litte bit more difficult.
I tried a circular linked scheme, but this introduced more problems than
it solved.
Because filling and draining are completely asynchronous processes, this
requires some fiddling with pointers and mutexes.
Eventually, I came up with a system with 2 lists: an 'empty' frame list
and a 'full' frame list:
* Initially, all frame buffers but one are on the 'empty' list; the one
remaining buffer is our initial fill frame.
* If a frame is needed for filling, we try to take it from the 'empty'
list, unless that list is empty, in which case we take the buffer at
the head of the 'full' list.
* When our fill buffer has been filled, it is appended to the 'full'
list.
* If a frame is needed by read() or mmap(), it is taken from the head of
the 'full' list, handled, and then appended to the 'empty' list. If no
buffer is present on the 'full' list, we wait.
The advantage is that the buffer that is currently being decompressed/
converted, is on neither list, and thus not in our way (any other scheme
I tried had the problem of old data lingering in the queue).
Whatever strategy you choose, it always remains a tradeoff: with more
frame buffers the chances of a missed frame are reduced. On the other
hand, on slower machines it introduces lag because the queue will
always be full.
*/
/**
\brief Find next frame buffer to fill. Take from empty or full list, whichever comes first.
*/
static int pwc_next_fill_frame(struct pwc_device *pdev)
{ {
int ret; unsigned long flags = 0;
unsigned long flags; struct pwc_frame_buf *buf = NULL;
ret = 0; spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
spin_lock_irqsave(&pdev->ptrlock, flags); if (list_empty(&pdev->queued_bufs))
if (pdev->fill_frame != NULL) { goto leave;
/* append to 'full' list */
if (pdev->full_frames == NULL) { buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf, list);
pdev->full_frames = pdev->fill_frame; list_del(&buf->list);
pdev->full_frames_tail = pdev->full_frames; leave:
} spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
else { return buf;
pdev->full_frames_tail->next = pdev->fill_frame;
pdev->full_frames_tail = pdev->fill_frame;
}
}
if (pdev->empty_frames != NULL) {
/* We have empty frames available. That's easy */
pdev->fill_frame = pdev->empty_frames;
pdev->empty_frames = pdev->empty_frames->next;
}
else {
/* Hmm. Take it from the full list */
/* sanity check */
if (pdev->full_frames == NULL) {
PWC_ERROR("Neither empty or full frames available!\n");
spin_unlock_irqrestore(&pdev->ptrlock, flags);
return -EINVAL;
}
pdev->fill_frame = pdev->full_frames;
pdev->full_frames = pdev->full_frames->next;
ret = 1;
}
pdev->fill_frame->next = NULL;
spin_unlock_irqrestore(&pdev->ptrlock, flags);
return ret;
}
/**
\brief Reset all buffers, pointers and lists, except for the image_used[] buffer.
If the image_used[] buffer is cleared too, mmap()/VIDIOCSYNC will run into trouble.
*/
static void pwc_reset_buffers(struct pwc_device *pdev)
{
int i;
unsigned long flags;
PWC_DEBUG_MEMORY(">> %s __enter__\n", __func__);
spin_lock_irqsave(&pdev->ptrlock, flags);
pdev->full_frames = NULL;
pdev->full_frames_tail = NULL;
for (i = 0; i < default_fbufs; i++) {
pdev->fbuf[i].filled = 0;
if (i > 0)
pdev->fbuf[i].next = &pdev->fbuf[i - 1];
else
pdev->fbuf->next = NULL;
}
pdev->empty_frames = &pdev->fbuf[default_fbufs - 1];
pdev->empty_frames_tail = pdev->fbuf;
pdev->read_frame = NULL;
pdev->fill_frame = pdev->empty_frames;
pdev->empty_frames = pdev->empty_frames->next;
pdev->image_read_pos = 0;
pdev->fill_image = 0;
spin_unlock_irqrestore(&pdev->ptrlock, flags);
PWC_DEBUG_MEMORY("<< %s __leaving__\n", __func__);
}
/**
\brief Do all the handling for getting one frame: get pointer, decompress, advance pointers.
*/
int pwc_handle_frame(struct pwc_device *pdev)
{
int ret = 0;
unsigned long flags;
spin_lock_irqsave(&pdev->ptrlock, flags);
/* First grab our read_frame; this is removed from all lists, so
we can release the lock after this without problems */
if (pdev->read_frame != NULL) {
/* This can't theoretically happen */
PWC_ERROR("Huh? Read frame still in use?\n");
spin_unlock_irqrestore(&pdev->ptrlock, flags);
return ret;
}
if (pdev->full_frames == NULL) {
PWC_ERROR("Woops. No frames ready.\n");
}
else {
pdev->read_frame = pdev->full_frames;
pdev->full_frames = pdev->full_frames->next;
pdev->read_frame->next = NULL;
}
if (pdev->read_frame != NULL) {
/* Decompression is a lengthy process, so it's outside of the lock.
This gives the isoc_handler the opportunity to fill more frames
in the mean time.
*/
spin_unlock_irqrestore(&pdev->ptrlock, flags);
ret = pwc_decompress(pdev);
spin_lock_irqsave(&pdev->ptrlock, flags);
/* We're done with read_buffer, tack it to the end of the empty buffer list */
if (pdev->empty_frames == NULL) {
pdev->empty_frames = pdev->read_frame;
pdev->empty_frames_tail = pdev->empty_frames;
}
else {
pdev->empty_frames_tail->next = pdev->read_frame;
pdev->empty_frames_tail = pdev->read_frame;
}
pdev->read_frame = NULL;
}
spin_unlock_irqrestore(&pdev->ptrlock, flags);
return ret;
}
/**
\brief Advance pointers of image buffer (after each user request)
*/
void pwc_next_image(struct pwc_device *pdev)
{
pdev->image_used[pdev->fill_image] = 0;
pdev->fill_image = (pdev->fill_image + 1) % pwc_mbufs;
}
/**
* Print debug information when a frame is discarded because all of our buffer
* is full
*/
static void pwc_frame_dumped(struct pwc_device *pdev)
{
pdev->vframes_dumped++;
if (pdev->vframe_count < FRAME_LOWMARK)
return;
if (pdev->vframes_dumped < 20)
PWC_DEBUG_FLOW("Dumping frame %d\n", pdev->vframe_count);
else if (pdev->vframes_dumped == 20)
PWC_DEBUG_FLOW("Dumping frame %d (last message)\n",
pdev->vframe_count);
} }
static void pwc_snapshot_button(struct pwc_device *pdev, int down) static void pwc_snapshot_button(struct pwc_device *pdev, int down)
...@@ -570,9 +264,9 @@ static void pwc_snapshot_button(struct pwc_device *pdev, int down) ...@@ -570,9 +264,9 @@ static void pwc_snapshot_button(struct pwc_device *pdev, int down)
#endif #endif
} }
static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_buf *fbuf) static void pwc_frame_complete(struct pwc_device *pdev)
{ {
int awake = 0; struct pwc_frame_buf *fbuf = pdev->fill_buf;
/* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus
frames on the USB wire after an exposure change. This conditition is frames on the USB wire after an exposure change. This conditition is
...@@ -584,7 +278,6 @@ static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_ ...@@ -584,7 +278,6 @@ static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_
if (ptr[1] == 1 && ptr[0] & 0x10) { if (ptr[1] == 1 && ptr[0] & 0x10) {
PWC_TRACE("Hyundai CMOS sensor bug. Dropping frame.\n"); PWC_TRACE("Hyundai CMOS sensor bug. Dropping frame.\n");
pdev->drop_frames += 2; pdev->drop_frames += 2;
pdev->vframes_error++;
} }
if ((ptr[0] ^ pdev->vmirror) & 0x01) { if ((ptr[0] ^ pdev->vmirror) & 0x01) {
pwc_snapshot_button(pdev, ptr[0] & 0x01); pwc_snapshot_button(pdev, ptr[0] & 0x01);
...@@ -607,8 +300,7 @@ static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_ ...@@ -607,8 +300,7 @@ static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_
*/ */
if (fbuf->filled == 4) if (fbuf->filled == 4)
pdev->drop_frames++; pdev->drop_frames++;
} } else if (pdev->type == 740 || pdev->type == 720) {
else if (pdev->type == 740 || pdev->type == 720) {
unsigned char *ptr = (unsigned char *)fbuf->data; unsigned char *ptr = (unsigned char *)fbuf->data;
if ((ptr[0] ^ pdev->vmirror) & 0x01) { if ((ptr[0] ^ pdev->vmirror) & 0x01) {
pwc_snapshot_button(pdev, ptr[0] & 0x01); pwc_snapshot_button(pdev, ptr[0] & 0x01);
...@@ -616,33 +308,23 @@ static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_ ...@@ -616,33 +308,23 @@ static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_
pdev->vmirror = ptr[0] & 0x03; pdev->vmirror = ptr[0] & 0x03;
} }
/* In case we were instructed to drop the frame, do so silently. /* In case we were instructed to drop the frame, do so silently. */
The buffer pointers are not updated either (but the counters are reset below). if (pdev->drop_frames > 0) {
*/
if (pdev->drop_frames > 0)
pdev->drop_frames--; pdev->drop_frames--;
else { } else {
/* Check for underflow first */ /* Check for underflow first */
if (fbuf->filled < pdev->frame_total_size) { if (fbuf->filled < pdev->frame_total_size) {
PWC_DEBUG_FLOW("Frame buffer underflow (%d bytes);" PWC_DEBUG_FLOW("Frame buffer underflow (%d bytes);"
" discarded.\n", fbuf->filled); " discarded.\n", fbuf->filled);
pdev->vframes_error++; } else {
} fbuf->vb.v4l2_buf.field = V4L2_FIELD_NONE;
else { fbuf->vb.v4l2_buf.sequence = pdev->vframe_count;
/* Send only once per EOF */ vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE);
awake = 1; /* delay wake_ups */ pdev->fill_buf = NULL;
pdev->vsync = 0;
/* Find our next frame to fill. This will always succeed, since we
* nick a frame from either empty or full list, but if we had to
* take it from the full list, it means a frame got dropped.
*/
if (pwc_next_fill_frame(pdev))
pwc_frame_dumped(pdev);
} }
} /* !drop_frames */ } /* !drop_frames */
pdev->vframe_count++; pdev->vframe_count++;
return awake;
} }
/* This gets called for the Isochronous pipe (video). This is done in /* This gets called for the Isochronous pipe (video). This is done in
...@@ -650,24 +332,20 @@ static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_ ...@@ -650,24 +332,20 @@ static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_
*/ */
static void pwc_isoc_handler(struct urb *urb) static void pwc_isoc_handler(struct urb *urb)
{ {
struct pwc_device *pdev; struct pwc_device *pdev = (struct pwc_device *)urb->context;
int i, fst, flen; int i, fst, flen;
int awake; unsigned char *iso_buf = NULL;
struct pwc_frame_buf *fbuf;
unsigned char *fillptr = NULL, *iso_buf = NULL;
awake = 0; if (urb->status == -ENOENT || urb->status == -ECONNRESET ||
pdev = (struct pwc_device *)urb->context; urb->status == -ESHUTDOWN) {
if (pdev == NULL) {
PWC_ERROR("isoc_handler() called with NULL device?!\n");
return;
}
if (urb->status == -ENOENT || urb->status == -ECONNRESET) {
PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a"); PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a");
return; return;
} }
if (urb->status != -EINPROGRESS && urb->status != 0) {
if (pdev->fill_buf == NULL)
pdev->fill_buf = pwc_get_next_fill_buf(pdev);
if (urb->status != 0) {
const char *errmsg; const char *errmsg;
errmsg = "Unknown"; errmsg = "Unknown";
...@@ -679,29 +357,21 @@ static void pwc_isoc_handler(struct urb *urb) ...@@ -679,29 +357,21 @@ static void pwc_isoc_handler(struct urb *urb)
case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break; case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break;
case -ETIME: errmsg = "Device does not respond"; break; case -ETIME: errmsg = "Device does not respond"; break;
} }
PWC_DEBUG_FLOW("pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg); PWC_ERROR("pwc_isoc_handler() called with status %d [%s].\n",
/* Give up after a number of contiguous errors on the USB bus. urb->status, errmsg);
Appearantly something is wrong so we simulate an unplug event. /* Give up after a number of contiguous errors */
*/
if (++pdev->visoc_errors > MAX_ISOC_ERRORS) if (++pdev->visoc_errors > MAX_ISOC_ERRORS)
{ {
PWC_INFO("Too many ISOC errors, bailing out.\n"); PWC_ERROR("Too many ISOC errors, bailing out.\n");
pdev->error_status = EIO; if (pdev->fill_buf) {
awake = 1; vb2_buffer_done(&pdev->fill_buf->vb,
wake_up_interruptible(&pdev->frameq); VB2_BUF_STATE_ERROR);
pdev->fill_buf = NULL;
} }
goto handler_end; // ugly, but practical
} }
pdev->vsync = 0; /* Drop the current frame */
fbuf = pdev->fill_frame;
if (fbuf == NULL) {
PWC_ERROR("pwc_isoc_handler without valid fill frame.\n");
awake = 1;
goto handler_end; goto handler_end;
} }
else {
fillptr = fbuf->data + fbuf->filled;
}
/* Reset ISOC error counter. We did get here, after all. */ /* Reset ISOC error counter. We did get here, after all. */
pdev->visoc_errors = 0; pdev->visoc_errors = 0;
...@@ -715,65 +385,50 @@ static void pwc_isoc_handler(struct urb *urb) ...@@ -715,65 +385,50 @@ static void pwc_isoc_handler(struct urb *urb)
fst = urb->iso_frame_desc[i].status; fst = urb->iso_frame_desc[i].status;
flen = urb->iso_frame_desc[i].actual_length; flen = urb->iso_frame_desc[i].actual_length;
iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
if (fst == 0) { if (fst != 0) {
if (flen > 0) { /* if valid data... */ PWC_ERROR("Iso frame %d has error %d\n", i, fst);
if (pdev->vsync > 0) { /* ...and we are not sync-hunting... */ continue;
}
if (flen > 0 && pdev->vsync) {
struct pwc_frame_buf *fbuf = pdev->fill_buf;
if (pdev->vsync == 1) {
do_gettimeofday(&fbuf->vb.v4l2_buf.timestamp);
pdev->vsync = 2; pdev->vsync = 2;
}
/* ...copy data to frame buffer, if possible */
if (flen + fbuf->filled > pdev->frame_total_size) { if (flen + fbuf->filled > pdev->frame_total_size) {
PWC_DEBUG_FLOW("Frame buffer overflow (flen = %d, frame_total_size = %d).\n", flen, pdev->frame_total_size); PWC_ERROR("Frame overflow (%d > %d)\n",
pdev->vsync = 0; /* Hmm, let's wait for an EOF (end-of-frame) */ flen + fbuf->filled,
pdev->vframes_error++; pdev->frame_total_size);
} pdev->vsync = 0; /* Let's wait for an EOF */
else { } else {
memmove(fillptr, iso_buf, flen); memcpy(fbuf->data + fbuf->filled, iso_buf,
fillptr += flen; flen);
}
}
fbuf->filled += flen; fbuf->filled += flen;
} /* ..flen > 0 */
if (flen < pdev->vlast_packet_size) {
/* Shorter packet... We probably have the end of an image-frame;
wake up read() process and let select()/poll() do something.
Decompression is done in user time over there.
*/
if (pdev->vsync == 2) {
if (pwc_rcv_short_packet(pdev, fbuf)) {
awake = 1;
fbuf = pdev->fill_frame;
} }
} }
fbuf->filled = 0; if (flen < pdev->vlast_packet_size) {
fillptr = fbuf->data; /* Shorter packet... end of frame */
if (pdev->vsync == 2)
pwc_frame_complete(pdev);
if (pdev->fill_buf == NULL)
pdev->fill_buf = pwc_get_next_fill_buf(pdev);
if (pdev->fill_buf) {
pdev->fill_buf->filled = 0;
pdev->vsync = 1; pdev->vsync = 1;
} }
pdev->vlast_packet_size = flen;
} /* ..status == 0 */
else {
/* This is normally not interesting to the user, unless
* you are really debugging something, default = 0 */
static int iso_error;
iso_error++;
if (iso_error < 20)
PWC_DEBUG_FLOW("Iso frame %d of USB has error %d\n", i, fst);
} }
pdev->vlast_packet_size = flen;
} }
handler_end: handler_end:
if (awake)
wake_up_interruptible(&pdev->frameq);
urb->dev = pdev->udev;
i = usb_submit_urb(urb, GFP_ATOMIC); i = usb_submit_urb(urb, GFP_ATOMIC);
if (i != 0) if (i != 0)
PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i); PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
} }
static int pwc_isoc_init(struct pwc_device *pdev)
int pwc_isoc_init(struct pwc_device *pdev)
{ {
struct usb_device *udev; struct usb_device *udev;
struct urb *urb; struct urb *urb;
...@@ -784,6 +439,8 @@ int pwc_isoc_init(struct pwc_device *pdev) ...@@ -784,6 +439,8 @@ int pwc_isoc_init(struct pwc_device *pdev)
if (pdev->iso_init) if (pdev->iso_init)
return 0; return 0;
pdev->vsync = 0; pdev->vsync = 0;
pdev->fill_buf = NULL;
pdev->vframe_count = 0;
udev = pdev->udev; udev = pdev->udev;
/* Get the current alternate interface, adjust packet size */ /* Get the current alternate interface, adjust packet size */
...@@ -904,7 +561,7 @@ static void pwc_iso_free(struct pwc_device *pdev) ...@@ -904,7 +561,7 @@ static void pwc_iso_free(struct pwc_device *pdev)
} }
} }
void pwc_isoc_cleanup(struct pwc_device *pdev) static void pwc_isoc_cleanup(struct pwc_device *pdev)
{ {
PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n"); PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n");
...@@ -926,6 +583,22 @@ void pwc_isoc_cleanup(struct pwc_device *pdev) ...@@ -926,6 +583,22 @@ void pwc_isoc_cleanup(struct pwc_device *pdev)
PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n"); PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n");
} }
/*
* Release all queued buffers, no need to take queued_bufs_lock, since all
* iso urbs have been killed when we're called so pwc_isoc_handler won't run.
*/
static void pwc_cleanup_queued_bufs(struct pwc_device *pdev)
{
while (!list_empty(&pdev->queued_bufs)) {
struct pwc_frame_buf *buf;
buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf,
list);
list_del(&buf->list);
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
}
}
/********* /*********
* sysfs * sysfs
*********/ *********/
...@@ -1086,12 +759,6 @@ static int pwc_video_open(struct file *file) ...@@ -1086,12 +759,6 @@ static int pwc_video_open(struct file *file)
} }
/* Reset buffers & parameters */ /* Reset buffers & parameters */
pwc_reset_buffers(pdev);
for (i = 0; i < pwc_mbufs; i++)
pdev->image_used[i] = 0;
pdev->vframe_count = 0;
pdev->vframes_dumped = 0;
pdev->vframes_error = 0;
pdev->visoc_errors = 0; pdev->visoc_errors = 0;
pdev->error_status = 0; pdev->error_status = 0;
pwc_construct(pdev); /* set min/max sizes correct */ pwc_construct(pdev); /* set min/max sizes correct */
...@@ -1161,19 +828,12 @@ static int pwc_video_close(struct file *file) ...@@ -1161,19 +828,12 @@ static int pwc_video_close(struct file *file)
if (pdev->vopen == 0) if (pdev->vopen == 0)
PWC_DEBUG_MODULE("video_close() called on closed device?\n"); PWC_DEBUG_MODULE("video_close() called on closed device?\n");
/* Dump statistics, but only if a reasonable amount of frames were
processed (to prevent endless log-entries in case of snap-shot
programs)
*/
if (pdev->vframe_count > 20)
PWC_DEBUG_MODULE("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error);
if (DEVICE_USE_CODEC1(pdev->type)) if (DEVICE_USE_CODEC1(pdev->type))
pwc_dec1_exit(); pwc_dec1_exit();
else else
pwc_dec23_exit(); pwc_dec23_exit();
pwc_isoc_cleanup(pdev); vb2_queue_release(&pdev->vb_queue);
pwc_free_buffers(pdev); pwc_free_buffers(pdev);
/* Turn off LEDS and power down camera, but only when not unplugged */ /* Turn off LEDS and power down camera, but only when not unplugged */
...@@ -1193,182 +853,155 @@ static int pwc_video_close(struct file *file) ...@@ -1193,182 +853,155 @@ static int pwc_video_close(struct file *file)
return 0; return 0;
} }
/*
* FIXME: what about two parallel reads ????
* ANSWER: Not supported. You can't open the device more than once,
despite what the V4L1 interface says. First, I don't see
the need, second there's no mechanism of alerting the
2nd/3rd/... process of events like changing image size.
And I don't see the point of blocking that for the
2nd/3rd/... process.
In multi-threaded environments reading parallel from any
device is tricky anyhow.
*/
static ssize_t pwc_video_read(struct file *file, char __user *buf, static ssize_t pwc_video_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
struct video_device *vdev = file->private_data; struct video_device *vdev = file->private_data;
struct pwc_device *pdev; struct pwc_device *pdev = video_get_drvdata(vdev);
int noblock = file->f_flags & O_NONBLOCK;
DECLARE_WAITQUEUE(wait, current);
int bytes_to_read, rv = 0;
void *image_buffer_addr;
PWC_DEBUG_READ("pwc_video_read(vdev=0x%p, buf=%p, count=%zd) called.\n", if (pdev->error_status)
vdev, buf, count); return -pdev->error_status;
pdev = video_get_drvdata(vdev);
if (pdev->error_status) { return vb2_read(&pdev->vb_queue, buf, count, ppos,
rv = -pdev->error_status; /* Something happened, report what. */ file->f_flags & O_NONBLOCK);
goto err_out; }
}
/* Start the stream (if not already started) */
rv = pwc_isoc_init(pdev);
if (rv)
goto err_out;
/* In case we're doing partial reads, we don't have to wait for a frame */
if (pdev->image_read_pos == 0) {
/* Do wait queueing according to the (doc)book */
add_wait_queue(&pdev->frameq, &wait);
while (pdev->full_frames == NULL) {
/* Check for unplugged/etc. here */
if (pdev->error_status) {
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
rv = -pdev->error_status ;
goto err_out;
}
if (noblock) {
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
rv = -EWOULDBLOCK;
goto err_out;
}
if (signal_pending(current)) {
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
rv = -ERESTARTSYS;
goto err_out;
}
mutex_unlock(&pdev->modlock);
schedule();
set_current_state(TASK_INTERRUPTIBLE);
mutex_lock(&pdev->modlock);
}
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
/* Decompress and release frame */ static unsigned int pwc_video_poll(struct file *file, poll_table *wait)
rv = pwc_handle_frame(pdev); {
if (rv) struct video_device *vdev = file->private_data;
goto err_out; struct pwc_device *pdev = video_get_drvdata(vdev);
}
PWC_DEBUG_READ("Copying data to user space.\n"); if (pdev->error_status)
if (pdev->pixfmt != V4L2_PIX_FMT_YUV420) return POLL_ERR;
bytes_to_read = pdev->frame_size + sizeof(struct pwc_raw_frame);
else return vb2_poll(&pdev->vb_queue, file, wait);
bytes_to_read = pdev->view.size;
/* copy bytes to user space; we allow for partial reads */
if (count + pdev->image_read_pos > bytes_to_read)
count = bytes_to_read - pdev->image_read_pos;
image_buffer_addr = pdev->image_data;
image_buffer_addr += pdev->images[pdev->fill_image].offset;
image_buffer_addr += pdev->image_read_pos;
if (copy_to_user(buf, image_buffer_addr, count)) {
rv = -EFAULT;
goto err_out;
}
pdev->image_read_pos += count;
if (pdev->image_read_pos >= bytes_to_read) { /* All data has been read */
pdev->image_read_pos = 0;
pwc_next_image(pdev);
}
return count;
err_out:
return rv;
} }
static unsigned int pwc_video_poll(struct file *file, poll_table *wait) static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
{ {
struct video_device *vdev = file->private_data; struct video_device *vdev = file->private_data;
struct pwc_device *pdev; struct pwc_device *pdev = video_get_drvdata(vdev);
int ret;
pdev = video_get_drvdata(vdev); return vb2_mmap(&pdev->vb_queue, vma);
}
/* Start the stream (if not already started) */ /***************************************************************************/
ret = pwc_isoc_init(pdev); /* Videobuf2 operations */
if (ret)
return ret;
poll_wait(file, &pdev->frameq, wait); static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
if (pdev->error_status) unsigned int *nplanes, unsigned long sizes[],
return POLLERR; void *alloc_ctxs[])
if (pdev->full_frames != NULL) /* we have frames waiting */ {
return (POLLIN | POLLRDNORM); struct pwc_device *pdev = vb2_get_drv_priv(vq);
if (*nbuffers < MIN_FRAMES)
*nbuffers = MIN_FRAMES;
else if (*nbuffers > MAX_FRAMES)
*nbuffers = MAX_FRAMES;
*nplanes = 1;
sizes[0] = PAGE_ALIGN((pdev->abs_max.x * pdev->abs_max.y * 3) / 2);
return 0; return 0;
} }
static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) static int buffer_init(struct vb2_buffer *vb)
{ {
struct video_device *vdev = file->private_data; struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
struct pwc_device *pdev;
unsigned long start;
unsigned long size;
unsigned long page, pos = 0;
int index;
PWC_DEBUG_MEMORY(">> %s\n", __func__); /* need vmalloc since frame buffer > 128K */
pdev = video_get_drvdata(vdev); buf->data = vzalloc(PWC_FRAME_SIZE);
size = vma->vm_end - vma->vm_start; if (buf->data == NULL)
start = vma->vm_start; return -ENOMEM;
return 0;
}
static int buffer_prepare(struct vb2_buffer *vb)
{
struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
/* Don't allow queing new buffers after device disconnection */
if (pdev->error_status)
return -pdev->error_status;
return 0;
}
static int buffer_finish(struct vb2_buffer *vb)
{
struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
/* Find the idx buffer for this mapping */
for (index = 0; index < pwc_mbufs; index++) {
pos = pdev->images[index].offset;
if ((pos>>PAGE_SHIFT) == vma->vm_pgoff)
break;
}
if (index == MAX_IMAGES)
return -EINVAL;
if (index == 0) {
/* /*
* Special case for v4l1. In v4l1, we map only one big buffer, * Application has called dqbuf and is getting back a buffer we've
* but in v4l2 each buffer is mapped * filled, take the pwc data we've stored in buf->data and decompress
* it into a usable format, storing the result in the vb2_buffer
*/ */
unsigned long total_size; return pwc_decompress(pdev, buf);
total_size = pwc_mbufs * pdev->len_per_image; }
if (size != pdev->len_per_image && size != total_size) {
PWC_ERROR("Wrong size (%lu) needed to be len_per_image=%d or total_size=%lu\n", static void buffer_cleanup(struct vb2_buffer *vb)
size, pdev->len_per_image, total_size); {
return -EINVAL; struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
}
} else if (size > pdev->len_per_image) vfree(buf->data);
return -EINVAL; }
static void buffer_queue(struct vb2_buffer *vb)
{
struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
unsigned long flags = 0;
spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
list_add_tail(&buf->list, &pdev->queued_bufs);
spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
}
static int start_streaming(struct vb2_queue *vq)
{
struct pwc_device *pdev = vb2_get_drv_priv(vq);
return pwc_isoc_init(pdev);
}
static int stop_streaming(struct vb2_queue *vq)
{
struct pwc_device *pdev = vb2_get_drv_priv(vq);
pwc_isoc_cleanup(pdev);
pwc_cleanup_queued_bufs(pdev);
vma->vm_flags |= VM_IO; /* from 2.6.9-acX */
pos += (unsigned long)pdev->image_data;
while (size > 0) {
page = vmalloc_to_pfn((void *)pos);
if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
return -EAGAIN;
start += PAGE_SIZE;
pos += PAGE_SIZE;
if (size > PAGE_SIZE)
size -= PAGE_SIZE;
else
size = 0;
}
return 0; return 0;
} }
static void pwc_lock(struct vb2_queue *vq)
{
struct pwc_device *pdev = vb2_get_drv_priv(vq);
mutex_lock(&pdev->modlock);
}
static void pwc_unlock(struct vb2_queue *vq)
{
struct pwc_device *pdev = vb2_get_drv_priv(vq);
mutex_unlock(&pdev->modlock);
}
static struct vb2_ops pwc_vb_queue_ops = {
.queue_setup = queue_setup,
.buf_init = buffer_init,
.buf_prepare = buffer_prepare,
.buf_finish = buffer_finish,
.buf_cleanup = buffer_cleanup,
.buf_queue = buffer_queue,
.start_streaming = start_streaming,
.stop_streaming = stop_streaming,
.wait_prepare = pwc_unlock,
.wait_finish = pwc_lock,
};
/***************************************************************************/ /***************************************************************************/
/* USB functions */ /* USB functions */
...@@ -1648,12 +1281,22 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id ...@@ -1648,12 +1281,22 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
} }
mutex_init(&pdev->modlock); mutex_init(&pdev->modlock);
spin_lock_init(&pdev->ptrlock); spin_lock_init(&pdev->queued_bufs_lock);
INIT_LIST_HEAD(&pdev->queued_bufs);
pdev->udev = udev; pdev->udev = udev;
init_waitqueue_head(&pdev->frameq);
pdev->vcompression = pwc_preferred_compression; pdev->vcompression = pwc_preferred_compression;
/* Init videobuf2 queue structure */
memset(&pdev->vb_queue, 0, sizeof(pdev->vb_queue));
pdev->vb_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
pdev->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
pdev->vb_queue.drv_priv = pdev;
pdev->vb_queue.buf_struct_size = sizeof(struct pwc_frame_buf);
pdev->vb_queue.ops = &pwc_vb_queue_ops;
pdev->vb_queue.mem_ops = &vb2_vmalloc_memops;
vb2_queue_init(&pdev->vb_queue);
/* Init video_device structure */ /* Init video_device structure */
memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template)); memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template));
pdev->vdev.parent = &intf->dev; pdev->vdev.parent = &intf->dev;
...@@ -1752,11 +1395,9 @@ static void usb_pwc_disconnect(struct usb_interface *intf) ...@@ -1752,11 +1395,9 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
pdev->error_status = EPIPE; pdev->error_status = EPIPE;
pdev->unplugged = 1; pdev->unplugged = 1;
/* Alert waiting processes */
wake_up_interruptible(&pdev->frameq);
/* No need to keep the urbs around after disconnection */ /* No need to keep the urbs around after disconnection */
pwc_isoc_cleanup(pdev); pwc_isoc_cleanup(pdev);
pwc_cleanup_queued_bufs(pdev);
mutex_unlock(&pdev->modlock); mutex_unlock(&pdev->modlock);
...@@ -1776,8 +1417,6 @@ static void usb_pwc_disconnect(struct usb_interface *intf) ...@@ -1776,8 +1417,6 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
static char *size; static char *size;
static int fps; static int fps;
static int fbufs;
static int mbufs;
static int compression = -1; static int compression = -1;
static int leds[2] = { -1, -1 }; static int leds[2] = { -1, -1 };
static unsigned int leds_nargs; static unsigned int leds_nargs;
...@@ -1786,8 +1425,6 @@ static unsigned int dev_hint_nargs; ...@@ -1786,8 +1425,6 @@ static unsigned int dev_hint_nargs;
module_param(size, charp, 0444); module_param(size, charp, 0444);
module_param(fps, int, 0444); module_param(fps, int, 0444);
module_param(fbufs, int, 0444);
module_param(mbufs, int, 0444);
#ifdef CONFIG_USB_PWC_DEBUG #ifdef CONFIG_USB_PWC_DEBUG
module_param_named(trace, pwc_trace, int, 0644); module_param_named(trace, pwc_trace, int, 0644);
#endif #endif
...@@ -1798,8 +1435,6 @@ module_param_array(dev_hint, charp, &dev_hint_nargs, 0444); ...@@ -1798,8 +1435,6 @@ module_param_array(dev_hint, charp, &dev_hint_nargs, 0444);
MODULE_PARM_DESC(size, "Initial image size. One of sqcif, qsif, qcif, sif, cif, vga"); MODULE_PARM_DESC(size, "Initial image size. One of sqcif, qsif, qcif, sif, cif, vga");
MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 5-30"); MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 5-30");
MODULE_PARM_DESC(fbufs, "Number of internal frame buffers to reserve");
MODULE_PARM_DESC(mbufs, "Number of external (mmap()ed) image buffers");
#ifdef CONFIG_USB_PWC_DEBUG #ifdef CONFIG_USB_PWC_DEBUG
MODULE_PARM_DESC(trace, "For debugging purposes"); MODULE_PARM_DESC(trace, "For debugging purposes");
#endif #endif
...@@ -1847,22 +1482,6 @@ static int __init usb_pwc_init(void) ...@@ -1847,22 +1482,6 @@ static int __init usb_pwc_init(void)
} }
PWC_DEBUG_MODULE("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y); PWC_DEBUG_MODULE("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y);
} }
if (mbufs) {
if (mbufs < 1 || mbufs > MAX_IMAGES) {
PWC_ERROR("Illegal number of mmap() buffers; use a number between 1 and %d.\n", MAX_IMAGES);
return -EINVAL;
}
pwc_mbufs = mbufs;
PWC_DEBUG_MODULE("Number of image buffers set to %d.\n", pwc_mbufs);
}
if (fbufs) {
if (fbufs < 2 || fbufs > MAX_FRAMES) {
PWC_ERROR("Illegal number of frame buffers; use a number between 2 and %d.\n", MAX_FRAMES);
return -EINVAL;
}
default_fbufs = fbufs;
PWC_DEBUG_MODULE("Number of frame buffers set to %d.\n", default_fbufs);
}
#ifdef CONFIG_USB_PWC_DEBUG #ifdef CONFIG_USB_PWC_DEBUG
if (pwc_trace >= 0) { if (pwc_trace >= 0) {
PWC_DEBUG_MODULE("Trace options: 0x%04x\n", pwc_trace); PWC_DEBUG_MODULE("Trace options: 0x%04x\n", pwc_trace);
......
...@@ -126,8 +126,4 @@ void pwc_construct(struct pwc_device *pdev) ...@@ -126,8 +126,4 @@ void pwc_construct(struct pwc_device *pdev)
pdev->pixfmt = V4L2_PIX_FMT_YUV420; /* default */ pdev->pixfmt = V4L2_PIX_FMT_YUV420; /* default */
pdev->view_min.size = pdev->view_min.x * pdev->view_min.y; pdev->view_min.size = pdev->view_min.x * pdev->view_min.y;
pdev->view_max.size = pdev->view_max.x * pdev->view_max.y; pdev->view_max.size = pdev->view_max.x * pdev->view_max.y;
/* length of image, in YUV format; always allocate enough memory. */
pdev->len_per_image = PAGE_ALIGN((pdev->abs_max.x * pdev->abs_max.y * 3) / 2);
} }
...@@ -34,17 +34,14 @@ ...@@ -34,17 +34,14 @@
#include "pwc-dec1.h" #include "pwc-dec1.h"
#include "pwc-dec23.h" #include "pwc-dec23.h"
int pwc_decompress(struct pwc_device *pdev) int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf)
{ {
struct pwc_frame_buf *fbuf;
int n, line, col, stride; int n, line, col, stride;
void *yuv, *image; void *yuv, *image;
u16 *src; u16 *src;
u16 *dsty, *dstu, *dstv; u16 *dsty, *dstu, *dstv;
fbuf = pdev->read_frame; image = vb2_plane_vaddr(&fbuf->vb, 0);
image = pdev->image_data;
image += pdev->images[pdev->fill_image].offset;
yuv = fbuf->data + pdev->frame_header_size; /* Skip header */ yuv = fbuf->data + pdev->frame_header_size; /* Skip header */
...@@ -59,9 +56,13 @@ int pwc_decompress(struct pwc_device *pdev) ...@@ -59,9 +56,13 @@ int pwc_decompress(struct pwc_device *pdev)
* determine this using the type of the webcam */ * determine this using the type of the webcam */
memcpy(raw_frame->cmd, pdev->cmd_buf, 4); memcpy(raw_frame->cmd, pdev->cmd_buf, 4);
memcpy(raw_frame+1, yuv, pdev->frame_size); memcpy(raw_frame+1, yuv, pdev->frame_size);
vb2_set_plane_payload(&fbuf->vb, 0,
pdev->frame_size + sizeof(struct pwc_raw_frame));
return 0; return 0;
} }
vb2_set_plane_payload(&fbuf->vb, 0, pdev->view.size);
if (pdev->vbandlength == 0) { if (pdev->vbandlength == 0) {
/* Uncompressed mode. /* Uncompressed mode.
* We copy the data into the output buffer, using the viewport * We copy the data into the output buffer, using the viewport
......
...@@ -309,7 +309,7 @@ static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f) ...@@ -309,7 +309,7 @@ static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f)
pixelformat != V4L2_PIX_FMT_PWC2) pixelformat != V4L2_PIX_FMT_PWC2)
return -EINVAL; return -EINVAL;
if (pdev->iso_init) if (vb2_is_streaming(&pdev->vb_queue))
return -EBUSY; return -EBUSY;
PWC_DEBUG_IOCTL("Trying to set format to: width=%d height=%d fps=%d " PWC_DEBUG_IOCTL("Trying to set format to: width=%d height=%d fps=%d "
...@@ -673,150 +673,47 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) ...@@ -673,150 +673,47 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
return pwc_vidioc_set_fmt(pdev, f); return pwc_vidioc_set_fmt(pdev, f);
} }
static int pwc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb) static int pwc_reqbufs(struct file *file, void *fh,
struct v4l2_requestbuffers *rb)
{ {
int nbuffers; struct pwc_device *pdev = video_drvdata(file);
PWC_DEBUG_IOCTL("ioctl(VIDIOC_REQBUFS) count=%d\n", rb->count);
if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (rb->memory != V4L2_MEMORY_MMAP)
return -EINVAL;
nbuffers = rb->count; return vb2_reqbufs(&pdev->vb_queue, rb);
if (nbuffers < 2)
nbuffers = 2;
else if (nbuffers > pwc_mbufs)
nbuffers = pwc_mbufs;
/* Force to use our # of buffers */
rb->count = pwc_mbufs;
return 0;
} }
static int pwc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) static int pwc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
{ {
struct pwc_device *pdev = video_drvdata(file); struct pwc_device *pdev = video_drvdata(file);
int index;
PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) index=%d\n", buf->index);
if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad type\n");
return -EINVAL;
}
index = buf->index;
if (index < 0 || index >= pwc_mbufs) {
PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad index %d\n", buf->index);
return -EINVAL;
}
buf->m.offset = index * pdev->len_per_image;
if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
else
buf->bytesused = pdev->view.size;
buf->field = V4L2_FIELD_NONE;
buf->memory = V4L2_MEMORY_MMAP;
/*buf->flags = V4L2_BUF_FLAG_MAPPED;*/
buf->length = pdev->len_per_image;
PWC_DEBUG_READ("VIDIOC_QUERYBUF: index=%d\n", buf->index); return vb2_querybuf(&pdev->vb_queue, buf);
PWC_DEBUG_READ("VIDIOC_QUERYBUF: m.offset=%d\n", buf->m.offset);
PWC_DEBUG_READ("VIDIOC_QUERYBUF: bytesused=%d\n", buf->bytesused);
return 0;
} }
static int pwc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) static int pwc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
{ {
PWC_DEBUG_IOCTL("ioctl(VIDIOC_QBUF) index=%d\n", buf->index); struct pwc_device *pdev = video_drvdata(file);
if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (buf->memory != V4L2_MEMORY_MMAP)
return -EINVAL;
if (buf->index >= pwc_mbufs)
return -EINVAL;
buf->flags |= V4L2_BUF_FLAG_QUEUED;
buf->flags &= ~V4L2_BUF_FLAG_DONE;
return 0; return vb2_qbuf(&pdev->vb_queue, buf);
} }
static int pwc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) static int pwc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
{ {
DECLARE_WAITQUEUE(wait, current);
struct pwc_device *pdev = video_drvdata(file); struct pwc_device *pdev = video_drvdata(file);
int ret;
PWC_DEBUG_IOCTL("ioctl(VIDIOC_DQBUF)\n");
if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
add_wait_queue(&pdev->frameq, &wait);
while (pdev->full_frames == NULL) {
if (pdev->error_status) {
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
return -pdev->error_status;
}
if (signal_pending(current)) {
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
return -ERESTARTSYS;
}
mutex_unlock(&pdev->modlock);
schedule();
set_current_state(TASK_INTERRUPTIBLE);
mutex_lock(&pdev->modlock);
}
remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING);
PWC_DEBUG_IOCTL("VIDIOC_DQBUF: frame ready.\n");
/* Decompress data in pdev->images[pdev->fill_image] */
ret = pwc_handle_frame(pdev);
if (ret)
return ret;
PWC_DEBUG_IOCTL("VIDIOC_DQBUF: after pwc_handle_frame\n");
buf->index = pdev->fill_image;
if (pdev->pixfmt != V4L2_PIX_FMT_YUV420)
buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
else
buf->bytesused = pdev->view.size;
buf->flags = V4L2_BUF_FLAG_MAPPED;
buf->field = V4L2_FIELD_NONE;
do_gettimeofday(&buf->timestamp);
buf->sequence = 0;
buf->memory = V4L2_MEMORY_MMAP;
buf->m.offset = pdev->fill_image * pdev->len_per_image;
buf->length = pdev->len_per_image;
pwc_next_image(pdev);
PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->index=%d\n", buf->index);
PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->length=%d\n", buf->length);
PWC_DEBUG_IOCTL("VIDIOC_DQBUF: m.offset=%d\n", buf->m.offset);
PWC_DEBUG_IOCTL("VIDIOC_DQBUF: bytesused=%d\n", buf->bytesused);
PWC_DEBUG_IOCTL("VIDIOC_DQBUF: leaving\n");
return 0;
return vb2_dqbuf(&pdev->vb_queue, buf, file->f_flags & O_NONBLOCK);
} }
static int pwc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) static int pwc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
{ {
struct pwc_device *pdev = video_drvdata(file); struct pwc_device *pdev = video_drvdata(file);
return pwc_isoc_init(pdev); return vb2_streamon(&pdev->vb_queue, i);
} }
static int pwc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) static int pwc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
{ {
struct pwc_device *pdev = video_drvdata(file); struct pwc_device *pdev = video_drvdata(file);
pwc_isoc_cleanup(pdev); return vb2_streamoff(&pdev->vb_queue, i);
return 0;
} }
static int pwc_enum_framesizes(struct file *file, void *fh, static int pwc_enum_framesizes(struct file *file, void *fh,
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/videodev2.h> #include <linux/videodev2.h>
#include <media/v4l2-common.h> #include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h> #include <media/v4l2-ioctl.h>
#include <media/videobuf2-vmalloc.h>
#ifdef CONFIG_USB_PWC_INPUT_EVDEV #ifdef CONFIG_USB_PWC_INPUT_EVDEV
#include <linux/input.h> #include <linux/input.h>
#endif #endif
...@@ -112,18 +113,17 @@ ...@@ -112,18 +113,17 @@
#define FRAME_LOWMARK 5 #define FRAME_LOWMARK 5
/* Size and number of buffers for the ISO pipe. */ /* Size and number of buffers for the ISO pipe. */
#define MAX_ISO_BUFS 2 #define MAX_ISO_BUFS 3
#define ISO_FRAMES_PER_DESC 10 #define ISO_FRAMES_PER_DESC 10
#define ISO_MAX_FRAME_SIZE 960 #define ISO_MAX_FRAME_SIZE 960
#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) #define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE)
/* Frame buffers: contains compressed or uncompressed video data. */
#define MAX_FRAMES 5
/* Maximum size after decompression is 640x480 YUV data, 1.5 * 640 * 480 */ /* Maximum size after decompression is 640x480 YUV data, 1.5 * 640 * 480 */
#define PWC_FRAME_SIZE (460800 + TOUCAM_HEADER_SIZE + TOUCAM_TRAILER_SIZE) #define PWC_FRAME_SIZE (460800 + TOUCAM_HEADER_SIZE + TOUCAM_TRAILER_SIZE)
/* Absolute maximum number of buffers available for mmap() */ /* Absolute minimum and maximum number of buffers available for mmap() */
#define MAX_IMAGES 10 #define MIN_FRAMES 2
#define MAX_FRAMES 16
/* Some macros to quickly find the type of a webcam */ /* Some macros to quickly find the type of a webcam */
#define DEVICE_USE_CODEC1(x) ((x)<675) #define DEVICE_USE_CODEC1(x) ((x)<675)
...@@ -143,16 +143,10 @@ struct pwc_iso_buf ...@@ -143,16 +143,10 @@ struct pwc_iso_buf
/* intermediate buffers with raw data from the USB cam */ /* intermediate buffers with raw data from the USB cam */
struct pwc_frame_buf struct pwc_frame_buf
{ {
struct vb2_buffer vb; /* common v4l buffer stuff -- must be first */
struct list_head list;
void *data; void *data;
volatile int filled; /* number of bytes filled */ int filled; /* number of bytes filled */
struct pwc_frame_buf *next; /* list */
};
/* additionnal informations used when dealing image between kernel and userland */
struct pwc_imgbuf
{
unsigned long offset; /* offset of this buffer in the big array of image_data */
int vma_use_count; /* count the number of time this memory is mapped */
}; };
struct pwc_device struct pwc_device
...@@ -177,8 +171,6 @@ struct pwc_device ...@@ -177,8 +171,6 @@ struct pwc_device
int vframes, vsize; /* frames-per-second & size (see PSZ_*) */ int vframes, vsize; /* frames-per-second & size (see PSZ_*) */
int pixfmt; /* pixelformat: V4L2_PIX_FMT_YUV420 or raw: _PWC1, _PWC2 */ int pixfmt; /* pixelformat: V4L2_PIX_FMT_YUV420 or raw: _PWC1, _PWC2 */
int vframe_count; /* received frames */ int vframe_count; /* received frames */
int vframes_dumped; /* counter for dumped frames */
int vframes_error; /* frames received in error */
int vmax_packet_size; /* USB maxpacket size */ int vmax_packet_size; /* USB maxpacket size */
int vlast_packet_size; /* for frame synchronisation */ int vlast_packet_size; /* for frame synchronisation */
int visoc_errors; /* number of contiguous ISOC errors */ int visoc_errors; /* number of contiguous ISOC errors */
...@@ -192,35 +184,29 @@ struct pwc_device ...@@ -192,35 +184,29 @@ struct pwc_device
int cmd_len; int cmd_len;
unsigned char cmd_buf[13]; unsigned char cmd_buf[13];
/* The image acquisition requires 3 to 4 steps:
1. data is gathered in short packets from the USB controller
2. data is synchronized and packed into a frame buffer
3a. in case data is compressed, decompress it directly into image buffer
3b. in case data is uncompressed, copy into image buffer with viewport
4. data is transferred to the user process
Note that MAX_ISO_BUFS != MAX_FRAMES != MAX_IMAGES....
We have in effect a back-to-back-double-buffer system.
*/
/* 1: isoc */
struct pwc_iso_buf sbuf[MAX_ISO_BUFS]; struct pwc_iso_buf sbuf[MAX_ISO_BUFS];
char iso_init; char iso_init;
/* 2: frame */ /* videobuf2 queue and queued buffers list */
struct pwc_frame_buf *fbuf; /* all frames */ struct vb2_queue vb_queue;
struct pwc_frame_buf *empty_frames, *empty_frames_tail; /* all empty frames */ struct list_head queued_bufs;
struct pwc_frame_buf *full_frames, *full_frames_tail; /* all filled frames */ spinlock_t queued_bufs_lock;
struct pwc_frame_buf *fill_frame; /* frame currently being filled */
struct pwc_frame_buf *read_frame; /* frame currently read by user process */ /*
* Frame currently being filled, this only gets touched by the
* isoc urb complete handler, and by stream start / stop since
* start / stop touch it before / after starting / killing the urbs
* no locking is needed around this
*/
struct pwc_frame_buf *fill_buf;
int frame_header_size, frame_trailer_size; int frame_header_size, frame_trailer_size;
int frame_size; int frame_size;
int frame_total_size; /* including header & trailer */ int frame_total_size; /* including header & trailer */
int drop_frames; int drop_frames;
/* 3: decompression */
void *decompress_data; /* private data for decompression engine */ void *decompress_data; /* private data for decompression engine */
/* 4: image */
/* We have an 'image' and a 'view', where 'image' is the fixed-size image /* We have an 'image' and a 'view', where 'image' is the fixed-size image
as delivered by the camera, and 'view' is the size requested by the as delivered by the camera, and 'view' is the size requested by the
program. The camera image is centered in this viewport, laced with program. The camera image is centered in this viewport, laced with
...@@ -232,15 +218,7 @@ struct pwc_device ...@@ -232,15 +218,7 @@ struct pwc_device
struct pwc_coord image, view; /* image and viewport size */ struct pwc_coord image, view; /* image and viewport size */
struct pwc_coord offset; /* offset within the viewport */ struct pwc_coord offset; /* offset within the viewport */
void *image_data; /* total buffer, which is subdivided into ... */
struct pwc_imgbuf images[MAX_IMAGES];/* ...several images... */
int fill_image; /* ...which are rotated. */
int len_per_image; /* length per image */
int image_read_pos; /* In case we read data in pieces, keep track of were we are in the imagebuffer */
int image_used[MAX_IMAGES]; /* For MCAPTURE and SYNC */
struct mutex modlock; /* to prevent races in video_open(), etc */ struct mutex modlock; /* to prevent races in video_open(), etc */
spinlock_t ptrlock; /* for manipulating the buffer pointers */
/*** motorized pan/tilt feature */ /*** motorized pan/tilt feature */
struct pwc_mpt_range angle_range; struct pwc_mpt_range angle_range;
...@@ -253,7 +231,6 @@ struct pwc_device ...@@ -253,7 +231,6 @@ struct pwc_device
#endif #endif
/*** Misc. data ***/ /*** Misc. data ***/
wait_queue_head_t frameq; /* When waiting for a frame to finish... */
#if PWC_INT_PIPE #if PWC_INT_PIPE
void *usb_int_handler; /* for the interrupt endpoint */ void *usb_int_handler; /* for the interrupt endpoint */
#endif #endif
...@@ -263,13 +240,6 @@ struct pwc_device ...@@ -263,13 +240,6 @@ struct pwc_device
#ifdef CONFIG_USB_PWC_DEBUG #ifdef CONFIG_USB_PWC_DEBUG
extern int pwc_trace; extern int pwc_trace;
#endif #endif
extern int pwc_mbufs;
/** functions in pwc-if.c */
int pwc_handle_frame(struct pwc_device *pdev);
void pwc_next_image(struct pwc_device *pdev);
int pwc_isoc_init(struct pwc_device *pdev);
void pwc_isoc_cleanup(struct pwc_device *pdev);
/** Functions in pwc-misc.c */ /** Functions in pwc-misc.c */
/* sizes in pixels */ /* sizes in pixels */
...@@ -334,6 +304,6 @@ extern const struct v4l2_ioctl_ops pwc_ioctl_ops; ...@@ -334,6 +304,6 @@ extern const struct v4l2_ioctl_ops pwc_ioctl_ops;
/** pwc-uncompress.c */ /** pwc-uncompress.c */
/* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */ /* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */
extern int pwc_decompress(struct pwc_device *pdev); int pwc_decompress(struct pwc_device *pdev, struct pwc_frame_buf *fbuf);
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment