From 880c35c5850464c934f02a5ec7d4c1a4df386fce Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 25 Jun 2011 10:07:39 -0300 Subject: [media] pwc: Use the default version for VIDIOC_QUERYCAP After discussing with Hans, change pwc to use the default version control. The only version ever used for pwc driver is 10.0.12, due to commit 2b455db6d456ef2d44808a8377fd3bc832e08317. Changing it to 3.x.y won't conflict with the old version. There's no namespace conflicts in any predictable future. Even on the remote far-away case where we might have a conflict, it will be on just one specific stable Kernel release (Kernel 10.0.12), if we ever have such stable release. So, it is safe and consistent on using 3.x.y numering schema for it. Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pwc/pwc-v4l.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/media/video/pwc/pwc-v4l.c') diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index f85c51249c7..059bd95c122 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -349,7 +349,6 @@ static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap strcpy(cap->driver, PWC_NAME); strlcpy(cap->card, vdev->name, sizeof(cap->card)); usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->version = PWC_VERSION_CODE; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | -- cgit v1.2.3-70-g09d2 From c246412117d871a3a90cd4e8ba2c6dea18a59f71 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 6 Jun 2011 15:25:18 -0300 Subject: [media] pwc: Remove a bunch of bogus sanity checks / don't return EFAULT wrongly The chances if any of these becoming NULL magically are 0% And if they do become NULL oopsing is the right thing to do (so that the user logs a bug with the kernel rather then with whatever app he was using). Returning EFAULT to userspace should only be done when userspace supplies a bad address, not on driver bugs / hw issues, so in the few cases where the check is not bogus return something else. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pwc/pwc-if.c | 43 +++----------------------------- drivers/media/video/pwc/pwc-uncompress.c | 5 ---- drivers/media/video/pwc/pwc-v4l.c | 2 +- 3 files changed, 5 insertions(+), 45 deletions(-) (limited to 'drivers/media/video/pwc/pwc-v4l.c') diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index b0bde5a87c8..3d3ff603098 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -226,9 +226,6 @@ static int pwc_allocate_buffers(struct pwc_device *pdev) PWC_DEBUG_MEMORY(">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev); - if (pdev == NULL) - return -ENXIO; - /* Allocate Isochronuous pipe buffers */ for (i = 0; i < MAX_ISO_BUFS; i++) { if (pdev->sbuf[i].data == NULL) { @@ -306,8 +303,6 @@ static void pwc_free_buffers(struct pwc_device *pdev) PWC_DEBUG_MEMORY("Entering free_buffers(%p).\n", pdev); - if (pdev == NULL) - return; /* Release Iso-pipe buffers */ for (i = 0; i < MAX_ISO_BUFS; i++) if (pdev->sbuf[i].data != NULL) { @@ -783,26 +778,20 @@ int pwc_isoc_init(struct pwc_device *pdev) struct usb_device *udev; struct urb *urb; int i, j, ret; - struct usb_interface *intf; struct usb_host_interface *idesc = NULL; - if (pdev == NULL) - return -EFAULT; if (pdev->iso_init) return 0; pdev->vsync = 0; udev = pdev->udev; /* Get the current alternate interface, adjust packet size */ - if (!udev->actconfig) - return -EFAULT; intf = usb_ifnum_to_if(udev, 0); if (intf) idesc = usb_altnum_to_altsetting(intf, pdev->valternate); - if (!idesc) - return -EFAULT; + return -EIO; /* Search video endpoint */ pdev->vmax_packet_size = -1; @@ -918,8 +907,7 @@ static void pwc_iso_free(struct pwc_device *pdev) void pwc_isoc_cleanup(struct pwc_device *pdev) { PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n"); - if (pdev == NULL) - return; + if (pdev->iso_init == 0) return; @@ -1058,7 +1046,6 @@ static int pwc_video_open(struct file *file) PWC_DEBUG_OPEN(">> video_open called(vdev = 0x%p).\n", vdev); pdev = video_get_drvdata(vdev); - BUG_ON(!pdev); if (pdev->vopen) { PWC_DEBUG_OPEN("I'm busy, someone is using the device.\n"); return -EBUSY; @@ -1230,11 +1217,7 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf, PWC_DEBUG_READ("pwc_video_read(vdev=0x%p, buf=%p, count=%zd) called.\n", vdev, buf, count); - if (vdev == NULL) - return -EFAULT; pdev = video_get_drvdata(vdev); - if (pdev == NULL) - return -EFAULT; if (pdev->error_status) { rv = -pdev->error_status; /* Something happened, report what. */ @@ -1279,10 +1262,9 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf, set_current_state(TASK_RUNNING); /* Decompress and release frame */ - if (pwc_handle_frame(pdev)) { - rv = -EFAULT; + rv = pwc_handle_frame(pdev); + if (rv) goto err_out; - } } PWC_DEBUG_READ("Copying data to user space.\n"); @@ -1317,11 +1299,7 @@ static unsigned int pwc_video_poll(struct file *file, poll_table *wait) struct pwc_device *pdev; int ret; - if (vdev == NULL) - return -EFAULT; pdev = video_get_drvdata(vdev); - if (pdev == NULL) - return -EFAULT; /* Start the stream (if not already started) */ ret = pwc_isoc_init(pdev); @@ -1769,18 +1747,6 @@ static void usb_pwc_disconnect(struct usb_interface *intf) mutex_lock(&pdev->modlock); usb_set_intfdata (intf, NULL); - if (pdev == NULL) { - PWC_ERROR("pwc_disconnect() Called without private pointer.\n"); - goto disconnect_out; - } - if (pdev->udev == NULL) { - PWC_ERROR("pwc_disconnect() already called for %p\n", pdev); - goto disconnect_out; - } - if (pdev->udev != interface_to_usbdev(intf)) { - PWC_ERROR("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n"); - goto disconnect_out; - } /* We got unplugged; this is signalled by an EPIPE error code */ pdev->error_status = EPIPE; @@ -1792,7 +1758,6 @@ static void usb_pwc_disconnect(struct usb_interface *intf) /* No need to keep the urbs around after disconnection */ pwc_isoc_cleanup(pdev); -disconnect_out: mutex_unlock(&pdev->modlock); pwc_remove_sysfs_files(pdev); diff --git a/drivers/media/video/pwc/pwc-uncompress.c b/drivers/media/video/pwc/pwc-uncompress.c index 3b73f295f03..4118184951e 100644 --- a/drivers/media/video/pwc/pwc-uncompress.c +++ b/drivers/media/video/pwc/pwc-uncompress.c @@ -42,12 +42,7 @@ int pwc_decompress(struct pwc_device *pdev) u16 *src; u16 *dsty, *dstu, *dstv; - if (pdev == NULL) - return -EFAULT; - fbuf = pdev->read_frame; - if (fbuf == NULL) - return -EFAULT; image = pdev->image_data; image += pdev->images[pdev->fill_image].offset; diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 059bd95c122..8e72e0f5655 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -778,7 +778,7 @@ static int pwc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) /* Decompress data in pdev->images[pdev->fill_image] */ ret = pwc_handle_frame(pdev); if (ret) - return -EFAULT; + return ret; PWC_DEBUG_IOCTL("VIDIOC_DQBUF: after pwc_handle_frame\n"); buf->index = pdev->fill_image; -- cgit v1.2.3-70-g09d2 From 885fe18f5542fe283a17f70583383c6cadcba1c3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 6 Jun 2011 15:33:44 -0300 Subject: [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: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pwc/Kconfig | 1 + drivers/media/video/pwc/pwc-ctrl.c | 19 +- drivers/media/video/pwc/pwc-if.c | 847 +++++++++---------------------- drivers/media/video/pwc/pwc-misc.c | 4 - drivers/media/video/pwc/pwc-uncompress.c | 11 +- drivers/media/video/pwc/pwc-v4l.c | 125 +---- drivers/media/video/pwc/pwc.h | 76 +-- 7 files changed, 275 insertions(+), 808 deletions(-) (limited to 'drivers/media/video/pwc/pwc-v4l.c') diff --git a/drivers/media/video/pwc/Kconfig b/drivers/media/video/pwc/Kconfig index 8da42e4f1ba..d63d0a85003 100644 --- a/drivers/media/video/pwc/Kconfig +++ b/drivers/media/video/pwc/Kconfig @@ -1,6 +1,7 @@ config USB_PWC tristate "USB Philips Cameras" depends on VIDEO_V4L2 + select VIDEOBUF2_VMALLOC ---help--- Say Y or M here if you want to use one of these Philips & OEM webcams: diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c index 760b4de13ad..19221cbca8c 100644 --- a/drivers/media/video/pwc/pwc-ctrl.c +++ b/drivers/media/video/pwc/pwc-ctrl.c @@ -511,13 +511,9 @@ unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned i 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) { - int i, factor = 0; + int factor = 0; /* for V4L2_PIX_FMT_YUV420 */ switch (pdev->pixfmt) { @@ -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.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 */ - int pwc_get_brightness(struct pwc_device *pdev) { char buf; diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 3d3ff603098..907db23fdb5 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -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 void usb_pwc_disconnect(struct usb_interface *intf); +static void pwc_isoc_cleanup(struct pwc_device *pdev); static struct usb_driver pwc_driver = { .name = "Philips webcam", /* name */ @@ -129,8 +130,6 @@ static struct usb_driver pwc_driver = { static int default_size = PSZ_QCIF; 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 int pwc_trace = PWC_DEBUG_LEVEL; #endif @@ -173,52 +172,6 @@ static struct video_device pwc_template = { /***************************************************************************/ /* 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) { int i, err; @@ -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 */ if (DEVICE_USE_CODEC1(pdev->type)) err = pwc_dec1_alloc(pdev); @@ -274,25 +203,6 @@ static int pwc_allocate_buffers(struct pwc_device *pdev) 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"); return 0; } @@ -311,19 +221,6 @@ static void pwc_free_buffers(struct pwc_device *pdev) 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 */ if (pdev->decompress_data != NULL) { 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) 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"); } -/* The frame & image buffer mess. - - 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) +struct pwc_frame_buf *pwc_get_next_fill_buf(struct pwc_device *pdev) { - int ret; - unsigned long flags; - - ret = 0; - spin_lock_irqsave(&pdev->ptrlock, flags); - if (pdev->fill_frame != NULL) { - /* append to 'full' list */ - if (pdev->full_frames == NULL) { - pdev->full_frames = pdev->fill_frame; - pdev->full_frames_tail = pdev->full_frames; - } - else { - 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); + unsigned long flags = 0; + struct pwc_frame_buf *buf = NULL; + + spin_lock_irqsave(&pdev->queued_bufs_lock, flags); + if (list_empty(&pdev->queued_bufs)) + goto leave; + + buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf, list); + list_del(&buf->list); +leave: + spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags); + return buf; } 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 } -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 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_ if (ptr[1] == 1 && ptr[0] & 0x10) { PWC_TRACE("Hyundai CMOS sensor bug. Dropping frame.\n"); pdev->drop_frames += 2; - pdev->vframes_error++; } if ((ptr[0] ^ pdev->vmirror) & 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_ */ if (fbuf->filled == 4) 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; if ((ptr[0] ^ pdev->vmirror) & 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_ pdev->vmirror = ptr[0] & 0x03; } - /* 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) + /* In case we were instructed to drop the frame, do so silently. */ + if (pdev->drop_frames > 0) { pdev->drop_frames--; - else { + } else { /* Check for underflow first */ if (fbuf->filled < pdev->frame_total_size) { PWC_DEBUG_FLOW("Frame buffer underflow (%d bytes);" " discarded.\n", fbuf->filled); - pdev->vframes_error++; - } - else { - /* Send only once per EOF */ - awake = 1; /* delay wake_ups */ - - /* 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); - + } else { + fbuf->vb.v4l2_buf.field = V4L2_FIELD_NONE; + fbuf->vb.v4l2_buf.sequence = pdev->vframe_count; + vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); + pdev->fill_buf = NULL; + pdev->vsync = 0; } } /* !drop_frames */ pdev->vframe_count++; - return awake; } /* 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_ */ 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 awake; - struct pwc_frame_buf *fbuf; - unsigned char *fillptr = NULL, *iso_buf = NULL; - - awake = 0; - pdev = (struct pwc_device *)urb->context; - if (pdev == NULL) { - PWC_ERROR("isoc_handler() called with NULL device?!\n"); - return; - } + unsigned char *iso_buf = NULL; - if (urb->status == -ENOENT || urb->status == -ECONNRESET) { + if (urb->status == -ENOENT || urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN) { PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a"); 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; errmsg = "Unknown"; @@ -679,29 +357,21 @@ static void pwc_isoc_handler(struct urb *urb) case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break; case -ETIME: errmsg = "Device does not respond"; break; } - PWC_DEBUG_FLOW("pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg); - /* Give up after a number of contiguous errors on the USB bus. - Appearantly something is wrong so we simulate an unplug event. - */ + PWC_ERROR("pwc_isoc_handler() called with status %d [%s].\n", + urb->status, errmsg); + /* Give up after a number of contiguous errors */ if (++pdev->visoc_errors > MAX_ISOC_ERRORS) { - PWC_INFO("Too many ISOC errors, bailing out.\n"); - pdev->error_status = EIO; - awake = 1; - wake_up_interruptible(&pdev->frameq); + PWC_ERROR("Too many ISOC errors, bailing out.\n"); + if (pdev->fill_buf) { + vb2_buffer_done(&pdev->fill_buf->vb, + VB2_BUF_STATE_ERROR); + pdev->fill_buf = NULL; + } } - goto handler_end; // ugly, but practical - } - - fbuf = pdev->fill_frame; - if (fbuf == NULL) { - PWC_ERROR("pwc_isoc_handler without valid fill frame.\n"); - awake = 1; + pdev->vsync = 0; /* Drop the current frame */ goto handler_end; } - else { - fillptr = fbuf->data + fbuf->filled; - } /* Reset ISOC error counter. We did get here, after all. */ pdev->visoc_errors = 0; @@ -715,65 +385,50 @@ static void pwc_isoc_handler(struct urb *urb) fst = urb->iso_frame_desc[i].status; flen = urb->iso_frame_desc[i].actual_length; iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - if (fst == 0) { - if (flen > 0) { /* if valid data... */ - if (pdev->vsync > 0) { /* ...and we are not sync-hunting... */ - pdev->vsync = 2; - - /* ...copy data to frame buffer, if possible */ - 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); - pdev->vsync = 0; /* Hmm, let's wait for an EOF (end-of-frame) */ - pdev->vframes_error++; - } - else { - memmove(fillptr, iso_buf, flen); - fillptr += flen; - } - } + if (fst != 0) { + PWC_ERROR("Iso frame %d has error %d\n", i, fst); + 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; + } + + if (flen + fbuf->filled > pdev->frame_total_size) { + PWC_ERROR("Frame overflow (%d > %d)\n", + flen + fbuf->filled, + pdev->frame_total_size); + pdev->vsync = 0; /* Let's wait for an EOF */ + } else { + memcpy(fbuf->data + fbuf->filled, iso_buf, + 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; - fillptr = fbuf->data; + } + } + if (flen < pdev->vlast_packet_size) { + /* 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->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: - if (awake) - wake_up_interruptible(&pdev->frameq); - - urb->dev = pdev->udev; i = usb_submit_urb(urb, GFP_ATOMIC); if (i != 0) PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i); } - -int pwc_isoc_init(struct pwc_device *pdev) +static int pwc_isoc_init(struct pwc_device *pdev) { struct usb_device *udev; struct urb *urb; @@ -784,6 +439,8 @@ int pwc_isoc_init(struct pwc_device *pdev) if (pdev->iso_init) return 0; pdev->vsync = 0; + pdev->fill_buf = NULL; + pdev->vframe_count = 0; udev = pdev->udev; /* Get the current alternate interface, adjust packet size */ @@ -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"); @@ -926,6 +583,22 @@ void pwc_isoc_cleanup(struct pwc_device *pdev) 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 *********/ @@ -1086,12 +759,6 @@ static int pwc_video_open(struct file *file) } /* 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->error_status = 0; pwc_construct(pdev); /* set min/max sizes correct */ @@ -1161,19 +828,12 @@ static int pwc_video_close(struct file *file) if (pdev->vopen == 0) 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)) pwc_dec1_exit(); else pwc_dec23_exit(); - pwc_isoc_cleanup(pdev); + vb2_queue_release(&pdev->vb_queue); pwc_free_buffers(pdev); /* Turn off LEDS and power down camera, but only when not unplugged */ @@ -1193,182 +853,155 @@ static int pwc_video_close(struct file *file) 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, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { struct video_device *vdev = file->private_data; - struct pwc_device *pdev; - int noblock = file->f_flags & O_NONBLOCK; - DECLARE_WAITQUEUE(wait, current); - int bytes_to_read, rv = 0; - void *image_buffer_addr; + struct pwc_device *pdev = video_get_drvdata(vdev); - PWC_DEBUG_READ("pwc_video_read(vdev=0x%p, buf=%p, count=%zd) called.\n", - vdev, buf, count); - pdev = video_get_drvdata(vdev); + if (pdev->error_status) + return -pdev->error_status; - if (pdev->error_status) { - rv = -pdev->error_status; /* Something happened, report what. */ - goto err_out; - } + return vb2_read(&pdev->vb_queue, buf, count, ppos, + file->f_flags & O_NONBLOCK); +} - /* 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); +static unsigned int pwc_video_poll(struct file *file, poll_table *wait) +{ + struct video_device *vdev = file->private_data; + struct pwc_device *pdev = video_get_drvdata(vdev); - /* Decompress and release frame */ - rv = pwc_handle_frame(pdev); - if (rv) - goto err_out; - } + if (pdev->error_status) + return POLL_ERR; - PWC_DEBUG_READ("Copying data to user space.\n"); - if (pdev->pixfmt != V4L2_PIX_FMT_YUV420) - bytes_to_read = pdev->frame_size + sizeof(struct pwc_raw_frame); - else - 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; + return vb2_poll(&pdev->vb_queue, file, wait); } -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 pwc_device *pdev; - int ret; + struct pwc_device *pdev = video_get_drvdata(vdev); - pdev = video_get_drvdata(vdev); + return vb2_mmap(&pdev->vb_queue, vma); +} - /* Start the stream (if not already started) */ - ret = pwc_isoc_init(pdev); - if (ret) - return ret; +/***************************************************************************/ +/* Videobuf2 operations */ + +static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned long sizes[], + void *alloc_ctxs[]) +{ + struct pwc_device *pdev = vb2_get_drv_priv(vq); - poll_wait(file, &pdev->frameq, wait); + 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; +} + +static int buffer_init(struct vb2_buffer *vb) +{ + struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb); + + /* need vmalloc since frame buffer > 128K */ + buf->data = vzalloc(PWC_FRAME_SIZE); + if (buf->data == NULL) + 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 POLLERR; - if (pdev->full_frames != NULL) /* we have frames waiting */ - return (POLLIN | POLLRDNORM); + return -pdev->error_status; return 0; } -static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) +static int buffer_finish(struct vb2_buffer *vb) { - struct video_device *vdev = file->private_data; - struct pwc_device *pdev; - unsigned long start; - unsigned long size; - unsigned long page, pos = 0; - int index; + struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue); + struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb); - PWC_DEBUG_MEMORY(">> %s\n", __func__); - pdev = video_get_drvdata(vdev); - size = vma->vm_end - vma->vm_start; - start = vma->vm_start; + /* + * Application has called dqbuf and is getting back a buffer we've + * 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 + */ + return pwc_decompress(pdev, buf); +} + +static void buffer_cleanup(struct vb2_buffer *vb) +{ + struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb); + + vfree(buf->data); +} + +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); - /* 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, - * but in v4l2 each buffer is mapped - */ - unsigned long total_size; - 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", - size, pdev->len_per_image, total_size); - return -EINVAL; - } - } else if (size > pdev->len_per_image) - return -EINVAL; - - 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; } +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 */ @@ -1648,12 +1281,22 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id } 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; - init_waitqueue_head(&pdev->frameq); 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 */ memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template)); pdev->vdev.parent = &intf->dev; @@ -1752,11 +1395,9 @@ static void usb_pwc_disconnect(struct usb_interface *intf) pdev->error_status = EPIPE; pdev->unplugged = 1; - /* Alert waiting processes */ - wake_up_interruptible(&pdev->frameq); - /* No need to keep the urbs around after disconnection */ pwc_isoc_cleanup(pdev); + pwc_cleanup_queued_bufs(pdev); mutex_unlock(&pdev->modlock); @@ -1776,8 +1417,6 @@ static void usb_pwc_disconnect(struct usb_interface *intf) static char *size; static int fps; -static int fbufs; -static int mbufs; static int compression = -1; static int leds[2] = { -1, -1 }; static unsigned int leds_nargs; @@ -1786,8 +1425,6 @@ static unsigned int dev_hint_nargs; module_param(size, charp, 0444); module_param(fps, int, 0444); -module_param(fbufs, int, 0444); -module_param(mbufs, int, 0444); #ifdef CONFIG_USB_PWC_DEBUG module_param_named(trace, pwc_trace, int, 0644); #endif @@ -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(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 MODULE_PARM_DESC(trace, "For debugging purposes"); #endif @@ -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); } - 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 if (pwc_trace >= 0) { PWC_DEBUG_MODULE("Trace options: 0x%04x\n", pwc_trace); diff --git a/drivers/media/video/pwc/pwc-misc.c b/drivers/media/video/pwc/pwc-misc.c index 6af5bb53835..0b031336eab 100644 --- a/drivers/media/video/pwc/pwc-misc.c +++ b/drivers/media/video/pwc/pwc-misc.c @@ -126,8 +126,4 @@ void pwc_construct(struct pwc_device *pdev) pdev->pixfmt = V4L2_PIX_FMT_YUV420; /* default */ pdev->view_min.size = pdev->view_min.x * pdev->view_min.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); } - - diff --git a/drivers/media/video/pwc/pwc-uncompress.c b/drivers/media/video/pwc/pwc-uncompress.c index 4118184951e..d110e38c4de 100644 --- a/drivers/media/video/pwc/pwc-uncompress.c +++ b/drivers/media/video/pwc/pwc-uncompress.c @@ -34,17 +34,14 @@ #include "pwc-dec1.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; void *yuv, *image; u16 *src; u16 *dsty, *dstu, *dstv; - fbuf = pdev->read_frame; - image = pdev->image_data; - image += pdev->images[pdev->fill_image].offset; + image = vb2_plane_vaddr(&fbuf->vb, 0); yuv = fbuf->data + pdev->frame_header_size; /* Skip header */ @@ -59,9 +56,13 @@ int pwc_decompress(struct pwc_device *pdev) * determine this using the type of the webcam */ memcpy(raw_frame->cmd, pdev->cmd_buf, 4); 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; } + vb2_set_plane_payload(&fbuf->vb, 0, pdev->view.size); + if (pdev->vbandlength == 0) { /* Uncompressed mode. * We copy the data into the output buffer, using the viewport diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 8e72e0f5655..cda38837adc 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -309,7 +309,7 @@ static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f) pixelformat != V4L2_PIX_FMT_PWC2) return -EINVAL; - if (pdev->iso_init) + if (vb2_is_streaming(&pdev->vb_queue)) return -EBUSY; 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) 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; - - 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; + struct pwc_device *pdev = video_drvdata(file); - nbuffers = rb->count; - 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; + return vb2_reqbufs(&pdev->vb_queue, rb); } static int pwc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) { 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); - PWC_DEBUG_READ("VIDIOC_QUERYBUF: m.offset=%d\n", buf->m.offset); - PWC_DEBUG_READ("VIDIOC_QUERYBUF: bytesused=%d\n", buf->bytesused); - - return 0; + return vb2_querybuf(&pdev->vb_queue, 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); - 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; + struct pwc_device *pdev = video_drvdata(file); - return 0; + return vb2_qbuf(&pdev->vb_queue, 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); - 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) { 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) { struct pwc_device *pdev = video_drvdata(file); - pwc_isoc_cleanup(pdev); - return 0; + return vb2_streamoff(&pdev->vb_queue, i); } static int pwc_enum_framesizes(struct file *file, void *fh, diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index 421e75b69f2..ae92fe211f6 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -36,6 +36,7 @@ #include #include #include +#include #ifdef CONFIG_USB_PWC_INPUT_EVDEV #include #endif @@ -112,18 +113,17 @@ #define FRAME_LOWMARK 5 /* 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_MAX_FRAME_SIZE 960 #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 */ #define PWC_FRAME_SIZE (460800 + TOUCAM_HEADER_SIZE + TOUCAM_TRAILER_SIZE) -/* Absolute maximum number of buffers available for mmap() */ -#define MAX_IMAGES 10 +/* Absolute minimum and maximum number of buffers available for mmap() */ +#define MIN_FRAMES 2 +#define MAX_FRAMES 16 /* Some macros to quickly find the type of a webcam */ #define DEVICE_USE_CODEC1(x) ((x)<675) @@ -143,16 +143,10 @@ struct pwc_iso_buf /* intermediate buffers with raw data from the USB cam */ struct pwc_frame_buf { - void *data; - volatile 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 vb2_buffer vb; /* common v4l buffer stuff -- must be first */ + struct list_head list; + void *data; + int filled; /* number of bytes filled */ }; struct pwc_device @@ -177,8 +171,6 @@ struct pwc_device int vframes, vsize; /* frames-per-second & size (see PSZ_*) */ int pixfmt; /* pixelformat: V4L2_PIX_FMT_YUV420 or raw: _PWC1, _PWC2 */ 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 vlast_packet_size; /* for frame synchronisation */ int visoc_errors; /* number of contiguous ISOC errors */ @@ -192,35 +184,29 @@ struct pwc_device int cmd_len; 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]; char iso_init; - /* 2: frame */ - struct pwc_frame_buf *fbuf; /* all frames */ - struct pwc_frame_buf *empty_frames, *empty_frames_tail; /* all empty frames */ - struct pwc_frame_buf *full_frames, *full_frames_tail; /* all filled frames */ - struct pwc_frame_buf *fill_frame; /* frame currently being filled */ - struct pwc_frame_buf *read_frame; /* frame currently read by user process */ + /* videobuf2 queue and queued buffers list */ + struct vb2_queue vb_queue; + struct list_head queued_bufs; + spinlock_t queued_bufs_lock; + + /* + * 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_size; int frame_total_size; /* including header & trailer */ int drop_frames; - /* 3: decompression */ void *decompress_data; /* private data for decompression engine */ - /* 4: 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 program. The camera image is centered in this viewport, laced with @@ -232,15 +218,7 @@ struct pwc_device struct pwc_coord image, view; /* image and viewport size */ 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 */ - spinlock_t ptrlock; /* for manipulating the buffer pointers */ /*** motorized pan/tilt feature */ struct pwc_mpt_range angle_range; @@ -253,7 +231,6 @@ struct pwc_device #endif /*** Misc. data ***/ - wait_queue_head_t frameq; /* When waiting for a frame to finish... */ #if PWC_INT_PIPE void *usb_int_handler; /* for the interrupt endpoint */ #endif @@ -263,13 +240,6 @@ struct pwc_device #ifdef CONFIG_USB_PWC_DEBUG extern int pwc_trace; #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 */ /* sizes in pixels */ @@ -334,6 +304,6 @@ extern const struct v4l2_ioctl_ops pwc_ioctl_ops; /** pwc-uncompress.c */ /* 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 -- cgit v1.2.3-70-g09d2 From b824bb4b12548fedd622686d443310d574eb084e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 25 Jun 2011 17:39:19 -0300 Subject: [media] pwc: Get rid of error_status and unplugged variables Having 2 ways of tracking disconnection is too much, remove both and instead simply set pdev->udev to NULL on disconnect. Also check for pdev->udev being NULL in all possible entry paths. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pwc/pwc-if.c | 38 +++++++++++++++++--------------------- drivers/media/video/pwc/pwc-v4l.c | 24 ++++++++++++++++++++++++ drivers/media/video/pwc/pwc.h | 2 -- 3 files changed, 41 insertions(+), 23 deletions(-) (limited to 'drivers/media/video/pwc/pwc-v4l.c') diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 907db23fdb5..6bff33b357b 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -570,14 +570,7 @@ static void pwc_isoc_cleanup(struct pwc_device *pdev) pwc_iso_stop(pdev); pwc_iso_free(pdev); - - /* Stop camera, but only if we are sure the camera is still there (unplug - is signalled by EPIPE) - */ - if (pdev->error_status != EPIPE) { - PWC_DEBUG_OPEN("Setting alternate interface 0.\n"); - usb_set_interface(pdev->udev, 0, 0); - } + usb_set_interface(pdev->udev, 0, 0); pdev->iso_init = 0; PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n"); @@ -719,6 +712,9 @@ static int pwc_video_open(struct file *file) PWC_DEBUG_OPEN(">> video_open called(vdev = 0x%p).\n", vdev); pdev = video_get_drvdata(vdev); + if (!pdev->udev) + return -ENODEV; + if (pdev->vopen) { PWC_DEBUG_OPEN("I'm busy, someone is using the device.\n"); return -EBUSY; @@ -760,7 +756,6 @@ static int pwc_video_open(struct file *file) /* Reset buffers & parameters */ pdev->visoc_errors = 0; - pdev->error_status = 0; pwc_construct(pdev); /* set min/max sizes correct */ /* Set some defaults */ @@ -837,7 +832,7 @@ static int pwc_video_close(struct file *file) pwc_free_buffers(pdev); /* Turn off LEDS and power down camera, but only when not unplugged */ - if (!pdev->unplugged) { + if (pdev->udev) { /* Turn LEDs off */ if (pwc_set_leds(pdev, 0, 0) < 0) PWC_DEBUG_MODULE("Failed to set LED on/off time.\n"); @@ -859,8 +854,8 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf, struct video_device *vdev = file->private_data; struct pwc_device *pdev = video_get_drvdata(vdev); - if (pdev->error_status) - return -pdev->error_status; + if (!pdev->udev) + return -ENODEV; return vb2_read(&pdev->vb_queue, buf, count, ppos, file->f_flags & O_NONBLOCK); @@ -871,7 +866,7 @@ static unsigned int pwc_video_poll(struct file *file, poll_table *wait) struct video_device *vdev = file->private_data; struct pwc_device *pdev = video_get_drvdata(vdev); - if (pdev->error_status) + if (!pdev->udev) return POLL_ERR; return vb2_poll(&pdev->vb_queue, file, wait); @@ -923,8 +918,8 @@ 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; + if (!pdev->udev) + return -ENODEV; return 0; } @@ -964,6 +959,9 @@ static int start_streaming(struct vb2_queue *vq) { struct pwc_device *pdev = vb2_get_drv_priv(vq); + if (!pdev->udev) + return -ENODEV; + return pwc_isoc_init(pdev); } @@ -971,7 +969,8 @@ static int stop_streaming(struct vb2_queue *vq) { struct pwc_device *pdev = vb2_get_drv_priv(vq); - pwc_isoc_cleanup(pdev); + if (pdev->udev) + pwc_isoc_cleanup(pdev); pwc_cleanup_queued_bufs(pdev); return 0; @@ -1389,15 +1388,12 @@ static void usb_pwc_disconnect(struct usb_interface *intf) struct pwc_device *pdev = usb_get_intfdata(intf); mutex_lock(&pdev->modlock); - usb_set_intfdata (intf, NULL); - - /* We got unplugged; this is signalled by an EPIPE error code */ - pdev->error_status = EPIPE; - pdev->unplugged = 1; + usb_set_intfdata(intf, NULL); /* No need to keep the urbs around after disconnection */ pwc_isoc_cleanup(pdev); pwc_cleanup_queued_bufs(pdev); + pdev->udev = NULL; mutex_unlock(&pdev->modlock); diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index cda38837adc..8bd0a681990 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -288,6 +288,9 @@ static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f) { int ret, fps, snapshot, compression, pixelformat; + if (!pdev->udev) + return -ENODEV; + ret = pwc_vidioc_try_fmt(pdev, f); if (ret<0) return ret; @@ -346,6 +349,9 @@ static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap struct video_device *vdev = video_devdata(file); struct pwc_device *pdev = video_drvdata(file); + if (!pdev->udev) + return -ENODEV; + strcpy(cap->driver, PWC_NAME); strlcpy(cap->card, vdev->name, sizeof(cap->card)); usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info)); @@ -414,6 +420,9 @@ static int pwc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) struct pwc_device *pdev = video_drvdata(file); int ret; + if (!pdev->udev) + return -ENODEV; + switch (c->id) { case V4L2_CID_BRIGHTNESS: c->value = pwc_get_brightness(pdev); @@ -517,6 +526,9 @@ static int pwc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) struct pwc_device *pdev = video_drvdata(file); int ret; + if (!pdev->udev) + return -ENODEV; + switch (c->id) { case V4L2_CID_BRIGHTNESS: c->value <<= 9; @@ -692,6 +704,9 @@ static int pwc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { struct pwc_device *pdev = video_drvdata(file); + if (!pdev->udev) + return -ENODEV; + return vb2_qbuf(&pdev->vb_queue, buf); } @@ -699,6 +714,9 @@ static int pwc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { struct pwc_device *pdev = video_drvdata(file); + if (!pdev->udev) + return -ENODEV; + return vb2_dqbuf(&pdev->vb_queue, buf, file->f_flags & O_NONBLOCK); } @@ -706,6 +724,9 @@ static int pwc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) { struct pwc_device *pdev = video_drvdata(file); + if (!pdev->udev) + return -ENODEV; + return vb2_streamon(&pdev->vb_queue, i); } @@ -713,6 +734,9 @@ static int pwc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) { struct pwc_device *pdev = video_drvdata(file); + if (!pdev->udev) + return -ENODEV; + return vb2_streamoff(&pdev->vb_queue, i); } diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index cfe82319f34..13b85246a26 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -161,7 +161,6 @@ struct pwc_device int release; /* release number */ int features; /* feature bits */ char serial[30]; /* serial number (string) */ - int error_status; /* set when something goes wrong */ int usb_init; /* set when the cam has been initialized */ /*** Video data ***/ @@ -180,7 +179,6 @@ struct pwc_device char vsnapshot; /* snapshot mode */ char vsync; /* used by isoc handler */ char vmirror; /* for ToUCaM series */ - char unplugged; int cmd_len; unsigned char cmd_buf[13]; -- cgit v1.2.3-70-g09d2 From 4fba471e405f8f983085fd9f2fd9637bfc275f8f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 26 Jun 2011 12:13:44 -0300 Subject: [media] pwc: Allow multiple opens Allow multiple opens of the /dev/video node so that control panel apps can be open to-gether with streaming apps. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pwc/pwc-if.c | 23 ++++++++++++++--------- drivers/media/video/pwc/pwc-v4l.c | 35 +++++++++++++++++++++++++++-------- drivers/media/video/pwc/pwc.h | 2 +- 3 files changed, 42 insertions(+), 18 deletions(-) (limited to 'drivers/media/video/pwc/pwc-v4l.c') diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index b591b723f5a..a6b5fde5c3a 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -678,12 +678,6 @@ static int pwc_video_open(struct file *file) if (!pdev->udev) return -ENODEV; - if (pdev->vopen) { - PWC_DEBUG_OPEN("I'm busy, someone is using the device.\n"); - return -EBUSY; - } - - pdev->vopen++; file->private_data = vdev; PWC_DEBUG_OPEN("<< video_open() returns 0.\n"); return 0; @@ -718,10 +712,12 @@ static int pwc_video_close(struct file *file) PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev); pdev = video_get_drvdata(vdev); - vb2_queue_release(&pdev->vb_queue); - pdev->vopen--; + if (pdev->capt_file == file) { + vb2_queue_release(&pdev->vb_queue); + pdev->capt_file = NULL; + } - PWC_DEBUG_OPEN("<< video_close() vopen=%d\n", pdev->vopen); + PWC_DEBUG_OPEN("<< video_close()\n"); return 0; } @@ -734,6 +730,12 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf, if (!pdev->udev) return -ENODEV; + if (pdev->capt_file != NULL && + pdev->capt_file != file) + return -EBUSY; + + pdev->capt_file = file; + return vb2_read(&pdev->vb_queue, buf, count, ppos, file->f_flags & O_NONBLOCK); } @@ -754,6 +756,9 @@ static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) struct video_device *vdev = file->private_data; struct pwc_device *pdev = video_get_drvdata(vdev); + if (pdev->capt_file != file) + return -EBUSY; + return vb2_mmap(&pdev->vb_queue, vma); } diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 8bd0a681990..834055b71e0 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -284,13 +284,21 @@ static int pwc_vidioc_try_fmt(struct pwc_device *pdev, struct v4l2_format *f) } /* ioctl(VIDIOC_SET_FMT) */ -static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f) + +static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) { + struct pwc_device *pdev = video_drvdata(file); int ret, fps, snapshot, compression, pixelformat; if (!pdev->udev) return -ENODEV; + if (pdev->capt_file != NULL && + pdev->capt_file != file) + return -EBUSY; + + pdev->capt_file = file; + ret = pwc_vidioc_try_fmt(pdev, f); if (ret<0) return ret; @@ -678,18 +686,17 @@ static int pwc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format * return pwc_vidioc_try_fmt(pdev, f); } -static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) -{ - struct pwc_device *pdev = video_drvdata(file); - - return pwc_vidioc_set_fmt(pdev, f); -} - static int pwc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb) { struct pwc_device *pdev = video_drvdata(file); + if (pdev->capt_file != NULL && + pdev->capt_file != file) + return -EBUSY; + + pdev->capt_file = file; + return vb2_reqbufs(&pdev->vb_queue, rb); } @@ -707,6 +714,9 @@ static int pwc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) if (!pdev->udev) return -ENODEV; + if (pdev->capt_file != file) + return -EBUSY; + return vb2_qbuf(&pdev->vb_queue, buf); } @@ -717,6 +727,9 @@ static int pwc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) if (!pdev->udev) return -ENODEV; + if (pdev->capt_file != file) + return -EBUSY; + return vb2_dqbuf(&pdev->vb_queue, buf, file->f_flags & O_NONBLOCK); } @@ -727,6 +740,9 @@ static int pwc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) if (!pdev->udev) return -ENODEV; + if (pdev->capt_file != file) + return -EBUSY; + return vb2_streamon(&pdev->vb_queue, i); } @@ -737,6 +753,9 @@ static int pwc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) if (!pdev->udev) return -ENODEV; + if (pdev->capt_file != file) + return -EBUSY; + return vb2_streamoff(&pdev->vb_queue, i); } diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index 8d82c6aac42..d65cd14ef9f 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -160,7 +160,7 @@ struct pwc_device char serial[30]; /* serial number (string) */ /*** Video data ***/ - int vopen; /* flag */ + struct file *capt_file; /* file doing video capture */ int vendpoint; /* video isoc endpoint */ int vcinterface; /* video control interface */ int valternate; /* alternate interface needed */ -- cgit v1.2.3-70-g09d2 From 6c9cac89c009c049a9ad29cdf0f51892410fe751 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 26 Jun 2011 12:52:01 -0300 Subject: [media] pwc: Replace control code with v4l2-ctrls framework Also remove all the converting from native range to 0-65535 and back that was going on. This is no longer needed now that we no longer support v4l1. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pwc/pwc-ctrl.c | 729 ++++---------------------- drivers/media/video/pwc/pwc-if.c | 18 +- drivers/media/video/pwc/pwc-v4l.c | 1008 +++++++++++++++++++++--------------- drivers/media/video/pwc/pwc.h | 152 ++++-- 4 files changed, 823 insertions(+), 1084 deletions(-) (limited to 'drivers/media/video/pwc/pwc-v4l.c') diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c index 9d800c66807..8e0cc537e1e 100644 --- a/drivers/media/video/pwc/pwc-ctrl.c +++ b/drivers/media/video/pwc/pwc-ctrl.c @@ -3,6 +3,7 @@ video modes. (C) 1999-2003 Nemosoft Unv. (C) 2004-2006 Luc Saillard (luc@saillard.org) + (C) 2011 Hans de Goede NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -49,55 +50,7 @@ #include "pwc-dec1.h" #include "pwc-dec23.h" -/* Request types: video */ -#define SET_LUM_CTL 0x01 -#define GET_LUM_CTL 0x02 -#define SET_CHROM_CTL 0x03 -#define GET_CHROM_CTL 0x04 -#define SET_STATUS_CTL 0x05 -#define GET_STATUS_CTL 0x06 -#define SET_EP_STREAM_CTL 0x07 -#define GET_EP_STREAM_CTL 0x08 -#define GET_XX_CTL 0x09 -#define SET_XX_CTL 0x0A -#define GET_XY_CTL 0x0B -#define SET_XY_CTL 0x0C -#define SET_MPT_CTL 0x0D -#define GET_MPT_CTL 0x0E - -/* Selectors for the Luminance controls [GS]ET_LUM_CTL */ -#define AGC_MODE_FORMATTER 0x2000 -#define PRESET_AGC_FORMATTER 0x2100 -#define SHUTTER_MODE_FORMATTER 0x2200 -#define PRESET_SHUTTER_FORMATTER 0x2300 -#define PRESET_CONTOUR_FORMATTER 0x2400 -#define AUTO_CONTOUR_FORMATTER 0x2500 -#define BACK_LIGHT_COMPENSATION_FORMATTER 0x2600 -#define CONTRAST_FORMATTER 0x2700 -#define DYNAMIC_NOISE_CONTROL_FORMATTER 0x2800 -#define FLICKERLESS_MODE_FORMATTER 0x2900 -#define AE_CONTROL_SPEED 0x2A00 -#define BRIGHTNESS_FORMATTER 0x2B00 -#define GAMMA_FORMATTER 0x2C00 - -/* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */ -#define WB_MODE_FORMATTER 0x1000 -#define AWB_CONTROL_SPEED_FORMATTER 0x1100 -#define AWB_CONTROL_DELAY_FORMATTER 0x1200 -#define PRESET_MANUAL_RED_GAIN_FORMATTER 0x1300 -#define PRESET_MANUAL_BLUE_GAIN_FORMATTER 0x1400 -#define COLOUR_MODE_FORMATTER 0x1500 -#define SATURATION_MODE_FORMATTER1 0x1600 -#define SATURATION_MODE_FORMATTER2 0x1700 - -/* Selectors for the Status controls [GS]ET_STATUS_CTL */ -#define SAVE_USER_DEFAULTS_FORMATTER 0x0200 -#define RESTORE_USER_DEFAULTS_FORMATTER 0x0300 -#define RESTORE_FACTORY_DEFAULTS_FORMATTER 0x0400 -#define READ_AGC_FORMATTER 0x0500 -#define READ_SHUTTER_FORMATTER 0x0600 -#define READ_RED_GAIN_FORMATTER 0x0700 -#define READ_BLUE_GAIN_FORMATTER 0x0800 +/* Selectors for status controls used only in this file */ #define GET_STATUS_B00 0x0B00 #define SENSOR_TYPE_FORMATTER1 0x0C00 #define GET_STATUS_3000 0x3000 @@ -116,11 +69,6 @@ /* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */ #define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100 -/* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */ -#define PT_RELATIVE_CONTROL_FORMATTER 0x01 -#define PT_RESET_CONTROL_FORMATTER 0x02 -#define PT_STATUS_FORMATTER 0x03 - static const char *size2name[PSZ_MAX] = { "subQCIF", @@ -160,7 +108,7 @@ static void pwc_set_image_buffer_size(struct pwc_device *pdev); /****************************************************************************/ static int _send_control_msg(struct pwc_device *pdev, - u8 request, u16 value, int index, void *buf, int buflen, int timeout) + u8 request, u16 value, int index, void *buf, int buflen) { int rc; void *kbuf = NULL; @@ -177,7 +125,7 @@ static int _send_control_msg(struct pwc_device *pdev, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, index, - kbuf, buflen, timeout); + kbuf, buflen, USB_CTRL_SET_TIMEOUT); kfree(kbuf); return rc; @@ -197,9 +145,13 @@ static int recv_control_msg(struct pwc_device *pdev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, pdev->vcinterface, - kbuf, buflen, 500); + kbuf, buflen, USB_CTRL_GET_TIMEOUT); memcpy(buf, kbuf, buflen); kfree(kbuf); + + if (rc < 0) + PWC_ERROR("recv_control_msg error %d req %02x val %04x\n", + rc, request, value); return rc; } @@ -210,18 +162,16 @@ static inline int send_video_command(struct pwc_device *pdev, SET_EP_STREAM_CTL, VIDEO_OUTPUT_CONTROL_FORMATTER, index, - buf, buflen, 1000); + buf, buflen); } static inline int send_control_msg(struct pwc_device *pdev, u8 request, u16 value, void *buf, int buflen) { return _send_control_msg(pdev, - request, value, pdev->vcinterface, buf, buflen, 500); + request, value, pdev->vcinterface, buf, buflen); } - - static int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames) { unsigned char buf[3]; @@ -549,246 +499,78 @@ static void pwc_set_image_buffer_size(struct pwc_device *pdev) pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE; } -/* BRIGHTNESS */ -int pwc_get_brightness(struct pwc_device *pdev) +int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) { - char buf; int ret; + u8 buf; - ret = recv_control_msg(pdev, - GET_LUM_CTL, BRIGHTNESS_FORMATTER, &buf, sizeof(buf)); + ret = recv_control_msg(pdev, request, value, &buf, sizeof(buf)); if (ret < 0) return ret; - return buf; -} - -int pwc_set_brightness(struct pwc_device *pdev, int value) -{ - char buf; - if (value < 0) - value = 0; - if (value > 0xffff) - value = 0xffff; - buf = (value >> 9) & 0x7f; - return send_control_msg(pdev, - SET_LUM_CTL, BRIGHTNESS_FORMATTER, &buf, sizeof(buf)); + *data = buf; + return 0; } -/* CONTRAST */ - -int pwc_get_contrast(struct pwc_device *pdev) +int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data) { - char buf; int ret; - ret = recv_control_msg(pdev, - GET_LUM_CTL, CONTRAST_FORMATTER, &buf, sizeof(buf)); + ret = send_control_msg(pdev, request, value, &data, sizeof(data)); if (ret < 0) return ret; - return buf; -} -int pwc_set_contrast(struct pwc_device *pdev, int value) -{ - char buf; - - if (value < 0) - value = 0; - if (value > 0xffff) - value = 0xffff; - buf = (value >> 10) & 0x3f; - return send_control_msg(pdev, - SET_LUM_CTL, CONTRAST_FORMATTER, &buf, sizeof(buf)); + return 0; } -/* GAMMA */ - -int pwc_get_gamma(struct pwc_device *pdev) +int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) { - char buf; int ret; + s8 buf; - ret = recv_control_msg(pdev, - GET_LUM_CTL, GAMMA_FORMATTER, &buf, sizeof(buf)); + ret = recv_control_msg(pdev, request, value, &buf, sizeof(buf)); if (ret < 0) return ret; - return buf; -} - -int pwc_set_gamma(struct pwc_device *pdev, int value) -{ - char buf; - - if (value < 0) - value = 0; - if (value > 0xffff) - value = 0xffff; - buf = (value >> 11) & 0x1f; - return send_control_msg(pdev, - SET_LUM_CTL, GAMMA_FORMATTER, &buf, sizeof(buf)); -} - - -/* SATURATION */ - -/* return a value between [-100 , 100] */ -int pwc_get_saturation(struct pwc_device *pdev, int *value) -{ - char buf; - int ret, saturation_register; - if (pdev->type < 675) - return -EINVAL; - if (pdev->type < 730) - saturation_register = SATURATION_MODE_FORMATTER2; - else - saturation_register = SATURATION_MODE_FORMATTER1; - ret = recv_control_msg(pdev, - GET_CHROM_CTL, saturation_register, &buf, sizeof(buf)); - if (ret < 0) - return ret; - *value = (signed)buf; + *data = buf; return 0; } -/* @param value saturation color between [-100 , 100] */ -int pwc_set_saturation(struct pwc_device *pdev, int value) -{ - char buf; - int saturation_register; - - if (pdev->type < 675) - return -EINVAL; - if (value < -100) - value = -100; - if (value > 100) - value = 100; - if (pdev->type < 730) - saturation_register = SATURATION_MODE_FORMATTER2; - else - saturation_register = SATURATION_MODE_FORMATTER1; - return send_control_msg(pdev, - SET_CHROM_CTL, saturation_register, &buf, sizeof(buf)); -} - -/* AGC */ - -int pwc_set_agc(struct pwc_device *pdev, int mode, int value) +int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) { - char buf; int ret; + u8 buf[2]; - if (mode) - buf = 0x0; /* auto */ - else - buf = 0xff; /* fixed */ - - ret = send_control_msg(pdev, - SET_LUM_CTL, AGC_MODE_FORMATTER, &buf, sizeof(buf)); - - if (!mode && ret >= 0) { - if (value < 0) - value = 0; - if (value > 0xffff) - value = 0xffff; - buf = (value >> 10) & 0x3F; - ret = send_control_msg(pdev, - SET_LUM_CTL, PRESET_AGC_FORMATTER, &buf, sizeof(buf)); - } + ret = recv_control_msg(pdev, request, value, buf, sizeof(buf)); if (ret < 0) return ret; + + *data = (buf[1] << 8) | buf[0]; return 0; } -int pwc_get_agc(struct pwc_device *pdev, int *value) +int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data) { - unsigned char buf; int ret; + u8 buf[2]; - ret = recv_control_msg(pdev, - GET_LUM_CTL, AGC_MODE_FORMATTER, &buf, sizeof(buf)); + buf[0] = data & 0xff; + buf[1] = data >> 8; + ret = send_control_msg(pdev, request, value, buf, sizeof(buf)); if (ret < 0) return ret; - if (buf != 0) { /* fixed */ - ret = recv_control_msg(pdev, - GET_LUM_CTL, PRESET_AGC_FORMATTER, &buf, sizeof(buf)); - if (ret < 0) - return ret; - if (buf > 0x3F) - buf = 0x3F; - *value = (buf << 10); - } - else { /* auto */ - ret = recv_control_msg(pdev, - GET_STATUS_CTL, READ_AGC_FORMATTER, &buf, sizeof(buf)); - if (ret < 0) - return ret; - /* Gah... this value ranges from 0x00 ... 0x9F */ - if (buf > 0x9F) - buf = 0x9F; - *value = -(48 + buf * 409); - } - return 0; } -int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value) -{ - char buf[2]; - int speed, ret; - - - if (mode) - buf[0] = 0x0; /* auto */ - else - buf[0] = 0xff; /* fixed */ - - ret = send_control_msg(pdev, - SET_LUM_CTL, SHUTTER_MODE_FORMATTER, &buf, 1); - - if (!mode && ret >= 0) { - if (value < 0) - value = 0; - if (value > 0xffff) - value = 0xffff; - - if (DEVICE_USE_CODEC2(pdev->type)) { - /* speed ranges from 0x0 to 0x290 (656) */ - speed = (value / 100); - buf[1] = speed >> 8; - buf[0] = speed & 0xff; - } else if (DEVICE_USE_CODEC3(pdev->type)) { - /* speed seems to range from 0x0 to 0xff */ - buf[1] = 0; - buf[0] = value >> 8; - } - - ret = send_control_msg(pdev, - SET_LUM_CTL, PRESET_SHUTTER_FORMATTER, - &buf, sizeof(buf)); - } - return ret; -} - -/* This function is not exported to v4l1, so output values between 0 -> 256 */ -int pwc_get_shutter_speed(struct pwc_device *pdev, int *value) +int pwc_button_ctrl(struct pwc_device *pdev, u16 value) { - unsigned char buf[2]; int ret; - ret = recv_control_msg(pdev, - GET_STATUS_CTL, READ_SHUTTER_FORMATTER, &buf, sizeof(buf)); + ret = send_control_msg(pdev, SET_STATUS_CTL, value, NULL, 0); if (ret < 0) return ret; - *value = buf[0] + (buf[1] << 8); - if (DEVICE_USE_CODEC2(pdev->type)) { - /* speed ranges from 0x0 to 0x290 (656) */ - *value *= 256/656; - } else if (DEVICE_USE_CODEC3(pdev->type)) { - /* speed seems to range from 0x0 to 0xff */ - } + return 0; } @@ -817,162 +599,6 @@ void pwc_camera_power(struct pwc_device *pdev, int power) power ? "on" : "off", r); } -/* private calls */ -int pwc_restore_user(struct pwc_device *pdev) -{ - return send_control_msg(pdev, - SET_STATUS_CTL, RESTORE_USER_DEFAULTS_FORMATTER, NULL, 0); -} - -int pwc_save_user(struct pwc_device *pdev) -{ - return send_control_msg(pdev, - SET_STATUS_CTL, SAVE_USER_DEFAULTS_FORMATTER, NULL, 0); -} - -int pwc_restore_factory(struct pwc_device *pdev) -{ - return send_control_msg(pdev, - SET_STATUS_CTL, RESTORE_FACTORY_DEFAULTS_FORMATTER, NULL, 0); -} - - /* ************************************************* */ - /* Patch by Alvarado: (not in the original version */ - - /* - * the camera recognizes modes from 0 to 4: - * - * 00: indoor (incandescant lighting) - * 01: outdoor (sunlight) - * 02: fluorescent lighting - * 03: manual - * 04: auto - */ -int pwc_set_awb(struct pwc_device *pdev, int mode) -{ - char buf; - int ret; - - if (mode < 0) - mode = 0; - - if (mode > 4) - mode = 4; - - buf = mode & 0x07; /* just the lowest three bits */ - - ret = send_control_msg(pdev, - SET_CHROM_CTL, WB_MODE_FORMATTER, &buf, sizeof(buf)); - - if (ret < 0) - return ret; - return 0; -} - -int pwc_get_awb(struct pwc_device *pdev) -{ - unsigned char buf; - int ret; - - ret = recv_control_msg(pdev, - GET_CHROM_CTL, WB_MODE_FORMATTER, &buf, sizeof(buf)); - - if (ret < 0) - return ret; - return buf; -} - -int pwc_set_red_gain(struct pwc_device *pdev, int value) -{ - unsigned char buf; - - if (value < 0) - value = 0; - if (value > 0xffff) - value = 0xffff; - /* only the msb is considered */ - buf = value >> 8; - return send_control_msg(pdev, - SET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, - &buf, sizeof(buf)); -} - -int pwc_get_red_gain(struct pwc_device *pdev, int *value) -{ - unsigned char buf; - int ret; - - ret = recv_control_msg(pdev, - GET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, - &buf, sizeof(buf)); - if (ret < 0) - return ret; - *value = buf << 8; - return 0; -} - - -int pwc_set_blue_gain(struct pwc_device *pdev, int value) -{ - unsigned char buf; - - if (value < 0) - value = 0; - if (value > 0xffff) - value = 0xffff; - /* only the msb is considered */ - buf = value >> 8; - return send_control_msg(pdev, - SET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, - &buf, sizeof(buf)); -} - -int pwc_get_blue_gain(struct pwc_device *pdev, int *value) -{ - unsigned char buf; - int ret; - - ret = recv_control_msg(pdev, - GET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, - &buf, sizeof(buf)); - if (ret < 0) - return ret; - *value = buf << 8; - return 0; -} - - -/* The following two functions are different, since they only read the - internal red/blue gains, which may be different from the manual - gains set or read above. - */ -static int pwc_read_red_gain(struct pwc_device *pdev, int *value) -{ - unsigned char buf; - int ret; - - ret = recv_control_msg(pdev, - GET_STATUS_CTL, READ_RED_GAIN_FORMATTER, &buf, sizeof(buf)); - if (ret < 0) - return ret; - *value = buf << 8; - return 0; -} - -static int pwc_read_blue_gain(struct pwc_device *pdev, int *value) -{ - unsigned char buf; - int ret; - - ret = recv_control_msg(pdev, - GET_STATUS_CTL, READ_BLUE_GAIN_FORMATTER, &buf, sizeof(buf)); - if (ret < 0) - return ret; - *value = buf << 8; - return 0; -} - - static int pwc_set_wb_speed(struct pwc_device *pdev, int speed) { unsigned char buf; @@ -1070,164 +696,6 @@ static int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value) return 0; } -int pwc_set_contour(struct pwc_device *pdev, int contour) -{ - unsigned char buf; - int ret; - - if (contour < 0) - buf = 0xff; /* auto contour on */ - else - buf = 0x0; /* auto contour off */ - ret = send_control_msg(pdev, - SET_LUM_CTL, AUTO_CONTOUR_FORMATTER, &buf, sizeof(buf)); - if (ret < 0) - return ret; - - if (contour < 0) - return 0; - if (contour > 0xffff) - contour = 0xffff; - - buf = (contour >> 10); /* contour preset is [0..3f] */ - ret = send_control_msg(pdev, - SET_LUM_CTL, PRESET_CONTOUR_FORMATTER, &buf, sizeof(buf)); - if (ret < 0) - return ret; - return 0; -} - -int pwc_get_contour(struct pwc_device *pdev, int *contour) -{ - unsigned char buf; - int ret; - - ret = recv_control_msg(pdev, - GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, &buf, sizeof(buf)); - if (ret < 0) - return ret; - - if (buf == 0) { - /* auto mode off, query current preset value */ - ret = recv_control_msg(pdev, - GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, - &buf, sizeof(buf)); - if (ret < 0) - return ret; - *contour = buf << 10; - } - else - *contour = -1; - return 0; -} - - -int pwc_set_backlight(struct pwc_device *pdev, int backlight) -{ - unsigned char buf; - - if (backlight) - buf = 0xff; - else - buf = 0x0; - return send_control_msg(pdev, - SET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, - &buf, sizeof(buf)); -} - -int pwc_get_backlight(struct pwc_device *pdev, int *backlight) -{ - int ret; - unsigned char buf; - - ret = recv_control_msg(pdev, - GET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, - &buf, sizeof(buf)); - if (ret < 0) - return ret; - *backlight = !!buf; - return 0; -} - -int pwc_set_colour_mode(struct pwc_device *pdev, int colour) -{ - unsigned char buf; - - if (colour) - buf = 0xff; - else - buf = 0x0; - return send_control_msg(pdev, - SET_CHROM_CTL, COLOUR_MODE_FORMATTER, &buf, sizeof(buf)); -} - -int pwc_get_colour_mode(struct pwc_device *pdev, int *colour) -{ - int ret; - unsigned char buf; - - ret = recv_control_msg(pdev, - GET_CHROM_CTL, COLOUR_MODE_FORMATTER, &buf, sizeof(buf)); - if (ret < 0) - return ret; - *colour = !!buf; - return 0; -} - - -int pwc_set_flicker(struct pwc_device *pdev, int flicker) -{ - unsigned char buf; - - if (flicker) - buf = 0xff; - else - buf = 0x0; - return send_control_msg(pdev, - SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, &buf, sizeof(buf)); -} - -int pwc_get_flicker(struct pwc_device *pdev, int *flicker) -{ - int ret; - unsigned char buf; - - ret = recv_control_msg(pdev, - GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, &buf, sizeof(buf)); - if (ret < 0) - return ret; - *flicker = !!buf; - return 0; -} - -int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise) -{ - unsigned char buf; - - if (noise < 0) - noise = 0; - if (noise > 3) - noise = 3; - buf = noise; - return send_control_msg(pdev, - SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, - &buf, sizeof(buf)); -} - -int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise) -{ - int ret; - unsigned char buf; - - ret = recv_control_msg(pdev, - GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, - &buf, sizeof(buf)); - if (ret < 0) - return ret; - *noise = buf; - return 0; -} - static int _pwc_mpt_reset(struct pwc_device *pdev, int flags) { unsigned char buf; @@ -1357,37 +825,41 @@ int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) /* copy local variable to arg */ #define ARG_OUT(ARG_name) /* nothing */ +/* + * Our ctrls use native values, but the old custom pwc ioctl interface expects + * values from 0 - 65535, define 2 helper functions to scale things. */ +static int pwc_ioctl_g_ctrl(struct v4l2_ctrl *ctrl) +{ + return v4l2_ctrl_g_ctrl(ctrl) * 65535 / ctrl->maximum; +} + +static int pwc_ioctl_s_ctrl(struct v4l2_ctrl *ctrl, int val) +{ + return v4l2_ctrl_s_ctrl(ctrl, val * ctrl->maximum / 65535); +} + long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) { long ret = 0; switch(cmd) { case VIDIOCPWCRUSER: - { - if (pwc_restore_user(pdev)) - ret = -EINVAL; + ret = pwc_button_ctrl(pdev, RESTORE_USER_DEFAULTS_FORMATTER); break; - } case VIDIOCPWCSUSER: - { - if (pwc_save_user(pdev)) - ret = -EINVAL; + ret = pwc_button_ctrl(pdev, SAVE_USER_DEFAULTS_FORMATTER); break; - } case VIDIOCPWCFACTORY: - { - if (pwc_restore_factory(pdev)) - ret = -EINVAL; + ret = pwc_button_ctrl(pdev, RESTORE_FACTORY_DEFAULTS_FORMATTER); break; - } case VIDIOCPWCSCQUAL: { ARG_DEF(int, qual) - if (pdev->iso_init) { + if (vb2_is_streaming(&pdev->vb_queue)) { ret = -EBUSY; break; } @@ -1431,71 +903,59 @@ long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) case VIDIOCPWCSAGC: { ARG_DEF(int, agc) - ARG_IN(agc) - if (pwc_set_agc(pdev, ARGR(agc) < 0 ? 1 : 0, ARGR(agc))) - ret = -EINVAL; + ret = v4l2_ctrl_s_ctrl(pdev->autogain, ARGR(agc) < 0); + if (ret == 0 && ARGR(agc) >= 0) + ret = pwc_ioctl_s_ctrl(pdev->gain, ARGR(agc)); break; } case VIDIOCPWCGAGC: { ARG_DEF(int, agc) - - if (pwc_get_agc(pdev, ARGA(agc))) - ret = -EINVAL; + if (v4l2_ctrl_g_ctrl(pdev->autogain)) + ARGR(agc) = -1; + else + ARGR(agc) = pwc_ioctl_g_ctrl(pdev->gain); ARG_OUT(agc) break; } case VIDIOCPWCSSHUTTER: { - ARG_DEF(int, shutter_speed) - - ARG_IN(shutter_speed) - ret = pwc_set_shutter_speed(pdev, ARGR(shutter_speed) < 0 ? 1 : 0, ARGR(shutter_speed)); + ARG_DEF(int, shutter) + ARG_IN(shutter) + ret = v4l2_ctrl_s_ctrl(pdev->exposure_auto, + /* Menu idx 0 = auto, idx 1 = manual */ + ARGR(shutter) >= 0); + if (ret == 0 && ARGR(shutter) >= 0) + ret = pwc_ioctl_s_ctrl(pdev->exposure, ARGR(shutter)); break; } case VIDIOCPWCSAWB: { ARG_DEF(struct pwc_whitebalance, wb) - ARG_IN(wb) - ret = pwc_set_awb(pdev, ARGR(wb).mode); - if (ret >= 0 && ARGR(wb).mode == PWC_WB_MANUAL) { - pwc_set_red_gain(pdev, ARGR(wb).manual_red); - pwc_set_blue_gain(pdev, ARGR(wb).manual_blue); - } + ret = v4l2_ctrl_s_ctrl(pdev->auto_white_balance, + ARGR(wb).mode); + if (ret == 0 && ARGR(wb).mode == PWC_WB_MANUAL) + ret = pwc_ioctl_s_ctrl(pdev->red_balance, + ARGR(wb).manual_red); + if (ret == 0 && ARGR(wb).mode == PWC_WB_MANUAL) + ret = pwc_ioctl_s_ctrl(pdev->blue_balance, + ARGR(wb).manual_blue); break; } case VIDIOCPWCGAWB: { ARG_DEF(struct pwc_whitebalance, wb) - - memset(ARGA(wb), 0, sizeof(struct pwc_whitebalance)); - ARGR(wb).mode = pwc_get_awb(pdev); - if (ARGR(wb).mode < 0) - ret = -EINVAL; - else { - if (ARGR(wb).mode == PWC_WB_MANUAL) { - ret = pwc_get_red_gain(pdev, &ARGR(wb).manual_red); - if (ret < 0) - break; - ret = pwc_get_blue_gain(pdev, &ARGR(wb).manual_blue); - if (ret < 0) - break; - } - if (ARGR(wb).mode == PWC_WB_AUTO) { - ret = pwc_read_red_gain(pdev, &ARGR(wb).read_red); - if (ret < 0) - break; - ret = pwc_read_blue_gain(pdev, &ARGR(wb).read_blue); - if (ret < 0) - break; - } - } + ARGR(wb).mode = v4l2_ctrl_g_ctrl(pdev->auto_white_balance); + ARGR(wb).manual_red = ARGR(wb).read_red = + pwc_ioctl_g_ctrl(pdev->red_balance); + ARGR(wb).manual_blue = ARGR(wb).read_blue = + pwc_ioctl_g_ctrl(pdev->blue_balance); ARG_OUT(wb) break; } @@ -1549,17 +1009,20 @@ long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) case VIDIOCPWCSCONTOUR: { ARG_DEF(int, contour) - ARG_IN(contour) - ret = pwc_set_contour(pdev, ARGR(contour)); + ret = v4l2_ctrl_s_ctrl(pdev->autocontour, ARGR(contour) < 0); + if (ret == 0 && ARGR(contour) >= 0) + ret = pwc_ioctl_s_ctrl(pdev->contour, ARGR(contour)); break; } case VIDIOCPWCGCONTOUR: { ARG_DEF(int, contour) - - ret = pwc_get_contour(pdev, ARGA(contour)); + if (v4l2_ctrl_g_ctrl(pdev->autocontour)) + ARGR(contour) = -1; + else + ARGR(contour) = pwc_ioctl_g_ctrl(pdev->contour); ARG_OUT(contour) break; } @@ -1567,17 +1030,15 @@ long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) case VIDIOCPWCSBACKLIGHT: { ARG_DEF(int, backlight) - ARG_IN(backlight) - ret = pwc_set_backlight(pdev, ARGR(backlight)); + ret = v4l2_ctrl_s_ctrl(pdev->backlight, ARGR(backlight)); break; } case VIDIOCPWCGBACKLIGHT: { ARG_DEF(int, backlight) - - ret = pwc_get_backlight(pdev, ARGA(backlight)); + ARGR(backlight) = v4l2_ctrl_g_ctrl(pdev->backlight); ARG_OUT(backlight) break; } @@ -1585,17 +1046,15 @@ long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) case VIDIOCPWCSFLICKER: { ARG_DEF(int, flicker) - ARG_IN(flicker) - ret = pwc_set_flicker(pdev, ARGR(flicker)); + ret = v4l2_ctrl_s_ctrl(pdev->flicker, ARGR(flicker)); break; } case VIDIOCPWCGFLICKER: { ARG_DEF(int, flicker) - - ret = pwc_get_flicker(pdev, ARGA(flicker)); + ARGR(flicker) = v4l2_ctrl_g_ctrl(pdev->flicker); ARG_OUT(flicker) break; } @@ -1603,17 +1062,15 @@ long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) case VIDIOCPWCSDYNNOISE: { ARG_DEF(int, dynnoise) - ARG_IN(dynnoise) - ret = pwc_set_dynamic_noise(pdev, ARGR(dynnoise)); + ret = v4l2_ctrl_s_ctrl(pdev->noise_reduction, ARGR(dynnoise)); break; } case VIDIOCPWCGDYNNOISE: { ARG_DEF(int, dynnoise) - - ret = pwc_get_dynamic_noise(pdev, ARGA(dynnoise)); + ARGR(dynnoise) = v4l2_ctrl_g_ctrl(pdev->noise_reduction); ARG_OUT(dynnoise); break; } diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 4c1c056ffdf..1d5a6db915f 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -679,6 +679,8 @@ static void pwc_video_release(struct video_device *vfd) pdev->decompress_data = NULL; } + v4l2_ctrl_handler_free(&pdev->ctrl_handler); + kfree(pdev); } @@ -1224,9 +1226,14 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id if (rc) goto err_free_mem; - /* Initialize the webcam to sane values */ - pwc_set_brightness(pdev, 0x7fff); - pwc_set_agc(pdev, 1, 0); + /* Register controls (and read default values from camera */ + rc = pwc_init_controls(pdev); + if (rc) { + PWC_ERROR("Failed to register v4l2 controls (%d).\n", rc); + goto err_free_mem; + } + + pdev->vdev.ctrl_handler = &pdev->ctrl_handler; /* And powerdown the camera until streaming starts */ pwc_camera_power(pdev, 0); @@ -1234,7 +1241,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, video_nr); if (rc < 0) { PWC_ERROR("Failed to register as video device (%d).\n", rc); - goto err_free_mem; + goto err_free_controls; } rc = pwc_create_sysfs_files(pdev); if (rc) @@ -1277,7 +1284,10 @@ err_video_unreg: if (hint < MAX_DEV_HINTS) device_hint[hint].pdev = NULL; video_unregister_device(&pdev->vdev); +err_free_controls: + v4l2_ctrl_handler_free(&pdev->ctrl_handler); err_free_mem: + usb_set_intfdata(intf, NULL); kfree(pdev); return rc; } diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 834055b71e0..986dd5d6187 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -2,6 +2,7 @@ USB and Video4Linux interface part. (C) 1999-2004 Nemosoft Unv. (C) 2004-2006 Luc Saillard (luc@saillard.org) + (C) 2011 Hans de Goede NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -31,184 +32,314 @@ #include #include #include +#include #include #include "pwc.h" -static struct v4l2_queryctrl pwc_controls[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 128, - .step = 1, - .default_value = 64, - }, - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 64, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = -100, - .maximum = 100, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_GAMMA, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gamma", - .minimum = 0, - .maximum = 32, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Gain", - .minimum = 0, - .maximum = 256, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Gain", - .minimum = 0, - .maximum = 256, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto White Balance", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Shutter Speed (Exposure)", - .minimum = 0, - .maximum = 256, - .step = 1, - .default_value = 200, - }, - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Gain Enabled", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain Level", - .minimum = 0, - .maximum = 256, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_PRIVATE_SAVE_USER, - .type = V4L2_CTRL_TYPE_BUTTON, - .name = "Save User Settings", - .minimum = 0, - .maximum = 0, - .step = 0, - .default_value = 0, - }, - { - .id = V4L2_CID_PRIVATE_RESTORE_USER, - .type = V4L2_CTRL_TYPE_BUTTON, - .name = "Restore User Settings", - .minimum = 0, - .maximum = 0, - .step = 0, - .default_value = 0, - }, - { - .id = V4L2_CID_PRIVATE_RESTORE_FACTORY, - .type = V4L2_CTRL_TYPE_BUTTON, - .name = "Restore Factory Settings", - .minimum = 0, - .maximum = 0, - .step = 0, - .default_value = 0, - }, - { - .id = V4L2_CID_PRIVATE_COLOUR_MODE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Colour mode", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_PRIVATE_AUTOCONTOUR, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto contour", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_PRIVATE_CONTOUR, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contour", - .minimum = 0, - .maximum = 63, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_PRIVATE_BACKLIGHT, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Backlight compensation", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_PRIVATE_FLICKERLESS, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flickerless", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_PRIVATE_NOISE_REDUCTION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Noise reduction", - .minimum = 0, - .maximum = 3, - .step = 1, - .default_value = 0, - }, +#define PWC_CID_CUSTOM(ctrl) ((V4L2_CID_USER_BASE | 0xf000) + custom_ ## ctrl) + +static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl); +static int pwc_s_ctrl(struct v4l2_ctrl *ctrl); + +static const struct v4l2_ctrl_ops pwc_ctrl_ops = { + .g_volatile_ctrl = pwc_g_volatile_ctrl, + .s_ctrl = pwc_s_ctrl, +}; + +enum { awb_indoor, awb_outdoor, awb_fl, awb_manual, awb_auto }; +enum { custom_autocontour, custom_contour, custom_noise_reduction, + custom_save_user, custom_restore_user, custom_restore_factory }; + +const char * const pwc_auto_whitebal_qmenu[] = { + "Indoor (Incandescant Lighting) Mode", + "Outdoor (Sunlight) Mode", + "Indoor (Fluorescent Lighting) Mode", + "Manual Mode", + "Auto Mode", + NULL +}; + +static const struct v4l2_ctrl_config pwc_auto_white_balance_cfg = { + .ops = &pwc_ctrl_ops, + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_MENU, + .max = awb_auto, + .qmenu = pwc_auto_whitebal_qmenu, +}; + +static const struct v4l2_ctrl_config pwc_autocontour_cfg = { + .ops = &pwc_ctrl_ops, + .id = PWC_CID_CUSTOM(autocontour), + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto contour", + .min = 0, + .max = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config pwc_contour_cfg = { + .ops = &pwc_ctrl_ops, + .id = PWC_CID_CUSTOM(contour), + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contour", + .min = 0, + .max = 63, + .step = 1, +}; + +static const struct v4l2_ctrl_config pwc_backlight_cfg = { + .ops = &pwc_ctrl_ops, + .id = V4L2_CID_BACKLIGHT_COMPENSATION, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, }; +static const struct v4l2_ctrl_config pwc_flicker_cfg = { + .ops = &pwc_ctrl_ops, + .id = V4L2_CID_BAND_STOP_FILTER, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, +}; + +static const struct v4l2_ctrl_config pwc_noise_reduction_cfg = { + .ops = &pwc_ctrl_ops, + .id = PWC_CID_CUSTOM(noise_reduction), + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Dynamic Noise Reduction", + .min = 0, + .max = 3, + .step = 1, +}; + +static const struct v4l2_ctrl_config pwc_save_user_cfg = { + .ops = &pwc_ctrl_ops, + .id = PWC_CID_CUSTOM(save_user), + .type = V4L2_CTRL_TYPE_BUTTON, + .name = "Save User Settings", +}; + +static const struct v4l2_ctrl_config pwc_restore_user_cfg = { + .ops = &pwc_ctrl_ops, + .id = PWC_CID_CUSTOM(restore_user), + .type = V4L2_CTRL_TYPE_BUTTON, + .name = "Restore User Settings", +}; + +static const struct v4l2_ctrl_config pwc_restore_factory_cfg = { + .ops = &pwc_ctrl_ops, + .id = PWC_CID_CUSTOM(restore_factory), + .type = V4L2_CTRL_TYPE_BUTTON, + .name = "Restore Factory Settings", +}; + +int pwc_init_controls(struct pwc_device *pdev) +{ + struct v4l2_ctrl_handler *hdl; + struct v4l2_ctrl_config cfg; + int r, def; + + hdl = &pdev->ctrl_handler; + r = v4l2_ctrl_handler_init(hdl, 20); + if (r) + return r; + + /* Brightness, contrast, saturation, gamma */ + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, BRIGHTNESS_FORMATTER, &def); + if (r || def > 127) + def = 63; + pdev->brightness = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 127, 1, def); + + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, CONTRAST_FORMATTER, &def); + if (r || def > 63) + def = 31; + pdev->contrast = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_CONTRAST, 0, 63, 1, def); + + if (pdev->type >= 675) { + if (pdev->type < 730) + pdev->saturation_fmt = SATURATION_MODE_FORMATTER2; + else + pdev->saturation_fmt = SATURATION_MODE_FORMATTER1; + r = pwc_get_s8_ctrl(pdev, GET_CHROM_CTL, pdev->saturation_fmt, + &def); + if (r || def < -100 || def > 100) + def = 0; + pdev->saturation = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_SATURATION, -100, 100, 1, def); + } + + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, GAMMA_FORMATTER, &def); + if (r || def > 31) + def = 15; + pdev->gamma = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_GAMMA, 0, 31, 1, def); + + /* auto white balance, red gain, blue gain */ + r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, WB_MODE_FORMATTER, &def); + if (r || def > awb_auto) + def = awb_auto; + cfg = pwc_auto_white_balance_cfg; + cfg.name = v4l2_ctrl_get_name(cfg.id); + cfg.def = def; + pdev->auto_white_balance = v4l2_ctrl_new_custom(hdl, &cfg, NULL); + /* check auto controls to avoid NULL deref in v4l2_ctrl_auto_cluster */ + if (!pdev->auto_white_balance) + return hdl->error; + + r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, + PRESET_MANUAL_RED_GAIN_FORMATTER, &def); + if (r) + def = 127; + pdev->red_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 255, 1, def); + + r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, + PRESET_MANUAL_BLUE_GAIN_FORMATTER, &def); + if (r) + def = 127; + pdev->blue_balance = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 0, 255, 1, def); + + v4l2_ctrl_auto_cluster(3, &pdev->auto_white_balance, awb_manual, + pdev->auto_white_balance->cur.val == awb_auto); + + /* autogain, gain */ + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AGC_MODE_FORMATTER, &def); + if (r || (def != 0 && def != 0xff)) + def = 0; + /* Note a register value if 0 means auto gain is on */ + pdev->autogain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, def == 0); + if (!pdev->autogain) + return hdl->error; + + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_AGC_FORMATTER, &def); + if (r || def > 63) + def = 31; + pdev->gain = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_GAIN, 0, 63, 1, def); + + /* auto exposure, exposure */ + if (DEVICE_USE_CODEC2(pdev->type)) { + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, SHUTTER_MODE_FORMATTER, + &def); + if (r || (def != 0 && def != 0xff)) + def = 0; + /* + * def = 0 auto, def = ff manual + * menu idx 0 = auto, idx 1 = manual + */ + pdev->exposure_auto = v4l2_ctrl_new_std_menu(hdl, + &pwc_ctrl_ops, + V4L2_CID_EXPOSURE_AUTO, + 1, 0, def != 0); + if (!pdev->exposure_auto) + return hdl->error; + + /* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */ + r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL, + READ_SHUTTER_FORMATTER, &def); + if (r || def > 655) + def = 655; + pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 655, 1, def); + /* CODEC2: separate auto gain & auto exposure */ + v4l2_ctrl_auto_cluster(2, &pdev->autogain, 0, true); + v4l2_ctrl_auto_cluster(2, &pdev->exposure_auto, + V4L2_EXPOSURE_MANUAL, true); + } else if (DEVICE_USE_CODEC3(pdev->type)) { + /* GET_LUM_CTL, PRESET_SHUTTER_FORMATTER is unreliable */ + r = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL, + READ_SHUTTER_FORMATTER, &def); + if (r || def > 255) + def = 255; + pdev->exposure = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 255, 1, def); + /* CODEC3: both gain and exposure controlled by autogain */ + pdev->autogain_expo_cluster[0] = pdev->autogain; + pdev->autogain_expo_cluster[1] = pdev->gain; + pdev->autogain_expo_cluster[2] = pdev->exposure; + v4l2_ctrl_auto_cluster(3, pdev->autogain_expo_cluster, + 0, true); + } + + /* color / bw setting */ + r = pwc_get_u8_ctrl(pdev, GET_CHROM_CTL, COLOUR_MODE_FORMATTER, + &def); + if (r || (def != 0 && def != 0xff)) + def = 0xff; + /* def = 0 bw, def = ff color, menu idx 0 = color, idx 1 = bw */ + pdev->colorfx = v4l2_ctrl_new_std_menu(hdl, &pwc_ctrl_ops, + V4L2_CID_COLORFX, 1, 0, def == 0); + + /* autocontour, contour */ + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, &def); + if (r || (def != 0 && def != 0xff)) + def = 0; + cfg = pwc_autocontour_cfg; + cfg.def = def == 0; + pdev->autocontour = v4l2_ctrl_new_custom(hdl, &cfg, NULL); + if (!pdev->autocontour) + return hdl->error; + + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, &def); + if (r || def > 63) + def = 31; + cfg = pwc_contour_cfg; + cfg.def = def; + pdev->contour = v4l2_ctrl_new_custom(hdl, &cfg, NULL); + + v4l2_ctrl_auto_cluster(2, &pdev->autocontour, 0, false); + + /* backlight */ + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, + BACK_LIGHT_COMPENSATION_FORMATTER, &def); + if (r || (def != 0 && def != 0xff)) + def = 0; + cfg = pwc_backlight_cfg; + cfg.name = v4l2_ctrl_get_name(cfg.id); + cfg.def = def == 0; + pdev->backlight = v4l2_ctrl_new_custom(hdl, &cfg, NULL); + + /* flikker rediction */ + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, + FLICKERLESS_MODE_FORMATTER, &def); + if (r || (def != 0 && def != 0xff)) + def = 0; + cfg = pwc_flicker_cfg; + cfg.name = v4l2_ctrl_get_name(cfg.id); + cfg.def = def == 0; + pdev->flicker = v4l2_ctrl_new_custom(hdl, &cfg, NULL); + + /* Dynamic noise reduction */ + r = pwc_get_u8_ctrl(pdev, GET_LUM_CTL, + DYNAMIC_NOISE_CONTROL_FORMATTER, &def); + if (r || def > 3) + def = 2; + cfg = pwc_noise_reduction_cfg; + cfg.def = def; + pdev->noise_reduction = v4l2_ctrl_new_custom(hdl, &cfg, NULL); + + /* Save / Restore User / Factory Settings */ + pdev->save_user = v4l2_ctrl_new_custom(hdl, &pwc_save_user_cfg, NULL); + pdev->restore_user = v4l2_ctrl_new_custom(hdl, &pwc_restore_user_cfg, + NULL); + if (pdev->restore_user) + pdev->restore_user->flags = V4L2_CTRL_FLAG_UPDATE; + pdev->restore_factory = v4l2_ctrl_new_custom(hdl, + &pwc_restore_factory_cfg, + NULL); + if (pdev->restore_factory) + pdev->restore_factory->flags = V4L2_CTRL_FLAG_UPDATE; + + return hdl->error; +} static void pwc_vidioc_fill_fmt(const struct pwc_device *pdev, struct v4l2_format *f) { @@ -354,14 +485,13 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { - struct video_device *vdev = video_devdata(file); struct pwc_device *pdev = video_drvdata(file); if (!pdev->udev) return -ENODEV; strcpy(cap->driver, PWC_NAME); - strlcpy(cap->card, vdev->name, sizeof(cap->card)); + strlcpy(cap->card, pdev->vdev.name, sizeof(cap->card)); usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info)); cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | @@ -390,261 +520,328 @@ static int pwc_s_input(struct file *file, void *fh, unsigned int i) return i ? -EINVAL : 0; } -static int pwc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c) +static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { - int i, idx; - u32 id; - - id = c->id; - if (id & V4L2_CTRL_FLAG_NEXT_CTRL) { - id &= V4L2_CTRL_ID_MASK; - id++; - idx = -1; - for (i = 0; i < ARRAY_SIZE(pwc_controls); i++) { - if (pwc_controls[i].id < id) - continue; - if (idx >= 0 - && pwc_controls[i].id > pwc_controls[idx].id) - continue; - idx = i; + struct pwc_device *pdev = + container_of(ctrl->handler, struct pwc_device, ctrl_handler); + int ret = 0; + + if (!pdev->udev) + return -ENODEV; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + if (pdev->color_bal_valid && time_before(jiffies, + pdev->last_color_bal_update + HZ / 4)) { + pdev->red_balance->val = pdev->last_red_balance; + pdev->blue_balance->val = pdev->last_blue_balance; + break; } - if (idx < 0) - return -EINVAL; - memcpy(c, &pwc_controls[idx], sizeof pwc_controls[0]); - return 0; - } - for (i = 0; i < sizeof(pwc_controls) / sizeof(struct v4l2_queryctrl); i++) { - if (pwc_controls[i].id == c->id) { - PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) found\n"); - memcpy(c, &pwc_controls[i], sizeof(struct v4l2_queryctrl)); - return 0; + ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, + READ_RED_GAIN_FORMATTER, + &pdev->red_balance->val); + if (ret) + break; + ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, + READ_BLUE_GAIN_FORMATTER, + &pdev->blue_balance->val); + if (ret) + break; + pdev->last_red_balance = pdev->red_balance->val; + pdev->last_blue_balance = pdev->blue_balance->val; + pdev->last_color_bal_update = jiffies; + pdev->color_bal_valid = true; + break; + case V4L2_CID_AUTOGAIN: + if (pdev->gain_valid && time_before(jiffies, + pdev->last_gain_update + HZ / 4)) { + pdev->gain->val = pdev->last_gain; + break; } + ret = pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, + READ_AGC_FORMATTER, &pdev->gain->val); + if (ret) + break; + pdev->last_gain = pdev->gain->val; + pdev->last_gain_update = jiffies; + pdev->gain_valid = true; + if (!DEVICE_USE_CODEC3(pdev->type)) + break; + /* Fall through for CODEC3 where autogain also controls expo */ + case V4L2_CID_EXPOSURE_AUTO: + if (pdev->exposure_valid && time_before(jiffies, + pdev->last_exposure_update + HZ / 4)) { + pdev->exposure->val = pdev->last_exposure; + break; + } + ret = pwc_get_u16_ctrl(pdev, GET_STATUS_CTL, + READ_SHUTTER_FORMATTER, + &pdev->exposure->val); + if (ret) + break; + pdev->last_exposure = pdev->exposure->val; + pdev->last_exposure_update = jiffies; + pdev->exposure_valid = true; + break; + default: + ret = -EINVAL; } - return -EINVAL; + + if (ret) + PWC_ERROR("g_ctrl %s error %d\n", ctrl->name, ret); + + return ret; } -static int pwc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) +static int pwc_set_awb(struct pwc_device *pdev) { - struct pwc_device *pdev = video_drvdata(file); - int ret; + int ret = 0; + + if (pdev->auto_white_balance->is_new) { + ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, + WB_MODE_FORMATTER, + pdev->auto_white_balance->val); + if (ret) + return ret; + + /* Update val when coming from auto or going to a preset */ + if (pdev->red_balance->is_volatile || + pdev->auto_white_balance->val == awb_indoor || + pdev->auto_white_balance->val == awb_outdoor || + pdev->auto_white_balance->val == awb_fl) { + if (!pdev->red_balance->is_new) + pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, + READ_RED_GAIN_FORMATTER, + &pdev->red_balance->val); + if (!pdev->blue_balance->is_new) + pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, + READ_BLUE_GAIN_FORMATTER, + &pdev->blue_balance->val); + } + if (pdev->auto_white_balance->val == awb_auto) { + pdev->red_balance->is_volatile = true; + pdev->blue_balance->is_volatile = true; + pdev->color_bal_valid = false; /* Force cache update */ + } else { + pdev->red_balance->is_volatile = false; + pdev->blue_balance->is_volatile = false; + } + } - if (!pdev->udev) - return -ENODEV; + if (ret == 0 && pdev->red_balance->is_new) { + if (pdev->auto_white_balance->val != awb_manual) + return -EBUSY; + ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, + PRESET_MANUAL_RED_GAIN_FORMATTER, + pdev->red_balance->val); + } - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - c->value = pwc_get_brightness(pdev); - if (c->value < 0) - return -EINVAL; - return 0; - case V4L2_CID_CONTRAST: - c->value = pwc_get_contrast(pdev); - if (c->value < 0) - return -EINVAL; - return 0; - case V4L2_CID_SATURATION: - ret = pwc_get_saturation(pdev, &c->value); - if (ret < 0) - return -EINVAL; - return 0; - case V4L2_CID_GAMMA: - c->value = pwc_get_gamma(pdev); - if (c->value < 0) - return -EINVAL; - return 0; - case V4L2_CID_RED_BALANCE: - ret = pwc_get_red_gain(pdev, &c->value); - if (ret < 0) - return -EINVAL; - c->value >>= 8; - return 0; - case V4L2_CID_BLUE_BALANCE: - ret = pwc_get_blue_gain(pdev, &c->value); - if (ret < 0) - return -EINVAL; - c->value >>= 8; - return 0; - case V4L2_CID_AUTO_WHITE_BALANCE: - ret = pwc_get_awb(pdev); - if (ret < 0) - return -EINVAL; - c->value = (ret == PWC_WB_MANUAL) ? 0 : 1; - return 0; - case V4L2_CID_GAIN: - ret = pwc_get_agc(pdev, &c->value); - if (ret < 0) - return -EINVAL; - c->value >>= 8; - return 0; - case V4L2_CID_AUTOGAIN: - ret = pwc_get_agc(pdev, &c->value); - if (ret < 0) - return -EINVAL; - c->value = (c->value < 0) ? 1 : 0; - return 0; - case V4L2_CID_EXPOSURE: - ret = pwc_get_shutter_speed(pdev, &c->value); - if (ret < 0) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_COLOUR_MODE: - ret = pwc_get_colour_mode(pdev, &c->value); - if (ret < 0) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_AUTOCONTOUR: - ret = pwc_get_contour(pdev, &c->value); - if (ret < 0) - return -EINVAL; - c->value = (c->value == -1 ? 1 : 0); - return 0; - case V4L2_CID_PRIVATE_CONTOUR: - ret = pwc_get_contour(pdev, &c->value); - if (ret < 0) - return -EINVAL; - c->value >>= 10; - return 0; - case V4L2_CID_PRIVATE_BACKLIGHT: - ret = pwc_get_backlight(pdev, &c->value); - if (ret < 0) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_FLICKERLESS: - ret = pwc_get_flicker(pdev, &c->value); - if (ret < 0) - return -EINVAL; - c->value = (c->value ? 1 : 0); - return 0; - case V4L2_CID_PRIVATE_NOISE_REDUCTION: - ret = pwc_get_dynamic_noise(pdev, &c->value); - if (ret < 0) - return -EINVAL; - return 0; + if (ret == 0 && pdev->blue_balance->is_new) { + if (pdev->auto_white_balance->val != awb_manual) + return -EBUSY; + ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, + PRESET_MANUAL_BLUE_GAIN_FORMATTER, + pdev->blue_balance->val); + } + return ret; +} - case V4L2_CID_PRIVATE_SAVE_USER: - case V4L2_CID_PRIVATE_RESTORE_USER: - case V4L2_CID_PRIVATE_RESTORE_FACTORY: - return -EINVAL; +/* For CODEC2 models which have separate autogain and auto exposure */ +static int pwc_set_autogain(struct pwc_device *pdev) +{ + int ret = 0; + + if (pdev->autogain->is_new) { + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + AGC_MODE_FORMATTER, + pdev->autogain->val ? 0 : 0xff); + if (ret) + return ret; + if (pdev->autogain->val) + pdev->gain_valid = false; /* Force cache update */ + else if (!pdev->gain->is_new) + pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, + READ_AGC_FORMATTER, + &pdev->gain->val); } - return -EINVAL; + if (ret == 0 && pdev->gain->is_new) { + if (pdev->autogain->val) + return -EBUSY; + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + PRESET_AGC_FORMATTER, + pdev->gain->val); + } + return ret; } -static int pwc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) +/* For CODEC2 models which have separate autogain and auto exposure */ +static int pwc_set_exposure_auto(struct pwc_device *pdev) { - struct pwc_device *pdev = video_drvdata(file); - int ret; + int ret = 0; + int is_auto = pdev->exposure_auto->val == V4L2_EXPOSURE_AUTO; + + if (pdev->exposure_auto->is_new) { + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + SHUTTER_MODE_FORMATTER, + is_auto ? 0 : 0xff); + if (ret) + return ret; + if (is_auto) + pdev->exposure_valid = false; /* Force cache update */ + else if (!pdev->exposure->is_new) + pwc_get_u16_ctrl(pdev, GET_STATUS_CTL, + READ_SHUTTER_FORMATTER, + &pdev->exposure->val); + } + if (ret == 0 && pdev->exposure->is_new) { + if (is_auto) + return -EBUSY; + ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL, + PRESET_SHUTTER_FORMATTER, + pdev->exposure->val); + } + return ret; +} + +/* For CODEC3 models which have autogain controlling both gain and exposure */ +static int pwc_set_autogain_expo(struct pwc_device *pdev) +{ + int ret = 0; + + if (pdev->autogain->is_new) { + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + AGC_MODE_FORMATTER, + pdev->autogain->val ? 0 : 0xff); + if (ret) + return ret; + if (pdev->autogain->val) { + pdev->gain_valid = false; /* Force cache update */ + pdev->exposure_valid = false; /* Force cache update */ + } else { + if (!pdev->gain->is_new) + pwc_get_u8_ctrl(pdev, GET_STATUS_CTL, + READ_AGC_FORMATTER, + &pdev->gain->val); + if (!pdev->exposure->is_new) + pwc_get_u16_ctrl(pdev, GET_STATUS_CTL, + READ_SHUTTER_FORMATTER, + &pdev->exposure->val); + } + } + if (ret == 0 && pdev->gain->is_new) { + if (pdev->autogain->val) + return -EBUSY; + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + PRESET_AGC_FORMATTER, + pdev->gain->val); + } + if (ret == 0 && pdev->exposure->is_new) { + if (pdev->autogain->val) + return -EBUSY; + ret = pwc_set_u16_ctrl(pdev, SET_LUM_CTL, + PRESET_SHUTTER_FORMATTER, + pdev->exposure->val); + } + return ret; +} + +static int pwc_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct pwc_device *pdev = + container_of(ctrl->handler, struct pwc_device, ctrl_handler); + int ret = 0; if (!pdev->udev) return -ENODEV; - switch (c->id) { + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - c->value <<= 9; - ret = pwc_set_brightness(pdev, c->value); - if (ret < 0) - return -EINVAL; - return 0; + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + BRIGHTNESS_FORMATTER, ctrl->val); + break; case V4L2_CID_CONTRAST: - c->value <<= 10; - ret = pwc_set_contrast(pdev, c->value); - if (ret < 0) - return -EINVAL; - return 0; + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + CONTRAST_FORMATTER, ctrl->val); + break; case V4L2_CID_SATURATION: - ret = pwc_set_saturation(pdev, c->value); - if (ret < 0) - return -EINVAL; - return 0; + ret = pwc_set_s8_ctrl(pdev, SET_CHROM_CTL, + pdev->saturation_fmt, ctrl->val); + break; case V4L2_CID_GAMMA: - c->value <<= 11; - ret = pwc_set_gamma(pdev, c->value); - if (ret < 0) - return -EINVAL; - return 0; - case V4L2_CID_RED_BALANCE: - c->value <<= 8; - ret = pwc_set_red_gain(pdev, c->value); - if (ret < 0) - return -EINVAL; - return 0; - case V4L2_CID_BLUE_BALANCE: - c->value <<= 8; - ret = pwc_set_blue_gain(pdev, c->value); - if (ret < 0) - return -EINVAL; - return 0; + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + GAMMA_FORMATTER, ctrl->val); + break; case V4L2_CID_AUTO_WHITE_BALANCE: - c->value = (c->value == 0) ? PWC_WB_MANUAL : PWC_WB_AUTO; - ret = pwc_set_awb(pdev, c->value); - if (ret < 0) - return -EINVAL; - return 0; - case V4L2_CID_EXPOSURE: - c->value <<= 8; - ret = pwc_set_shutter_speed(pdev, c->value ? 0 : 1, c->value); - if (ret < 0) - return -EINVAL; - return 0; + ret = pwc_set_awb(pdev); + break; case V4L2_CID_AUTOGAIN: - /* autogain off means nothing without a gain */ - if (c->value == 0) - return 0; - ret = pwc_set_agc(pdev, c->value, 0); - if (ret < 0) - return -EINVAL; - return 0; - case V4L2_CID_GAIN: - c->value <<= 8; - ret = pwc_set_agc(pdev, 0, c->value); - if (ret < 0) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_SAVE_USER: - if (pwc_save_user(pdev)) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_RESTORE_USER: - if (pwc_restore_user(pdev)) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_RESTORE_FACTORY: - if (pwc_restore_factory(pdev)) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_COLOUR_MODE: - ret = pwc_set_colour_mode(pdev, c->value); - if (ret < 0) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_AUTOCONTOUR: - c->value = (c->value == 1) ? -1 : 0; - ret = pwc_set_contour(pdev, c->value); - if (ret < 0) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_CONTOUR: - c->value <<= 10; - ret = pwc_set_contour(pdev, c->value); - if (ret < 0) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_BACKLIGHT: - ret = pwc_set_backlight(pdev, c->value); - if (ret < 0) - return -EINVAL; - return 0; - case V4L2_CID_PRIVATE_FLICKERLESS: - ret = pwc_set_flicker(pdev, c->value); - if (ret < 0) - return -EINVAL; - case V4L2_CID_PRIVATE_NOISE_REDUCTION: - ret = pwc_set_dynamic_noise(pdev, c->value); - if (ret < 0) - return -EINVAL; - return 0; - + if (DEVICE_USE_CODEC2(pdev->type)) + ret = pwc_set_autogain(pdev); + else if (DEVICE_USE_CODEC3(pdev->type)) + ret = pwc_set_autogain_expo(pdev); + else + ret = -EINVAL; + break; + case V4L2_CID_EXPOSURE_AUTO: + if (DEVICE_USE_CODEC2(pdev->type)) + ret = pwc_set_exposure_auto(pdev); + else + ret = -EINVAL; + break; + case V4L2_CID_COLORFX: + ret = pwc_set_u8_ctrl(pdev, SET_CHROM_CTL, + COLOUR_MODE_FORMATTER, + ctrl->val ? 0 : 0xff); + break; + case PWC_CID_CUSTOM(autocontour): + if (pdev->autocontour->is_new) { + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + AUTO_CONTOUR_FORMATTER, + pdev->autocontour->val ? 0 : 0xff); + } + if (ret == 0 && pdev->contour->is_new) { + if (pdev->autocontour->val) { + ret = -EBUSY; + break; + } + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + PRESET_CONTOUR_FORMATTER, + pdev->contour->val); + } + break; + case V4L2_CID_BACKLIGHT_COMPENSATION: + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + BACK_LIGHT_COMPENSATION_FORMATTER, + ctrl->val ? 0 : 0xff); + break; + case V4L2_CID_BAND_STOP_FILTER: + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + FLICKERLESS_MODE_FORMATTER, + ctrl->val ? 0 : 0xff); + break; + case PWC_CID_CUSTOM(noise_reduction): + ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, + DYNAMIC_NOISE_CONTROL_FORMATTER, + ctrl->val); + break; + case PWC_CID_CUSTOM(save_user): + ret = pwc_button_ctrl(pdev, SAVE_USER_DEFAULTS_FORMATTER); + break; + case PWC_CID_CUSTOM(restore_user): + ret = pwc_button_ctrl(pdev, RESTORE_USER_DEFAULTS_FORMATTER); + break; + case PWC_CID_CUSTOM(restore_factory): + ret = pwc_button_ctrl(pdev, + RESTORE_FACTORY_DEFAULTS_FORMATTER); + break; + default: + ret = -EINVAL; } - return -EINVAL; + + if (ret) + PWC_ERROR("s_ctrl %s error %d\n", ctrl->name, ret); + + return ret; } static int pwc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) @@ -835,9 +1032,6 @@ const struct v4l2_ioctl_ops pwc_ioctl_ops = { .vidioc_g_fmt_vid_cap = pwc_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = pwc_s_fmt_vid_cap, .vidioc_try_fmt_vid_cap = pwc_try_fmt_vid_cap, - .vidioc_queryctrl = pwc_queryctrl, - .vidioc_g_ctrl = pwc_g_ctrl, - .vidioc_s_ctrl = pwc_s_ctrl, .vidioc_reqbufs = pwc_reqbufs, .vidioc_querybuf = pwc_querybuf, .vidioc_qbuf = pwc_qbuf, diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index 1cfb8b475a3..601a549988b 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #ifdef CONFIG_USB_PWC_INPUT_EVDEV #include @@ -128,6 +129,61 @@ #define DEVICE_USE_CODEC3(x) ((x)>=700) #define DEVICE_USE_CODEC23(x) ((x)>=675) +/* Request types: video */ +#define SET_LUM_CTL 0x01 +#define GET_LUM_CTL 0x02 +#define SET_CHROM_CTL 0x03 +#define GET_CHROM_CTL 0x04 +#define SET_STATUS_CTL 0x05 +#define GET_STATUS_CTL 0x06 +#define SET_EP_STREAM_CTL 0x07 +#define GET_EP_STREAM_CTL 0x08 +#define GET_XX_CTL 0x09 +#define SET_XX_CTL 0x0A +#define GET_XY_CTL 0x0B +#define SET_XY_CTL 0x0C +#define SET_MPT_CTL 0x0D +#define GET_MPT_CTL 0x0E + +/* Selectors for the Luminance controls [GS]ET_LUM_CTL */ +#define AGC_MODE_FORMATTER 0x2000 +#define PRESET_AGC_FORMATTER 0x2100 +#define SHUTTER_MODE_FORMATTER 0x2200 +#define PRESET_SHUTTER_FORMATTER 0x2300 +#define PRESET_CONTOUR_FORMATTER 0x2400 +#define AUTO_CONTOUR_FORMATTER 0x2500 +#define BACK_LIGHT_COMPENSATION_FORMATTER 0x2600 +#define CONTRAST_FORMATTER 0x2700 +#define DYNAMIC_NOISE_CONTROL_FORMATTER 0x2800 +#define FLICKERLESS_MODE_FORMATTER 0x2900 +#define AE_CONTROL_SPEED 0x2A00 +#define BRIGHTNESS_FORMATTER 0x2B00 +#define GAMMA_FORMATTER 0x2C00 + +/* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */ +#define WB_MODE_FORMATTER 0x1000 +#define AWB_CONTROL_SPEED_FORMATTER 0x1100 +#define AWB_CONTROL_DELAY_FORMATTER 0x1200 +#define PRESET_MANUAL_RED_GAIN_FORMATTER 0x1300 +#define PRESET_MANUAL_BLUE_GAIN_FORMATTER 0x1400 +#define COLOUR_MODE_FORMATTER 0x1500 +#define SATURATION_MODE_FORMATTER1 0x1600 +#define SATURATION_MODE_FORMATTER2 0x1700 + +/* Selectors for the Status controls [GS]ET_STATUS_CTL */ +#define SAVE_USER_DEFAULTS_FORMATTER 0x0200 +#define RESTORE_USER_DEFAULTS_FORMATTER 0x0300 +#define RESTORE_FACTORY_DEFAULTS_FORMATTER 0x0400 +#define READ_AGC_FORMATTER 0x0500 +#define READ_SHUTTER_FORMATTER 0x0600 +#define READ_RED_GAIN_FORMATTER 0x0700 +#define READ_BLUE_GAIN_FORMATTER 0x0800 + +/* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */ +#define PT_RELATIVE_CONTROL_FORMATTER 0x01 +#define PT_RESET_CONTROL_FORMATTER 0x02 +#define PT_STATUS_FORMATTER 0x03 + /* intermediate buffers with raw data from the USB cam */ struct pwc_frame_buf { @@ -220,6 +276,55 @@ struct pwc_device struct input_dev *button_dev; /* webcam snapshot button input */ char button_phys[64]; #endif + + /* controls */ + struct v4l2_ctrl_handler ctrl_handler; + u16 saturation_fmt; + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *contrast; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *gamma; + struct { + /* awb / red-blue balance cluster */ + struct v4l2_ctrl *auto_white_balance; + struct v4l2_ctrl *red_balance; + struct v4l2_ctrl *blue_balance; + /* usb ctrl transfers are slow, so we cache things */ + int color_bal_valid; + unsigned long last_color_bal_update; /* In jiffies */ + s32 last_red_balance; + s32 last_blue_balance; + }; + struct { + /* autogain / gain cluster */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *gain; + int gain_valid; + unsigned long last_gain_update; /* In jiffies */ + s32 last_gain; + }; + struct { + /* exposure_auto / exposure cluster */ + struct v4l2_ctrl *exposure_auto; + struct v4l2_ctrl *exposure; + int exposure_valid; + unsigned long last_exposure_update; /* In jiffies */ + s32 last_exposure; + }; + struct v4l2_ctrl *colorfx; + struct { + /* autocontour/contour cluster */ + struct v4l2_ctrl *autocontour; + struct v4l2_ctrl *contour; + }; + struct v4l2_ctrl *backlight; + struct v4l2_ctrl *flicker; + struct v4l2_ctrl *noise_reduction; + struct v4l2_ctrl *save_user; + struct v4l2_ctrl *restore_user; + struct v4l2_ctrl *restore_factory; + /* CODEC3 models have both gain and exposure controlled by autogain */ + struct v4l2_ctrl *autogain_expo_cluster[3]; }; /* Global variables */ @@ -238,47 +343,20 @@ void pwc_construct(struct pwc_device *pdev); /* Request a certain video mode. Returns < 0 if not possible */ extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot); extern unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size); -/* Calculate the number of bytes per image (not frame) */ extern int pwc_mpt_reset(struct pwc_device *pdev, int flags); extern int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt); - -/* Various controls; should be obvious. Value 0..65535, or < 0 on error */ -extern int pwc_get_brightness(struct pwc_device *pdev); -extern int pwc_set_brightness(struct pwc_device *pdev, int value); -extern int pwc_get_contrast(struct pwc_device *pdev); -extern int pwc_set_contrast(struct pwc_device *pdev, int value); -extern int pwc_get_gamma(struct pwc_device *pdev); -extern int pwc_set_gamma(struct pwc_device *pdev, int value); -extern int pwc_get_saturation(struct pwc_device *pdev, int *value); -extern int pwc_set_saturation(struct pwc_device *pdev, int value); extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value); extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor); -extern int pwc_restore_user(struct pwc_device *pdev); -extern int pwc_save_user(struct pwc_device *pdev); -extern int pwc_restore_factory(struct pwc_device *pdev); - -/* exported for use by v4l2 controls */ -extern int pwc_get_red_gain(struct pwc_device *pdev, int *value); -extern int pwc_set_red_gain(struct pwc_device *pdev, int value); -extern int pwc_get_blue_gain(struct pwc_device *pdev, int *value); -extern int pwc_set_blue_gain(struct pwc_device *pdev, int value); -extern int pwc_get_awb(struct pwc_device *pdev); -extern int pwc_set_awb(struct pwc_device *pdev, int mode); -extern int pwc_set_agc(struct pwc_device *pdev, int mode, int value); -extern int pwc_get_agc(struct pwc_device *pdev, int *value); -extern int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value); -extern int pwc_get_shutter_speed(struct pwc_device *pdev, int *value); - -extern int pwc_set_colour_mode(struct pwc_device *pdev, int colour); -extern int pwc_get_colour_mode(struct pwc_device *pdev, int *colour); -extern int pwc_set_contour(struct pwc_device *pdev, int contour); -extern int pwc_get_contour(struct pwc_device *pdev, int *contour); -extern int pwc_set_backlight(struct pwc_device *pdev, int backlight); -extern int pwc_get_backlight(struct pwc_device *pdev, int *backlight); -extern int pwc_set_flicker(struct pwc_device *pdev, int flicker); -extern int pwc_get_flicker(struct pwc_device *pdev, int *flicker); -extern int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise); -extern int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise); + +/* Control get / set helpers */ +int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data); +int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data); +int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data); +#define pwc_set_s8_ctrl pwc_set_u8_ctrl +int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *dat); +int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data); +int pwc_button_ctrl(struct pwc_device *pdev, u16 value); +int pwc_init_controls(struct pwc_device *pdev); /* Power down or up the camera; not supported by all models */ extern void pwc_camera_power(struct pwc_device *pdev, int power); -- cgit v1.2.3-70-g09d2 From c11271349ad5d4647e69e511fc481b2dd390efc4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 3 Jul 2011 11:50:51 -0300 Subject: [media] pwc: Allow dqbuf / read to complete while waiting for controls Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pwc/pwc-if.c | 3 +++ drivers/media/video/pwc/pwc-v4l.c | 36 ++++++++++++++++++++++++++++++++---- drivers/media/video/pwc/pwc.h | 3 +++ 3 files changed, 38 insertions(+), 4 deletions(-) (limited to 'drivers/media/video/pwc/pwc-v4l.c') diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 1d5a6db915f..fdf113fe51c 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -1160,6 +1160,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pwc_construct(pdev); /* set min/max sizes correct */ mutex_init(&pdev->modlock); + mutex_init(&pdev->udevlock); spin_lock_init(&pdev->queued_bufs_lock); INIT_LIST_HEAD(&pdev->queued_bufs); @@ -1297,6 +1298,7 @@ static void usb_pwc_disconnect(struct usb_interface *intf) { struct pwc_device *pdev = usb_get_intfdata(intf); + mutex_lock(&pdev->udevlock); mutex_lock(&pdev->modlock); usb_set_intfdata(intf, NULL); @@ -1306,6 +1308,7 @@ static void usb_pwc_disconnect(struct usb_interface *intf) pdev->udev = NULL; mutex_unlock(&pdev->modlock); + mutex_unlock(&pdev->udevlock); pwc_remove_sysfs_files(pdev); video_unregister_device(&pdev->vdev); diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 986dd5d6187..537657283e7 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -526,8 +526,24 @@ static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) container_of(ctrl->handler, struct pwc_device, ctrl_handler); int ret = 0; - if (!pdev->udev) - return -ENODEV; + /* + * Sometimes it can take quite long for the pwc to complete usb control + * transfers, so release the modlock to give streaming by another + * process / thread the chance to continue with a dqbuf. + */ + mutex_unlock(&pdev->modlock); + + /* + * Take the udev-lock to protect against the disconnect handler + * completing and setting dev->udev to NULL underneath us. Other code + * does not need to do this since it is protected by the modlock. + */ + mutex_lock(&pdev->udevlock); + + if (!pdev->udev) { + ret = -ENODEV; + goto leave; + } switch (ctrl->id) { case V4L2_CID_AUTO_WHITE_BALANCE: @@ -590,6 +606,9 @@ static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) if (ret) PWC_ERROR("g_ctrl %s error %d\n", ctrl->name, ret); +leave: + mutex_unlock(&pdev->udevlock); + mutex_lock(&pdev->modlock); return ret; } @@ -751,8 +770,14 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl) container_of(ctrl->handler, struct pwc_device, ctrl_handler); int ret = 0; - if (!pdev->udev) - return -ENODEV; + /* See the comments on locking in pwc_g_volatile_ctrl */ + mutex_unlock(&pdev->modlock); + mutex_lock(&pdev->udevlock); + + if (!pdev->udev) { + ret = -ENODEV; + goto leave; + } switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: @@ -841,6 +866,9 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl) if (ret) PWC_ERROR("s_ctrl %s error %d\n", ctrl->name, ret); +leave: + mutex_unlock(&pdev->udevlock); + mutex_lock(&pdev->modlock); return ret; } diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index 601a549988b..e02dbf74515 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -200,6 +200,9 @@ struct pwc_device /* Pointer to our usb_device, may be NULL after unplug */ struct usb_device *udev; + /* Protects the setting of udev to NULL by our disconnect handler */ + struct mutex udevlock; + /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */ int type; int release; /* release number */ -- cgit v1.2.3-70-g09d2 From 294e289602d7827f1389b081535fda3b7553a651 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 3 Jul 2011 12:23:24 -0300 Subject: [media] pwc: Add v4l2 controls for pan/tilt on Logitech QuickCam Orbit/Sphere This makes the API for this: 1) v4l2 spec compliant 2) match that of the UVC Logitech QuickCam Sphere models For now this operates in parellel to the sysfs interface for this, but the intend is to deprecate the sysfs interface and remove it. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pwc/pwc-ctrl.c | 2 +- drivers/media/video/pwc/pwc-v4l.c | 56 ++++++++++++++++++++++++++++++++++++++ drivers/media/video/pwc/pwc.h | 9 ++++++ 3 files changed, 66 insertions(+), 1 deletion(-) (limited to 'drivers/media/video/pwc/pwc-v4l.c') diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c index 8e0cc537e1e..d09413c44c8 100644 --- a/drivers/media/video/pwc/pwc-ctrl.c +++ b/drivers/media/video/pwc/pwc-ctrl.c @@ -165,7 +165,7 @@ static inline int send_video_command(struct pwc_device *pdev, buf, buflen); } -static inline int send_control_msg(struct pwc_device *pdev, +int send_control_msg(struct pwc_device *pdev, u8 request, u16 value, void *buf, int buflen) { return _send_control_msg(pdev, diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 537657283e7..e9a0e94b999 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -338,6 +338,22 @@ int pwc_init_controls(struct pwc_device *pdev) if (pdev->restore_factory) pdev->restore_factory->flags = V4L2_CTRL_FLAG_UPDATE; + if (!pdev->features & FEATURE_MOTOR_PANTILT) + return hdl->error; + + /* Motor pan / tilt / reset */ + pdev->motor_pan = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_PAN_RELATIVE, -4480, 4480, 64, 0); + if (!pdev->motor_pan) + return hdl->error; + pdev->motor_tilt = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_TILT_RELATIVE, -1920, 1920, 64, 0); + pdev->motor_pan_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_PAN_RESET, 0, 0, 0, 0); + pdev->motor_tilt_reset = v4l2_ctrl_new_std(hdl, &pwc_ctrl_ops, + V4L2_CID_TILT_RESET, 0, 0, 0, 0); + v4l2_ctrl_cluster(4, &pdev->motor_pan); + return hdl->error; } @@ -764,6 +780,43 @@ static int pwc_set_autogain_expo(struct pwc_device *pdev) return ret; } +static int pwc_set_motor(struct pwc_device *pdev) +{ + int ret; + u8 buf[4]; + + buf[0] = 0; + if (pdev->motor_pan_reset->is_new) + buf[0] |= 0x01; + if (pdev->motor_tilt_reset->is_new) + buf[0] |= 0x02; + if (pdev->motor_pan_reset->is_new || pdev->motor_tilt_reset->is_new) { + ret = send_control_msg(pdev, SET_MPT_CTL, + PT_RESET_CONTROL_FORMATTER, buf, 1); + if (ret < 0) + return ret; + } + + memset(buf, 0, sizeof(buf)); + if (pdev->motor_pan->is_new) { + buf[0] = pdev->motor_pan->val & 0xFF; + buf[1] = (pdev->motor_pan->val >> 8); + } + if (pdev->motor_tilt->is_new) { + buf[2] = pdev->motor_tilt->val & 0xFF; + buf[3] = (pdev->motor_tilt->val >> 8); + } + if (pdev->motor_pan->is_new || pdev->motor_tilt->is_new) { + ret = send_control_msg(pdev, SET_MPT_CTL, + PT_RELATIVE_CONTROL_FORMATTER, + buf, sizeof(buf)); + if (ret < 0) + return ret; + } + + return 0; +} + static int pwc_s_ctrl(struct v4l2_ctrl *ctrl) { struct pwc_device *pdev = @@ -859,6 +912,9 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl) ret = pwc_button_ctrl(pdev, RESTORE_FACTORY_DEFAULTS_FORMATTER); break; + case V4L2_CID_PAN_RELATIVE: + ret = pwc_set_motor(pdev); + break; default: ret = -EINVAL; } diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index e02dbf74515..8f3607be5a7 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -326,6 +326,13 @@ struct pwc_device struct v4l2_ctrl *save_user; struct v4l2_ctrl *restore_user; struct v4l2_ctrl *restore_factory; + struct { + /* motor control cluster */ + struct v4l2_ctrl *motor_pan; + struct v4l2_ctrl *motor_tilt; + struct v4l2_ctrl *motor_pan_reset; + struct v4l2_ctrl *motor_tilt_reset; + }; /* CODEC3 models have both gain and exposure controlled by autogain */ struct v4l2_ctrl *autogain_expo_cluster[3]; }; @@ -350,6 +357,8 @@ extern int pwc_mpt_reset(struct pwc_device *pdev, int flags); extern int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt); extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value); extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor); +extern int send_control_msg(struct pwc_device *pdev, + u8 request, u16 value, void *buf, int buflen); /* Control get / set helpers */ int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data); -- cgit v1.2.3-70-g09d2 From 4264a8b6388f5ba16a5c362857cb8bda0b14167f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 23 Jul 2011 15:53:03 -0300 Subject: [media] pwc: precedence bug in pwc_init_controls() '!' has higher precedence than '&' so we need parenthesis here. Signed-off-by: Dan Carpenter Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pwc/pwc-v4l.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/video/pwc/pwc-v4l.c') diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index e9a0e94b999..8c70e64444e 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -338,7 +338,7 @@ int pwc_init_controls(struct pwc_device *pdev) if (pdev->restore_factory) pdev->restore_factory->flags = V4L2_CTRL_FLAG_UPDATE; - if (!pdev->features & FEATURE_MOTOR_PANTILT) + if (!(pdev->features & FEATURE_MOTOR_PANTILT)) return hdl->error; /* Motor pan / tilt / reset */ -- cgit v1.2.3-70-g09d2