2005-04-17 06:20:36 +08:00
|
|
|
/* Linux driver for Philips webcam
|
|
|
|
USB and Video4Linux interface part.
|
|
|
|
(C) 1999-2004 Nemosoft Unv.
|
2006-04-24 21:29:46 +08:00
|
|
|
(C) 2004-2006 Luc Saillard (luc@saillard.org)
|
2011-06-26 17:49:59 +08:00
|
|
|
(C) 2011 Hans de Goede <hdegoede@redhat.com>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
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.
|
|
|
|
Please send bug reports and support requests to <luc@saillard.org>.
|
|
|
|
The decompression routines have been implemented by reverse-engineering the
|
|
|
|
Nemosoft binary pwcx module. Caveat emptor.
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2006-03-25 20:19:53 +08:00
|
|
|
/*
|
2005-04-17 06:20:36 +08:00
|
|
|
This code forms the interface between the USB layers and the Philips
|
|
|
|
specific stuff. Some adanved stuff of the driver falls under an
|
|
|
|
NDA, signed between me and Philips B.V., Eindhoven, the Netherlands, and
|
2006-03-25 20:19:53 +08:00
|
|
|
is thus not distributed in source form. The binary pwcx.o module
|
2005-04-17 06:20:36 +08:00
|
|
|
contains the code that falls under the NDA.
|
2006-03-25 20:19:53 +08:00
|
|
|
|
|
|
|
In case you're wondering: 'pwc' stands for "Philips WebCam", but
|
2005-04-17 06:20:36 +08:00
|
|
|
I really didn't want to type 'philips_web_cam' every time (I'm lazy as
|
|
|
|
any Linux kernel hacker, but I don't like uncomprehensible abbreviations
|
|
|
|
without explanation).
|
2006-03-25 20:19:53 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
Oh yes, convention: to disctinguish between all the various pointers to
|
|
|
|
device-structures, I use these names for the pointer variables:
|
|
|
|
udev: struct usb_device *
|
2011-06-07 01:43:39 +08:00
|
|
|
vdev: struct video_device (member of pwc_dev)
|
2005-04-17 06:20:36 +08:00
|
|
|
pdev: struct pwc_devive *
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Contributors:
|
|
|
|
- Alvarado: adding whitebalance code
|
|
|
|
- Alistar Moire: QuickCam 3000 Pro device/product ID
|
|
|
|
- Tony Hoyle: Creative Labs Webcam 5 device/product ID
|
|
|
|
- Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged
|
|
|
|
- Jk Fang: Sotec Afina Eye ID
|
|
|
|
- Xavier Roche: QuickCam Pro 4000 ID
|
|
|
|
- Jens Knudsen: QuickCam Zoom ID
|
|
|
|
- J. Debert: QuickCam for Notebooks ID
|
2009-01-12 13:50:17 +08:00
|
|
|
- Pham Thanh Nam: webcam snapshot button as an event input device
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/poll.h>
|
|
|
|
#include <linux/slab.h>
|
2009-01-12 13:50:17 +08:00
|
|
|
#ifdef CONFIG_USB_PWC_INPUT_EVDEV
|
|
|
|
#include <linux/usb/input.h>
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/vmalloc.h>
|
|
|
|
#include <asm/io.h>
|
2009-09-24 18:58:09 +08:00
|
|
|
#include <linux/kernel.h> /* simple_strtol() */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include "pwc.h"
|
|
|
|
#include "pwc-kiara.h"
|
|
|
|
#include "pwc-timon.h"
|
2006-04-24 21:29:46 +08:00
|
|
|
#include "pwc-dec23.h"
|
|
|
|
#include "pwc-dec1.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Function prototypes and driver templates */
|
|
|
|
|
|
|
|
/* hotplug device table support */
|
2006-04-24 21:29:46 +08:00
|
|
|
static const struct usb_device_id pwc_device_table [] = {
|
2005-04-17 06:20:36 +08:00
|
|
|
{ USB_DEVICE(0x0471, 0x0302) }, /* Philips models */
|
|
|
|
{ USB_DEVICE(0x0471, 0x0303) },
|
|
|
|
{ USB_DEVICE(0x0471, 0x0304) },
|
|
|
|
{ USB_DEVICE(0x0471, 0x0307) },
|
|
|
|
{ USB_DEVICE(0x0471, 0x0308) },
|
|
|
|
{ USB_DEVICE(0x0471, 0x030C) },
|
|
|
|
{ USB_DEVICE(0x0471, 0x0310) },
|
2006-04-24 21:29:46 +08:00
|
|
|
{ USB_DEVICE(0x0471, 0x0311) }, /* Philips ToUcam PRO II */
|
2005-04-17 06:20:36 +08:00
|
|
|
{ USB_DEVICE(0x0471, 0x0312) },
|
|
|
|
{ USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */
|
2006-04-24 21:29:46 +08:00
|
|
|
{ USB_DEVICE(0x0471, 0x0329) }, /* Philips SPC 900NC PC Camera */
|
2005-04-17 06:20:36 +08:00
|
|
|
{ USB_DEVICE(0x069A, 0x0001) }, /* Askey */
|
|
|
|
{ USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */
|
|
|
|
{ USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */
|
|
|
|
{ USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam Pro 4000 */
|
|
|
|
{ USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */
|
|
|
|
{ USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */
|
|
|
|
{ USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */
|
2007-03-22 03:35:28 +08:00
|
|
|
{ USB_DEVICE(0x046D, 0x08B6) }, /* Cisco VT Camera */
|
|
|
|
{ USB_DEVICE(0x046D, 0x08B7) }, /* Logitech ViewPort AV 100 */
|
2005-04-17 06:20:36 +08:00
|
|
|
{ USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */
|
2006-04-24 21:29:46 +08:00
|
|
|
{ USB_DEVICE(0x055D, 0x9000) }, /* Samsung MPC-C10 */
|
|
|
|
{ USB_DEVICE(0x055D, 0x9001) }, /* Samsung MPC-C30 */
|
|
|
|
{ USB_DEVICE(0x055D, 0x9002) }, /* Samsung SNC-35E (Ver3.0) */
|
2005-04-17 06:20:36 +08:00
|
|
|
{ USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */
|
|
|
|
{ USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */
|
|
|
|
{ USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */
|
|
|
|
{ USB_DEVICE(0x06BE, 0x8116) }, /* new Afina Eye */
|
|
|
|
{ USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */
|
|
|
|
{ USB_DEVICE(0x0d81, 0x1900) },
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
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);
|
2011-06-07 02:33:44 +08:00
|
|
|
static void pwc_isoc_cleanup(struct pwc_device *pdev);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static struct usb_driver pwc_driver = {
|
|
|
|
.name = "Philips webcam", /* name */
|
|
|
|
.id_table = pwc_device_table,
|
|
|
|
.probe = usb_pwc_probe, /* probe() */
|
|
|
|
.disconnect = usb_pwc_disconnect, /* disconnect() */
|
|
|
|
};
|
|
|
|
|
|
|
|
#define MAX_DEV_HINTS 20
|
|
|
|
#define MAX_ISOC_ERRORS 20
|
|
|
|
|
2007-01-31 10:26:01 +08:00
|
|
|
#ifdef CONFIG_USB_PWC_DEBUG
|
2007-08-28 05:16:54 +08:00
|
|
|
int pwc_trace = PWC_DEBUG_LEVEL;
|
2006-04-24 21:29:46 +08:00
|
|
|
#endif
|
2011-06-26 14:51:19 +08:00
|
|
|
static int power_save = -1;
|
2008-04-23 01:41:48 +08:00
|
|
|
static int led_on = 100, led_off; /* defaults to LED that is on while in use */
|
2005-04-17 06:20:36 +08:00
|
|
|
static struct {
|
|
|
|
int type;
|
|
|
|
char serial_number[30];
|
|
|
|
int device_node;
|
|
|
|
struct pwc_device *pdev;
|
|
|
|
} device_hint[MAX_DEV_HINTS];
|
|
|
|
|
|
|
|
/***/
|
|
|
|
|
2008-12-30 17:58:20 +08:00
|
|
|
static int pwc_video_close(struct file *file);
|
2006-04-24 21:29:46 +08:00
|
|
|
static ssize_t pwc_video_read(struct file *file, char __user *buf,
|
2005-04-17 06:20:36 +08:00
|
|
|
size_t count, loff_t *ppos);
|
|
|
|
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);
|
|
|
|
|
2008-12-30 17:58:20 +08:00
|
|
|
static const struct v4l2_file_operations pwc_fops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.owner = THIS_MODULE,
|
2011-07-19 18:14:22 +08:00
|
|
|
.open = v4l2_fh_open,
|
2005-04-17 06:20:36 +08:00
|
|
|
.release = pwc_video_close,
|
|
|
|
.read = pwc_video_read,
|
|
|
|
.poll = pwc_video_poll,
|
|
|
|
.mmap = pwc_video_mmap,
|
2011-01-22 17:34:55 +08:00
|
|
|
.unlocked_ioctl = video_ioctl2,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
static struct video_device pwc_template = {
|
|
|
|
.name = "Philips Webcam", /* Filled in later */
|
2011-07-19 18:14:22 +08:00
|
|
|
.release = video_device_release_empty,
|
2005-04-17 06:20:36 +08:00
|
|
|
.fops = &pwc_fops,
|
2011-06-07 01:43:39 +08:00
|
|
|
.ioctl_ops = &pwc_ioctl_ops,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/***************************************************************************/
|
|
|
|
/* Private functions */
|
|
|
|
|
2011-06-07 02:33:44 +08:00
|
|
|
struct pwc_frame_buf *pwc_get_next_fill_buf(struct pwc_device *pdev)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-06-07 02:33:44 +08:00
|
|
|
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;
|
2006-04-24 21:29:46 +08:00
|
|
|
}
|
|
|
|
|
2009-01-12 13:50:17 +08:00
|
|
|
static void pwc_snapshot_button(struct pwc_device *pdev, int down)
|
|
|
|
{
|
|
|
|
if (down) {
|
|
|
|
PWC_TRACE("Snapshot button pressed.\n");
|
|
|
|
} else {
|
|
|
|
PWC_TRACE("Snapshot button released.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_USB_PWC_INPUT_EVDEV
|
|
|
|
if (pdev->button_dev) {
|
2009-06-11 22:19:33 +08:00
|
|
|
input_report_key(pdev->button_dev, KEY_CAMERA, down);
|
2009-01-12 13:50:17 +08:00
|
|
|
input_sync(pdev->button_dev);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2011-06-07 02:33:44 +08:00
|
|
|
static void pwc_frame_complete(struct pwc_device *pdev)
|
2006-04-24 21:29:46 +08:00
|
|
|
{
|
2011-06-07 02:33:44 +08:00
|
|
|
struct pwc_frame_buf *fbuf = pdev->fill_buf;
|
2006-04-24 21:29:46 +08:00
|
|
|
|
|
|
|
/* 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
|
|
|
|
however detected in the cam and a bit is set in the header.
|
|
|
|
*/
|
|
|
|
if (pdev->type == 730) {
|
|
|
|
unsigned char *ptr = (unsigned char *)fbuf->data;
|
|
|
|
|
|
|
|
if (ptr[1] == 1 && ptr[0] & 0x10) {
|
|
|
|
PWC_TRACE("Hyundai CMOS sensor bug. Dropping frame.\n");
|
|
|
|
pdev->drop_frames += 2;
|
|
|
|
}
|
|
|
|
if ((ptr[0] ^ pdev->vmirror) & 0x01) {
|
2009-01-12 13:50:17 +08:00
|
|
|
pwc_snapshot_button(pdev, ptr[0] & 0x01);
|
2006-04-24 21:29:46 +08:00
|
|
|
}
|
|
|
|
if ((ptr[0] ^ pdev->vmirror) & 0x02) {
|
|
|
|
if (ptr[0] & 0x02)
|
|
|
|
PWC_TRACE("Image is mirrored.\n");
|
|
|
|
else
|
|
|
|
PWC_TRACE("Image is normal.\n");
|
|
|
|
}
|
|
|
|
pdev->vmirror = ptr[0] & 0x03;
|
|
|
|
/* Sometimes the trailer of the 730 is still sent as a 4 byte packet
|
|
|
|
after a short frame; this condition is filtered out specifically. A 4 byte
|
|
|
|
frame doesn't make sense anyway.
|
|
|
|
So we get either this sequence:
|
|
|
|
drop_bit set -> 4 byte frame -> short frame -> good frame
|
|
|
|
Or this one:
|
|
|
|
drop_bit set -> short frame -> good frame
|
|
|
|
So we drop either 3 or 2 frames in all!
|
|
|
|
*/
|
|
|
|
if (fbuf->filled == 4)
|
|
|
|
pdev->drop_frames++;
|
2011-06-07 02:33:44 +08:00
|
|
|
} else if (pdev->type == 740 || pdev->type == 720) {
|
2006-04-24 21:29:46 +08:00
|
|
|
unsigned char *ptr = (unsigned char *)fbuf->data;
|
|
|
|
if ((ptr[0] ^ pdev->vmirror) & 0x01) {
|
2009-01-12 13:50:17 +08:00
|
|
|
pwc_snapshot_button(pdev, ptr[0] & 0x01);
|
2006-04-24 21:29:46 +08:00
|
|
|
}
|
|
|
|
pdev->vmirror = ptr[0] & 0x03;
|
|
|
|
}
|
|
|
|
|
2011-06-07 02:33:44 +08:00
|
|
|
/* In case we were instructed to drop the frame, do so silently. */
|
|
|
|
if (pdev->drop_frames > 0) {
|
2006-04-24 21:29:46 +08:00
|
|
|
pdev->drop_frames--;
|
2011-06-07 02:33:44 +08:00
|
|
|
} else {
|
2006-04-24 21:29:46 +08:00
|
|
|
/* Check for underflow first */
|
|
|
|
if (fbuf->filled < pdev->frame_total_size) {
|
|
|
|
PWC_DEBUG_FLOW("Frame buffer underflow (%d bytes);"
|
|
|
|
" discarded.\n", fbuf->filled);
|
2011-06-07 02:33:44 +08:00
|
|
|
} 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;
|
2006-04-24 21:29:46 +08:00
|
|
|
}
|
|
|
|
} /* !drop_frames */
|
|
|
|
pdev->vframe_count++;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* This gets called for the Isochronous pipe (video). This is done in
|
|
|
|
* interrupt time, so it has to be fast, not crash, and not stall. Neat.
|
|
|
|
*/
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
static void pwc_isoc_handler(struct urb *urb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-06-07 02:33:44 +08:00
|
|
|
struct pwc_device *pdev = (struct pwc_device *)urb->context;
|
2005-04-17 06:20:36 +08:00
|
|
|
int i, fst, flen;
|
2011-06-07 02:33:44 +08:00
|
|
|
unsigned char *iso_buf = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-06-07 02:33:44 +08:00
|
|
|
if (urb->status == -ENOENT || urb->status == -ECONNRESET ||
|
|
|
|
urb->status == -ESHUTDOWN) {
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
2011-06-07 02:33:44 +08:00
|
|
|
|
|
|
|
if (pdev->fill_buf == NULL)
|
|
|
|
pdev->fill_buf = pwc_get_next_fill_buf(pdev);
|
|
|
|
|
|
|
|
if (urb->status != 0) {
|
2005-04-17 06:20:36 +08:00
|
|
|
const char *errmsg;
|
|
|
|
|
|
|
|
errmsg = "Unknown";
|
|
|
|
switch(urb->status) {
|
|
|
|
case -ENOSR: errmsg = "Buffer error (overrun)"; break;
|
|
|
|
case -EPIPE: errmsg = "Stalled (device not responding)"; break;
|
|
|
|
case -EOVERFLOW: errmsg = "Babble (bad cable?)"; break;
|
|
|
|
case -EPROTO: errmsg = "Bit-stuff error (bad cable?)"; break;
|
|
|
|
case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break;
|
2006-09-19 13:49:02 +08:00
|
|
|
case -ETIME: errmsg = "Device does not respond"; break;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2011-06-07 02:33:44 +08:00
|
|
|
PWC_ERROR("pwc_isoc_handler() called with status %d [%s].\n",
|
|
|
|
urb->status, errmsg);
|
|
|
|
/* Give up after a number of contiguous errors */
|
2005-04-17 06:20:36 +08:00
|
|
|
if (++pdev->visoc_errors > MAX_ISOC_ERRORS)
|
|
|
|
{
|
2011-06-07 02:33:44 +08:00
|
|
|
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;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2011-06-07 02:33:44 +08:00
|
|
|
pdev->vsync = 0; /* Drop the current frame */
|
2005-04-17 06:20:36 +08:00
|
|
|
goto handler_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reset ISOC error counter. We did get here, after all. */
|
|
|
|
pdev->visoc_errors = 0;
|
|
|
|
|
|
|
|
/* vsync: 0 = don't copy data
|
2006-03-25 20:19:53 +08:00
|
|
|
1 = sync-hunt
|
|
|
|
2 = synched
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
/* Compact data */
|
|
|
|
for (i = 0; i < urb->number_of_packets; i++) {
|
|
|
|
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;
|
2011-06-07 02:33:44 +08:00
|
|
|
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);
|
2005-04-17 06:20:36 +08:00
|
|
|
fbuf->filled += flen;
|
2011-06-07 02:33:44 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
2005-04-17 06:20:36 +08:00
|
|
|
pdev->vsync = 1;
|
2006-04-24 21:29:46 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2011-06-07 02:33:44 +08:00
|
|
|
pdev->vlast_packet_size = flen;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
handler_end:
|
|
|
|
i = usb_submit_urb(urb, GFP_ATOMIC);
|
|
|
|
if (i != 0)
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-06-07 02:33:44 +08:00
|
|
|
static int pwc_isoc_init(struct pwc_device *pdev)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct usb_device *udev;
|
|
|
|
struct urb *urb;
|
|
|
|
int i, j, ret;
|
|
|
|
struct usb_interface *intf;
|
|
|
|
struct usb_host_interface *idesc = NULL;
|
2012-01-05 05:48:05 +08:00
|
|
|
int compression = 0; /* 0..3 = uncompressed..high */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (pdev->iso_init)
|
|
|
|
return 0;
|
2011-06-26 17:49:59 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
pdev->vsync = 0;
|
2011-06-26 17:49:59 +08:00
|
|
|
pdev->vlast_packet_size = 0;
|
2011-06-07 02:33:44 +08:00
|
|
|
pdev->fill_buf = NULL;
|
|
|
|
pdev->vframe_count = 0;
|
2011-06-26 17:49:59 +08:00
|
|
|
pdev->visoc_errors = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
udev = pdev->udev;
|
|
|
|
|
2012-01-05 05:48:05 +08:00
|
|
|
retry:
|
|
|
|
/* We first try with low compression and then retry with a higher
|
|
|
|
compression setting if there is not enough bandwidth. */
|
2012-01-11 00:14:46 +08:00
|
|
|
ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt,
|
|
|
|
pdev->vframes, &compression, 1);
|
2012-01-05 05:48:05 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Get the current alternate interface, adjust packet size */
|
|
|
|
intf = usb_ifnum_to_if(udev, 0);
|
|
|
|
if (intf)
|
|
|
|
idesc = usb_altnum_to_altsetting(intf, pdev->valternate);
|
|
|
|
if (!idesc)
|
2011-06-07 02:25:18 +08:00
|
|
|
return -EIO;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Search video endpoint */
|
|
|
|
pdev->vmax_packet_size = -1;
|
2006-04-24 21:29:46 +08:00
|
|
|
for (i = 0; i < idesc->desc.bNumEndpoints; i++) {
|
2005-04-17 06:20:36 +08:00
|
|
|
if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) {
|
|
|
|
pdev->vmax_packet_size = le16_to_cpu(idesc->endpoint[i].desc.wMaxPacketSize);
|
|
|
|
break;
|
|
|
|
}
|
2006-04-24 21:29:46 +08:00
|
|
|
}
|
2006-03-25 20:19:53 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) {
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_ERROR("Failed to find packet size for video endpoint in current alternate setting.\n");
|
2005-05-04 09:07:24 +08:00
|
|
|
return -ENFILE; /* Odd error, that should be noticeable */
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set alternate interface */
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_DEBUG_OPEN("Setting alternate interface %d\n", pdev->valternate);
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = usb_set_interface(pdev->udev, 0, pdev->valternate);
|
2012-01-05 05:48:05 +08:00
|
|
|
if (ret == -ENOSPC && compression < 3) {
|
|
|
|
compression++;
|
|
|
|
goto retry;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2011-06-27 00:57:15 +08:00
|
|
|
/* Allocate and init Isochronuous urbs */
|
2005-04-17 06:20:36 +08:00
|
|
|
for (i = 0; i < MAX_ISO_BUFS; i++) {
|
|
|
|
urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
|
|
|
|
if (urb == NULL) {
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_ERROR("Failed to allocate urb %d\n", i);
|
2011-06-26 17:49:59 +08:00
|
|
|
pdev->iso_init = 1;
|
|
|
|
pwc_isoc_cleanup(pdev);
|
|
|
|
return -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2011-06-27 00:57:15 +08:00
|
|
|
pdev->urbs[i] = urb;
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_DEBUG_MEMORY("Allocated URB at 0x%p\n", urb);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
urb->interval = 1; // devik
|
|
|
|
urb->dev = udev;
|
2006-03-25 20:19:53 +08:00
|
|
|
urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint);
|
2011-06-27 00:57:15 +08:00
|
|
|
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
|
|
|
|
urb->transfer_buffer = usb_alloc_coherent(udev,
|
|
|
|
ISO_BUFFER_SIZE,
|
|
|
|
GFP_KERNEL,
|
|
|
|
&urb->transfer_dma);
|
|
|
|
if (urb->transfer_buffer == NULL) {
|
|
|
|
PWC_ERROR("Failed to allocate urb buffer %d\n", i);
|
|
|
|
pdev->iso_init = 1;
|
|
|
|
pwc_isoc_cleanup(pdev);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2006-03-25 20:19:53 +08:00
|
|
|
urb->transfer_buffer_length = ISO_BUFFER_SIZE;
|
|
|
|
urb->complete = pwc_isoc_handler;
|
|
|
|
urb->context = pdev;
|
2005-04-17 06:20:36 +08:00
|
|
|
urb->start_frame = 0;
|
|
|
|
urb->number_of_packets = ISO_FRAMES_PER_DESC;
|
|
|
|
for (j = 0; j < ISO_FRAMES_PER_DESC; j++) {
|
|
|
|
urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE;
|
|
|
|
urb->iso_frame_desc[j].length = pdev->vmax_packet_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* link */
|
|
|
|
for (i = 0; i < MAX_ISO_BUFS; i++) {
|
2011-06-27 00:57:15 +08:00
|
|
|
ret = usb_submit_urb(pdev->urbs[i], GFP_KERNEL);
|
2012-01-05 05:48:05 +08:00
|
|
|
if (ret == -ENOSPC && compression < 3) {
|
|
|
|
compression++;
|
|
|
|
pdev->iso_init = 1;
|
|
|
|
pwc_isoc_cleanup(pdev);
|
|
|
|
goto retry;
|
|
|
|
}
|
2010-11-16 23:32:09 +08:00
|
|
|
if (ret) {
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret);
|
2010-11-16 23:32:09 +08:00
|
|
|
pdev->iso_init = 1;
|
|
|
|
pwc_isoc_cleanup(pdev);
|
|
|
|
return ret;
|
|
|
|
}
|
2011-06-27 00:57:15 +08:00
|
|
|
PWC_DEBUG_MEMORY("URB 0x%p submitted.\n", pdev->urbs[i]);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* All is done... */
|
|
|
|
pdev->iso_init = 1;
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_DEBUG_OPEN("<< pwc_isoc_init()\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-09-26 21:19:01 +08:00
|
|
|
static void pwc_iso_stop(struct pwc_device *pdev)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Unlinking ISOC buffers one by one */
|
|
|
|
for (i = 0; i < MAX_ISO_BUFS; i++) {
|
2011-06-27 00:57:15 +08:00
|
|
|
if (pdev->urbs[i]) {
|
|
|
|
PWC_DEBUG_MEMORY("Unlinking URB %p\n", pdev->urbs[i]);
|
|
|
|
usb_kill_urb(pdev->urbs[i]);
|
2007-09-26 21:19:01 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pwc_iso_free(struct pwc_device *pdev)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Freeing ISOC buffers one by one */
|
|
|
|
for (i = 0; i < MAX_ISO_BUFS; i++) {
|
2011-06-27 00:57:15 +08:00
|
|
|
if (pdev->urbs[i]) {
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_DEBUG_MEMORY("Freeing URB\n");
|
2011-06-27 00:57:15 +08:00
|
|
|
if (pdev->urbs[i]->transfer_buffer) {
|
|
|
|
usb_free_coherent(pdev->udev,
|
|
|
|
pdev->urbs[i]->transfer_buffer_length,
|
|
|
|
pdev->urbs[i]->transfer_buffer,
|
|
|
|
pdev->urbs[i]->transfer_dma);
|
|
|
|
}
|
|
|
|
usb_free_urb(pdev->urbs[i]);
|
|
|
|
pdev->urbs[i] = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
2007-09-26 21:19:01 +08:00
|
|
|
}
|
|
|
|
|
2011-06-07 02:33:44 +08:00
|
|
|
static void pwc_isoc_cleanup(struct pwc_device *pdev)
|
2007-09-26 21:19:01 +08:00
|
|
|
{
|
|
|
|
PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n");
|
2011-06-07 02:25:18 +08:00
|
|
|
|
2007-09-26 21:19:01 +08:00
|
|
|
if (pdev->iso_init == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pwc_iso_stop(pdev);
|
|
|
|
pwc_iso_free(pdev);
|
2011-06-26 04:39:19 +08:00
|
|
|
usb_set_interface(pdev->udev, 0, 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
pdev->iso_init = 0;
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-06-07 02:33:44 +08:00
|
|
|
static void pwc_cleanup_queued_bufs(struct pwc_device *pdev)
|
|
|
|
{
|
2011-10-09 20:16:46 +08:00
|
|
|
unsigned long flags = 0;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
|
2011-06-07 02:33:44 +08:00
|
|
|
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);
|
|
|
|
}
|
2011-10-09 20:16:46 +08:00
|
|
|
spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
|
2011-06-07 02:33:44 +08:00
|
|
|
}
|
|
|
|
|
2007-01-31 10:26:01 +08:00
|
|
|
#ifdef CONFIG_USB_PWC_DEBUG
|
2006-04-24 21:29:46 +08:00
|
|
|
static const char *pwc_sensor_type_to_string(unsigned int sensor_type)
|
|
|
|
{
|
|
|
|
switch(sensor_type) {
|
|
|
|
case 0x00:
|
|
|
|
return "Hyundai CMOS sensor";
|
|
|
|
case 0x20:
|
|
|
|
return "Sony CCD sensor + TDA8787";
|
|
|
|
case 0x2E:
|
|
|
|
return "Sony CCD sensor + Exas 98L59";
|
|
|
|
case 0x2F:
|
|
|
|
return "Sony CCD sensor + ADI 9804";
|
|
|
|
case 0x30:
|
|
|
|
return "Sharp CCD sensor + TDA8787";
|
|
|
|
case 0x3E:
|
|
|
|
return "Sharp CCD sensor + Exas 98L59";
|
|
|
|
case 0x3F:
|
|
|
|
return "Sharp CCD sensor + ADI 9804";
|
|
|
|
case 0x40:
|
|
|
|
return "UPA 1021 sensor";
|
|
|
|
case 0x100:
|
|
|
|
return "VGA sensor";
|
|
|
|
case 0x101:
|
|
|
|
return "PAL MR sensor";
|
|
|
|
default:
|
2006-06-20 11:30:57 +08:00
|
|
|
return "unknown type of sensor";
|
2006-04-24 21:29:46 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/***************************************************************************/
|
|
|
|
/* Video4Linux functions */
|
|
|
|
|
2011-10-09 20:16:46 +08:00
|
|
|
int pwc_test_n_set_capt_file(struct pwc_device *pdev, struct file *file)
|
|
|
|
{
|
|
|
|
int r = 0;
|
|
|
|
|
|
|
|
mutex_lock(&pdev->capt_file_lock);
|
|
|
|
if (pdev->capt_file != NULL &&
|
|
|
|
pdev->capt_file != file) {
|
|
|
|
r = -EBUSY;
|
|
|
|
goto leave;
|
|
|
|
}
|
|
|
|
pdev->capt_file = file;
|
|
|
|
leave:
|
|
|
|
mutex_unlock(&pdev->capt_file_lock);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2011-07-19 18:14:22 +08:00
|
|
|
static void pwc_video_release(struct v4l2_device *v)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-07-19 18:14:22 +08:00
|
|
|
struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
|
2011-06-07 01:43:39 +08:00
|
|
|
int hint;
|
2009-01-12 13:50:17 +08:00
|
|
|
|
2011-06-07 01:43:39 +08:00
|
|
|
/* search device_hint[] table if we occupy a slot, by any chance */
|
|
|
|
for (hint = 0; hint < MAX_DEV_HINTS; hint++)
|
|
|
|
if (device_hint[hint].pdev == pdev)
|
|
|
|
device_hint[hint].pdev = NULL;
|
2009-08-14 13:22:52 +08:00
|
|
|
|
2011-06-26 23:52:01 +08:00
|
|
|
v4l2_ctrl_handler_free(&pdev->ctrl_handler);
|
|
|
|
|
2009-08-14 13:22:52 +08:00
|
|
|
kfree(pdev);
|
2007-08-21 13:10:42 +08:00
|
|
|
}
|
|
|
|
|
2008-12-30 17:58:20 +08:00
|
|
|
static int pwc_video_close(struct file *file)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-07-19 18:14:22 +08:00
|
|
|
struct pwc_device *pdev = video_drvdata(file);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-06-26 23:13:44 +08:00
|
|
|
if (pdev->capt_file == file) {
|
|
|
|
vb2_queue_release(&pdev->vb_queue);
|
|
|
|
pdev->capt_file = NULL;
|
|
|
|
}
|
2011-07-19 18:14:22 +08:00
|
|
|
return v4l2_fh_release(file);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-04-24 21:29:46 +08:00
|
|
|
static ssize_t pwc_video_read(struct file *file, char __user *buf,
|
2011-06-07 02:33:44 +08:00
|
|
|
size_t count, loff_t *ppos)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-07-19 18:14:22 +08:00
|
|
|
struct pwc_device *pdev = video_drvdata(file);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-06-26 04:39:19 +08:00
|
|
|
if (!pdev->udev)
|
|
|
|
return -ENODEV;
|
2007-09-26 21:19:01 +08:00
|
|
|
|
2011-10-09 20:16:46 +08:00
|
|
|
if (pwc_test_n_set_capt_file(pdev, file))
|
2011-06-26 23:13:44 +08:00
|
|
|
return -EBUSY;
|
|
|
|
|
2011-06-07 02:33:44 +08:00
|
|
|
return vb2_read(&pdev->vb_queue, buf, count, ppos,
|
|
|
|
file->f_flags & O_NONBLOCK);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-06-07 02:33:44 +08:00
|
|
|
static unsigned int pwc_video_poll(struct file *file, poll_table *wait)
|
|
|
|
{
|
2011-07-19 18:14:22 +08:00
|
|
|
struct pwc_device *pdev = video_drvdata(file);
|
2006-03-25 20:19:53 +08:00
|
|
|
|
2011-06-26 04:39:19 +08:00
|
|
|
if (!pdev->udev)
|
2011-06-07 02:33:44 +08:00
|
|
|
return POLL_ERR;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-06-07 02:33:44 +08:00
|
|
|
return vb2_poll(&pdev->vb_queue, file, wait);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-06-07 02:33:44 +08:00
|
|
|
static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-07-19 18:14:22 +08:00
|
|
|
struct pwc_device *pdev = video_drvdata(file);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-06-26 23:13:44 +08:00
|
|
|
if (pdev->capt_file != file)
|
|
|
|
return -EBUSY;
|
|
|
|
|
2011-06-07 02:33:44 +08:00
|
|
|
return vb2_mmap(&pdev->vb_queue, vma);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-06-07 02:33:44 +08:00
|
|
|
/***************************************************************************/
|
|
|
|
/* Videobuf2 operations */
|
|
|
|
|
2011-08-24 21:30:21 +08:00
|
|
|
static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
|
|
|
|
unsigned int *nbuffers, unsigned int *nplanes,
|
|
|
|
unsigned int sizes[], void *alloc_ctxs[])
|
2011-06-07 02:33:44 +08:00
|
|
|
{
|
|
|
|
struct pwc_device *pdev = vb2_get_drv_priv(vq);
|
2012-01-05 03:58:44 +08:00
|
|
|
int size;
|
2011-06-07 02:33:44 +08:00
|
|
|
|
|
|
|
if (*nbuffers < MIN_FRAMES)
|
|
|
|
*nbuffers = MIN_FRAMES;
|
|
|
|
else if (*nbuffers > MAX_FRAMES)
|
|
|
|
*nbuffers = MAX_FRAMES;
|
|
|
|
|
|
|
|
*nplanes = 1;
|
|
|
|
|
2012-01-05 03:58:44 +08:00
|
|
|
size = pwc_get_size(pdev, MAX_WIDTH, MAX_HEIGHT);
|
|
|
|
sizes[0] = PAGE_ALIGN(pwc_image_sizes[size][0] *
|
|
|
|
pwc_image_sizes[size][1] * 3 / 2);
|
2011-06-07 02:33:44 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int buffer_init(struct vb2_buffer *vb)
|
|
|
|
{
|
|
|
|
struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
|
2010-11-16 22:39:25 +08:00
|
|
|
|
2011-06-07 02:33:44 +08:00
|
|
|
/* 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 */
|
2011-06-26 04:39:19 +08:00
|
|
|
if (!pdev->udev)
|
|
|
|
return -ENODEV;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-06-07 02:33:44 +08:00
|
|
|
static int buffer_finish(struct vb2_buffer *vb)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-06-07 02:33:44 +08:00
|
|
|
struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
|
|
|
|
struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
|
2006-03-25 20:19:53 +08:00
|
|
|
|
2011-06-07 02:33:44 +08:00
|
|
|
/*
|
|
|
|
* 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);
|
2011-10-09 20:16:46 +08:00
|
|
|
/* Check the device has not disconnected between prep and queuing */
|
|
|
|
if (pdev->udev)
|
|
|
|
list_add_tail(&buf->list, &pdev->queued_bufs);
|
|
|
|
else
|
|
|
|
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
|
2011-06-07 02:33:44 +08:00
|
|
|
spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
|
|
|
|
}
|
|
|
|
|
2011-08-29 19:51:49 +08:00
|
|
|
static int start_streaming(struct vb2_queue *vq, unsigned int count)
|
2011-06-07 02:33:44 +08:00
|
|
|
{
|
|
|
|
struct pwc_device *pdev = vb2_get_drv_priv(vq);
|
2011-10-09 20:16:46 +08:00
|
|
|
int r;
|
2011-06-07 02:33:44 +08:00
|
|
|
|
2011-10-09 20:16:46 +08:00
|
|
|
mutex_lock(&pdev->udevlock);
|
|
|
|
if (!pdev->udev) {
|
|
|
|
r = -ENODEV;
|
|
|
|
goto leave;
|
|
|
|
}
|
2011-06-26 04:39:19 +08:00
|
|
|
|
2011-06-26 17:49:59 +08:00
|
|
|
/* Turn on camera and set LEDS on */
|
|
|
|
pwc_camera_power(pdev, 1);
|
|
|
|
pwc_set_leds(pdev, led_on, led_off);
|
|
|
|
|
2011-10-09 20:16:46 +08:00
|
|
|
r = pwc_isoc_init(pdev);
|
2012-01-05 05:48:05 +08:00
|
|
|
if (r) {
|
|
|
|
/* If we failed turn camera and LEDS back off */
|
|
|
|
pwc_set_leds(pdev, 0, 0);
|
|
|
|
pwc_camera_power(pdev, 0);
|
|
|
|
/* And cleanup any queued bufs!! */
|
|
|
|
pwc_cleanup_queued_bufs(pdev);
|
|
|
|
}
|
2011-10-09 20:16:46 +08:00
|
|
|
leave:
|
|
|
|
mutex_unlock(&pdev->udevlock);
|
|
|
|
return r;
|
2011-06-07 02:33:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int stop_streaming(struct vb2_queue *vq)
|
|
|
|
{
|
|
|
|
struct pwc_device *pdev = vb2_get_drv_priv(vq);
|
|
|
|
|
2011-10-09 20:16:46 +08:00
|
|
|
mutex_lock(&pdev->udevlock);
|
2011-06-26 17:49:59 +08:00
|
|
|
if (pdev->udev) {
|
|
|
|
pwc_set_leds(pdev, 0, 0);
|
|
|
|
pwc_camera_power(pdev, 0);
|
2011-06-26 04:39:19 +08:00
|
|
|
pwc_isoc_cleanup(pdev);
|
2011-06-26 17:49:59 +08:00
|
|
|
}
|
2011-10-09 20:16:46 +08:00
|
|
|
mutex_unlock(&pdev->udevlock);
|
|
|
|
|
2011-06-07 02:33:44 +08:00
|
|
|
pwc_cleanup_queued_bufs(pdev);
|
2006-03-25 20:19:53 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-06-07 02:33:44 +08:00
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/***************************************************************************/
|
|
|
|
/* USB functions */
|
|
|
|
|
|
|
|
/* This function gets called when a new device is plugged in or the usb core
|
|
|
|
* is loaded.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
|
|
|
{
|
|
|
|
struct usb_device *udev = interface_to_usbdev(intf);
|
|
|
|
struct pwc_device *pdev = NULL;
|
|
|
|
int vendor_id, product_id, type_id;
|
2009-08-14 13:22:52 +08:00
|
|
|
int hint, rc;
|
2005-04-17 06:20:36 +08:00
|
|
|
int features = 0;
|
2012-01-05 05:48:05 +08:00
|
|
|
int compression = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
int video_nr = -1; /* default: use next available device */
|
2011-06-26 14:51:19 +08:00
|
|
|
int my_power_save = power_save;
|
2005-04-17 06:20:36 +08:00
|
|
|
char serial_number[30], *name;
|
|
|
|
|
2006-04-24 21:29:46 +08:00
|
|
|
vendor_id = le16_to_cpu(udev->descriptor.idVendor);
|
|
|
|
product_id = le16_to_cpu(udev->descriptor.idProduct);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Check if we can handle this device */
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_DEBUG_PROBE("probe() called [%04X %04X], if %d\n",
|
|
|
|
vendor_id, product_id,
|
2005-04-17 06:20:36 +08:00
|
|
|
intf->altsetting->desc.bInterfaceNumber);
|
|
|
|
|
|
|
|
/* the interfaces are probed one by one. We are only interested in the
|
|
|
|
video interface (0) now.
|
|
|
|
Interface 1 is the Audio Control, and interface 2 Audio itself.
|
|
|
|
*/
|
|
|
|
if (intf->altsetting->desc.bInterfaceNumber > 0)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (vendor_id == 0x0471) {
|
|
|
|
switch (product_id) {
|
|
|
|
case 0x0302:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Philips PCA645VC USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Philips 645 webcam";
|
|
|
|
type_id = 645;
|
|
|
|
break;
|
|
|
|
case 0x0303:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Philips PCA646VC USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Philips 646 webcam";
|
|
|
|
type_id = 646;
|
|
|
|
break;
|
|
|
|
case 0x0304:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Askey VC010 type 2 USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Askey VC010 webcam";
|
|
|
|
type_id = 646;
|
|
|
|
break;
|
|
|
|
case 0x0307:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Philips PCVC675K (Vesta) USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Philips 675 webcam";
|
|
|
|
type_id = 675;
|
|
|
|
break;
|
|
|
|
case 0x0308:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Philips PCVC680K (Vesta Pro) USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Philips 680 webcam";
|
|
|
|
type_id = 680;
|
|
|
|
break;
|
|
|
|
case 0x030C:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Philips 690 webcam";
|
|
|
|
type_id = 690;
|
|
|
|
break;
|
|
|
|
case 0x0310:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Philips 730 webcam";
|
|
|
|
type_id = 730;
|
|
|
|
break;
|
|
|
|
case 0x0311:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Philips 740 webcam";
|
|
|
|
type_id = 740;
|
|
|
|
break;
|
|
|
|
case 0x0312:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Philips 750 webcam";
|
|
|
|
type_id = 750;
|
|
|
|
break;
|
|
|
|
case 0x0313:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Philips 720K/40 webcam";
|
|
|
|
type_id = 720;
|
|
|
|
break;
|
2006-04-24 21:29:46 +08:00
|
|
|
case 0x0329:
|
|
|
|
PWC_INFO("Philips SPC 900NC USB webcam detected.\n");
|
|
|
|
name = "Philips SPC 900NC webcam";
|
2007-04-23 10:54:36 +08:00
|
|
|
type_id = 740;
|
2006-04-24 21:29:46 +08:00
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
default:
|
|
|
|
return -ENODEV;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (vendor_id == 0x069A) {
|
|
|
|
switch(product_id) {
|
|
|
|
case 0x0001:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Askey VC010 type 1 USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Askey VC010 webcam";
|
|
|
|
type_id = 645;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -ENODEV;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (vendor_id == 0x046d) {
|
|
|
|
switch(product_id) {
|
|
|
|
case 0x08b0:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Logitech QuickCam Pro 3000 USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Logitech QuickCam Pro 3000";
|
|
|
|
type_id = 740; /* CCD sensor */
|
|
|
|
break;
|
|
|
|
case 0x08b1:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Logitech QuickCam Notebook Pro USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Logitech QuickCam Notebook Pro";
|
|
|
|
type_id = 740; /* CCD sensor */
|
|
|
|
break;
|
|
|
|
case 0x08b2:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Logitech QuickCam 4000 Pro USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Logitech QuickCam Pro 4000";
|
|
|
|
type_id = 740; /* CCD sensor */
|
2011-07-04 02:52:54 +08:00
|
|
|
if (my_power_save == -1)
|
|
|
|
my_power_save = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
case 0x08b3:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Logitech QuickCam Zoom USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Logitech QuickCam Zoom";
|
|
|
|
type_id = 740; /* CCD sensor */
|
|
|
|
break;
|
|
|
|
case 0x08B4:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Logitech QuickCam Zoom (new model) USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Logitech QuickCam Zoom";
|
|
|
|
type_id = 740; /* CCD sensor */
|
2011-06-26 14:51:19 +08:00
|
|
|
if (my_power_save == -1)
|
|
|
|
my_power_save = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
case 0x08b5:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Logitech QuickCam Orbit/Sphere USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Logitech QuickCam Orbit";
|
|
|
|
type_id = 740; /* CCD sensor */
|
2011-07-04 02:52:54 +08:00
|
|
|
if (my_power_save == -1)
|
|
|
|
my_power_save = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
features |= FEATURE_MOTOR_PANTILT;
|
|
|
|
break;
|
|
|
|
case 0x08b6:
|
V4L/DVB (5461): Pwc: cisco VT Camera support
I have a Cisco VT Camera, and it was just collecting dust. I decided to
try connecting it to my Linux box at home.
Just a disgression about the product. The Cisco VT Camera is a webcam
Cisco sold to work with their IP phone hardware and software. It's mostly
useless on Windows, as it interfaces only to Cisco software. You can find
some for cheap on eBay...
Physically, it's just a Logitech Pro 4000. The only difference with the
Pro 4000 is the Cisco logo and that it's grey like the Pro 3000. I believe
Cisco is now selling the Cisco VT Camera II, which look to be something
else...
So, assuming that it was a Pro 4000 inside, I created the little patch
attached.
I'm new to webcam under Linux, but I managed to get an image from it using
xawtv, and the image looked all right, so I consider that a success. The
imaged seemed a bit small and I could not get the microphone driver loaded,
but I assume it's my lack of experience. Note that I did not try any other
type_id, but this one works great.
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2007-03-22 03:29:16 +08:00
|
|
|
PWC_INFO("Logitech/Cisco VT Camera webcam detected.\n");
|
|
|
|
name = "Cisco VT Camera";
|
|
|
|
type_id = 740; /* CCD sensor */
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
case 0x08b7:
|
2007-03-22 03:35:28 +08:00
|
|
|
PWC_INFO("Logitech ViewPort AV 100 webcam detected.\n");
|
|
|
|
name = "Logitech ViewPort AV 100";
|
|
|
|
type_id = 740; /* CCD sensor */
|
|
|
|
break;
|
|
|
|
case 0x08b8: /* Where this released? */
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Logitech QuickCam detected (reserved ID).\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Logitech QuickCam (res.)";
|
|
|
|
type_id = 730; /* Assuming CMOS */
|
|
|
|
break;
|
2006-03-25 20:19:53 +08:00
|
|
|
default:
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENODEV;
|
2006-03-25 20:19:53 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
else if (vendor_id == 0x055d) {
|
|
|
|
/* I don't know the difference between the C10 and the C30;
|
|
|
|
I suppose the difference is the sensor, but both cameras
|
|
|
|
work equally well with a type_id of 675
|
|
|
|
*/
|
|
|
|
switch(product_id) {
|
|
|
|
case 0x9000:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Samsung MPC-C10 USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Samsung MPC-C10";
|
|
|
|
type_id = 675;
|
|
|
|
break;
|
|
|
|
case 0x9001:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Samsung MPC-C30 USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Samsung MPC-C30";
|
|
|
|
type_id = 675;
|
|
|
|
break;
|
2006-04-24 21:29:46 +08:00
|
|
|
case 0x9002:
|
|
|
|
PWC_INFO("Samsung SNC-35E (v3.0) USB webcam detected.\n");
|
|
|
|
name = "Samsung MPC-C30";
|
|
|
|
type_id = 740;
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
default:
|
|
|
|
return -ENODEV;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (vendor_id == 0x041e) {
|
|
|
|
switch(product_id) {
|
|
|
|
case 0x400c:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Creative Labs Webcam 5 detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Creative Labs Webcam 5";
|
|
|
|
type_id = 730;
|
2011-07-04 02:52:54 +08:00
|
|
|
if (my_power_save == -1)
|
|
|
|
my_power_save = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
case 0x4011:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Creative Labs Webcam Pro Ex detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Creative Labs Webcam Pro Ex";
|
|
|
|
type_id = 740;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -ENODEV;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (vendor_id == 0x04cc) {
|
|
|
|
switch(product_id) {
|
|
|
|
case 0x8116:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Sotec Afina Eye USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Sotec Afina Eye";
|
|
|
|
type_id = 730;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -ENODEV;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (vendor_id == 0x06be) {
|
|
|
|
switch(product_id) {
|
|
|
|
case 0x8116:
|
|
|
|
/* This is essentially the same cam as the Sotec Afina Eye */
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("AME Co. Afina Eye USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "AME Co. Afina Eye";
|
|
|
|
type_id = 750;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -ENODEV;
|
|
|
|
break;
|
|
|
|
}
|
2006-03-25 20:19:53 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
else if (vendor_id == 0x0d81) {
|
|
|
|
switch(product_id) {
|
|
|
|
case 0x1900:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Visionite VCS-UC300 USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Visionite VCS-UC300";
|
|
|
|
type_id = 740; /* CCD sensor */
|
|
|
|
break;
|
|
|
|
case 0x1910:
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Visionite VCS-UM100 USB webcam detected.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
name = "Visionite VCS-UM100";
|
|
|
|
type_id = 730; /* CMOS sensor */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -ENODEV;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2006-03-25 20:19:53 +08:00
|
|
|
else
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENODEV; /* Not any of the know types; but the list keeps growing. */
|
|
|
|
|
2011-06-26 14:51:19 +08:00
|
|
|
if (my_power_save == -1)
|
|
|
|
my_power_save = 0;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
memset(serial_number, 0, 30);
|
|
|
|
usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_DEBUG_PROBE("Device serial number is %s\n", serial_number);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (udev->descriptor.bNumConfigurations > 1)
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_WARNING("Warning: more than 1 configuration available.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */
|
2006-02-28 04:29:43 +08:00
|
|
|
pdev = kzalloc(sizeof(struct pwc_device), GFP_KERNEL);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (pdev == NULL) {
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_ERROR("Oops, could not allocate memory for pwc_device.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
pdev->type = type_id;
|
|
|
|
pdev->features = features;
|
2011-06-26 14:51:19 +08:00
|
|
|
pwc_construct(pdev); /* set min/max sizes correct */
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-10-09 20:16:46 +08:00
|
|
|
mutex_init(&pdev->capt_file_lock);
|
2011-07-03 22:50:51 +08:00
|
|
|
mutex_init(&pdev->udevlock);
|
2011-06-07 02:33:44 +08:00
|
|
|
spin_lock_init(&pdev->queued_bufs_lock);
|
|
|
|
INIT_LIST_HEAD(&pdev->queued_bufs);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
pdev->udev = udev;
|
2011-06-26 14:51:19 +08:00
|
|
|
pdev->power_save = my_power_save;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-06-07 02:33:44 +08:00
|
|
|
/* 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);
|
|
|
|
|
2011-06-07 01:43:39 +08:00
|
|
|
/* Init video_device structure */
|
|
|
|
memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template));
|
|
|
|
strcpy(pdev->vdev.name, name);
|
2011-07-19 18:14:22 +08:00
|
|
|
set_bit(V4L2_FL_USE_FH_PRIO, &pdev->vdev.flags);
|
2011-06-07 01:43:39 +08:00
|
|
|
video_set_drvdata(&pdev->vdev, pdev);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
pdev->release = le16_to_cpu(udev->descriptor.bcdDevice);
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_DEBUG_PROBE("Release: %04x\n", pdev->release);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Now search device_hint[] table for a match, so we can hint a node number. */
|
|
|
|
for (hint = 0; hint < MAX_DEV_HINTS; hint++) {
|
|
|
|
if (((device_hint[hint].type == -1) || (device_hint[hint].type == pdev->type)) &&
|
|
|
|
(device_hint[hint].pdev == NULL)) {
|
|
|
|
/* so far, so good... try serial number */
|
|
|
|
if ((device_hint[hint].serial_number[0] == '*') || !strcmp(device_hint[hint].serial_number, serial_number)) {
|
2006-06-20 11:30:57 +08:00
|
|
|
/* match! */
|
|
|
|
video_nr = device_hint[hint].device_node;
|
|
|
|
PWC_DEBUG_PROBE("Found hint, will try to register as /dev/video%d\n", video_nr);
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* occupy slot */
|
2006-03-25 20:19:53 +08:00
|
|
|
if (hint < MAX_DEV_HINTS)
|
2005-04-17 06:20:36 +08:00
|
|
|
device_hint[hint].pdev = pdev;
|
|
|
|
|
2011-06-26 17:49:59 +08:00
|
|
|
#ifdef CONFIG_USB_PWC_DEBUG
|
|
|
|
/* Query sensor type */
|
|
|
|
if (pwc_get_cmos_sensor(pdev, &rc) >= 0) {
|
|
|
|
PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n",
|
|
|
|
pdev->vdev.name,
|
|
|
|
pwc_sensor_type_to_string(rc), rc);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2006-04-24 21:29:46 +08:00
|
|
|
/* Set the leds off */
|
|
|
|
pwc_set_leds(pdev, 0, 0);
|
2011-06-26 17:49:59 +08:00
|
|
|
|
|
|
|
/* Setup intial videomode */
|
2012-01-11 00:01:41 +08:00
|
|
|
rc = pwc_set_video_mode(pdev, MAX_WIDTH, MAX_HEIGHT,
|
2012-01-11 00:14:46 +08:00
|
|
|
V4L2_PIX_FMT_YUV420, 30, &compression, 1);
|
2011-06-26 17:49:59 +08:00
|
|
|
if (rc)
|
|
|
|
goto err_free_mem;
|
|
|
|
|
2011-06-26 23:52:01 +08:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2011-06-26 17:49:59 +08:00
|
|
|
/* And powerdown the camera until streaming starts */
|
2006-04-24 21:29:46 +08:00
|
|
|
pwc_camera_power(pdev, 0);
|
|
|
|
|
2011-07-19 18:14:22 +08:00
|
|
|
/* Register the v4l2_device structure */
|
|
|
|
pdev->v4l2_dev.release = pwc_video_release;
|
|
|
|
rc = v4l2_device_register(&intf->dev, &pdev->v4l2_dev);
|
|
|
|
if (rc) {
|
|
|
|
PWC_ERROR("Failed to register v4l2-device (%d).\n", rc);
|
|
|
|
goto err_free_controls;
|
|
|
|
}
|
|
|
|
|
|
|
|
pdev->v4l2_dev.ctrl_handler = &pdev->ctrl_handler;
|
|
|
|
pdev->vdev.v4l2_dev = &pdev->v4l2_dev;
|
|
|
|
|
2011-06-07 01:43:39 +08:00
|
|
|
rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, video_nr);
|
2010-09-13 04:05:11 +08:00
|
|
|
if (rc < 0) {
|
|
|
|
PWC_ERROR("Failed to register as video device (%d).\n", rc);
|
2011-07-19 18:14:22 +08:00
|
|
|
goto err_unregister_v4l2_dev;
|
2010-09-13 04:05:11 +08:00
|
|
|
}
|
2011-06-07 01:43:39 +08:00
|
|
|
PWC_INFO("Registered as %s.\n", video_device_node_name(&pdev->vdev));
|
2010-09-13 04:05:11 +08:00
|
|
|
|
2009-01-12 13:50:17 +08:00
|
|
|
#ifdef CONFIG_USB_PWC_INPUT_EVDEV
|
|
|
|
/* register webcam snapshot button input device */
|
|
|
|
pdev->button_dev = input_allocate_device();
|
|
|
|
if (!pdev->button_dev) {
|
|
|
|
PWC_ERROR("Err, insufficient memory for webcam snapshot button device.");
|
2009-08-14 13:22:52 +08:00
|
|
|
rc = -ENOMEM;
|
|
|
|
goto err_video_unreg;
|
2009-01-12 13:50:17 +08:00
|
|
|
}
|
|
|
|
|
2009-08-14 13:22:52 +08:00
|
|
|
usb_make_path(udev, pdev->button_phys, sizeof(pdev->button_phys));
|
|
|
|
strlcat(pdev->button_phys, "/input0", sizeof(pdev->button_phys));
|
|
|
|
|
2009-01-12 13:50:17 +08:00
|
|
|
pdev->button_dev->name = "PWC snapshot button";
|
2009-08-14 13:22:52 +08:00
|
|
|
pdev->button_dev->phys = pdev->button_phys;
|
2009-01-12 13:50:17 +08:00
|
|
|
usb_to_input_id(pdev->udev, &pdev->button_dev->id);
|
|
|
|
pdev->button_dev->dev.parent = &pdev->udev->dev;
|
|
|
|
pdev->button_dev->evbit[0] = BIT_MASK(EV_KEY);
|
2009-06-11 22:19:33 +08:00
|
|
|
pdev->button_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA);
|
2009-01-12 13:50:17 +08:00
|
|
|
|
|
|
|
rc = input_register_device(pdev->button_dev);
|
|
|
|
if (rc) {
|
|
|
|
input_free_device(pdev->button_dev);
|
|
|
|
pdev->button_dev = NULL;
|
2009-08-14 13:22:52 +08:00
|
|
|
goto err_video_unreg;
|
2009-01-12 13:50:17 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
2006-10-13 18:17:32 +08:00
|
|
|
|
2009-08-14 13:22:52 +08:00
|
|
|
err_video_unreg:
|
2011-06-07 01:43:39 +08:00
|
|
|
video_unregister_device(&pdev->vdev);
|
2011-07-19 18:14:22 +08:00
|
|
|
err_unregister_v4l2_dev:
|
|
|
|
v4l2_device_unregister(&pdev->v4l2_dev);
|
2011-06-26 23:52:01 +08:00
|
|
|
err_free_controls:
|
|
|
|
v4l2_ctrl_handler_free(&pdev->ctrl_handler);
|
2009-08-14 13:22:52 +08:00
|
|
|
err_free_mem:
|
2011-07-19 20:05:49 +08:00
|
|
|
if (hint < MAX_DEV_HINTS)
|
|
|
|
device_hint[hint].pdev = NULL;
|
2009-08-14 13:22:52 +08:00
|
|
|
kfree(pdev);
|
2006-10-13 18:17:32 +08:00
|
|
|
return rc;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-08-14 13:22:52 +08:00
|
|
|
/* The user yanked out the cable... */
|
2005-04-17 06:20:36 +08:00
|
|
|
static void usb_pwc_disconnect(struct usb_interface *intf)
|
|
|
|
{
|
2011-07-19 18:14:22 +08:00
|
|
|
struct v4l2_device *v = usb_get_intfdata(intf);
|
|
|
|
struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-07-03 22:50:51 +08:00
|
|
|
mutex_lock(&pdev->udevlock);
|
2011-06-07 01:43:39 +08:00
|
|
|
/* No need to keep the urbs around after disconnection */
|
|
|
|
pwc_isoc_cleanup(pdev);
|
2011-06-26 04:39:19 +08:00
|
|
|
pdev->udev = NULL;
|
2011-07-03 22:50:51 +08:00
|
|
|
mutex_unlock(&pdev->udevlock);
|
2011-06-07 01:43:39 +08:00
|
|
|
|
2011-10-09 20:16:46 +08:00
|
|
|
pwc_cleanup_queued_bufs(pdev);
|
|
|
|
|
2011-06-07 01:43:39 +08:00
|
|
|
video_unregister_device(&pdev->vdev);
|
2011-07-19 18:14:22 +08:00
|
|
|
v4l2_device_unregister(&pdev->v4l2_dev);
|
2011-06-07 01:43:39 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_USB_PWC_INPUT_EVDEV
|
|
|
|
if (pdev->button_dev)
|
|
|
|
input_unregister_device(pdev->button_dev);
|
|
|
|
#endif
|
2011-07-19 18:14:22 +08:00
|
|
|
|
|
|
|
v4l2_device_put(&pdev->v4l2_dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-25 20:19:53 +08:00
|
|
|
/*
|
|
|
|
* Initialization code & module stuff
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
static int leds[2] = { -1, -1 };
|
2007-10-15 02:35:30 +08:00
|
|
|
static unsigned int leds_nargs;
|
2006-04-24 21:29:46 +08:00
|
|
|
static char *dev_hint[MAX_DEV_HINTS];
|
2007-10-15 02:35:30 +08:00
|
|
|
static unsigned int dev_hint_nargs;
|
2006-04-24 21:29:46 +08:00
|
|
|
|
2007-01-31 10:26:01 +08:00
|
|
|
#ifdef CONFIG_USB_PWC_DEBUG
|
2006-04-24 21:29:46 +08:00
|
|
|
module_param_named(trace, pwc_trace, int, 0644);
|
|
|
|
#endif
|
2011-06-26 14:51:19 +08:00
|
|
|
module_param(power_save, int, 0644);
|
2006-04-24 21:29:46 +08:00
|
|
|
module_param_array(leds, int, &leds_nargs, 0444);
|
|
|
|
module_param_array(dev_hint, charp, &dev_hint_nargs, 0444);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-12-11 03:26:10 +08:00
|
|
|
#ifdef CONFIG_USB_PWC_DEBUG
|
2005-04-17 06:20:36 +08:00
|
|
|
MODULE_PARM_DESC(trace, "For debugging purposes");
|
2009-12-11 03:26:10 +08:00
|
|
|
#endif
|
2011-06-26 14:51:19 +08:00
|
|
|
MODULE_PARM_DESC(power_save, "Turn power saving for new cameras on or off");
|
2005-04-17 06:20:36 +08:00
|
|
|
MODULE_PARM_DESC(leds, "LED on,off time in milliseconds");
|
|
|
|
MODULE_PARM_DESC(dev_hint, "Device node hints");
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION("Philips & OEM USB webcam driver");
|
|
|
|
MODULE_AUTHOR("Luc Saillard <luc@saillard.org>");
|
|
|
|
MODULE_LICENSE("GPL");
|
2006-04-24 21:29:46 +08:00
|
|
|
MODULE_ALIAS("pwcx");
|
|
|
|
MODULE_VERSION( PWC_VERSION );
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static int __init usb_pwc_init(void)
|
|
|
|
{
|
2011-06-26 17:49:59 +08:00
|
|
|
int i;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-06-26 17:49:59 +08:00
|
|
|
#ifdef CONFIG_USB_PWC_DEBUG
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Philips webcam module version " PWC_VERSION " loaded.\n");
|
|
|
|
PWC_INFO("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n");
|
|
|
|
PWC_INFO("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n");
|
|
|
|
PWC_INFO("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-06-26 17:49:59 +08:00
|
|
|
if (pwc_trace >= 0) {
|
|
|
|
PWC_DEBUG_MODULE("Trace options: 0x%04x\n", pwc_trace);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (leds[0] >= 0)
|
|
|
|
led_on = leds[0];
|
|
|
|
if (leds[1] >= 0)
|
|
|
|
led_off = leds[1];
|
|
|
|
|
2005-05-04 09:07:24 +08:00
|
|
|
/* Big device node whoopla. Basically, it allows you to assign a
|
2005-04-17 06:20:36 +08:00
|
|
|
device node (/dev/videoX) to a camera, based on its type
|
|
|
|
& serial number. The format is [type[.serialnumber]:]node.
|
|
|
|
|
|
|
|
Any camera that isn't matched by these rules gets the next
|
|
|
|
available free device node.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < MAX_DEV_HINTS; i++) {
|
|
|
|
char *s, *colon, *dot;
|
|
|
|
|
|
|
|
/* This loop also initializes the array */
|
|
|
|
device_hint[i].pdev = NULL;
|
|
|
|
s = dev_hint[i];
|
|
|
|
if (s != NULL && *s != '\0') {
|
|
|
|
device_hint[i].type = -1; /* wildcard */
|
|
|
|
strcpy(device_hint[i].serial_number, "*");
|
|
|
|
|
|
|
|
/* parse string: chop at ':' & '/' */
|
|
|
|
colon = dot = s;
|
|
|
|
while (*colon != '\0' && *colon != ':')
|
|
|
|
colon++;
|
|
|
|
while (*dot != '\0' && *dot != '.')
|
|
|
|
dot++;
|
|
|
|
/* Few sanity checks */
|
|
|
|
if (*dot != '\0' && dot > colon) {
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_ERROR("Malformed camera hint: the colon must be after the dot.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*colon == '\0') {
|
|
|
|
/* No colon */
|
|
|
|
if (*dot != '\0') {
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_ERROR("Malformed camera hint: no colon + device node given.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* No type or serial number specified, just a number. */
|
2009-09-24 18:58:09 +08:00
|
|
|
device_hint[i].device_node =
|
|
|
|
simple_strtol(s, NULL, 10);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* There's a colon, so we have at least a type and a device node */
|
2009-09-24 18:58:09 +08:00
|
|
|
device_hint[i].type =
|
|
|
|
simple_strtol(s, NULL, 10);
|
|
|
|
device_hint[i].device_node =
|
|
|
|
simple_strtol(colon + 1, NULL, 10);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (*dot != '\0') {
|
|
|
|
/* There's a serial number as well */
|
|
|
|
int k;
|
2006-03-25 20:19:53 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
dot++;
|
|
|
|
k = 0;
|
|
|
|
while (*dot != ':' && k < 29) {
|
|
|
|
device_hint[i].serial_number[k++] = *dot;
|
|
|
|
dot++;
|
|
|
|
}
|
|
|
|
device_hint[i].serial_number[k] = '\0';
|
|
|
|
}
|
|
|
|
}
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_TRACE("device_hint[%d]:\n", i);
|
|
|
|
PWC_TRACE(" type : %d\n", device_hint[i].type);
|
|
|
|
PWC_TRACE(" serial# : %s\n", device_hint[i].serial_number);
|
|
|
|
PWC_TRACE(" node : %d\n", device_hint[i].device_node);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
device_hint[i].type = 0; /* not filled */
|
|
|
|
} /* ..for MAX_DEV_HINTS */
|
|
|
|
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_DEBUG_PROBE("Registering driver at address 0x%p.\n", &pwc_driver);
|
2005-04-17 06:20:36 +08:00
|
|
|
return usb_register(&pwc_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit usb_pwc_exit(void)
|
|
|
|
{
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_DEBUG_MODULE("Deregistering driver.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
usb_deregister(&pwc_driver);
|
2006-04-24 21:29:46 +08:00
|
|
|
PWC_INFO("Philips webcam module removed.\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(usb_pwc_init);
|
|
|
|
module_exit(usb_pwc_exit);
|
|
|
|
|
2006-04-24 21:29:46 +08:00
|
|
|
/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */
|