aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/pwc/pwc-if.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/pwc/pwc-if.c')
-rw-r--r--drivers/media/video/pwc/pwc-if.c1335
1 files changed, 560 insertions, 775 deletions
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index 41418294a32bc9..98059c8d6f69f9 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -1,7 +1,7 @@
/* Linux driver for Philips webcam
USB and Video4Linux interface part.
(C) 1999-2004 Nemosoft Unv.
- (C) 2004 Luc Saillard (luc@saillard.org)
+ (C) 2004-2006 Luc Saillard (luc@saillard.org)
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.
@@ -62,18 +62,21 @@
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
+#include <linux/version.h>
#include <asm/io.h>
+#include <linux/moduleparam.h>
#include "pwc.h"
-#include "pwc-ioctl.h"
#include "pwc-kiara.h"
#include "pwc-timon.h"
+#include "pwc-dec23.h"
+#include "pwc-dec1.h"
#include "pwc-uncompress.h"
/* Function prototypes and driver templates */
/* hotplug device table support */
-static struct usb_device_id pwc_device_table [] = {
+static const struct usb_device_id pwc_device_table [] = {
{ USB_DEVICE(0x0471, 0x0302) }, /* Philips models */
{ USB_DEVICE(0x0471, 0x0303) },
{ USB_DEVICE(0x0471, 0x0304) },
@@ -81,9 +84,10 @@ static struct usb_device_id pwc_device_table [] = {
{ USB_DEVICE(0x0471, 0x0308) },
{ USB_DEVICE(0x0471, 0x030C) },
{ USB_DEVICE(0x0471, 0x0310) },
- { USB_DEVICE(0x0471, 0x0311) },
+ { USB_DEVICE(0x0471, 0x0311) }, /* Philips ToUcam PRO II */
{ USB_DEVICE(0x0471, 0x0312) },
{ USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */
+ { USB_DEVICE(0x0471, 0x0329) }, /* Philips SPC 900NC PC Camera */
{ USB_DEVICE(0x069A, 0x0001) }, /* Askey */
{ USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */
{ USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */
@@ -94,8 +98,9 @@ static struct usb_device_id pwc_device_table [] = {
{ USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */
{ USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */
{ USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */
- { USB_DEVICE(0x055D, 0x9000) }, /* Samsung */
- { USB_DEVICE(0x055D, 0x9001) },
+ { USB_DEVICE(0x055D, 0x9000) }, /* Samsung MPC-C10 */
+ { USB_DEVICE(0x055D, 0x9001) }, /* Samsung MPC-C30 */
+ { USB_DEVICE(0x055D, 0x9002) }, /* Samsung SNC-35E (Ver3.0) */
{ USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */
{ USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */
{ USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */
@@ -122,11 +127,13 @@ 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 */
-static int default_mbufs = 2; /* Default number of mmap() buffers */
- int pwc_trace = TRACE_MODULE | TRACE_FLOW | TRACE_PWCX;
+ int pwc_mbufs = 2; /* Default number of mmap() buffers */
+#if CONFIG_PWC_DEBUG
+ int pwc_trace = PWC_DEBUG_LEVEL;
+#endif
static int power_save = 0;
static int led_on = 100, led_off = 0; /* defaults to LED that is on while in use */
-static int pwc_preferred_compression = 2; /* 0..3 = uncompressed..high */
+ int pwc_preferred_compression = 1; /* 0..3 = uncompressed..high */
static struct {
int type;
char serial_number[30];
@@ -138,7 +145,7 @@ static struct {
static int pwc_video_open(struct inode *inode, struct file *file);
static int pwc_video_close(struct inode *inode, struct file *file);
-static ssize_t pwc_video_read(struct file *file, char __user * buf,
+static ssize_t pwc_video_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos);
static unsigned int pwc_video_poll(struct file *file, poll_table *wait);
static int pwc_video_ioctl(struct inode *inode, struct file *file,
@@ -153,7 +160,6 @@ static struct file_operations pwc_fops = {
.poll = pwc_video_poll,
.mmap = pwc_video_mmap,
.ioctl = pwc_video_ioctl,
- .compat_ioctl = v4l_compat_ioctl32,
.llseek = no_llseek,
};
static struct video_device pwc_template = {
@@ -203,52 +209,44 @@ static struct video_device pwc_template = {
/* Here we want the physical address of the memory.
* This is used when initializing the contents of the area.
*/
-static inline unsigned long kvirt_to_pa(unsigned long adr)
-{
- unsigned long kva, ret;
- kva = (unsigned long) page_address(vmalloc_to_page((void *)adr));
- kva |= adr & (PAGE_SIZE-1); /* restore the offset */
- ret = __pa(kva);
- return ret;
-}
-static void * rvmalloc(unsigned long size)
+
+static void *pwc_rvmalloc(unsigned long size)
{
void * mem;
unsigned long adr;
- size=PAGE_ALIGN(size);
mem=vmalloc_32(size);
- if (mem)
- {
- 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;
- }
- }
+ 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 rvfree(void * mem, unsigned long size)
+static void pwc_rvfree(void * mem, unsigned long size)
{
unsigned long adr;
- if (mem)
- {
- adr=(unsigned long) mem;
- while ((long) size > 0)
- {
- ClearPageReserved(vmalloc_to_page((void *)adr));
- adr+=PAGE_SIZE;
- size-=PAGE_SIZE;
- }
- vfree(mem);
- }
+ 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);
}
@@ -256,100 +254,83 @@ static void rvfree(void * mem, unsigned long size)
static int pwc_allocate_buffers(struct pwc_device *pdev)
{
- int i;
+ int i, err;
void *kbuf;
- Trace(TRACE_MEMORY, ">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev);
+ PWC_DEBUG_MEMORY(">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev);
if (pdev == NULL)
return -ENXIO;
-#ifdef PWC_MAGIC
- if (pdev->magic != PWC_MAGIC) {
- Err("allocate_buffers(): magic failed.\n");
- return -ENXIO;
- }
-#endif
- /* Allocate Isochronous pipe buffers */
+ /* Allocate Isochronuous pipe buffers */
for (i = 0; i < MAX_ISO_BUFS; i++) {
if (pdev->sbuf[i].data == NULL) {
- kbuf = kmalloc(ISO_BUFFER_SIZE, GFP_KERNEL);
+ kbuf = kzalloc(ISO_BUFFER_SIZE, GFP_KERNEL);
if (kbuf == NULL) {
- Err("Failed to allocate iso buffer %d.\n", i);
+ PWC_ERROR("Failed to allocate iso buffer %d.\n", i);
return -ENOMEM;
}
- Trace(TRACE_MEMORY, "Allocated iso buffer at %p.\n", kbuf);
+ PWC_DEBUG_MEMORY("Allocated iso buffer at %p.\n", kbuf);
pdev->sbuf[i].data = kbuf;
- memset(kbuf, 0, ISO_BUFFER_SIZE);
}
}
/* Allocate frame buffer structure */
if (pdev->fbuf == NULL) {
- kbuf = kmalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL);
+ kbuf = kzalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL);
if (kbuf == NULL) {
- Err("Failed to allocate frame buffer structure.\n");
+ PWC_ERROR("Failed to allocate frame buffer structure.\n");
return -ENOMEM;
}
- Trace(TRACE_MEMORY, "Allocated frame buffer structure at %p.\n", kbuf);
+ PWC_DEBUG_MEMORY("Allocated frame buffer structure at %p.\n", kbuf);
pdev->fbuf = kbuf;
- memset(kbuf, 0, default_fbufs * sizeof(struct pwc_frame_buf));
}
+
/* create frame buffers, and make circular ring */
for (i = 0; i < default_fbufs; i++) {
if (pdev->fbuf[i].data == NULL) {
kbuf = vmalloc(PWC_FRAME_SIZE); /* need vmalloc since frame buffer > 128K */
if (kbuf == NULL) {
- Err("Failed to allocate frame buffer %d.\n", i);
+ PWC_ERROR("Failed to allocate frame buffer %d.\n", i);
return -ENOMEM;
}
- Trace(TRACE_MEMORY, "Allocated frame buffer %d at %p.\n", i, kbuf);
+ PWC_DEBUG_MEMORY("Allocated frame buffer %d at %p.\n", i, kbuf);
pdev->fbuf[i].data = kbuf;
- memset(kbuf, 128, PWC_FRAME_SIZE);
+ memset(kbuf, 0, PWC_FRAME_SIZE);
}
}
/* Allocate decompressor table space */
- kbuf = NULL;
- switch (pdev->type)
- {
- case 675:
- case 680:
- case 690:
- case 720:
- case 730:
- case 740:
- case 750:
-#if 0
- Trace(TRACE_MEMORY,"private_data(%zu)\n",sizeof(struct pwc_dec23_private));
- kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL); /* Timon & Kiara */
- break;
- case 645:
- case 646:
- /* TODO & FIXME */
- kbuf = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL);
- break;
-#endif
- ;
- }
- pdev->decompress_data = kbuf;
+ if (DEVICE_USE_CODEC1(pdev->type))
+ err = pwc_dec1_alloc(pdev);
+ else
+ err = pwc_dec23_alloc(pdev);
+
+ if (err) {
+ PWC_ERROR("Failed to allocate decompress table.\n");
+ return err;
+ }
/* Allocate image buffer; double buffer for mmap() */
- kbuf = rvmalloc(default_mbufs * pdev->len_per_image);
+ kbuf = pwc_rvmalloc(pwc_mbufs * pdev->len_per_image);
if (kbuf == NULL) {
- Err("Failed to allocate image buffer(s). needed (%d)\n",default_mbufs * pdev->len_per_image);
+ PWC_ERROR("Failed to allocate image buffer(s). needed (%d)\n",
+ pwc_mbufs * pdev->len_per_image);
return -ENOMEM;
}
- Trace(TRACE_MEMORY, "Allocated image buffer at %p.\n", kbuf);
+ PWC_DEBUG_MEMORY("Allocated image buffer at %p.\n", kbuf);
pdev->image_data = kbuf;
- for (i = 0; i < default_mbufs; i++)
- pdev->image_ptr[i] = kbuf + i * pdev->len_per_image;
- for (; i < MAX_IMAGES; i++)
- pdev->image_ptr[i] = NULL;
+ 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;
- Trace(TRACE_MEMORY, "<< pwc_allocate_buffers()\n");
+ PWC_DEBUG_MEMORY("<< pwc_allocate_buffers()\n");
return 0;
}
@@ -357,21 +338,14 @@ static void pwc_free_buffers(struct pwc_device *pdev)
{
int i;
- Trace(TRACE_MEMORY, "Entering free_buffers(%p).\n", pdev);
+ PWC_DEBUG_MEMORY("Entering free_buffers(%p).\n", pdev);
if (pdev == NULL)
return;
-#ifdef PWC_MAGIC
- if (pdev->magic != PWC_MAGIC) {
- Err("free_buffers(): magic failed.\n");
- return;
- }
-#endif
-
/* Release Iso-pipe buffers */
for (i = 0; i < MAX_ISO_BUFS; i++)
if (pdev->sbuf[i].data != NULL) {
- Trace(TRACE_MEMORY, "Freeing ISO buffer at %p.\n", pdev->sbuf[i].data);
+ PWC_DEBUG_MEMORY("Freeing ISO buffer at %p.\n", pdev->sbuf[i].data);
kfree(pdev->sbuf[i].data);
pdev->sbuf[i].data = NULL;
}
@@ -380,7 +354,7 @@ static void pwc_free_buffers(struct pwc_device *pdev)
if (pdev->fbuf != NULL) {
for (i = 0; i < default_fbufs; i++) {
if (pdev->fbuf[i].data != NULL) {
- Trace(TRACE_MEMORY, "Freeing frame buffer %d at %p.\n", i, pdev->fbuf[i].data);
+ 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;
}
@@ -391,20 +365,19 @@ static void pwc_free_buffers(struct pwc_device *pdev)
/* Intermediate decompression buffer & tables */
if (pdev->decompress_data != NULL) {
- Trace(TRACE_MEMORY, "Freeing decompression buffer at %p.\n", pdev->decompress_data);
+ PWC_DEBUG_MEMORY("Freeing decompression buffer at %p.\n", pdev->decompress_data);
kfree(pdev->decompress_data);
pdev->decompress_data = NULL;
}
- pdev->decompressor = NULL;
/* Release image buffers */
if (pdev->image_data != NULL) {
- Trace(TRACE_MEMORY, "Freeing image buffer at %p.\n", pdev->image_data);
- rvfree(pdev->image_data, default_mbufs * pdev->len_per_image);
+ 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;
- Trace(TRACE_MEMORY, "Leaving free_buffers().\n");
+ PWC_DEBUG_MEMORY("Leaving free_buffers().\n");
}
/* The frame & image buffer mess.
@@ -464,7 +437,7 @@ static void pwc_free_buffers(struct pwc_device *pdev)
/**
\brief Find next frame buffer to fill. Take from empty or full list, whichever comes first.
*/
-static inline int pwc_next_fill_frame(struct pwc_device *pdev)
+static int pwc_next_fill_frame(struct pwc_device *pdev)
{
int ret;
unsigned long flags;
@@ -489,23 +462,17 @@ static inline int pwc_next_fill_frame(struct pwc_device *pdev)
}
else {
/* Hmm. Take it from the full list */
-#if PWC_DEBUG
/* sanity check */
if (pdev->full_frames == NULL) {
- Err("Neither empty or full frames available!\n");
+ PWC_ERROR("Neither empty or full frames available!\n");
spin_unlock_irqrestore(&pdev->ptrlock, flags);
return -EINVAL;
}
-#endif
pdev->fill_frame = pdev->full_frames;
pdev->full_frames = pdev->full_frames->next;
ret = 1;
}
pdev->fill_frame->next = NULL;
-#if PWC_DEBUG
- Trace(TRACE_SEQUENCE, "Assigning sequence number %d.\n", pdev->sequence);
- pdev->fill_frame->sequence = pdev->sequence++;
-#endif
spin_unlock_irqrestore(&pdev->ptrlock, flags);
return ret;
}
@@ -521,6 +488,8 @@ static void pwc_reset_buffers(struct pwc_device *pdev)
int i;
unsigned long flags;
+ PWC_DEBUG_MEMORY(">> %s __enter__\n", __FUNCTION__);
+
spin_lock_irqsave(&pdev->ptrlock, flags);
pdev->full_frames = NULL;
pdev->full_frames_tail = NULL;
@@ -540,13 +509,15 @@ static void pwc_reset_buffers(struct pwc_device *pdev)
pdev->image_read_pos = 0;
pdev->fill_image = 0;
spin_unlock_irqrestore(&pdev->ptrlock, flags);
+
+ PWC_DEBUG_MEMORY("<< %s __leaving__\n", __FUNCTION__);
}
/**
\brief Do all the handling for getting one frame: get pointer, decompress, advance pointers.
*/
-static int pwc_handle_frame(struct pwc_device *pdev)
+int pwc_handle_frame(struct pwc_device *pdev)
{
int ret = 0;
unsigned long flags;
@@ -556,41 +527,40 @@ static int pwc_handle_frame(struct pwc_device *pdev)
we can release the lock after this without problems */
if (pdev->read_frame != NULL) {
/* This can't theoretically happen */
- Err("Huh? Read frame still in use?\n");
+ 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 {
- if (pdev->full_frames == NULL) {
- Err("Woops. No frames ready.\n");
+ 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 lenghty 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->read_frame = pdev->full_frames;
- pdev->full_frames = pdev->full_frames->next;
- pdev->read_frame->next = NULL;
- }
-
- if (pdev->read_frame != NULL) {
-#if PWC_DEBUG
- Trace(TRACE_SEQUENCE, "Decompressing frame %d\n", pdev->read_frame->sequence);
-#endif
- /* Decompression is a lenghty 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;
+ 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;
@@ -599,12 +569,114 @@ static int pwc_handle_frame(struct pwc_device *pdev)
/**
\brief Advance pointers of image buffer (after each user request)
*/
-static inline void pwc_next_image(struct pwc_device *pdev)
+void pwc_next_image(struct pwc_device *pdev)
{
pdev->image_used[pdev->fill_image] = 0;
- pdev->fill_image = (pdev->fill_image + 1) % default_mbufs;
+ pdev->fill_image = (pdev->fill_image + 1) % pwc_mbufs;
}
+/**
+ * Print debug information when a frame is discarded because all of our buffer
+ * is full
+ */
+static void pwc_frame_dumped(struct pwc_device *pdev)
+{
+ pdev->vframes_dumped++;
+ if (pdev->vframe_count < FRAME_LOWMARK)
+ return;
+
+ if (pdev->vframes_dumped < 20)
+ PWC_DEBUG_FLOW("Dumping frame %d\n", pdev->vframe_count);
+ else if (pdev->vframes_dumped == 20)
+ PWC_DEBUG_FLOW("Dumping frame %d (last message)\n",
+ pdev->vframe_count);
+}
+
+static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_buf *fbuf)
+{
+ int awake = 0;
+
+ /* 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;
+ pdev->vframes_error++;
+ }
+ if ((ptr[0] ^ pdev->vmirror) & 0x01) {
+ if (ptr[0] & 0x01) {
+ pdev->snapshot_button_status = 1;
+ PWC_TRACE("Snapshot button pressed.\n");
+ }
+ else {
+ PWC_TRACE("Snapshot button released.\n");
+ }
+ }
+ 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++;
+ }
+ else if (pdev->type == 740 || pdev->type == 720) {
+ unsigned char *ptr = (unsigned char *)fbuf->data;
+ if ((ptr[0] ^ pdev->vmirror) & 0x01) {
+ if (ptr[0] & 0x01) {
+ pdev->snapshot_button_status = 1;
+ PWC_TRACE("Snapshot button pressed.\n");
+ }
+ else
+ PWC_TRACE("Snapshot button released.\n");
+ }
+ 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)
+ pdev->drop_frames--;
+ 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);
+
+ }
+ } /* !drop_frames */
+ pdev->vframe_count++;
+ return awake;
+}
/* 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.
@@ -620,17 +692,12 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
awake = 0;
pdev = (struct pwc_device *)urb->context;
if (pdev == NULL) {
- Err("isoc_handler() called with NULL device?!\n");
+ PWC_ERROR("isoc_handler() called with NULL device?!\n");
return;
}
-#ifdef PWC_MAGIC
- if (pdev->magic != PWC_MAGIC) {
- Err("isoc_handler() called with bad magic!\n");
- return;
- }
-#endif
+
if (urb->status == -ENOENT || urb->status == -ECONNRESET) {
- Trace(TRACE_OPEN, "pwc_isoc_handler(): URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a");
+ PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a");
return;
}
if (urb->status != -EINPROGRESS && urb->status != 0) {
@@ -645,13 +712,13 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break;
case -ETIMEDOUT: errmsg = "NAK (device does not respond)"; break;
}
- Trace(TRACE_FLOW, "pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg);
+ 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.
*/
if (++pdev->visoc_errors > MAX_ISOC_ERRORS)
{
- Info("Too many ISOC errors, bailing out.\n");
+ PWC_INFO("Too many ISOC errors, bailing out.\n");
pdev->error_status = EIO;
awake = 1;
wake_up_interruptible(&pdev->frameq);
@@ -661,7 +728,7 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
fbuf = pdev->fill_frame;
if (fbuf == NULL) {
- Err("pwc_isoc_handler without valid fill frame.\n");
+ PWC_ERROR("pwc_isoc_handler without valid fill frame.\n");
awake = 1;
goto handler_end;
}
@@ -688,7 +755,7 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
/* ...copy data to frame buffer, if possible */
if (flen + fbuf->filled > pdev->frame_total_size) {
- Trace(TRACE_FLOW, "Frame buffer overflow (flen = %d, frame_total_size = %d).\n", flen, 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++;
}
@@ -704,96 +771,28 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
/* 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) {
- /* 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) {
-#if PWC_DEBUG
- Debug("Hyundai CMOS sensor bug. Dropping frame %d.\n", fbuf->sequence);
-#endif
- pdev->drop_frames += 2;
- pdev->vframes_error++;
- }
- if ((ptr[0] ^ pdev->vmirror) & 0x01) {
- if (ptr[0] & 0x01)
- Info("Snapshot button pressed.\n");
- else
- Info("Snapshot button released.\n");
- }
- if ((ptr[0] ^ pdev->vmirror) & 0x02) {
- if (ptr[0] & 0x02)
- Info("Image is mirrored.\n");
- else
- Info("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++;
+ if (pwc_rcv_short_packet(pdev, fbuf)) {
+ awake = 1;
+ fbuf = pdev->fill_frame;
}
-
- /* 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)
- pdev->drop_frames--;
- else {
- /* Check for underflow first */
- if (fbuf->filled < pdev->frame_total_size) {
- Trace(TRACE_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)) {
- pdev->vframes_dumped++;
- if ((pdev->vframe_count > FRAME_LOWMARK) && (pwc_trace & TRACE_FLOW)) {
- if (pdev->vframes_dumped < 20)
- Trace(TRACE_FLOW, "Dumping frame %d.\n", pdev->vframe_count);
- if (pdev->vframes_dumped == 20)
- Trace(TRACE_FLOW, "Dumping frame %d (last message).\n", pdev->vframe_count);
- }
- }
- fbuf = pdev->fill_frame;
- }
- } /* !drop_frames */
- pdev->vframe_count++;
}
fbuf->filled = 0;
fillptr = fbuf->data;
pdev->vsync = 1;
- } /* .. flen < last_packet_size */
+ }
+
pdev->vlast_packet_size = flen;
} /* ..status == 0 */
-#if PWC_DEBUG
- /* This is normally not interesting to the user, unless you are really debugging something */
else {
+ /* This is normally not interesting to the user, unless
+ * you are really debugging something */
static int iso_error = 0;
iso_error++;
if (iso_error < 20)
- Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n", i, fst);
+ PWC_DEBUG_FLOW("Iso frame %d of USB has error %d\n", i, fst);
}
-#endif
}
handler_end:
@@ -803,11 +802,11 @@ handler_end:
urb->dev = pdev->udev;
i = usb_submit_urb(urb, GFP_ATOMIC);
if (i != 0)
- Err("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
+ PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
}
-static int pwc_isoc_init(struct pwc_device *pdev)
+int pwc_isoc_init(struct pwc_device *pdev)
{
struct usb_device *udev;
struct urb *urb;
@@ -826,7 +825,6 @@ static int pwc_isoc_init(struct pwc_device *pdev)
/* 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);
@@ -836,20 +834,21 @@ static int pwc_isoc_init(struct pwc_device *pdev)
/* Search video endpoint */
pdev->vmax_packet_size = -1;
- for (i = 0; i < idesc->desc.bNumEndpoints; i++)
+ for (i = 0; i < idesc->desc.bNumEndpoints; i++) {
if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) {
pdev->vmax_packet_size = le16_to_cpu(idesc->endpoint[i].desc.wMaxPacketSize);
break;
}
+ }
if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) {
- Err("Failed to find packet size for video endpoint in current alternate setting.\n");
+ PWC_ERROR("Failed to find packet size for video endpoint in current alternate setting.\n");
return -ENFILE; /* Odd error, that should be noticeable */
}
/* Set alternate interface */
ret = 0;
- Trace(TRACE_OPEN, "Setting alternate interface %d\n", pdev->valternate);
+ PWC_DEBUG_OPEN("Setting alternate interface %d\n", pdev->valternate);
ret = usb_set_interface(pdev->udev, 0, pdev->valternate);
if (ret < 0)
return ret;
@@ -857,12 +856,12 @@ static int pwc_isoc_init(struct pwc_device *pdev)
for (i = 0; i < MAX_ISO_BUFS; i++) {
urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
if (urb == NULL) {
- Err("Failed to allocate urb %d\n", i);
+ PWC_ERROR("Failed to allocate urb %d\n", i);
ret = -ENOMEM;
break;
}
pdev->sbuf[i].urb = urb;
- Trace(TRACE_MEMORY, "Allocated URB at 0x%p\n", urb);
+ PWC_DEBUG_MEMORY("Allocated URB at 0x%p\n", urb);
}
if (ret) {
/* De-allocate in reverse order */
@@ -899,24 +898,26 @@ static int pwc_isoc_init(struct pwc_device *pdev)
for (i = 0; i < MAX_ISO_BUFS; i++) {
ret = usb_submit_urb(pdev->sbuf[i].urb, GFP_KERNEL);
if (ret)
- Err("isoc_init() submit_urb %d failed with error %d\n", i, ret);
+ PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret);
else
- Trace(TRACE_MEMORY, "URB 0x%p submitted.\n", pdev->sbuf[i].urb);
+ PWC_DEBUG_MEMORY("URB 0x%p submitted.\n", pdev->sbuf[i].urb);
}
/* All is done... */
pdev->iso_init = 1;
- Trace(TRACE_OPEN, "<< pwc_isoc_init()\n");
+ PWC_DEBUG_OPEN("<< pwc_isoc_init()\n");
return 0;
}
-static void pwc_isoc_cleanup(struct pwc_device *pdev)
+void pwc_isoc_cleanup(struct pwc_device *pdev)
{
int i;
- Trace(TRACE_OPEN, ">> pwc_isoc_cleanup()\n");
+ PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n");
if (pdev == NULL)
return;
+ if (pdev->iso_init == 0)
+ return;
/* Unlinking ISOC buffers one by one */
for (i = 0; i < MAX_ISO_BUFS; i++) {
@@ -925,10 +926,10 @@ static void pwc_isoc_cleanup(struct pwc_device *pdev)
urb = pdev->sbuf[i].urb;
if (urb != 0) {
if (pdev->iso_init) {
- Trace(TRACE_MEMORY, "Unlinking URB %p\n", urb);
+ PWC_DEBUG_MEMORY("Unlinking URB %p\n", urb);
usb_kill_urb(urb);
}
- Trace(TRACE_MEMORY, "Freeing URB\n");
+ PWC_DEBUG_MEMORY("Freeing URB\n");
usb_free_urb(urb);
pdev->sbuf[i].urb = NULL;
}
@@ -938,12 +939,12 @@ static void pwc_isoc_cleanup(struct pwc_device *pdev)
is signalled by EPIPE)
*/
if (pdev->error_status && pdev->error_status != EPIPE) {
- Trace(TRACE_OPEN, "Setting alternate interface 0.\n");
+ PWC_DEBUG_OPEN("Setting alternate interface 0.\n");
usb_set_interface(pdev->udev, 0, 0);
}
pdev->iso_init = 0;
- Trace(TRACE_OPEN, "<< pwc_isoc_cleanup()\n");
+ PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n");
}
int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot)
@@ -957,18 +958,18 @@ int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_f
/* Try to set video mode... */
start = ret = pwc_set_video_mode(pdev, width, height, new_fps, new_compression, new_snapshot);
if (ret) {
- Trace(TRACE_FLOW, "pwc_set_video_mode attempt 1 failed.\n");
+ PWC_DEBUG_FLOW("pwc_set_video_mode attempt 1 failed.\n");
/* That failed... restore old mode (we know that worked) */
start = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
if (start) {
- Trace(TRACE_FLOW, "pwc_set_video_mode attempt 2 failed.\n");
+ PWC_DEBUG_FLOW("pwc_set_video_mode attempt 2 failed.\n");
}
}
if (start == 0)
{
if (pwc_isoc_init(pdev) < 0)
{
- Info("Failed to restart ISOC transfers in pwc_try_video_mode.\n");
+ PWC_WARNING("Failed to restart ISOC transfers in pwc_try_video_mode.\n");
ret = -EAGAIN; /* let's try again, who knows if it works a second time */
}
}
@@ -976,54 +977,129 @@ int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_f
return ret; /* Return original error code */
}
+/*********
+ * sysfs
+ *********/
+static struct pwc_device *cd_to_pwc(struct class_device *cd)
+{
+ struct video_device *vdev = to_video_device(cd);
+ return video_get_drvdata(vdev);
+}
+
+static ssize_t show_pan_tilt(struct class_device *class_dev, char *buf)
+{
+ struct pwc_device *pdev = cd_to_pwc(class_dev);
+ return sprintf(buf, "%d %d\n", pdev->pan_angle, pdev->tilt_angle);
+}
+
+static ssize_t store_pan_tilt(struct class_device *class_dev, const char *buf,
+ size_t count)
+{
+ struct pwc_device *pdev = cd_to_pwc(class_dev);
+ int pan, tilt;
+ int ret = -EINVAL;
+
+ if (strncmp(buf, "reset", 5) == 0)
+ ret = pwc_mpt_reset(pdev, 0x3);
+
+ else if (sscanf(buf, "%d %d", &pan, &tilt) > 0)
+ ret = pwc_mpt_set_angle(pdev, pan, tilt);
+
+ if (ret < 0)
+ return ret;
+ return strlen(buf);
+}
+static CLASS_DEVICE_ATTR(pan_tilt, S_IRUGO | S_IWUSR, show_pan_tilt,
+ store_pan_tilt);
+
+static ssize_t show_snapshot_button_status(struct class_device *class_dev, char *buf)
+{
+ struct pwc_device *pdev = cd_to_pwc(class_dev);
+ int status = pdev->snapshot_button_status;
+ pdev->snapshot_button_status = 0;
+ return sprintf(buf, "%d\n", status);
+}
+
+static CLASS_DEVICE_ATTR(button, S_IRUGO | S_IWUSR, show_snapshot_button_status,
+ NULL);
+
+static void pwc_create_sysfs_files(struct video_device *vdev)
+{
+ struct pwc_device *pdev = video_get_drvdata(vdev);
+ if (pdev->features & FEATURE_MOTOR_PANTILT)
+ video_device_create_file(vdev, &class_device_attr_pan_tilt);
+ video_device_create_file(vdev, &class_device_attr_button);
+}
+
+static void pwc_remove_sysfs_files(struct video_device *vdev)
+{
+ struct pwc_device *pdev = video_get_drvdata(vdev);
+ if (pdev->features & FEATURE_MOTOR_PANTILT)
+ video_device_remove_file(vdev, &class_device_attr_pan_tilt);
+ video_device_remove_file(vdev, &class_device_attr_button);
+}
+
+#if CONFIG_PWC_DEBUG
+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:
+ return "unknown type of sensor";
+ }
+}
+#endif
/***************************************************************************/
/* Video4Linux functions */
static int pwc_video_open(struct inode *inode, struct file *file)
{
- int i;
+ int i, ret;
struct video_device *vdev = video_devdata(file);
struct pwc_device *pdev;
- Trace(TRACE_OPEN, ">> video_open called(vdev = 0x%p).\n", vdev);
+ PWC_DEBUG_OPEN(">> video_open called(vdev = 0x%p).\n", vdev);
pdev = (struct pwc_device *)vdev->priv;
if (pdev == NULL)
BUG();
- if (pdev->vopen)
+ if (pdev->vopen) {
+ PWC_DEBUG_OPEN("I'm busy, someone is using the device.\n");
return -EBUSY;
+ }
down(&pdev->modlock);
if (!pdev->usb_init) {
- Trace(TRACE_OPEN, "Doing first time initialization.\n");
+ PWC_DEBUG_OPEN("Doing first time initialization.\n");
pdev->usb_init = 1;
- if (pwc_trace & TRACE_OPEN)
+ /* Query sensor type */
+ ret = pwc_get_cmos_sensor(pdev, &i);
+ if (ret >= 0)
{
- /* Query sensor type */
- const char *sensor_type = NULL;
- int ret;
-
- ret = pwc_get_cmos_sensor(pdev, &i);
- if (ret >= 0)
- {
- switch(i) {
- case 0x00: sensor_type = "Hyundai CMOS sensor"; break;
- case 0x20: sensor_type = "Sony CCD sensor + TDA8787"; break;
- case 0x2E: sensor_type = "Sony CCD sensor + Exas 98L59"; break;
- case 0x2F: sensor_type = "Sony CCD sensor + ADI 9804"; break;
- case 0x30: sensor_type = "Sharp CCD sensor + TDA8787"; break;
- case 0x3E: sensor_type = "Sharp CCD sensor + Exas 98L59"; break;
- case 0x3F: sensor_type = "Sharp CCD sensor + ADI 9804"; break;
- case 0x40: sensor_type = "UPA 1021 sensor"; break;
- case 0x100: sensor_type = "VGA sensor"; break;
- case 0x101: sensor_type = "PAL MR sensor"; break;
- default: sensor_type = "unknown type of sensor"; break;
- }
- }
- if (sensor_type != NULL)
- Info("This %s camera is equipped with a %s (%d).\n", pdev->vdev->name, sensor_type, i);
+ PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n",
+ pdev->vdev->name,
+ pwc_sensor_type_to_string(i), i);
}
}
@@ -1031,34 +1107,32 @@ static int pwc_video_open(struct inode *inode, struct file *file)
if (power_save) {
i = pwc_camera_power(pdev, 1);
if (i < 0)
- Info("Failed to restore power to the camera! (%d)\n", i);
+ PWC_DEBUG_OPEN("Failed to restore power to the camera! (%d)\n", i);
}
/* Set LED on/off time */
if (pwc_set_leds(pdev, led_on, led_off) < 0)
- Info("Failed to set LED on/off time.\n");
+ PWC_DEBUG_OPEN("Failed to set LED on/off time.\n");
pwc_construct(pdev); /* set min/max sizes correct */
/* So far, so good. Allocate memory. */
i = pwc_allocate_buffers(pdev);
if (i < 0) {
- Trace(TRACE_OPEN, "Failed to allocate buffer memory.\n");
+ PWC_DEBUG_OPEN("Failed to allocate buffers memory.\n");
+ pwc_free_buffers(pdev);
up(&pdev->modlock);
return i;
}
/* Reset buffers & parameters */
pwc_reset_buffers(pdev);
- for (i = 0; i < default_mbufs; i++)
+ 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;
-#if PWC_DEBUG
- pdev->sequence = 0;
-#endif
pwc_construct(pdev); /* set min/max sizes correct */
/* Set some defaults */
@@ -1070,29 +1144,44 @@ static int pwc_video_open(struct inode *inode, struct file *file)
*/
i = pwc_set_video_mode(pdev, pwc_image_sizes[pdev->vsize].x, pwc_image_sizes[pdev->vsize].y, pdev->vframes, pdev->vcompression, 0);
if (i) {
- Trace(TRACE_OPEN, "First attempt at set_video_mode failed.\n");
- if (pdev->type == 730 || pdev->type == 740 || pdev->type == 750)
- i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QSIF].x, pwc_image_sizes[PSZ_QSIF].y, 10, pdev->vcompression, 0);
+ unsigned int default_resolution;
+ PWC_DEBUG_OPEN("First attempt at set_video_mode failed.\n");
+ if (pdev->type>= 730)
+ default_resolution = PSZ_QSIF;
else
- i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QCIF].x, pwc_image_sizes[PSZ_QCIF].y, 10, pdev->vcompression, 0);
+ default_resolution = PSZ_QCIF;
+
+ i = pwc_set_video_mode(pdev,
+ pwc_image_sizes[default_resolution].x,
+ pwc_image_sizes[default_resolution].y,
+ 10,
+ pdev->vcompression,
+ 0);
}
if (i) {
- Trace(TRACE_OPEN, "Second attempt at set_video_mode failed.\n");
+ PWC_DEBUG_OPEN("Second attempt at set_video_mode failed.\n");
+ pwc_free_buffers(pdev);
up(&pdev->modlock);
return i;
}
i = pwc_isoc_init(pdev);
if (i) {
- Trace(TRACE_OPEN, "Failed to init ISOC stuff = %d.\n", i);
+ PWC_DEBUG_OPEN("Failed to init ISOC stuff = %d.\n", i);
+ pwc_isoc_cleanup(pdev);
+ pwc_free_buffers(pdev);
up(&pdev->modlock);
return i;
}
+ /* Initialize the webcam to sane value */
+ pwc_set_brightness(pdev, 0x7fff);
+ pwc_set_agc(pdev, 1, 0);
+
pdev->vopen++;
file->private_data = vdev;
up(&pdev->modlock);
- Trace(TRACE_OPEN, "<< video_open() returns 0.\n");
+ PWC_DEBUG_OPEN("<< video_open() returns 0.\n");
return 0;
}
@@ -1103,35 +1192,23 @@ static int pwc_video_close(struct inode *inode, struct file *file)
struct pwc_device *pdev;
int i;
- Trace(TRACE_OPEN, ">> video_close called(vdev = 0x%p).\n", vdev);
+ PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev);
pdev = (struct pwc_device *)vdev->priv;
if (pdev->vopen == 0)
- Info("video_close() called on closed device?\n");
+ PWC_DEBUG_MODULE("video_close() called on closed device?\n");
/* Dump statistics, but only if a reasonable amount of frames were
processed (to prevent endless log-entries in case of snap-shot
programs)
*/
if (pdev->vframe_count > 20)
- Info("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error);
+ 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);
- switch (pdev->type)
- {
- case 675:
- case 680:
- case 690:
- case 720:
- case 730:
- case 740:
- case 750:
-/* pwc_dec23_exit(); *//* Timon & Kiara */
- break;
- case 645:
- case 646:
-/* pwc_dec1_exit(); */
- break;
- }
+ if (DEVICE_USE_CODEC1(pdev->type))
+ pwc_dec1_exit();
+ else
+ pwc_dec23_exit();
pwc_isoc_cleanup(pdev);
pwc_free_buffers(pdev);
@@ -1140,15 +1217,15 @@ static int pwc_video_close(struct inode *inode, struct file *file)
if (pdev->error_status != EPIPE) {
/* Turn LEDs off */
if (pwc_set_leds(pdev, 0, 0) < 0)
- Info("Failed to set LED on/off time.\n");
+ PWC_DEBUG_MODULE("Failed to set LED on/off time.\n");
if (power_save) {
i = pwc_camera_power(pdev, 0);
if (i < 0)
- Err("Failed to power down camera (%d)\n", i);
+ PWC_ERROR("Failed to power down camera (%d)\n", i);
}
}
- pdev->vopen = 0;
- Trace(TRACE_OPEN, "<< video_close()\n");
+ pdev->vopen--;
+ PWC_DEBUG_OPEN("<< video_close() vopen=%d\n", pdev->vopen);
return 0;
}
@@ -1164,7 +1241,7 @@ static int pwc_video_close(struct inode *inode, struct file *file)
device is tricky anyhow.
*/
-static ssize_t pwc_video_read(struct file *file, char __user * buf,
+static ssize_t pwc_video_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
struct video_device *vdev = file->private_data;
@@ -1172,8 +1249,10 @@ static ssize_t pwc_video_read(struct file *file, char __user * buf,
int noblock = file->f_flags & O_NONBLOCK;
DECLARE_WAITQUEUE(wait, current);
int bytes_to_read;
+ void *image_buffer_addr;
- Trace(TRACE_READ, "video_read(0x%p, %p, %zu) called.\n", vdev, buf, count);
+ PWC_DEBUG_READ("pwc_video_read(vdev=0x%p, buf=%p, count=%zd) called.\n",
+ vdev, buf, count);
if (vdev == NULL)
return -EFAULT;
pdev = vdev->priv;
@@ -1214,16 +1293,19 @@ static ssize_t pwc_video_read(struct file *file, char __user * buf,
return -EFAULT;
}
- Trace(TRACE_READ, "Copying data to user space.\n");
+ PWC_DEBUG_READ("Copying data to user space.\n");
if (pdev->vpalette == VIDEO_PALETTE_RAW)
- bytes_to_read = pdev->frame_size;
+ 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;
- if (copy_to_user(buf, pdev->image_ptr[pdev->fill_image] + pdev->image_read_pos, count))
+ 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))
return -EFAULT;
pdev->image_read_pos += count;
if (pdev->image_read_pos >= bytes_to_read) { /* All data has been read */
@@ -1253,370 +1335,56 @@ static unsigned int pwc_video_poll(struct file *file, poll_table *wait)
return 0;
}
-static int pwc_video_do_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg)
-{
- struct video_device *vdev = file->private_data;
- struct pwc_device *pdev;
- DECLARE_WAITQUEUE(wait, current);
-
- if (vdev == NULL)
- return -EFAULT;
- pdev = vdev->priv;
- if (pdev == NULL)
- return -EFAULT;
-
- switch (cmd) {
- /* Query cabapilities */
- case VIDIOCGCAP:
- {
- struct video_capability *caps = arg;
-
- strcpy(caps->name, vdev->name);
- caps->type = VID_TYPE_CAPTURE;
- caps->channels = 1;
- caps->audios = 1;
- caps->minwidth = pdev->view_min.x;
- caps->minheight = pdev->view_min.y;
- caps->maxwidth = pdev->view_max.x;
- caps->maxheight = pdev->view_max.y;
- break;
- }
-
- /* Channel functions (simulate 1 channel) */
- case VIDIOCGCHAN:
- {
- struct video_channel *v = arg;
-
- if (v->channel != 0)
- return -EINVAL;
- v->flags = 0;
- v->tuners = 0;
- v->type = VIDEO_TYPE_CAMERA;
- strcpy(v->name, "Webcam");
- return 0;
- }
-
- case VIDIOCSCHAN:
- {
- /* The spec says the argument is an integer, but
- the bttv driver uses a video_channel arg, which
- makes sense becasue it also has the norm flag.
- */
- struct video_channel *v = arg;
- if (v->channel != 0)
- return -EINVAL;
- return 0;
- }
-
-
- /* Picture functions; contrast etc. */
- case VIDIOCGPICT:
- {
- struct video_picture *p = arg;
- int val;
-
- val = pwc_get_brightness(pdev);
- if (val >= 0)
- p->brightness = val;
- else
- p->brightness = 0xffff;
- val = pwc_get_contrast(pdev);
- if (val >= 0)
- p->contrast = val;
- else
- p->contrast = 0xffff;
- /* Gamma, Whiteness, what's the difference? :) */
- val = pwc_get_gamma(pdev);
- if (val >= 0)
- p->whiteness = val;
- else
- p->whiteness = 0xffff;
- val = pwc_get_saturation(pdev);
- if (val >= 0)
- p->colour = val;
- else
- p->colour = 0xffff;
- p->depth = 24;
- p->palette = pdev->vpalette;
- p->hue = 0xFFFF; /* N/A */
- break;
- }
-
- case VIDIOCSPICT:
- {
- struct video_picture *p = arg;
- /*
- * FIXME: Suppose we are mid read
- ANSWER: No problem: the firmware of the camera
- can handle brightness/contrast/etc
- changes at _any_ time, and the palette
- is used exactly once in the uncompress
- routine.
- */
- pwc_set_brightness(pdev, p->brightness);
- pwc_set_contrast(pdev, p->contrast);
- pwc_set_gamma(pdev, p->whiteness);
- pwc_set_saturation(pdev, p->colour);
- if (p->palette && p->palette != pdev->vpalette) {
- switch (p->palette) {
- case VIDEO_PALETTE_YUV420P:
- case VIDEO_PALETTE_RAW:
- pdev->vpalette = p->palette;
- return pwc_try_video_mode(pdev, pdev->image.x, pdev->image.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
- break;
- default:
- return -EINVAL;
- break;
- }
- }
- break;
- }
-
- /* Window/size parameters */
- case VIDIOCGWIN:
- {
- struct video_window *vw = arg;
-
- vw->x = 0;
- vw->y = 0;
- vw->width = pdev->view.x;
- vw->height = pdev->view.y;
- vw->chromakey = 0;
- vw->flags = (pdev->vframes << PWC_FPS_SHIFT) |
- (pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0);
- break;
- }
-
- case VIDIOCSWIN:
- {
- struct video_window *vw = arg;
- int fps, snapshot, ret;
-
- fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT;
- snapshot = vw->flags & PWC_FPS_SNAPSHOT;
- if (fps == 0)
- fps = pdev->vframes;
- if (pdev->view.x == vw->width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot)
- return 0;
- ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot);
- if (ret)
- return ret;
- break;
- }
-
- /* We don't have overlay support (yet) */
- case VIDIOCGFBUF:
- {
- struct video_buffer *vb = arg;
-
- memset(vb,0,sizeof(*vb));
- break;
- }
-
- /* mmap() functions */
- case VIDIOCGMBUF:
- {
- /* Tell the user program how much memory is needed for a mmap() */
- struct video_mbuf *vm = arg;
- int i;
-
- memset(vm, 0, sizeof(*vm));
- vm->size = default_mbufs * pdev->len_per_image;
- vm->frames = default_mbufs; /* double buffering should be enough for most applications */
- for (i = 0; i < default_mbufs; i++)
- vm->offsets[i] = i * pdev->len_per_image;
- break;
- }
-
- case VIDIOCMCAPTURE:
- {
- /* Start capture into a given image buffer (called 'frame' in video_mmap structure) */
- struct video_mmap *vm = arg;
-
- Trace(TRACE_READ, "VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format);
- if (vm->frame < 0 || vm->frame >= default_mbufs)
- return -EINVAL;
-
- /* xawtv is nasty. It probes the available palettes
- by setting a very small image size and trying
- various palettes... The driver doesn't support
- such small images, so I'm working around it.
- */
- if (vm->format)
- {
- switch (vm->format)
- {
- case VIDEO_PALETTE_YUV420P:
- case VIDEO_PALETTE_RAW:
- break;
- default:
- return -EINVAL;
- break;
- }
- }
-
- if ((vm->width != pdev->view.x || vm->height != pdev->view.y) &&
- (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) {
- int ret;
-
- Trace(TRACE_OPEN, "VIDIOCMCAPTURE: changing size to please xawtv :-(.\n");
- ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
- if (ret)
- return ret;
- } /* ... size mismatch */
-
- /* FIXME: should we lock here? */
- if (pdev->image_used[vm->frame])
- return -EBUSY; /* buffer wasn't available. Bummer */
- pdev->image_used[vm->frame] = 1;
-
- /* Okay, we're done here. In the SYNC call we wait until a
- frame comes available, then expand image into the given
- buffer.
- In contrast to the CPiA cam the Philips cams deliver a
- constant stream, almost like a grabber card. Also,
- we have separate buffers for the rawdata and the image,
- meaning we can nearly always expand into the requested buffer.
- */
- Trace(TRACE_READ, "VIDIOCMCAPTURE done.\n");
- break;
- }
-
- case VIDIOCSYNC:
- {
- /* The doc says: "Whenever a buffer is used it should
- call VIDIOCSYNC to free this frame up and continue."
-
- The only odd thing about this whole procedure is
- that MCAPTURE flags the buffer as "in use", and
- SYNC immediately unmarks it, while it isn't
- after SYNC that you know that the buffer actually
- got filled! So you better not start a CAPTURE in
- the same frame immediately (use double buffering).
- This is not a problem for this cam, since it has
- extra intermediate buffers, but a hardware
- grabber card will then overwrite the buffer
- you're working on.
- */
- int *mbuf = arg;
- int ret;
-
- Trace(TRACE_READ, "VIDIOCSYNC called (%d).\n", *mbuf);
-
- /* bounds check */
- if (*mbuf < 0 || *mbuf >= default_mbufs)
- return -EINVAL;
- /* check if this buffer was requested anyway */
- if (pdev->image_used[*mbuf] == 0)
- return -EINVAL;
-
- /* Add ourselves to the frame wait-queue.
-
- FIXME: needs auditing for safety.
- QUESTION: In what respect? I think that using the
- frameq is safe now.
- */
- 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;
- }
- schedule();
- set_current_state(TASK_INTERRUPTIBLE);
- }
- remove_wait_queue(&pdev->frameq, &wait);
- set_current_state(TASK_RUNNING);
-
- /* The frame is ready. Expand in the image buffer
- requested by the user. I don't care if you
- mmap() 5 buffers and request data in this order:
- buffer 4 2 3 0 1 2 3 0 4 3 1 . . .
- Grabber hardware may not be so forgiving.
- */
- Trace(TRACE_READ, "VIDIOCSYNC: frame ready.\n");
- pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */
- /* Decompress, etc */
- ret = pwc_handle_frame(pdev);
- pdev->image_used[*mbuf] = 0;
- if (ret)
- return -EFAULT;
- break;
- }
-
- case VIDIOCGAUDIO:
- {
- struct video_audio *v = arg;
-
- strcpy(v->name, "Microphone");
- v->audio = -1; /* unknown audio minor */
- v->flags = 0;
- v->mode = VIDEO_SOUND_MONO;
- v->volume = 0;
- v->bass = 0;
- v->treble = 0;
- v->balance = 0x8000;
- v->step = 1;
- break;
- }
-
- case VIDIOCSAUDIO:
- {
- /* Dummy: nothing can be set */
- break;
- }
-
- case VIDIOCGUNIT:
- {
- struct video_unit *vu = arg;
-
- vu->video = pdev->vdev->minor & 0x3F;
- vu->audio = -1; /* not known yet */
- vu->vbi = -1;
- vu->radio = -1;
- vu->teletext = -1;
- break;
- }
- default:
- return pwc_ioctl(pdev, cmd, arg);
- } /* ..switch */
- return 0;
-}
-
static int pwc_video_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
return video_usercopy(inode, file, cmd, arg, pwc_video_do_ioctl);
}
-
static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
{
struct video_device *vdev = file->private_data;
struct pwc_device *pdev;
- unsigned long start = vma->vm_start;
- unsigned long size = vma->vm_end-vma->vm_start;
- unsigned long page, pos;
+ unsigned long start;
+ unsigned long size;
+ unsigned long page, pos = 0;
+ int index;
- Trace(TRACE_MEMORY, "mmap(0x%p, 0x%lx, %lu) called.\n", vdev, start, size);
+ PWC_DEBUG_MEMORY(">> %s\n", __FUNCTION__);
pdev = vdev->priv;
+ size = vma->vm_end - vma->vm_start;
+ start = vma->vm_start;
- vma->vm_flags |= VM_IO;
+ /* 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;
+ 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)
@@ -1624,7 +1392,6 @@ static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
else
size = 0;
}
-
return 0;
}
@@ -1645,10 +1412,12 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
int video_nr = -1; /* default: use next available device */
char serial_number[30], *name;
+ vendor_id = le16_to_cpu(udev->descriptor.idVendor);
+ product_id = le16_to_cpu(udev->descriptor.idProduct);
+
/* Check if we can handle this device */
- Trace(TRACE_PROBE, "probe() called [%04X %04X], if %d\n",
- le16_to_cpu(udev->descriptor.idVendor),
- le16_to_cpu(udev->descriptor.idProduct),
+ PWC_DEBUG_PROBE("probe() called [%04X %04X], if %d\n",
+ vendor_id, product_id,
intf->altsetting->desc.bInterfaceNumber);
/* the interfaces are probed one by one. We are only interested in the
@@ -1658,61 +1427,63 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
if (intf->altsetting->desc.bInterfaceNumber > 0)
return -ENODEV;
- vendor_id = le16_to_cpu(udev->descriptor.idVendor);
- product_id = le16_to_cpu(udev->descriptor.idProduct);
-
if (vendor_id == 0x0471) {
switch (product_id) {
case 0x0302:
- Info("Philips PCA645VC USB webcam detected.\n");
+ PWC_INFO("Philips PCA645VC USB webcam detected.\n");
name = "Philips 645 webcam";
type_id = 645;
break;
case 0x0303:
- Info("Philips PCA646VC USB webcam detected.\n");
+ PWC_INFO("Philips PCA646VC USB webcam detected.\n");
name = "Philips 646 webcam";
type_id = 646;
break;
case 0x0304:
- Info("Askey VC010 type 2 USB webcam detected.\n");
+ PWC_INFO("Askey VC010 type 2 USB webcam detected.\n");
name = "Askey VC010 webcam";
type_id = 646;
break;
case 0x0307:
- Info("Philips PCVC675K (Vesta) USB webcam detected.\n");
+ PWC_INFO("Philips PCVC675K (Vesta) USB webcam detected.\n");
name = "Philips 675 webcam";
type_id = 675;
break;
case 0x0308:
- Info("Philips PCVC680K (Vesta Pro) USB webcam detected.\n");
+ PWC_INFO("Philips PCVC680K (Vesta Pro) USB webcam detected.\n");
name = "Philips 680 webcam";
type_id = 680;
break;
case 0x030C:
- Info("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n");
+ PWC_INFO("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n");
name = "Philips 690 webcam";
type_id = 690;
break;
case 0x0310:
- Info("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n");
+ PWC_INFO("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n");
name = "Philips 730 webcam";
type_id = 730;
break;
case 0x0311:
- Info("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n");
+ PWC_INFO("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n");
name = "Philips 740 webcam";
type_id = 740;
break;
case 0x0312:
- Info("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n");
+ PWC_INFO("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n");
name = "Philips 750 webcam";
type_id = 750;
break;
case 0x0313:
- Info("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n");
+ PWC_INFO("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n");
name = "Philips 720K/40 webcam";
type_id = 720;
break;
+ case 0x0329:
+ PWC_INFO("Philips SPC 900NC USB webcam detected.\n");
+ name = "Philips SPC 900NC webcam";
+ type_id = 720;
+ break;
default:
return -ENODEV;
break;
@@ -1721,7 +1492,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
else if (vendor_id == 0x069A) {
switch(product_id) {
case 0x0001:
- Info("Askey VC010 type 1 USB webcam detected.\n");
+ PWC_INFO("Askey VC010 type 1 USB webcam detected.\n");
name = "Askey VC010 webcam";
type_id = 645;
break;
@@ -1733,32 +1504,33 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
else if (vendor_id == 0x046d) {
switch(product_id) {
case 0x08b0:
- Info("Logitech QuickCam Pro 3000 USB webcam detected.\n");
+ PWC_INFO("Logitech QuickCam Pro 3000 USB webcam detected.\n");
name = "Logitech QuickCam Pro 3000";
type_id = 740; /* CCD sensor */
break;
case 0x08b1:
- Info("Logitech QuickCam Notebook Pro USB webcam detected.\n");
+ PWC_INFO("Logitech QuickCam Notebook Pro USB webcam detected.\n");
name = "Logitech QuickCam Notebook Pro";
type_id = 740; /* CCD sensor */
break;
case 0x08b2:
- Info("Logitech QuickCam 4000 Pro USB webcam detected.\n");
+ PWC_INFO("Logitech QuickCam 4000 Pro USB webcam detected.\n");
name = "Logitech QuickCam Pro 4000";
type_id = 740; /* CCD sensor */
break;
case 0x08b3:
- Info("Logitech QuickCam Zoom USB webcam detected.\n");
+ PWC_INFO("Logitech QuickCam Zoom USB webcam detected.\n");
name = "Logitech QuickCam Zoom";
type_id = 740; /* CCD sensor */
break;
case 0x08B4:
- Info("Logitech QuickCam Zoom (new model) USB webcam detected.\n");
+ PWC_INFO("Logitech QuickCam Zoom (new model) USB webcam detected.\n");
name = "Logitech QuickCam Zoom";
type_id = 740; /* CCD sensor */
+ power_save = 1;
break;
case 0x08b5:
- Info("Logitech QuickCam Orbit/Sphere USB webcam detected.\n");
+ PWC_INFO("Logitech QuickCam Orbit/Sphere USB webcam detected.\n");
name = "Logitech QuickCam Orbit";
type_id = 740; /* CCD sensor */
features |= FEATURE_MOTOR_PANTILT;
@@ -1766,7 +1538,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
case 0x08b6:
case 0x08b7:
case 0x08b8:
- Info("Logitech QuickCam detected (reserved ID).\n");
+ PWC_INFO("Logitech QuickCam detected (reserved ID).\n");
name = "Logitech QuickCam (res.)";
type_id = 730; /* Assuming CMOS */
break;
@@ -1782,15 +1554,20 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
*/
switch(product_id) {
case 0x9000:
- Info("Samsung MPC-C10 USB webcam detected.\n");
+ PWC_INFO("Samsung MPC-C10 USB webcam detected.\n");
name = "Samsung MPC-C10";
type_id = 675;
break;
case 0x9001:
- Info("Samsung MPC-C30 USB webcam detected.\n");
+ PWC_INFO("Samsung MPC-C30 USB webcam detected.\n");
name = "Samsung MPC-C30";
type_id = 675;
break;
+ case 0x9002:
+ PWC_INFO("Samsung SNC-35E (v3.0) USB webcam detected.\n");
+ name = "Samsung MPC-C30";
+ type_id = 740;
+ break;
default:
return -ENODEV;
break;
@@ -1799,12 +1576,12 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
else if (vendor_id == 0x041e) {
switch(product_id) {
case 0x400c:
- Info("Creative Labs Webcam 5 detected.\n");
+ PWC_INFO("Creative Labs Webcam 5 detected.\n");
name = "Creative Labs Webcam 5";
type_id = 730;
break;
case 0x4011:
- Info("Creative Labs Webcam Pro Ex detected.\n");
+ PWC_INFO("Creative Labs Webcam Pro Ex detected.\n");
name = "Creative Labs Webcam Pro Ex";
type_id = 740;
break;
@@ -1816,7 +1593,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
else if (vendor_id == 0x04cc) {
switch(product_id) {
case 0x8116:
- Info("Sotec Afina Eye USB webcam detected.\n");
+ PWC_INFO("Sotec Afina Eye USB webcam detected.\n");
name = "Sotec Afina Eye";
type_id = 730;
break;
@@ -1829,7 +1606,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
switch(product_id) {
case 0x8116:
/* This is essentially the same cam as the Sotec Afina Eye */
- Info("AME Co. Afina Eye USB webcam detected.\n");
+ PWC_INFO("AME Co. Afina Eye USB webcam detected.\n");
name = "AME Co. Afina Eye";
type_id = 750;
break;
@@ -1842,12 +1619,12 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
else if (vendor_id == 0x0d81) {
switch(product_id) {
case 0x1900:
- Info("Visionite VCS-UC300 USB webcam detected.\n");
+ PWC_INFO("Visionite VCS-UC300 USB webcam detected.\n");
name = "Visionite VCS-UC300";
type_id = 740; /* CCD sensor */
break;
case 0x1910:
- Info("Visionite VCS-UM100 USB webcam detected.\n");
+ PWC_INFO("Visionite VCS-UM100 USB webcam detected.\n");
name = "Visionite VCS-UM100";
type_id = 730; /* CMOS sensor */
break;
@@ -1861,15 +1638,15 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
memset(serial_number, 0, 30);
usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
- Trace(TRACE_PROBE, "Device serial number is %s\n", serial_number);
+ PWC_DEBUG_PROBE("Device serial number is %s\n", serial_number);
if (udev->descriptor.bNumConfigurations > 1)
- Info("Warning: more than 1 configuration available.\n");
+ PWC_WARNING("Warning: more than 1 configuration available.\n");
/* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */
pdev = kzalloc(sizeof(struct pwc_device), GFP_KERNEL);
if (pdev == NULL) {
- Err("Oops, could not allocate memory for pwc_device.\n");
+ PWC_ERROR("Oops, could not allocate memory for pwc_device.\n");
return -ENOMEM;
}
pdev->type = type_id;
@@ -1900,17 +1677,18 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
pdev->vdev = video_device_alloc();
if (pdev->vdev == 0)
{
- Err("Err, cannot allocate video_device struture. Failing probe.");
+ PWC_ERROR("Err, cannot allocate video_device struture. Failing probe.");
kfree(pdev);
return -ENOMEM;
}
memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template));
+ pdev->vdev->dev = &(udev->dev);
strcpy(pdev->vdev->name, name);
pdev->vdev->owner = THIS_MODULE;
video_set_drvdata(pdev->vdev, pdev);
pdev->release = le16_to_cpu(udev->descriptor.bcdDevice);
- Trace(TRACE_PROBE, "Release: %04x\n", pdev->release);
+ PWC_DEBUG_PROBE("Release: %04x\n", pdev->release);
/* Now search device_hint[] table for a match, so we can hint a node number. */
for (hint = 0; hint < MAX_DEV_HINTS; hint++) {
@@ -1920,7 +1698,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
if ((device_hint[hint].serial_number[0] == '*') || !strcmp(device_hint[hint].serial_number, serial_number)) {
/* match! */
video_nr = device_hint[hint].device_node;
- Trace(TRACE_PROBE, "Found hint, will try to register as /dev/video%d\n", video_nr);
+ PWC_DEBUG_PROBE("Found hint, will try to register as /dev/video%d\n", video_nr);
break;
}
}
@@ -1929,21 +1707,27 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
pdev->vdev->release = video_device_release;
i = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr);
if (i < 0) {
- Err("Failed to register as video device (%d).\n", i);
+ PWC_ERROR("Failed to register as video device (%d).\n", i);
video_device_release(pdev->vdev); /* Drip... drip... drip... */
kfree(pdev); /* Oops, no memory leaks please */
return -EIO;
}
else {
- Info("Registered as /dev/video%d.\n", pdev->vdev->minor & 0x3F);
+ PWC_INFO("Registered as /dev/video%d.\n", pdev->vdev->minor & 0x3F);
}
/* occupy slot */
if (hint < MAX_DEV_HINTS)
device_hint[hint].pdev = pdev;
- Trace(TRACE_PROBE, "probe() function returning struct at 0x%p.\n", pdev);
+ PWC_DEBUG_PROBE("probe() function returning struct at 0x%p.\n", pdev);
usb_set_intfdata (intf, pdev);
+ pwc_create_sysfs_files(pdev->vdev);
+
+ /* Set the leds off */
+ pwc_set_leds(pdev, 0, 0);
+ pwc_camera_power(pdev, 0);
+
return 0;
}
@@ -1957,27 +1741,21 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
pdev = usb_get_intfdata (intf);
usb_set_intfdata (intf, NULL);
if (pdev == NULL) {
- Err("pwc_disconnect() Called without private pointer.\n");
+ PWC_ERROR("pwc_disconnect() Called without private pointer.\n");
goto disconnect_out;
}
if (pdev->udev == NULL) {
- Err("pwc_disconnect() already called for %p\n", pdev);
+ PWC_ERROR("pwc_disconnect() already called for %p\n", pdev);
goto disconnect_out;
}
if (pdev->udev != interface_to_usbdev(intf)) {
- Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n");
- goto disconnect_out;
- }
-#ifdef PWC_MAGIC
- if (pdev->magic != PWC_MAGIC) {
- Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n");
+ PWC_ERROR("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n");
goto disconnect_out;
}
-#endif
/* We got unplugged; this is signalled by an EPIPE error code */
if (pdev->vopen) {
- Info("Disconnected while webcam is in use!\n");
+ PWC_INFO("Disconnected while webcam is in use!\n");
pdev->error_status = EPIPE;
}
@@ -1987,7 +1765,8 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
while (pdev->vopen)
schedule();
/* Device is now closed, so we can safely unregister it */
- Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n");
+ PWC_DEBUG_PROBE("Unregistering video device in disconnect().\n");
+ pwc_remove_sysfs_files(pdev->vdev);
video_unregister_device(pdev->vdev);
/* Free memory (don't set pdev to 0 just yet) */
@@ -2021,58 +1800,64 @@ static int pwc_atoi(const char *s)
* Initialization code & module stuff
*/
-static char size[10];
-static int fps = 0;
-static int fbufs = 0;
-static int mbufs = 0;
-static int trace = -1;
+static char *size;
+static int fps;
+static int fbufs;
+static int mbufs;
static int compression = -1;
static int leds[2] = { -1, -1 };
-static char *dev_hint[MAX_DEV_HINTS] = { };
+static int leds_nargs;
+static char *dev_hint[MAX_DEV_HINTS];
+static int dev_hint_nargs;
+
+module_param(size, charp, 0444);
+module_param(fps, int, 0444);
+module_param(fbufs, int, 0444);
+module_param(mbufs, int, 0444);
+#if CONFIG_PWC_DEBUG
+module_param_named(trace, pwc_trace, int, 0644);
+#endif
+module_param(power_save, int, 0444);
+module_param(compression, int, 0444);
+module_param_array(leds, int, &leds_nargs, 0444);
+module_param_array(dev_hint, charp, &dev_hint_nargs, 0444);
-module_param_string(size, size, sizeof(size), 0);
MODULE_PARM_DESC(size, "Initial image size. One of sqcif, qsif, qcif, sif, cif, vga");
-module_param(fps, int, 0000);
MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 5-30");
-module_param(fbufs, int, 0000);
MODULE_PARM_DESC(fbufs, "Number of internal frame buffers to reserve");
-module_param(mbufs, int, 0000);
MODULE_PARM_DESC(mbufs, "Number of external (mmap()ed) image buffers");
-module_param(trace, int, 0000);
MODULE_PARM_DESC(trace, "For debugging purposes");
-module_param(power_save, bool, 0000);
MODULE_PARM_DESC(power_save, "Turn power save feature in camera on or off");
-module_param(compression, int, 0000);
MODULE_PARM_DESC(compression, "Preferred compression quality. Range 0 (uncompressed) to 3 (high compression)");
-module_param_array(leds, int, NULL, 0000);
MODULE_PARM_DESC(leds, "LED on,off time in milliseconds");
-module_param_array(dev_hint, charp, NULL, 0000);
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");
+MODULE_ALIAS("pwcx");
+MODULE_VERSION( PWC_VERSION );
static int __init usb_pwc_init(void)
{
int i, sz;
char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" };
- Info("Philips webcam module version " PWC_VERSION " loaded.\n");
- Info("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n");
- Info("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n");
- Info("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n");
+ 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");
if (fps) {
if (fps < 4 || fps > 30) {
- Err("Framerate out of bounds (4-30).\n");
+ PWC_ERROR("Framerate out of bounds (4-30).\n");
return -EINVAL;
}
default_fps = fps;
- Info("Default framerate set to %d.\n", default_fps);
+ PWC_DEBUG_MODULE("Default framerate set to %d.\n", default_fps);
}
- if (size[0]) {
+ if (size) {
/* string; try matching with array */
for (sz = 0; sz < PSZ_MAX; sz++) {
if (!strcmp(sizenames[sz], size)) { /* Found! */
@@ -2081,41 +1866,42 @@ static int __init usb_pwc_init(void)
}
}
if (sz == PSZ_MAX) {
- Err("Size not recognized; try size=[sqcif | qsif | qcif | sif | cif | vga].\n");
+ PWC_ERROR("Size not recognized; try size=[sqcif | qsif | qcif | sif | cif | vga].\n");
return -EINVAL;
}
- Info("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y);
+ PWC_DEBUG_MODULE("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y);
}
if (mbufs) {
if (mbufs < 1 || mbufs > MAX_IMAGES) {
- Err("Illegal number of mmap() buffers; use a number between 1 and %d.\n", MAX_IMAGES);
+ PWC_ERROR("Illegal number of mmap() buffers; use a number between 1 and %d.\n", MAX_IMAGES);
return -EINVAL;
}
- default_mbufs = mbufs;
- Info("Number of image buffers set to %d.\n", default_mbufs);
+ pwc_mbufs = mbufs;
+ PWC_DEBUG_MODULE("Number of image buffers set to %d.\n", pwc_mbufs);
}
if (fbufs) {
if (fbufs < 2 || fbufs > MAX_FRAMES) {
- Err("Illegal number of frame buffers; use a number between 2 and %d.\n", MAX_FRAMES);
+ PWC_ERROR("Illegal number of frame buffers; use a number between 2 and %d.\n", MAX_FRAMES);
return -EINVAL;
}
default_fbufs = fbufs;
- Info("Number of frame buffers set to %d.\n", default_fbufs);
+ PWC_DEBUG_MODULE("Number of frame buffers set to %d.\n", default_fbufs);
}
- if (trace >= 0) {
- Info("Trace options: 0x%04x\n", trace);
- pwc_trace = trace;
+#if CONFIG_PWC_DEBUG
+ if (pwc_trace >= 0) {
+ PWC_DEBUG_MODULE("Trace options: 0x%04x\n", pwc_trace);
}
+#endif
if (compression >= 0) {
if (compression > 3) {
- Err("Invalid compression setting; use a number between 0 (uncompressed) and 3 (high).\n");
+ PWC_ERROR("Invalid compression setting; use a number between 0 (uncompressed) and 3 (high).\n");
return -EINVAL;
}
pwc_preferred_compression = compression;
- Info("Preferred compression set to %d.\n", pwc_preferred_compression);
+ PWC_DEBUG_MODULE("Preferred compression set to %d.\n", pwc_preferred_compression);
}
if (power_save)
- Info("Enabling power save on open/close.\n");
+ PWC_DEBUG_MODULE("Enabling power save on open/close.\n");
if (leds[0] >= 0)
led_on = leds[0];
if (leds[1] >= 0)
@@ -2146,14 +1932,14 @@ static int __init usb_pwc_init(void)
dot++;
/* Few sanity checks */
if (*dot != '\0' && dot > colon) {
- Err("Malformed camera hint: the colon must be after the dot.\n");
+ PWC_ERROR("Malformed camera hint: the colon must be after the dot.\n");
return -EINVAL;
}
if (*colon == '\0') {
/* No colon */
if (*dot != '\0') {
- Err("Malformed camera hint: no colon + device node given.\n");
+ PWC_ERROR("Malformed camera hint: no colon + device node given.\n");
return -EINVAL;
}
else {
@@ -2178,28 +1964,27 @@ static int __init usb_pwc_init(void)
device_hint[i].serial_number[k] = '\0';
}
}
-#if PWC_DEBUG
- Debug("device_hint[%d]:\n", i);
- Debug(" type : %d\n", device_hint[i].type);
- Debug(" serial# : %s\n", device_hint[i].serial_number);
- Debug(" node : %d\n", device_hint[i].device_node);
-#endif
+ 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);
}
else
device_hint[i].type = 0; /* not filled */
} /* ..for MAX_DEV_HINTS */
- Trace(TRACE_PROBE, "Registering driver at address 0x%p.\n", &pwc_driver);
+ PWC_DEBUG_PROBE("Registering driver at address 0x%p.\n", &pwc_driver);
return usb_register(&pwc_driver);
}
static void __exit usb_pwc_exit(void)
{
- Trace(TRACE_MODULE, "Deregistering driver.\n");
+ PWC_DEBUG_MODULE("Deregistering driver.\n");
usb_deregister(&pwc_driver);
- Info("Philips webcam module removed.\n");
+ PWC_INFO("Philips webcam module removed.\n");
}
module_init(usb_pwc_init);
module_exit(usb_pwc_exit);
+/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */