Commit a38f5292 authored by Nemosoft Unv's avatar Nemosoft Unv Committed by Greg Kroah-Hartman

[PATCH] USB: PWC 9.0.1

Attached you will find patches that will bring the PWC driver in the kernel
up to version 9.0.1 from 8.12 (9.0 wasn't accepted at first). Patches are
against 2.4.26 and 2.6.7. The main difference with 9.0 is that the
video_relase() routine is now hopefully in line with kernel requirements.
I've also added one more ioctl() call, upon request.

I'm also slightly in the dark on the status of the PWC in the 2.6 kernel;
I've seen two patches: the first was a bad one, since it would crash your
kernel when you unplug the cam. I've seen a second patch to reverse the
first one, but I don't know if that went in. Either way, you might get a
conflict in pwc-if.c in and about the pwc_video_release() routine; this
patch was generated against the clean 2.6.7 kernel source. Should you need
or want to fix it manually, this patch should remove the
pwc_video_release() in the 2.6 kernel.
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent 70247c61
This file contains some additional information for the Philips webcams. This file contains some additional information for the Philips and OEM webcams.
E-mail: webcam@smcc.demon.nl Last updated: 2001-09-24 E-mail: webcam@smcc.demon.nl Last updated: 2004-01-19
Site: http://www.smcc.demon.nl/webcam/
The main webpage for the Philips driver is http://www.smcc.demon.nl/webcam/.
It contains a lot of extra information, a FAQ, and the binary plugin As of this moment, the following cameras are supported:
'PWCX'. This plugin contains decompression routines that allow you to * Philips PCA645
use higher image sizes and framerates; in addition the webcam uses less * Philips PCA646
bandwidth on the USB bus (handy if you want to run more than 1 camera * Philips PCVC675
simultaneously). These routines fall under an NDA, and may therefor not be * Philips PCVC680
distributed as source; however, its use is completely optional. * Philips PCVC690
* Philips PCVC720/40
* Philips PCVC730
* Philips PCVC740
* Philips PCVC750
* Askey VC010
* Creative Labs Webcam 5
* Creative Labs Webcam Pro Ex
* Logitech QuickCam 3000 Pro
* Logitech QuickCam 4000 Pro
* Logitech QuickCam Notebook Pro
* Logitech QuickCam Zoom
* Logitech QuickCam Orbit
* Logitech QuickCam Sphere
* Samsung MPC-C10
* Samsung MPC-C30
* Sotec Afina Eye
* AME CU-001
* Visionite VCS-UM100
* Visionite VCS-UC300
The main webpage for the Philips driver is at the address above. It contains
a lot of extra information, a FAQ, and the binary plugin 'PWCX'. This plugin
contains decompression routines that allow you to use higher image sizes and
framerates; in addition the webcam uses less bandwidth on the USB bus (handy
if you want to run more than 1 camera simultaneously). These routines fall
under a NDA, and may therefor not be distributed as source; however, its use
is completely optional.
You can build this code either into your kernel, or as a module. I recommend You can build this code either into your kernel, or as a module. I recommend
the latter, since it makes troubleshooting a lot easier. The built-in the latter, since it makes troubleshooting a lot easier. The built-in
...@@ -27,14 +54,14 @@ fps ...@@ -27,14 +54,14 @@ fps
Specifies the desired framerate. Is an integer in the range of 4-30. Specifies the desired framerate. Is an integer in the range of 4-30.
fbufs fbufs
This parameter specifies the number of internal buffers to use for storing This paramter specifies the number of internal buffers to use for storing
frames from the cam. This will help if the process that reads images from frames from the cam. This will help if the process that reads images from
the cam is a bit slow or momentarily busy. However, on slow machines it the cam is a bit slow or momentarely busy. However, on slow machines it
only introduces lag, so choose carefully. The default is 3, which is only introduces lag, so choose carefully. The default is 3, which is
reasonable. You can set it between 2 and 5. reasonable. You can set it between 2 and 5.
mbufs mbufs
This is an integer between 1 and 4. It will tell the module the number of This is an integer between 1 and 10. It will tell the module the number of
buffers to reserve for mmap(), VIDIOCCGMBUF, VIDIOCMCAPTURE and friends. buffers to reserve for mmap(), VIDIOCCGMBUF, VIDIOCMCAPTURE and friends.
The default is 2, which is adequate for most applications (double The default is 2, which is adequate for most applications (double
buffering). buffering).
...@@ -45,9 +72,9 @@ mbufs ...@@ -45,9 +72,9 @@ mbufs
slack when your program is behind. But you need a multi-threaded or slack when your program is behind. But you need a multi-threaded or
forked program to really take advantage of these buffers. forked program to really take advantage of these buffers.
The absolute maximum is 4, but don't set it too high! Every buffer takes The absolute maximum is 10, but don't set it too high! Every buffer takes
up 1.22 MB of RAM, so unless you have a lot of memory setting this to up 460 KB of RAM, so unless you have a lot of memory setting this to
something more than 2 is an absolute waste. This memory is only something more than 4 is an absolute waste. This memory is only
allocated during open(), so nothing is wasted when the camera is not in allocated during open(), so nothing is wasted when the camera is not in
use. use.
...@@ -74,9 +101,10 @@ compression (only useful with the plugin) ...@@ -74,9 +101,10 @@ compression (only useful with the plugin)
introduce some unwanted artefacts. The default is 2, medium compression. introduce some unwanted artefacts. The default is 2, medium compression.
See the FAQ on the website for an overview of which modes require See the FAQ on the website for an overview of which modes require
compression. compression.
The compression parameter only applies to the Vesta & ToUCam cameras. The compression parameter does not apply to the 645 and 646 cameras
The 645 and 646 have fixed compression parameters. and OEM models derived from those (only a few). Most cams honour this
parameter.
leds leds
This settings takes 2 integers, that define the on/off time for the LED This settings takes 2 integers, that define the on/off time for the LED
...@@ -89,14 +117,17 @@ leds ...@@ -89,14 +117,17 @@ leds
leds=0,0 leds=0,0
the LED never goes on, making it suitable for silent survaillance. the LED never goes on, making it suitable for silent surveillance.
By default the camera's LED is on solid while in use, and turned off By default the camera's LED is on solid while in use, and turned off
when the camera is not used anymore. when the camera is not used anymore.
This parameter works only with the ToUCam range of cameras (730, 740, This parameter works only with the ToUCam range of cameras (720, 730, 740,
750). For other cameras this command is silently ignored, and the LED 750) and OEMs. For other cameras this command is silently ignored, and
cannot be controlled. the LED cannot be controlled.
Finally: this parameters does not take effect UNTIL the first time you
open the camera device. Until then, the LED remains on.
dev_hint dev_hint
A long standing problem with USB devices is their dynamic nature: you A long standing problem with USB devices is their dynamic nature: you
...@@ -126,7 +157,7 @@ dev_hint ...@@ -126,7 +157,7 @@ dev_hint
other cameras will get the first free other cameras will get the first free
available slot (see below). available slot (see below).
dev_hint=645:1,680=2 The PCA645 camera will get /dev/video1, dev_hint=645:1,680:2 The PCA645 camera will get /dev/video1,
and a PCVC680 /dev/video2. and a PCVC680 /dev/video2.
dev_hint=645.0123:3,645.4567:0 The PCA645 camera with serialnumber dev_hint=645.0123:3,645.4567:0 The PCA645 camera with serialnumber
...@@ -176,13 +207,16 @@ trace ...@@ -176,13 +207,16 @@ trace
64 0x40 Show viewport and image sizes Off 64 0x40 Show viewport and image sizes Off
128 0x80 PWCX debugging Off
For example, to trace the open() & read() fuctions, sum 8 + 4 = 12, For example, to trace the open() & read() fuctions, sum 8 + 4 = 12,
so you would supply trace=12 during insmod or modprobe. If so you would supply trace=12 during insmod or modprobe. If
you want to turn the initialization and probing tracing off, set trace=0. you want to turn the initialization and probing tracing off, set trace=0.
The default value for trace is 35 (0x23). The default value for trace is 35 (0x23).
Example:
Example:
# modprobe pwc size=cif fps=15 power_save=1 # modprobe pwc size=cif fps=15 power_save=1
...@@ -192,7 +226,7 @@ cameras. Each camera has its own set of buffers. ...@@ -192,7 +226,7 @@ cameras. Each camera has its own set of buffers.
size and fps only specify defaults when you open() the device; this is to size and fps only specify defaults when you open() the device; this is to
accommodate some tools that don't set the size. You can change these accommodate some tools that don't set the size. You can change these
settings after open() with the Video4Linux ioctl() calls. The default of settings after open() with the Video4Linux ioctl() calls. The default of
defaults is QCIF size at 10 fps, BGR order. defaults is QCIF size at 10 fps.
The compression parameter is semiglobal; it sets the initial compression The compression parameter is semiglobal; it sets the initial compression
preference for all camera's, but this parameter can be set per camera with preference for all camera's, but this parameter can be set per camera with
...@@ -200,4 +234,3 @@ the VIDIOCPWCSCQUAL ioctl() call. ...@@ -200,4 +234,3 @@ the VIDIOCPWCSCQUAL ioctl() call.
All parameters are optional. All parameters are optional.
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
#endif #endif
#include <asm/errno.h> #include <asm/errno.h>
#include <linux/version.h>
#include "pwc.h" #include "pwc.h"
#include "pwc-ioctl.h" #include "pwc-ioctl.h"
...@@ -127,19 +128,19 @@ static struct Nala_table_entry Nala_table[PSZ_MAX][8] = ...@@ -127,19 +128,19 @@ static struct Nala_table_entry Nala_table[PSZ_MAX][8] =
/* This tables contains entries for the 675/680/690 (Timon) camera, with /* This tables contains entries for the 675/680/690 (Timon) camera, with
4 different qualities (no compression, low, medium, high). 4 different qualities (no compression, low, medium, high).
It lists the bandwidth requirements for said mode by its alternate interface It lists the bandwidth requirements for said mode by its alternate interface
number. An alternate of 0 means that the mode is unavailable. number. An alternate of 0 means that the mode is unavailable.
There are 6 * 4 * 4 entries: There are 6 * 4 * 4 entries:
6 different resolutions subqcif, qsif, qcif, sif, cif, vga 6 different resolutions subqcif, qsif, qcif, sif, cif, vga
6 framerates: 5, 10, 15, 20, 25, 30 6 framerates: 5, 10, 15, 20, 25, 30
4 compression modi: none, low, medium, high 4 compression modi: none, low, medium, high
When an uncompressed mode is not available, the next available compressed mode When an uncompressed mode is not available, the next available compressed mode
will be chosen (unless the decompressor is absent). Sometimes there are only will be chosen (unless the decompressor is absent). Sometimes there are only
1 or 2 compressed modes available; in that case entries are duplicated. 1 or 2 compressed modes available; in that case entries are duplicated.
*/ */
struct Timon_table_entry struct Timon_table_entry
{ {
char alternate; /* USB alternate interface */ char alternate; /* USB alternate interface */
unsigned short packetsize; /* Normal packet size */ unsigned short packetsize; /* Normal packet size */
...@@ -147,7 +148,7 @@ struct Timon_table_entry ...@@ -147,7 +148,7 @@ struct Timon_table_entry
unsigned char mode[13]; /* precomputed mode settings for cam */ unsigned char mode[13]; /* precomputed mode settings for cam */
}; };
static struct Timon_table_entry Timon_table[PSZ_MAX][6][4] = static struct Timon_table_entry Timon_table[PSZ_MAX][6][4] =
{ {
#include "pwc_timon.h" #include "pwc_timon.h"
}; };
...@@ -194,7 +195,7 @@ void pwc_hexdump(void *p, int len) ...@@ -194,7 +195,7 @@ void pwc_hexdump(void *p, int len)
int i; int i;
unsigned char *s; unsigned char *s;
char buf[100], *d; char buf[100], *d;
s = (unsigned char *)p; s = (unsigned char *)p;
d = buf; d = buf;
*d = '\0'; *d = '\0';
...@@ -230,7 +231,7 @@ static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int fra ...@@ -230,7 +231,7 @@ static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int fra
unsigned char buf[3]; unsigned char buf[3];
int ret, fps; int ret, fps;
struct Nala_table_entry *pEntry; struct Nala_table_entry *pEntry;
int frames2frames[31] = int frames2frames[31] =
{ /* closest match of framerate */ { /* closest match of framerate */
0, 0, 0, 0, 4, /* 0-4 */ 0, 0, 0, 0, 4, /* 0-4 */
5, 5, 7, 7, 10, /* 5-9 */ 5, 5, 7, 7, 10, /* 5-9 */
...@@ -267,9 +268,12 @@ static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int fra ...@@ -267,9 +268,12 @@ static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int fra
Debug("Failed to send video command... %d\n", ret); Debug("Failed to send video command... %d\n", ret);
return ret; return ret;
} }
if (pEntry->compressed && pdev->decompressor != NULL) if (pEntry->compressed && pdev->decompressor != 0 && pdev->vpalette != VIDEO_PALETTE_RAW)
pdev->decompressor->init(pdev->release, buf, pdev->decompress_data); pdev->decompressor->init(pdev->type, pdev->release, buf, pdev->decompress_data);
pdev->cmd_len = 3;
memcpy(pdev->cmd_buf, buf, 3);
/* Set various parameters */ /* Set various parameters */
pdev->vframes = frames; pdev->vframes = frames;
pdev->vsize = size; pdev->vsize = size;
...@@ -303,13 +307,13 @@ static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int fr ...@@ -303,13 +307,13 @@ static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int fr
if (size == PSZ_VGA && frames > 15) if (size == PSZ_VGA && frames > 15)
return -EINVAL; return -EINVAL;
fps = (frames / 5) - 1; fps = (frames / 5) - 1;
/* Find a supported framerate with progressively higher compression ratios /* Find a supported framerate with progressively higher compression ratios
if the preferred ratio is not available. if the preferred ratio is not available.
*/ */
pChoose = NULL; pChoose = NULL;
if (pdev->decompressor == NULL) { if (pdev->decompressor == NULL) {
#if PWC_DEBUG #if PWC_DEBUG
Debug("Trying to find uncompressed mode.\n"); Debug("Trying to find uncompressed mode.\n");
#endif #endif
pChoose = &Timon_table[size][fps][0]; pChoose = &Timon_table[size][fps][0];
...@@ -319,7 +323,7 @@ static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int fr ...@@ -319,7 +323,7 @@ static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int fr
pChoose = &Timon_table[size][fps][compression]; pChoose = &Timon_table[size][fps][compression];
if (pChoose->alternate != 0) if (pChoose->alternate != 0)
break; break;
compression++; compression++;
} }
} }
if (pChoose == NULL || pChoose->alternate == 0) if (pChoose == NULL || pChoose->alternate == 0)
...@@ -332,9 +336,12 @@ static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int fr ...@@ -332,9 +336,12 @@ static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int fr
if (ret < 0) if (ret < 0)
return ret; return ret;
if (pChoose->bandlength > 0) if (pChoose->bandlength > 0 && pdev->decompressor != 0 && pdev->vpalette != VIDEO_PALETTE_RAW)
pdev->decompressor->init(pdev->release, buf, pdev->decompress_data); pdev->decompressor->init(pdev->type, pdev->release, buf, pdev->decompress_data);
pdev->cmd_len = 13;
memcpy(pdev->cmd_buf, buf, 13);
/* Set various parameters */ /* Set various parameters */
pdev->vframes = frames; pdev->vframes = frames;
pdev->vsize = size; pdev->vsize = size;
...@@ -342,7 +349,7 @@ static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int fr ...@@ -342,7 +349,7 @@ static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int fr
pdev->valternate = pChoose->alternate; pdev->valternate = pChoose->alternate;
pdev->image = pwc_image_sizes[size]; pdev->image = pwc_image_sizes[size];
pdev->vbandlength = pChoose->bandlength; pdev->vbandlength = pChoose->bandlength;
if (pChoose->bandlength > 0) if (pChoose->bandlength > 0)
pdev->frame_size = (pChoose->bandlength * pdev->image.y) / 4; pdev->frame_size = (pChoose->bandlength * pdev->image.y) / 4;
else else
pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8;
...@@ -352,33 +359,54 @@ static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int fr ...@@ -352,33 +359,54 @@ static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int fr
static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int compression, int snapshot)
{ {
struct Kiara_table_entry *pChoose; struct Kiara_table_entry *pChoose = 0;
int fps, ret; int fps, ret;
unsigned char buf[12]; unsigned char buf[12];
struct Kiara_table_entry RawEntry = {6, 773, 1272, {0xAD, 0xF4, 0x10, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}};
if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3) if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3)
return -EINVAL; return -EINVAL;
if (size == PSZ_VGA && frames > 15) if (size == PSZ_VGA && frames > 15)
return -EINVAL; return -EINVAL;
fps = (frames / 5) - 1; fps = (frames / 5) - 1;
/* Find a supported framerate with progressively higher compression ratios /* special case: VGA @ 5 fps and snapshot is raw bayer mode */
if the preferred ratio is not available. if (size == PSZ_VGA && frames == 5 && snapshot)
*/ {
pChoose = NULL; /* Only available in case the raw palette is selected or
if (pdev->decompressor == NULL) { we have the decompressor available. This mode is
#if PWC_DEBUG only available in compressed form
Debug("Trying to find uncompressed mode.\n"); */
#endif if (pdev->vpalette == VIDEO_PALETTE_RAW || pdev->decompressor != NULL)
pChoose = &Kiara_table[size][fps][0]; {
Info("Choosing VGA/5 BAYER mode (%d).\n", pdev->vpalette);
pChoose = &RawEntry;
}
else
{
Info("VGA/5 BAYER mode _must_ have a decompressor available, or use RAW palette.\n");
}
} }
else { else
while (compression <= 3) { {
pChoose = &Kiara_table[size][fps][compression]; /* Find a supported framerate with progressively higher compression ratios
if (pChoose->alternate != 0) if the preferred ratio is not available.
break; Skip this step when using RAW modes.
compression++; */
if (pdev->decompressor == NULL && pdev->vpalette != VIDEO_PALETTE_RAW) {
#if PWC_DEBUG
Debug("Trying to find uncompressed mode.\n");
#endif
pChoose = &Kiara_table[size][fps][0];
} }
else {
while (compression <= 3) {
pChoose = &Kiara_table[size][fps][compression];
if (pChoose->alternate != 0)
break;
compression++;
}
}
} }
if (pChoose == NULL || pChoose->alternate == 0) if (pChoose == NULL || pChoose->alternate == 0)
return -ENOENT; /* Not supported. */ return -ENOENT; /* Not supported. */
...@@ -393,9 +421,11 @@ static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int fr ...@@ -393,9 +421,11 @@ static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int fr
if (ret < 0) if (ret < 0)
return ret; return ret;
if (pChoose->bandlength > 0) if (pChoose->bandlength > 0 && pdev->decompressor != 0 && pdev->vpalette != VIDEO_PALETTE_RAW)
pdev->decompressor->init(pdev->release, buf, pdev->decompress_data); pdev->decompressor->init(pdev->type, pdev->release, buf, pdev->decompress_data);
pdev->cmd_len = 12;
memcpy(pdev->cmd_buf, buf, 12);
/* All set and go */ /* All set and go */
pdev->vframes = frames; pdev->vframes = frames;
pdev->vsize = size; pdev->vsize = size;
...@@ -403,15 +433,15 @@ static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int fr ...@@ -403,15 +433,15 @@ static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int fr
pdev->valternate = pChoose->alternate; pdev->valternate = pChoose->alternate;
pdev->image = pwc_image_sizes[size]; pdev->image = pwc_image_sizes[size];
pdev->vbandlength = pChoose->bandlength; pdev->vbandlength = pChoose->bandlength;
if (pChoose->bandlength > 0) if (pdev->vbandlength > 0)
pdev->frame_size = (pChoose->bandlength * pdev->image.y) / 4; pdev->frame_size = (pdev->vbandlength * pdev->image.y) / 4;
else else
pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8;
pdev->frame_size += (pdev->frame_header_size + pdev->frame_trailer_size);
return 0; return 0;
} }
/** /**
@pdev: device structure @pdev: device structure
@width: viewport width @width: viewport width
...@@ -422,14 +452,17 @@ static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int fr ...@@ -422,14 +452,17 @@ static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int fr
*/ */
int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot) int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot)
{ {
int ret, size; int ret, size;
Trace(TRACE_FLOW, "set_video_mode(%dx%d @ %d, palette %d).\n", width, height, frames, pdev->vpalette);
size = pwc_decode_size(pdev, width, height); size = pwc_decode_size(pdev, width, height);
if (size < 0) { if (size < 0) {
Debug("Could not find suitable size.\n"); Debug("Could not find suitable size.\n");
return -ERANGE; return -ERANGE;
} }
ret = -EINVAL; Debug("decode_size = %d.\n", size);
ret = -EINVAL;
switch(pdev->type) { switch(pdev->type) {
case 645: case 645:
case 646: case 646:
...@@ -441,7 +474,7 @@ int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frame ...@@ -441,7 +474,7 @@ int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frame
case 690: case 690:
ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot); ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot);
break; break;
case 720: case 720:
case 730: case 730:
case 740: case 740:
...@@ -459,6 +492,7 @@ int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frame ...@@ -459,6 +492,7 @@ int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frame
} }
pdev->view.x = width; pdev->view.x = width;
pdev->view.y = height; pdev->view.y = height;
pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size;
pwc_set_image_buffer_size(pdev); pwc_set_image_buffer_size(pdev);
Trace(TRACE_SIZE, "Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y); Trace(TRACE_SIZE, "Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y);
return 0; return 0;
...@@ -467,23 +501,33 @@ int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frame ...@@ -467,23 +501,33 @@ int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frame
void pwc_set_image_buffer_size(struct pwc_device *pdev) void pwc_set_image_buffer_size(struct pwc_device *pdev)
{ {
int factor, i, filler = 0; int i, factor = 0, filler = 0;
factor = 6; /* for PALETTE_YUV420P */
filler = 128; switch(pdev->vpalette)
{
case VIDEO_PALETTE_YUV420P:
factor = 6;
filler = 128;
break;
case VIDEO_PALETTE_RAW:
factor = 6; /* can be uncompressed YUV420P */
filler = 0;
break;
}
/* Set sizes in bytes */ /* Set sizes in bytes */
pdev->image.size = pdev->image.x * pdev->image.y * factor / 4; pdev->image.size = pdev->image.x * pdev->image.y * factor / 4;
pdev->view.size = pdev->view.x * pdev->view.y * factor / 4; pdev->view.size = pdev->view.x * pdev->view.y * factor / 4;
/* Align offset, or you'll get some very weird results in /* Align offset, or you'll get some very weird results in
YUV420 mode... x must be multiple of 4 (to get the Y's in YUV420 mode... x must be multiple of 4 (to get the Y's in
place), and y even (or you'll mixup U & V). This is less of a place), and y even (or you'll mixup U & V). This is less of a
problem for YUV420P. problem for YUV420P.
*/ */
pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC; pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC;
pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE; pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE;
/* Fill buffers with gray or black */ /* Fill buffers with gray or black */
for (i = 0; i < MAX_IMAGES; i++) { for (i = 0; i < MAX_IMAGES; i++) {
if (pdev->image_ptr[i] != NULL) if (pdev->image_ptr[i] != NULL)
...@@ -499,13 +543,8 @@ int pwc_get_brightness(struct pwc_device *pdev) ...@@ -499,13 +543,8 @@ int pwc_get_brightness(struct pwc_device *pdev)
{ {
char buf; char buf;
int ret; int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), ret = RecvControlMsg(GET_LUM_CTL, BRIGHTNESS_FORMATTER, 1);
GET_LUM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
BRIGHTNESS_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
return buf << 9; return buf << 9;
...@@ -520,12 +559,7 @@ int pwc_set_brightness(struct pwc_device *pdev, int value) ...@@ -520,12 +559,7 @@ int pwc_set_brightness(struct pwc_device *pdev, int value)
if (value > 0xffff) if (value > 0xffff)
value = 0xffff; value = 0xffff;
buf = (value >> 9) & 0x7f; buf = (value >> 9) & 0x7f;
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), return SendControlMsg(SET_LUM_CTL, BRIGHTNESS_FORMATTER, 1);
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
BRIGHTNESS_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
} }
/* CONTRAST */ /* CONTRAST */
...@@ -534,13 +568,8 @@ int pwc_get_contrast(struct pwc_device *pdev) ...@@ -534,13 +568,8 @@ int pwc_get_contrast(struct pwc_device *pdev)
{ {
char buf; char buf;
int ret; int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), ret = RecvControlMsg(GET_LUM_CTL, CONTRAST_FORMATTER, 1);
GET_LUM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
CONTRAST_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
return buf << 10; return buf << 10;
...@@ -555,12 +584,7 @@ int pwc_set_contrast(struct pwc_device *pdev, int value) ...@@ -555,12 +584,7 @@ int pwc_set_contrast(struct pwc_device *pdev, int value)
if (value > 0xffff) if (value > 0xffff)
value = 0xffff; value = 0xffff;
buf = (value >> 10) & 0x3f; buf = (value >> 10) & 0x3f;
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), return SendControlMsg(SET_LUM_CTL, CONTRAST_FORMATTER, 1);
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
CONTRAST_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
} }
/* GAMMA */ /* GAMMA */
...@@ -570,12 +594,7 @@ int pwc_get_gamma(struct pwc_device *pdev) ...@@ -570,12 +594,7 @@ int pwc_get_gamma(struct pwc_device *pdev)
char buf; char buf;
int ret; int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), ret = RecvControlMsg(GET_LUM_CTL, GAMMA_FORMATTER, 1);
GET_LUM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
GAMMA_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
return buf << 11; return buf << 11;
...@@ -590,12 +609,7 @@ int pwc_set_gamma(struct pwc_device *pdev, int value) ...@@ -590,12 +609,7 @@ int pwc_set_gamma(struct pwc_device *pdev, int value)
if (value > 0xffff) if (value > 0xffff)
value = 0xffff; value = 0xffff;
buf = (value >> 11) & 0x1f; buf = (value >> 11) & 0x1f;
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), return SendControlMsg(SET_LUM_CTL, GAMMA_FORMATTER, 1);
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
GAMMA_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
} }
...@@ -608,12 +622,7 @@ int pwc_get_saturation(struct pwc_device *pdev) ...@@ -608,12 +622,7 @@ int pwc_get_saturation(struct pwc_device *pdev)
if (pdev->type < 675) if (pdev->type < 675)
return -1; return -1;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), ret = RecvControlMsg(GET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1);
GET_CHROM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
return 32768 + buf * 327; return 32768 + buf * 327;
...@@ -631,12 +640,7 @@ int pwc_set_saturation(struct pwc_device *pdev, int value) ...@@ -631,12 +640,7 @@ int pwc_set_saturation(struct pwc_device *pdev, int value)
value = 0xffff; value = 0xffff;
/* saturation ranges from -100 to +100 */ /* saturation ranges from -100 to +100 */
buf = (value - 32768) / 327; buf = (value - 32768) / 327;
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), return SendControlMsg(SET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1);
SET_CHROM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1,
pdev->vcinterface,
&buf, 1, HZ / 2);
} }
/* AGC */ /* AGC */
...@@ -651,12 +655,7 @@ static inline int pwc_set_agc(struct pwc_device *pdev, int mode, int value) ...@@ -651,12 +655,7 @@ static inline int pwc_set_agc(struct pwc_device *pdev, int mode, int value)
else else
buf = 0xff; /* fixed */ buf = 0xff; /* fixed */
ret = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), ret = SendControlMsg(SET_LUM_CTL, AGC_MODE_FORMATTER, 1);
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
AGC_MODE_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (!mode && ret >= 0) { if (!mode && ret >= 0) {
if (value < 0) if (value < 0)
...@@ -664,12 +663,7 @@ static inline int pwc_set_agc(struct pwc_device *pdev, int mode, int value) ...@@ -664,12 +663,7 @@ static inline int pwc_set_agc(struct pwc_device *pdev, int mode, int value)
if (value > 0xffff) if (value > 0xffff)
value = 0xffff; value = 0xffff;
buf = (value >> 10) & 0x3F; buf = (value >> 10) & 0x3F;
ret = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), ret = SendControlMsg(SET_LUM_CTL, PRESET_AGC_FORMATTER, 1);
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_AGC_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
} }
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -681,22 +675,12 @@ static inline int pwc_get_agc(struct pwc_device *pdev, int *value) ...@@ -681,22 +675,12 @@ static inline int pwc_get_agc(struct pwc_device *pdev, int *value)
unsigned char buf; unsigned char buf;
int ret; int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), ret = RecvControlMsg(GET_LUM_CTL, AGC_MODE_FORMATTER, 1);
GET_LUM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
AGC_MODE_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (buf != 0) { /* fixed */ if (buf != 0) { /* fixed */
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), ret = RecvControlMsg(GET_LUM_CTL, PRESET_AGC_FORMATTER, 1);
GET_LUM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_AGC_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (buf > 0x3F) if (buf > 0x3F)
...@@ -704,12 +688,7 @@ static inline int pwc_get_agc(struct pwc_device *pdev, int *value) ...@@ -704,12 +688,7 @@ static inline int pwc_get_agc(struct pwc_device *pdev, int *value)
*value = (buf << 10); *value = (buf << 10);
} }
else { /* auto */ else { /* auto */
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), ret = RecvControlMsg(GET_STATUS_CTL, READ_AGC_FORMATTER, 1);
GET_STATUS_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
READ_AGC_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
/* Gah... this value ranges from 0x00 ... 0x9F */ /* Gah... this value ranges from 0x00 ... 0x9F */
...@@ -732,12 +711,7 @@ static inline int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int v ...@@ -732,12 +711,7 @@ static inline int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int v
else else
buf[0] = 0xff; /* fixed */ buf[0] = 0xff; /* fixed */
ret = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), ret = SendControlMsg(SET_LUM_CTL, SHUTTER_MODE_FORMATTER, 1);
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
SHUTTER_MODE_FORMATTER,
pdev->vcinterface,
buf, 1, HZ / 2);
if (!mode && ret >= 0) { if (!mode && ret >= 0) {
if (value < 0) if (value < 0)
...@@ -763,12 +737,7 @@ static inline int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int v ...@@ -763,12 +737,7 @@ static inline int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int v
break; break;
} }
ret = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), ret = SendControlMsg(SET_LUM_CTL, PRESET_SHUTTER_FORMATTER, 2);
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_SHUTTER_FORMATTER,
pdev->vcinterface,
&buf, 2, HZ / 2);
} }
return ret; return ret;
} }
...@@ -787,12 +756,7 @@ int pwc_camera_power(struct pwc_device *pdev, int power) ...@@ -787,12 +756,7 @@ int pwc_camera_power(struct pwc_device *pdev, int power)
buf = 0x00; /* active */ buf = 0x00; /* active */
else else
buf = 0xFF; /* power save */ buf = 0xFF; /* power save */
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), return SendControlMsg(SET_STATUS_CTL, SET_POWER_SAVE_MODE_FORMATTER, 1);
SET_STATUS_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
SET_POWER_SAVE_MODE_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
} }
...@@ -801,32 +765,20 @@ int pwc_camera_power(struct pwc_device *pdev, int power) ...@@ -801,32 +765,20 @@ int pwc_camera_power(struct pwc_device *pdev, int power)
static inline int pwc_restore_user(struct pwc_device *pdev) static inline int pwc_restore_user(struct pwc_device *pdev)
{ {
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), char buf; /* dummy */
SET_STATUS_CTL, return SendControlMsg(SET_STATUS_CTL, RESTORE_USER_DEFAULTS_FORMATTER, 0);
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
RESTORE_USER_DEFAULTS_FORMATTER,
pdev->vcinterface,
NULL, 0, HZ / 2);
} }
static inline int pwc_save_user(struct pwc_device *pdev) static inline int pwc_save_user(struct pwc_device *pdev)
{ {
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), char buf; /* dummy */
SET_STATUS_CTL, return SendControlMsg(SET_STATUS_CTL, SAVE_USER_DEFAULTS_FORMATTER, 0);
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
SAVE_USER_DEFAULTS_FORMATTER,
pdev->vcinterface,
NULL, 0, HZ / 2);
} }
static inline int pwc_restore_factory(struct pwc_device *pdev) static inline int pwc_restore_factory(struct pwc_device *pdev)
{ {
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), char buf; /* dummy */
SET_STATUS_CTL, return SendControlMsg(SET_STATUS_CTL, RESTORE_FACTORY_DEFAULTS_FORMATTER, 0);
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
RESTORE_FACTORY_DEFAULTS_FORMATTER,
pdev->vcinterface,
NULL, 0, HZ / 2);
} }
/* ************************************************* */ /* ************************************************* */
...@@ -854,12 +806,7 @@ static inline int pwc_set_awb(struct pwc_device *pdev, int mode) ...@@ -854,12 +806,7 @@ static inline int pwc_set_awb(struct pwc_device *pdev, int mode)
buf = mode & 0x07; /* just the lowest three bits */ buf = mode & 0x07; /* just the lowest three bits */
ret = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), ret = SendControlMsg(SET_CHROM_CTL, WB_MODE_FORMATTER, 1);
SET_CHROM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
WB_MODE_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -871,12 +818,7 @@ static inline int pwc_get_awb(struct pwc_device *pdev) ...@@ -871,12 +818,7 @@ static inline int pwc_get_awb(struct pwc_device *pdev)
unsigned char buf; unsigned char buf;
int ret; int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), ret = RecvControlMsg(GET_CHROM_CTL, WB_MODE_FORMATTER, 1);
GET_CHROM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
WB_MODE_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -891,34 +833,21 @@ static inline int pwc_set_red_gain(struct pwc_device *pdev, int value) ...@@ -891,34 +833,21 @@ static inline int pwc_set_red_gain(struct pwc_device *pdev, int value)
value = 0; value = 0;
if (value > 0xffff) if (value > 0xffff)
value = 0xffff; value = 0xffff;
/* only the msb is considered */
/* only the msb are considered */
buf = value >> 8; buf = value >> 8;
return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1);
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_CHROM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_MANUAL_RED_GAIN_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
} }
static inline int pwc_get_red_gain(struct pwc_device *pdev) static inline int pwc_get_red_gain(struct pwc_device *pdev, int *value)
{ {
unsigned char buf; unsigned char buf;
int ret; int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1);
GET_CHROM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_MANUAL_RED_GAIN_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
*value = buf << 8;
return (buf << 8); return 0;
} }
...@@ -930,34 +859,21 @@ static inline int pwc_set_blue_gain(struct pwc_device *pdev, int value) ...@@ -930,34 +859,21 @@ static inline int pwc_set_blue_gain(struct pwc_device *pdev, int value)
value = 0; value = 0;
if (value > 0xffff) if (value > 0xffff)
value = 0xffff; value = 0xffff;
/* only the msb is considered */
/* linear mapping of 0..0xffff to -0x80..0x7f */ buf = value >> 8;
buf = (value >> 8); return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1);
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
SET_CHROM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_MANUAL_BLUE_GAIN_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
} }
static inline int pwc_get_blue_gain(struct pwc_device *pdev) static inline int pwc_get_blue_gain(struct pwc_device *pdev, int *value)
{ {
unsigned char buf; unsigned char buf;
int ret; int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1);
GET_CHROM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_MANUAL_BLUE_GAIN_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
*value = buf << 8;
return (buf << 8); return 0;
} }
...@@ -965,40 +881,28 @@ static inline int pwc_get_blue_gain(struct pwc_device *pdev) ...@@ -965,40 +881,28 @@ static inline int pwc_get_blue_gain(struct pwc_device *pdev)
internal red/blue gains, which may be different from the manual internal red/blue gains, which may be different from the manual
gains set or read above. gains set or read above.
*/ */
static inline int pwc_read_red_gain(struct pwc_device *pdev) static inline int pwc_read_red_gain(struct pwc_device *pdev, int *value)
{ {
unsigned char buf; unsigned char buf;
int ret; int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), ret = RecvControlMsg(GET_STATUS_CTL, READ_RED_GAIN_FORMATTER, 1);
GET_STATUS_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
READ_RED_GAIN_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
*value = buf << 8;
return (buf << 8); return 0;
} }
static inline int pwc_read_blue_gain(struct pwc_device *pdev) static inline int pwc_read_blue_gain(struct pwc_device *pdev, int *value)
{ {
unsigned char buf; unsigned char buf;
int ret; int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), ret = RecvControlMsg(GET_STATUS_CTL, READ_BLUE_GAIN_FORMATTER, 1);
GET_STATUS_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
READ_BLUE_GAIN_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
*value = buf << 8;
return (buf << 8); return 0;
} }
...@@ -1008,28 +912,19 @@ static inline int pwc_set_wb_speed(struct pwc_device *pdev, int speed) ...@@ -1008,28 +912,19 @@ static inline int pwc_set_wb_speed(struct pwc_device *pdev, int speed)
/* useful range is 0x01..0x20 */ /* useful range is 0x01..0x20 */
buf = speed / 0x7f0; buf = speed / 0x7f0;
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1);
SET_CHROM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
AWB_CONTROL_SPEED_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
} }
static inline int pwc_get_wb_speed(struct pwc_device *pdev) static inline int pwc_get_wb_speed(struct pwc_device *pdev, int *value)
{ {
unsigned char buf; unsigned char buf;
int ret; int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1);
GET_CHROM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
AWB_CONTROL_SPEED_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
return (buf * 0x7f0); *value = buf * 0x7f0;
return 0;
} }
...@@ -1039,28 +934,19 @@ static inline int pwc_set_wb_delay(struct pwc_device *pdev, int delay) ...@@ -1039,28 +934,19 @@ static inline int pwc_set_wb_delay(struct pwc_device *pdev, int delay)
/* useful range is 0x01..0x3F */ /* useful range is 0x01..0x3F */
buf = (delay >> 10); buf = (delay >> 10);
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1);
SET_CHROM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
AWB_CONTROL_DELAY_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
} }
static inline int pwc_get_wb_delay(struct pwc_device *pdev) static inline int pwc_get_wb_delay(struct pwc_device *pdev, int *value)
{ {
unsigned char buf; unsigned char buf;
int ret; int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1);
GET_CHROM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
AWB_CONTROL_DELAY_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
return (buf << 10); *value = buf << 10;
return 0;
} }
...@@ -1115,12 +1001,7 @@ static inline int pwc_set_contour(struct pwc_device *pdev, int contour) ...@@ -1115,12 +1001,7 @@ static inline int pwc_set_contour(struct pwc_device *pdev, int contour)
buf = 0xff; /* auto contour on */ buf = 0xff; /* auto contour on */
else else
buf = 0x0; /* auto contour off */ buf = 0x0; /* auto contour off */
ret = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), ret = SendControlMsg(SET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1);
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
AUTO_CONTOUR_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -1130,12 +1011,7 @@ static inline int pwc_set_contour(struct pwc_device *pdev, int contour) ...@@ -1130,12 +1011,7 @@ static inline int pwc_set_contour(struct pwc_device *pdev, int contour)
contour = 0xffff; contour = 0xffff;
buf = (contour >> 10); /* contour preset is [0..3f] */ buf = (contour >> 10); /* contour preset is [0..3f] */
ret = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), ret = SendControlMsg(SET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1);
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_CONTOUR_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
return 0; return 0;
...@@ -1146,26 +1022,16 @@ static inline int pwc_get_contour(struct pwc_device *pdev, int *contour) ...@@ -1146,26 +1022,16 @@ static inline int pwc_get_contour(struct pwc_device *pdev, int *contour)
unsigned char buf; unsigned char buf;
int ret; int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), ret = RecvControlMsg(GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1);
GET_LUM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
AUTO_CONTOUR_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (buf == 0) { if (buf == 0) {
/* auto mode off, query current preset value */ /* auto mode off, query current preset value */
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), ret = RecvControlMsg(GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1);
GET_LUM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_CONTOUR_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
*contour = (buf << 10); *contour = buf << 10;
} }
else else
*contour = -1; *contour = -1;
...@@ -1181,28 +1047,19 @@ static inline int pwc_set_backlight(struct pwc_device *pdev, int backlight) ...@@ -1181,28 +1047,19 @@ static inline int pwc_set_backlight(struct pwc_device *pdev, int backlight)
buf = 0xff; buf = 0xff;
else else
buf = 0x0; buf = 0x0;
return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), return SendControlMsg(SET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1);
SET_LUM_CTL,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
BACK_LIGHT_COMPENSATION_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
} }
static inline int pwc_get_backlight(struct pwc_device *pdev) static inline int pwc_get_backlight(struct pwc_device *pdev, int *backlight)
{ {
int ret; int ret;
unsigned char buf; unsigned char buf;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), ret = RecvControlMsg(GET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1);
GET_LUM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
BACK_LIGHT_COMPENSATION_FORMATTER,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
return buf; *backlight = buf;
return 0;
} }
...@@ -1217,7 +1074,7 @@ static inline int pwc_set_flicker(struct pwc_device *pdev, int flicker) ...@@ -1217,7 +1074,7 @@ static inline int pwc_set_flicker(struct pwc_device *pdev, int flicker)
return SendControlMsg(SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); return SendControlMsg(SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1);
} }
static inline int pwc_get_flicker(struct pwc_device *pdev) static inline int pwc_get_flicker(struct pwc_device *pdev, int *flicker)
{ {
int ret; int ret;
unsigned char buf; unsigned char buf;
...@@ -1225,7 +1082,8 @@ static inline int pwc_get_flicker(struct pwc_device *pdev) ...@@ -1225,7 +1082,8 @@ static inline int pwc_get_flicker(struct pwc_device *pdev)
ret = RecvControlMsg(GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); ret = RecvControlMsg(GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1);
if (ret < 0) if (ret < 0)
return ret; return ret;
return buf; *flicker = buf;
return 0;
} }
...@@ -1241,7 +1099,7 @@ static inline int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise) ...@@ -1241,7 +1099,7 @@ static inline int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise)
return SendControlMsg(SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); return SendControlMsg(SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1);
} }
static inline int pwc_get_dynamic_noise(struct pwc_device *pdev) static inline int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise)
{ {
int ret; int ret;
unsigned char buf; unsigned char buf;
...@@ -1249,14 +1107,15 @@ static inline int pwc_get_dynamic_noise(struct pwc_device *pdev) ...@@ -1249,14 +1107,15 @@ static inline int pwc_get_dynamic_noise(struct pwc_device *pdev)
ret = RecvControlMsg(GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); ret = RecvControlMsg(GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1);
if (ret < 0) if (ret < 0)
return ret; return ret;
return buf; *noise = buf;
return 0;
} }
int pwc_mpt_reset(struct pwc_device *pdev, int flags) int pwc_mpt_reset(struct pwc_device *pdev, int flags)
{ {
unsigned char buf; unsigned char buf;
buf = flags & 0x03; // only lower two bits are currently used buf = flags & 0x03; // only lower two bits are currently used
return SendControlMsg(SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, 1); return SendControlMsg(SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, 1);
} }
...@@ -1293,7 +1152,7 @@ static inline int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_sta ...@@ -1293,7 +1152,7 @@ static inline int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_sta
} }
int pwc_get_cmos_sensor(struct pwc_device *pdev) int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor)
{ {
unsigned char buf; unsigned char buf;
int ret = -1, request; int ret = -1, request;
...@@ -1305,24 +1164,60 @@ int pwc_get_cmos_sensor(struct pwc_device *pdev) ...@@ -1305,24 +1164,60 @@ int pwc_get_cmos_sensor(struct pwc_device *pdev)
else else
request = SENSOR_TYPE_FORMATTER2; request = SENSOR_TYPE_FORMATTER2;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), ret = RecvControlMsg(GET_STATUS_CTL, request, 1);
GET_STATUS_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
request,
pdev->vcinterface,
&buf, 1, HZ / 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (pdev->type < 675) if (pdev->type < 675)
return buf | 0x100; *sensor = buf | 0x100;
else else
return buf; *sensor = buf;
return 0;
} }
/* End of Add-Ons */ /* End of Add-Ons */
/* ************************************************* */ /* ************************************************* */
/* Linux 2.5.something and 2.6 pass direct pointers to arguments of
ioctl() calls. With 2.4, you have to do tedious copy_from_user()
and copy_to_user() calls. With these macros we circumvent this,
and let me maintain only one source file. The functionality is
exactly the same otherwise.
*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
/* define local variable for arg */
#define ARG_DEF(ARG_type, ARG_name)\
ARG_type *ARG_name = arg;
/* copy arg to local variable */
#define ARG_IN(ARG_name) /* nothing */
/* argument itself (referenced) */
#define ARGR(ARG_name) (*ARG_name)
/* argument address */
#define ARGA(ARG_name) ARG_name
/* copy local variable to arg */
#define ARG_OUT(ARG_name) /* nothing */
#else
#define ARG_DEF(ARG_type, ARG_name)\
ARG_type ARG_name;
#define ARG_IN(ARG_name)\
if (copy_from_user(&ARG_name, arg, sizeof(ARG_name))) {\
ret = -EFAULT;\
break;\
}
#define ARGR(ARG_name) ARG_name
#define ARGA(ARG_name) &ARG_name
#define ARG_OUT(ARG_name)\
if (copy_to_user(arg, &ARG_name, sizeof(ARG_name))) {\
ret = -EFAULT;\
break;\
}
#endif
int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
{ {
int ret = 0; int ret = 0;
...@@ -1351,225 +1246,254 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) ...@@ -1351,225 +1246,254 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
case VIDIOCPWCSCQUAL: case VIDIOCPWCSCQUAL:
{ {
int *qual = arg; ARG_DEF(int, qual)
if (*qual < 0 || *qual > 3) ARG_IN(qual)
if (ARGR(qual) < 0 || ARGR(qual) > 3)
ret = -EINVAL; ret = -EINVAL;
else else
ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, *qual, pdev->vsnapshot); ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, ARGR(qual), pdev->vsnapshot);
if (ret >= 0) if (ret >= 0)
pdev->vcompression = *qual; pdev->vcompression = ARGR(qual);
break; break;
} }
case VIDIOCPWCGCQUAL: case VIDIOCPWCGCQUAL:
{ {
int *qual = arg; ARG_DEF(int, qual)
*qual = pdev->vcompression; ARGR(qual) = pdev->vcompression;
ARG_OUT(qual)
break; break;
} }
case VIDIOCPWCPROBE: case VIDIOCPWCPROBE:
{ {
struct pwc_probe *probe = arg; ARG_DEF(struct pwc_probe, probe)
strcpy(probe->name, pdev->vdev.name); strcpy(ARGR(probe).name, pdev->vdev->name);
probe->type = pdev->type; ARGR(probe).type = pdev->type;
ARG_OUT(probe)
break;
}
case VIDIOCPWCGSERIAL:
{
ARG_DEF(struct pwc_serial, serial)
strcpy(ARGR(serial).serial, pdev->serial);
ARG_OUT(serial)
break; break;
} }
case VIDIOCPWCSAGC: case VIDIOCPWCSAGC:
{ {
int *agc = arg; ARG_DEF(int, agc)
if (pwc_set_agc(pdev, *agc < 0 ? 1 : 0, *agc)) ARG_IN(agc)
if (pwc_set_agc(pdev, ARGR(agc) < 0 ? 1 : 0, ARGR(agc)))
ret = -EINVAL; ret = -EINVAL;
break; break;
} }
case VIDIOCPWCGAGC: case VIDIOCPWCGAGC:
{ {
int *agc = arg; ARG_DEF(int, agc)
if (pwc_get_agc(pdev, agc)) if (pwc_get_agc(pdev, ARGA(agc)))
ret = -EINVAL; ret = -EINVAL;
ARG_OUT(agc)
break; break;
} }
case VIDIOCPWCSSHUTTER: case VIDIOCPWCSSHUTTER:
{ {
int *shutter_speed = arg; ARG_DEF(int, shutter_speed)
ret = pwc_set_shutter_speed(pdev, *shutter_speed < 0 ? 1 : 0, *shutter_speed); ARG_IN(shutter_speed)
ret = pwc_set_shutter_speed(pdev, ARGR(shutter_speed) < 0 ? 1 : 0, ARGR(shutter_speed));
break; break;
} }
case VIDIOCPWCSAWB: case VIDIOCPWCSAWB:
{ {
struct pwc_whitebalance *wb = arg; ARG_DEF(struct pwc_whitebalance, wb)
ret = pwc_set_awb(pdev, wb->mode); ARG_IN(wb)
if (ret >= 0 && wb->mode == PWC_WB_MANUAL) { ret = pwc_set_awb(pdev, ARGR(wb).mode);
pwc_set_red_gain(pdev, wb->manual_red); if (ret >= 0 && ARGR(wb).mode == PWC_WB_MANUAL) {
pwc_set_blue_gain(pdev, wb->manual_blue); pwc_set_red_gain(pdev, ARGR(wb).manual_red);
pwc_set_blue_gain(pdev, ARGR(wb).manual_blue);
} }
break; break;
} }
case VIDIOCPWCGAWB: case VIDIOCPWCGAWB:
{ {
struct pwc_whitebalance *wb = arg; ARG_DEF(struct pwc_whitebalance, wb)
memset(wb, 0, sizeof(*wb)); memset(ARGA(wb), 0, sizeof(struct pwc_whitebalance));
wb->mode = pwc_get_awb(pdev); ARGR(wb).mode = pwc_get_awb(pdev);
if (wb->mode < 0) if (ARGR(wb).mode < 0)
ret = -EINVAL; ret = -EINVAL;
else { else {
if (wb->mode == PWC_WB_MANUAL) { if (ARGR(wb).mode == PWC_WB_MANUAL) {
wb->manual_red = pwc_get_red_gain(pdev); ret = pwc_get_red_gain(pdev, &ARGR(wb).manual_red);
wb->manual_blue = pwc_get_blue_gain(pdev); if (ret < 0)
break;
ret = pwc_get_blue_gain(pdev, &ARGR(wb).manual_blue);
if (ret < 0)
break;
} }
if (wb->mode == PWC_WB_AUTO) { if (ARGR(wb).mode == PWC_WB_AUTO) {
wb->read_red = pwc_read_red_gain(pdev); ret = pwc_read_red_gain(pdev, &ARGR(wb).read_red);
wb->read_blue = pwc_read_blue_gain(pdev); if (ret < 0)
break;
ret =pwc_read_blue_gain(pdev, &ARGR(wb).read_blue);
if (ret < 0)
break;
} }
} }
ARG_OUT(wb)
break; break;
} }
case VIDIOCPWCSAWBSPEED: case VIDIOCPWCSAWBSPEED:
{ {
struct pwc_wb_speed *wbs = arg; ARG_DEF(struct pwc_wb_speed, wbs)
if (wbs->control_speed > 0) { if (ARGR(wbs).control_speed > 0) {
ret = pwc_set_wb_speed(pdev, wbs->control_speed); ret = pwc_set_wb_speed(pdev, ARGR(wbs).control_speed);
} }
if (wbs->control_delay > 0) { if (ARGR(wbs).control_delay > 0) {
ret = pwc_set_wb_delay(pdev, wbs->control_delay); ret = pwc_set_wb_delay(pdev, ARGR(wbs).control_delay);
} }
break; break;
} }
case VIDIOCPWCGAWBSPEED: case VIDIOCPWCGAWBSPEED:
{ {
struct pwc_wb_speed *wbs = arg; ARG_DEF(struct pwc_wb_speed, wbs)
ret = pwc_get_wb_speed(pdev); ret = pwc_get_wb_speed(pdev, &ARGR(wbs).control_speed);
if (ret < 0) if (ret < 0)
break; break;
wbs->control_speed = ret; ret = pwc_get_wb_delay(pdev, &ARGR(wbs).control_delay);
ret = pwc_get_wb_delay(pdev);
if (ret < 0) if (ret < 0)
break; break;
wbs->control_delay = ret; ARG_OUT(wbs)
break; break;
} }
case VIDIOCPWCSLED: case VIDIOCPWCSLED:
{ {
struct pwc_leds *leds = arg; ARG_DEF(struct pwc_leds, leds)
ret = pwc_set_leds(pdev, leds->led_on, leds->led_off); ARG_IN(leds)
ret = pwc_set_leds(pdev, ARGR(leds).led_on, ARGR(leds).led_off);
break; break;
} }
case VIDIOCPWCGLED: case VIDIOCPWCGLED:
{ {
struct pwc_leds *leds = arg; ARG_DEF(struct pwc_leds, leds)
ret = pwc_get_leds(pdev, &leds->led_on, &leds->led_off); ret = pwc_get_leds(pdev, &ARGR(leds).led_on, &ARGR(leds).led_off);
ARG_OUT(leds)
break; break;
} }
case VIDIOCPWCSCONTOUR: case VIDIOCPWCSCONTOUR:
{ {
int *contour = arg; ARG_DEF(int, contour)
ret = pwc_set_contour(pdev, *contour); ARG_IN(contour)
ret = pwc_set_contour(pdev, ARGR(contour));
break; break;
} }
case VIDIOCPWCGCONTOUR: case VIDIOCPWCGCONTOUR:
{ {
int *contour = arg; ARG_DEF(int, contour)
ret = pwc_get_contour(pdev, contour); ret = pwc_get_contour(pdev, ARGA(contour));
ARG_OUT(contour)
break; break;
} }
case VIDIOCPWCSBACKLIGHT: case VIDIOCPWCSBACKLIGHT:
{ {
int *backlight = arg; ARG_DEF(int, backlight)
ret = pwc_set_backlight(pdev, *backlight); ARG_IN(backlight)
ret = pwc_set_backlight(pdev, ARGR(backlight));
break; break;
} }
case VIDIOCPWCGBACKLIGHT: case VIDIOCPWCGBACKLIGHT:
{ {
int *backlight = arg; ARG_DEF(int, backlight)
ret = pwc_get_backlight(pdev); ret = pwc_get_backlight(pdev, ARGA(backlight));
if (ret >= 0) ARG_OUT(backlight)
*backlight = ret;
break; break;
} }
case VIDIOCPWCSFLICKER: case VIDIOCPWCSFLICKER:
{ {
int *flicker = arg; ARG_DEF(int, flicker)
ret = pwc_set_flicker(pdev, *flicker); ARG_IN(flicker)
ret = pwc_set_flicker(pdev, ARGR(flicker));
break; break;
} }
case VIDIOCPWCGFLICKER: case VIDIOCPWCGFLICKER:
{ {
int *flicker = arg; ARG_DEF(int, flicker)
ret = pwc_get_flicker(pdev); ret = pwc_get_flicker(pdev, ARGA(flicker));
if (ret >= 0) ARG_OUT(flicker)
*flicker = ret;
break; break;
} }
case VIDIOCPWCSDYNNOISE: case VIDIOCPWCSDYNNOISE:
{ {
int *dynnoise = arg; ARG_DEF(int, dynnoise)
ret = pwc_set_dynamic_noise(pdev, *dynnoise); ARG_IN(dynnoise)
ret = pwc_set_dynamic_noise(pdev, ARGR(dynnoise));
break; break;
} }
case VIDIOCPWCGDYNNOISE: case VIDIOCPWCGDYNNOISE:
{ {
int *dynnoise = arg; ARG_DEF(int, dynnoise)
ret = pwc_get_dynamic_noise(pdev); ret = pwc_get_dynamic_noise(pdev, ARGA(dynnoise));
if (ret < 0) ARG_OUT(dynnoise);
break;
*dynnoise = ret;
break; break;
} }
case VIDIOCPWCGREALSIZE: case VIDIOCPWCGREALSIZE:
{ {
struct pwc_imagesize *size = arg; ARG_DEF(struct pwc_imagesize, size)
size->width = pdev->image.x; ARGR(size).width = pdev->image.x;
size->height = pdev->image.y; ARGR(size).height = pdev->image.y;
ARG_OUT(size)
break; break;
} }
case VIDIOCPWCMPTRESET: case VIDIOCPWCMPTRESET:
{ {
int *flags = arg;
if (pdev->features & FEATURE_MOTOR_PANTILT) if (pdev->features & FEATURE_MOTOR_PANTILT)
{ {
ret = pwc_mpt_reset(pdev, *flags); ARG_DEF(int, flags)
ARG_IN(flags)
ret = pwc_mpt_reset(pdev, ARGR(flags));
if (ret >= 0) if (ret >= 0)
{ {
pdev->pan_angle = 0; pdev->pan_angle = 0;
...@@ -1582,11 +1506,15 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) ...@@ -1582,11 +1506,15 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
} }
break; break;
} }
case VIDIOCPWCMPTGRANGE: case VIDIOCPWCMPTGRANGE:
{ {
if (pdev->features & FEATURE_MOTOR_PANTILT) if (pdev->features & FEATURE_MOTOR_PANTILT)
{ {
memcpy(arg, &pdev->angle_range, sizeof(struct pwc_mpt_range)); ARG_DEF(struct pwc_mpt_range, range)
ARGR(range) = pdev->angle_range;
ARG_OUT(range)
} }
else else
{ {
...@@ -1597,23 +1525,25 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) ...@@ -1597,23 +1525,25 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
case VIDIOCPWCMPTSANGLE: case VIDIOCPWCMPTSANGLE:
{ {
struct pwc_mpt_angles *angles = arg;
int new_pan, new_tilt; int new_pan, new_tilt;
if (pdev->features & FEATURE_MOTOR_PANTILT) if (pdev->features & FEATURE_MOTOR_PANTILT)
{ {
ARG_DEF(struct pwc_mpt_angles, angles)
ARG_IN(angles)
/* The camera can only set relative angles, so /* The camera can only set relative angles, so
do some calculations when getting an absolute angle . do some calculations when getting an absolute angle .
*/ */
if (angles->absolute) if (ARGR(angles).absolute)
{ {
new_pan = angles->pan; new_pan = ARGR(angles).pan;
new_tilt = angles->tilt; new_tilt = ARGR(angles).tilt;
} }
else else
{ {
new_pan = pdev->pan_angle + angles->pan; new_pan = pdev->pan_angle + ARGR(angles).pan;
new_tilt = pdev->tilt_angle + angles->tilt; new_tilt = pdev->tilt_angle + ARGR(angles).tilt;
} }
/* check absolute ranges */ /* check absolute ranges */
if (new_pan < pdev->angle_range.pan_min || if (new_pan < pdev->angle_range.pan_min ||
...@@ -1649,17 +1579,19 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) ...@@ -1649,17 +1579,19 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
ret = -ENXIO; ret = -ENXIO;
} }
break; break;
} }
case VIDIOCPWCMPTGANGLE: case VIDIOCPWCMPTGANGLE:
{ {
struct pwc_mpt_angles *angles = arg;
if (pdev->features & FEATURE_MOTOR_PANTILT) if (pdev->features & FEATURE_MOTOR_PANTILT)
{ {
angles->absolute = 1; ARG_DEF(struct pwc_mpt_angles, angles)
angles->pan = pdev->pan_angle;
angles->tilt = pdev->tilt_angle; ARGR(angles).absolute = 1;
ARGR(angles).pan = pdev->pan_angle;
ARGR(angles).tilt = pdev->tilt_angle;
ARG_OUT(angles)
} }
else else
{ {
...@@ -1670,19 +1602,34 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) ...@@ -1670,19 +1602,34 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
case VIDIOCPWCMPTSTATUS: case VIDIOCPWCMPTSTATUS:
{ {
struct pwc_mpt_status *status = arg;
if (pdev->features & FEATURE_MOTOR_PANTILT) if (pdev->features & FEATURE_MOTOR_PANTILT)
{ {
ret = pwc_mpt_get_status(pdev, status); ARG_DEF(struct pwc_mpt_status, status)
ret = pwc_mpt_get_status(pdev, ARGA(status));
ARG_OUT(status)
} }
else else
{ {
ret = -ENXIO; ret = -ENXIO;
} }
break; break;
} }
case VIDIOCPWCGVIDCMD:
{
ARG_DEF(struct pwc_video_command, cmd);
ARGR(cmd).type = pdev->type;
ARGR(cmd).release = pdev->release;
ARGR(cmd).command_len = pdev->cmd_len;
memcpy(&ARGR(cmd).command_buf, pdev->cmd_buf, pdev->cmd_len);
ARGR(cmd).bandlength = pdev->vbandlength;
ARGR(cmd).frame_size = pdev->frame_size;
ARG_OUT(cmd)
break;
}
default: default:
ret = -ENOIOCTLCMD; ret = -ENOIOCTLCMD;
break; break;
......
/* Linux driver for Philips webcam /* Linux driver for Philips webcam
USB and Video4Linux interface part. USB and Video4Linux interface part.
(C) 1999-2003 Nemosoft Unv. (C) 1999-2004 Nemosoft Unv.
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
- Alistar Moire: QuickCam 3000 Pro device/product ID - Alistar Moire: QuickCam 3000 Pro device/product ID
- Tony Hoyle: Creative Labs Webcam 5 device/product ID - Tony Hoyle: Creative Labs Webcam 5 device/product ID
- Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged
- Jk Fang: SOTEC Afina Eye ID - Jk Fang: Sotec Afina Eye ID
- Xavier Roche: QuickCam Pro 4000 ID - Xavier Roche: QuickCam Pro 4000 ID
- Jens Knudsen: QuickCam Zoom ID - Jens Knudsen: QuickCam Zoom ID
- J. Debert: QuickCam for Notebooks ID - J. Debert: QuickCam for Notebooks ID
...@@ -90,6 +90,7 @@ static struct usb_device_id pwc_device_table [] = { ...@@ -90,6 +90,7 @@ static struct usb_device_id pwc_device_table [] = {
{ USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */ { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */
{ USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */ { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */
{ USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */ { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */
{ USB_DEVICE(0x06BE, 0x8116) }, /* new Afina Eye */
{ USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */ { USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */
{ USB_DEVICE(0x0d81, 0x1900) }, { USB_DEVICE(0x0d81, 0x1900) },
{ } { }
...@@ -151,7 +152,9 @@ static struct video_device pwc_template = { ...@@ -151,7 +152,9 @@ static struct video_device pwc_template = {
.name = "Philips Webcam", /* Filled in later */ .name = "Philips Webcam", /* Filled in later */
.type = VID_TYPE_CAPTURE, .type = VID_TYPE_CAPTURE,
.hardware = VID_HARDWARE_PWC, .hardware = VID_HARDWARE_PWC,
.release = video_device_release,
.fops = &pwc_fops, .fops = &pwc_fops,
.minor = -1,
}; };
/***************************************************************************/ /***************************************************************************/
...@@ -258,7 +261,7 @@ static int pwc_allocate_buffers(struct pwc_device *pdev) ...@@ -258,7 +261,7 @@ static int pwc_allocate_buffers(struct pwc_device *pdev)
return -ENXIO; return -ENXIO;
} }
#endif #endif
/* Allocate Isochronous pipe buffers */ /* Allocate Isochronuous pipe buffers */
for (i = 0; i < MAX_ISO_BUFS; i++) { for (i = 0; i < MAX_ISO_BUFS; i++) {
if (pdev->sbuf[i].data == NULL) { if (pdev->sbuf[i].data == NULL) {
kbuf = kmalloc(ISO_BUFFER_SIZE, GFP_KERNEL); kbuf = kmalloc(ISO_BUFFER_SIZE, GFP_KERNEL);
...@@ -443,7 +446,7 @@ static inline int pwc_next_fill_frame(struct pwc_device *pdev) ...@@ -443,7 +446,7 @@ static inline int pwc_next_fill_frame(struct pwc_device *pdev)
{ {
int ret; int ret;
unsigned long flags; unsigned long flags;
ret = 0; ret = 0;
spin_lock_irqsave(&pdev->ptrlock, flags); spin_lock_irqsave(&pdev->ptrlock, flags);
if (pdev->fill_frame != NULL) { if (pdev->fill_frame != NULL) {
...@@ -488,7 +491,7 @@ static inline int pwc_next_fill_frame(struct pwc_device *pdev) ...@@ -488,7 +491,7 @@ static inline int pwc_next_fill_frame(struct pwc_device *pdev)
/** /**
\brief Reset all buffers, pointers and lists, except for the image_used[] buffer. \brief Reset all buffers, pointers and lists, except for the image_used[] buffer.
If the image_used[] buffer is cleared too, mmap()/VIDIOCSYNC will run into trouble. If the image_used[] buffer is cleared too, mmap()/VIDIOCSYNC will run into trouble.
*/ */
static void pwc_reset_buffers(struct pwc_device *pdev) static void pwc_reset_buffers(struct pwc_device *pdev)
...@@ -525,7 +528,7 @@ static int pwc_handle_frame(struct pwc_device *pdev) ...@@ -525,7 +528,7 @@ static int pwc_handle_frame(struct pwc_device *pdev)
{ {
int ret = 0; int ret = 0;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&pdev->ptrlock, flags); spin_lock_irqsave(&pdev->ptrlock, flags);
/* First grab our read_frame; this is removed from all lists, so /* First grab our read_frame; this is removed from all lists, so
we can release the lock after this without problems */ we can release the lock after this without problems */
...@@ -548,7 +551,7 @@ static int pwc_handle_frame(struct pwc_device *pdev) ...@@ -548,7 +551,7 @@ static int pwc_handle_frame(struct pwc_device *pdev)
Trace(TRACE_SEQUENCE, "Decompressing frame %d\n", pdev->read_frame->sequence); Trace(TRACE_SEQUENCE, "Decompressing frame %d\n", pdev->read_frame->sequence);
#endif #endif
/* Decompression is a lenghty process, so it's outside of the lock. /* Decompression is a lenghty process, so it's outside of the lock.
This gives the isoc_handler the opportunity to fill more frames This gives the isoc_handler the opportunity to fill more frames
in the mean time. in the mean time.
*/ */
spin_unlock_irqrestore(&pdev->ptrlock, flags); spin_unlock_irqrestore(&pdev->ptrlock, flags);
...@@ -643,7 +646,7 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs) ...@@ -643,7 +646,7 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
else { else {
fillptr = fbuf->data + fbuf->filled; fillptr = fbuf->data + fbuf->filled;
} }
/* Reset ISOC error counter. We did get here, after all. */ /* Reset ISOC error counter. We did get here, after all. */
pdev->visoc_errors = 0; pdev->visoc_errors = 0;
...@@ -662,8 +665,8 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs) ...@@ -662,8 +665,8 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
pdev->vsync = 2; pdev->vsync = 2;
/* ...copy data to frame buffer, if possible */ /* ...copy data to frame buffer, if possible */
if (flen + fbuf->filled > pdev->frame_size) { if (flen + fbuf->filled > pdev->frame_total_size) {
Trace(TRACE_FLOW, "Frame buffer overflow (flen = %d, frame_size = %d).\n", flen, pdev->frame_size); Trace(TRACE_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->vsync = 0; /* Hmm, let's wait for an EOF (end-of-frame) */
pdev->vframes_error++; pdev->vframes_error++;
} }
...@@ -728,7 +731,7 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs) ...@@ -728,7 +731,7 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
pdev->drop_frames--; pdev->drop_frames--;
else { else {
/* Check for underflow first */ /* Check for underflow first */
if (fbuf->filled < pdev->frame_size) { if (fbuf->filled < pdev->frame_total_size) {
Trace(TRACE_FLOW, "Frame buffer underflow (%d bytes); discarded.\n", fbuf->filled); Trace(TRACE_FLOW, "Frame buffer underflow (%d bytes); discarded.\n", fbuf->filled);
pdev->vframes_error++; pdev->vframes_error++;
} }
...@@ -817,7 +820,7 @@ static int pwc_isoc_init(struct pwc_device *pdev) ...@@ -817,7 +820,7 @@ static int pwc_isoc_init(struct pwc_device *pdev)
if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) { 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"); Err("Failed to find packet size for video endpoint in current alternate setting.\n");
return -ENFILE; /* Odd error, that should be noticeable */ return -ENFILE; /* Odd error, that should be noticable */
} }
/* Set alternate interface */ /* Set alternate interface */
...@@ -874,7 +877,7 @@ static int pwc_isoc_init(struct pwc_device *pdev) ...@@ -874,7 +877,7 @@ static int pwc_isoc_init(struct pwc_device *pdev)
if (ret) if (ret)
Err("isoc_init() submit_urb %d failed with error %d\n", i, ret); Err("isoc_init() submit_urb %d failed with error %d\n", i, ret);
else else
Trace(TRACE_OPEN, "URB 0x%p submitted.\n", pdev->sbuf[i].urb); Trace(TRACE_MEMORY, "URB 0x%p submitted.\n", pdev->sbuf[i].urb);
} }
/* All is done... */ /* All is done... */
...@@ -886,7 +889,7 @@ static int pwc_isoc_init(struct pwc_device *pdev) ...@@ -886,7 +889,7 @@ static int pwc_isoc_init(struct pwc_device *pdev)
static void pwc_isoc_cleanup(struct pwc_device *pdev) static void pwc_isoc_cleanup(struct pwc_device *pdev)
{ {
int i; int i;
Trace(TRACE_OPEN, ">> pwc_isoc_cleanup()\n"); Trace(TRACE_OPEN, ">> pwc_isoc_cleanup()\n");
if (pdev == NULL) if (pdev == NULL)
return; return;
...@@ -937,9 +940,9 @@ int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_f ...@@ -937,9 +940,9 @@ int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_f
Trace(TRACE_FLOW, "pwc_set_video_mode attempt 2 failed.\n"); Trace(TRACE_FLOW, "pwc_set_video_mode attempt 2 failed.\n");
} }
} }
if (start == 0) if (start == 0)
{ {
if (pwc_isoc_init(pdev) < 0) if (pwc_isoc_init(pdev) < 0)
{ {
Info("Failed to restart ISOC transfers in pwc_try_video_mode.\n"); Info("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 */ ret = -EAGAIN; /* let's try again, who knows if it works a second time */
...@@ -972,27 +975,31 @@ static int pwc_video_open(struct inode *inode, struct file *file) ...@@ -972,27 +975,31 @@ static int pwc_video_open(struct inode *inode, struct file *file)
Trace(TRACE_OPEN, "Doing first time initialization.\n"); Trace(TRACE_OPEN, "Doing first time initialization.\n");
pdev->usb_init = 1; pdev->usb_init = 1;
if (pwc_trace & TRACE_OPEN) { if (pwc_trace & TRACE_OPEN)
{
/* Query sensor type */ /* Query sensor type */
const char *sensor_type = NULL; const char *sensor_type = NULL;
int ret;
i = pwc_get_cmos_sensor(pdev); ret = pwc_get_cmos_sensor(pdev, &i);
switch(i) { if (ret >= 0)
case -1: /* Unknown, show nothing */; break; {
case 0x00: sensor_type = "Hyundai CMOS sensor"; break; switch(i) {
case 0x20: sensor_type = "Sony CCD sensor + TDA8787"; break; case 0x00: sensor_type = "Hyundai CMOS sensor"; break;
case 0x2E: sensor_type = "Sony CCD sensor + Exas 98L59"; break; case 0x20: sensor_type = "Sony CCD sensor + TDA8787"; break;
case 0x2F: sensor_type = "Sony CCD sensor + ADI 9804"; break; case 0x2E: sensor_type = "Sony CCD sensor + Exas 98L59"; break;
case 0x30: sensor_type = "Sharp CCD sensor + TDA8787"; break; case 0x2F: sensor_type = "Sony CCD sensor + ADI 9804"; break;
case 0x3E: sensor_type = "Sharp CCD sensor + Exas 98L59"; break; case 0x30: sensor_type = "Sharp CCD sensor + TDA8787"; break;
case 0x3F: sensor_type = "Sharp CCD sensor + ADI 9804"; break; case 0x3E: sensor_type = "Sharp CCD sensor + Exas 98L59"; break;
case 0x40: sensor_type = "UPA 1021 sensor"; break; case 0x3F: sensor_type = "Sharp CCD sensor + ADI 9804"; break;
case 0x100: sensor_type = "VGA sensor"; break; case 0x40: sensor_type = "UPA 1021 sensor"; break;
case 0x101: sensor_type = "PAL MR sensor"; break; case 0x100: sensor_type = "VGA sensor"; break;
default: sensor_type = "unknown type of sensor"; break; case 0x101: sensor_type = "PAL MR sensor"; break;
default: sensor_type = "unknown type of sensor"; break;
}
} }
if (sensor_type != NULL) if (sensor_type != NULL)
Info("This %s camera is equipped with a %s (%d).\n", pdev->vdev.name, sensor_type, i); Info("This %s camera is equipped with a %s (%d).\n", pdev->vdev->name, sensor_type, i);
} }
} }
...@@ -1037,6 +1044,7 @@ static int pwc_video_open(struct inode *inode, struct file *file) ...@@ -1037,6 +1044,7 @@ static int pwc_video_open(struct inode *inode, struct file *file)
/* Set some defaults */ /* Set some defaults */
pdev->vsnapshot = 0; pdev->vsnapshot = 0;
/* Start iso pipe for video; first try the last used video size /* Start iso pipe for video; first try the last used video size
(or the default one); if that fails try QCIF/10 or QSIF/10; (or the default one); if that fails try QCIF/10 or QSIF/10;
it that fails too, give up. it that fails too, give up.
...@@ -1090,7 +1098,7 @@ static int pwc_video_close(struct inode *inode, struct file *file) ...@@ -1090,7 +1098,7 @@ static int pwc_video_close(struct inode *inode, struct file *file)
/* Dump statistics, but only if a reasonable amount of frames were /* Dump statistics, but only if a reasonable amount of frames were
processed (to prevent endless log-entries in case of snap-shot processed (to prevent endless log-entries in case of snap-shot
programs) programs)
*/ */
if (pdev->vframe_count > 20) 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); Info("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error);
...@@ -1111,7 +1119,7 @@ static int pwc_video_close(struct inode *inode, struct file *file) ...@@ -1111,7 +1119,7 @@ static int pwc_video_close(struct inode *inode, struct file *file)
Info("Failed to set LED on/off time.\n"); Info("Failed to set LED on/off time.\n");
if (power_save) { if (power_save) {
i = pwc_camera_power(pdev, 0); i = pwc_camera_power(pdev, 0);
if (i < 0) if (i < 0)
Err("Failed to power down camera (%d)\n", i); Err("Failed to power down camera (%d)\n", i);
} }
} }
...@@ -1139,6 +1147,7 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf, ...@@ -1139,6 +1147,7 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf,
struct pwc_device *pdev; struct pwc_device *pdev;
int noblock = file->f_flags & O_NONBLOCK; int noblock = file->f_flags & O_NONBLOCK;
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
int bytes_to_read;
Trace(TRACE_READ, "video_read(0x%p, %p, %d) called.\n", vdev, buf, count); Trace(TRACE_READ, "video_read(0x%p, %p, %d) called.\n", vdev, buf, count);
if (vdev == NULL) if (vdev == NULL)
...@@ -1175,20 +1184,25 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf, ...@@ -1175,20 +1184,25 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf,
} }
remove_wait_queue(&pdev->frameq, &wait); remove_wait_queue(&pdev->frameq, &wait);
set_current_state(TASK_RUNNING); set_current_state(TASK_RUNNING);
/* Decompress and release frame */ /* Decompress and release frame */
if (pwc_handle_frame(pdev)) if (pwc_handle_frame(pdev))
return -EFAULT; return -EFAULT;
} }
Trace(TRACE_READ, "Copying data to user space.\n"); Trace(TRACE_READ, "Copying data to user space.\n");
if (pdev->vpalette == VIDEO_PALETTE_RAW)
bytes_to_read = pdev->frame_size;
else
bytes_to_read = pdev->view.size;
/* copy bytes to user space; we allow for partial reads */ /* copy bytes to user space; we allow for partial reads */
if (count + pdev->image_read_pos > pdev->view.size) if (count + pdev->image_read_pos > bytes_to_read)
count = pdev->view.size - pdev->image_read_pos; count = bytes_to_read - pdev->image_read_pos;
if (copy_to_user(buf, pdev->image_ptr[pdev->fill_image] + pdev->image_read_pos, count)) if (copy_to_user(buf, pdev->image_ptr[pdev->fill_image] + pdev->image_read_pos, count))
return -EFAULT; return -EFAULT;
pdev->image_read_pos += count; pdev->image_read_pos += count;
if (pdev->image_read_pos >= pdev->view.size) { /* All data has been read */ if (pdev->image_read_pos >= bytes_to_read) { /* All data has been read */
pdev->image_read_pos = 0; pdev->image_read_pos = 0;
pwc_next_image(pdev); pwc_next_image(pdev);
} }
...@@ -1260,7 +1274,7 @@ static int pwc_video_do_ioctl(struct inode *inode, struct file *file, ...@@ -1260,7 +1274,7 @@ static int pwc_video_do_ioctl(struct inode *inode, struct file *file,
} }
case VIDIOCSCHAN: case VIDIOCSCHAN:
{ {
/* The spec says the argument is an integer, but /* The spec says the argument is an integer, but
the bttv driver uses a video_channel arg, which the bttv driver uses a video_channel arg, which
makes sense becasue it also has the norm flag. makes sense becasue it also has the norm flag.
...@@ -1278,8 +1292,6 @@ static int pwc_video_do_ioctl(struct inode *inode, struct file *file, ...@@ -1278,8 +1292,6 @@ static int pwc_video_do_ioctl(struct inode *inode, struct file *file,
struct video_picture *p = arg; struct video_picture *p = arg;
int val; int val;
p->colour = 0x8000;
p->hue = 0x8000;
val = pwc_get_brightness(pdev); val = pwc_get_brightness(pdev);
if (val >= 0) if (val >= 0)
p->brightness = val; p->brightness = val;
...@@ -1302,11 +1314,11 @@ static int pwc_video_do_ioctl(struct inode *inode, struct file *file, ...@@ -1302,11 +1314,11 @@ static int pwc_video_do_ioctl(struct inode *inode, struct file *file,
else else
p->colour = 0xffff; p->colour = 0xffff;
p->depth = 24; p->depth = 24;
p->palette = VIDEO_PALETTE_YUV420P; p->palette = pdev->vpalette;
p->hue = 0xFFFF; /* N/A */ p->hue = 0xFFFF; /* N/A */
break; break;
} }
case VIDIOCSPICT: case VIDIOCSPICT:
{ {
struct video_picture *p = arg; struct video_picture *p = arg;
...@@ -1318,13 +1330,22 @@ static int pwc_video_do_ioctl(struct inode *inode, struct file *file, ...@@ -1318,13 +1330,22 @@ static int pwc_video_do_ioctl(struct inode *inode, struct file *file,
is used exactly once in the uncompress is used exactly once in the uncompress
routine. routine.
*/ */
if (p->palette && p->palette != VIDEO_PALETTE_YUV420P) {
return -EINVAL;
}
pwc_set_brightness(pdev, p->brightness); pwc_set_brightness(pdev, p->brightness);
pwc_set_contrast(pdev, p->contrast); pwc_set_contrast(pdev, p->contrast);
pwc_set_gamma(pdev, p->whiteness); pwc_set_gamma(pdev, p->whiteness);
pwc_set_saturation(pdev, p->colour); 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; break;
} }
...@@ -1398,13 +1419,23 @@ static int pwc_video_do_ioctl(struct inode *inode, struct file *file, ...@@ -1398,13 +1419,23 @@ static int pwc_video_do_ioctl(struct inode *inode, struct file *file,
various palettes... The driver doesn't support various palettes... The driver doesn't support
such small images, so I'm working around it. such small images, so I'm working around it.
*/ */
if (vm->format && vm->format != VIDEO_PALETTE_YUV420P) if (vm->format)
return -EINVAL; {
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) && if ((vm->width != pdev->view.x || vm->height != pdev->view.y) &&
(vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) { (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) {
int ret; int ret;
Trace(TRACE_OPEN, "VIDIOCMCAPTURE: changing size to please xawtv :-(.\n"); 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); ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
if (ret) if (ret)
...@@ -1523,7 +1554,7 @@ static int pwc_video_do_ioctl(struct inode *inode, struct file *file, ...@@ -1523,7 +1554,7 @@ static int pwc_video_do_ioctl(struct inode *inode, struct file *file,
{ {
struct video_unit *vu = arg; struct video_unit *vu = arg;
vu->video = pdev->vdev.minor & 0x3F; vu->video = pdev->vdev->minor & 0x3F;
vu->audio = -1; /* not known yet */ vu->audio = -1; /* not known yet */
vu->vbi = -1; vu->vbi = -1;
vu->radio = -1; vu->radio = -1;
...@@ -1636,12 +1667,12 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id ...@@ -1636,12 +1667,12 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
type_id = 690; type_id = 690;
break; break;
case 0x0310: case 0x0310:
Info("Philips PCVC730K (ToUCam Fun) USB webcam detected.\n"); Info("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n");
name = "Philips 730 webcam"; name = "Philips 730 webcam";
type_id = 730; type_id = 730;
break; break;
case 0x0311: case 0x0311:
Info("Philips PCVC740K (ToUCam Pro) USB webcam detected.\n"); Info("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n");
name = "Philips 740 webcam"; name = "Philips 740 webcam";
type_id = 740; type_id = 740;
break; break;
...@@ -1755,17 +1786,44 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id ...@@ -1755,17 +1786,44 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
break; break;
} }
} }
else if (vendor_id == 0x04cc) { else if (vendor_id == 0x04cc) {
switch(product_id) { switch(product_id) {
case 0x8116: case 0x8116:
Info("Sotec Afina Eye USB webcam detected.\n"); Info("Sotec Afina Eye USB webcam detected.\n");
name = "Sotec Afina Eye"; name = "Sotec Afina Eye";
type_id = 730; type_id = 730;
break; break;
default:
return -ENODEV;
break;
}
}
else if (vendor_id == 0x06be) {
switch(product_id) {
case 0x8116:
/* Basicly the same as the Sotec Afina Eye */
Info("AME CU-001 USB webcam detected.\n");
name = "AME CU-001";
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 */
Info("AME Co. Afina Eye USB webcam detected.\n");
name = "AME Co. Afina Eye";
type_id = 750;
break;
default: default:
return -ENODEV; return -ENODEV;
break; break;
} }
} }
else if (vendor_id == 0x0d81) { else if (vendor_id == 0x0d81) {
switch(product_id) { switch(product_id) {
...@@ -1804,6 +1862,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id ...@@ -1804,6 +1862,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
pdev->type = type_id; pdev->type = type_id;
pdev->vsize = default_size; pdev->vsize = default_size;
pdev->vframes = default_fps; pdev->vframes = default_fps;
strcpy(pdev->serial, serial_number);
pdev->features = features; pdev->features = features;
if (vendor_id == 0x046D && product_id == 0x08B5) if (vendor_id == 0x046D && product_id == 0x08B5)
{ {
...@@ -1815,8 +1874,6 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id ...@@ -1815,8 +1874,6 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
pdev->angle_range.pan_max = 7000; pdev->angle_range.pan_max = 7000;
pdev->angle_range.tilt_min = -3000; pdev->angle_range.tilt_min = -3000;
pdev->angle_range.tilt_max = 2500; pdev->angle_range.tilt_max = 2500;
pdev->angle_range.zoom_min = -1;
pdev->angle_range.zoom_max = -1;
} }
init_MUTEX(&pdev->modlock); init_MUTEX(&pdev->modlock);
...@@ -1826,11 +1883,19 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id ...@@ -1826,11 +1883,19 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
init_waitqueue_head(&pdev->frameq); init_waitqueue_head(&pdev->frameq);
pdev->vcompression = pwc_preferred_compression; pdev->vcompression = pwc_preferred_compression;
memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template)); /* Allocate video_device structure */
strcpy(pdev->vdev.name, name); pdev->vdev = video_device_alloc();
pdev->vdev.owner = THIS_MODULE; if (pdev->vdev == 0)
pdev->vdev.priv = pdev; {
Err("Err, cannot allocate video_device struture. Failing probe.");
kfree(pdev);
return -ENOMEM;
}
memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template));
strcpy(pdev->vdev->name, name);
pdev->vdev->owner = THIS_MODULE;
video_set_drvdata(pdev->vdev, pdev);
pdev->release = udev->descriptor.bcdDevice; pdev->release = udev->descriptor.bcdDevice;
Trace(TRACE_PROBE, "Release: %04x\n", pdev->release); Trace(TRACE_PROBE, "Release: %04x\n", pdev->release);
...@@ -1848,15 +1913,16 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id ...@@ -1848,15 +1913,16 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
} }
} }
pdev->vdev.release = video_device_release; pdev->vdev->release = video_device_release;
i = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, video_nr); i = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr);
if (i < 0) { if (i < 0) {
Err("Failed to register as video device (%d).\n", i); Err("Failed to register as video device (%d).\n", i);
video_device_release(pdev->vdev); /* Drip... drip... drip... */
kfree(pdev); /* Oops, no memory leaks please */ kfree(pdev); /* Oops, no memory leaks please */
return -EIO; return -EIO;
} }
else { else {
Info("Registered as /dev/video%d.\n", pdev->vdev.minor & 0x3F); Info("Registered as /dev/video%d.\n", pdev->vdev->minor & 0x3F);
} }
/* occupy slot */ /* occupy slot */
...@@ -1894,14 +1960,14 @@ static void usb_pwc_disconnect(struct usb_interface *intf) ...@@ -1894,14 +1960,14 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n"); Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n");
goto disconnect_out; goto disconnect_out;
} }
#endif #endif
/* We got unplugged; this is signalled by an EPIPE error code */ /* We got unplugged; this is signalled by an EPIPE error code */
if (pdev->vopen) { if (pdev->vopen) {
Info("Disconnected while webcam is in use!\n"); Info("Disconnected while webcam is in use!\n");
pdev->error_status = EPIPE; pdev->error_status = EPIPE;
} }
/* Alert waiting processes */ /* Alert waiting processes */
wake_up_interruptible(&pdev->frameq); wake_up_interruptible(&pdev->frameq);
/* Wait until device is closed */ /* Wait until device is closed */
...@@ -1909,7 +1975,7 @@ static void usb_pwc_disconnect(struct usb_interface *intf) ...@@ -1909,7 +1975,7 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
schedule(); schedule();
/* Device is now closed, so we can safely unregister it */ /* Device is now closed, so we can safely unregister it */
Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n"); Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n");
video_unregister_device(&pdev->vdev); video_unregister_device(pdev->vdev);
/* Free memory (don't set pdev to 0 just yet) */ /* Free memory (don't set pdev to 0 just yet) */
kfree(pdev); kfree(pdev);
...@@ -1928,7 +1994,7 @@ static void usb_pwc_disconnect(struct usb_interface *intf) ...@@ -1928,7 +1994,7 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
static int pwc_atoi(const char *s) static int pwc_atoi(const char *s)
{ {
int k = 0; int k = 0;
k = 0; k = 0;
while (*s != '\0' && *s >= '0' && *s <= '9') { while (*s != '\0' && *s >= '0' && *s <= '9') {
k = 10 * k + (*s - '0'); k = 10 * k + (*s - '0');
...@@ -1971,7 +2037,7 @@ MODULE_PARM(dev_hint, "0-20s"); ...@@ -1971,7 +2037,7 @@ MODULE_PARM(dev_hint, "0-20s");
MODULE_PARM_DESC(dev_hint, "Device node hints"); MODULE_PARM_DESC(dev_hint, "Device node hints");
MODULE_DESCRIPTION("Philips & OEM USB webcam driver"); MODULE_DESCRIPTION("Philips & OEM USB webcam driver");
MODULE_AUTHOR("Nemosoft Unv. <nemosoft@smcc.demon.nl>"); MODULE_AUTHOR("Nemosoft Unv. <webcam@smcc.demon.nl>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
static int __init usb_pwc_init(void) static int __init usb_pwc_init(void)
...@@ -1979,9 +2045,10 @@ static int __init usb_pwc_init(void) ...@@ -1979,9 +2045,10 @@ static int __init usb_pwc_init(void)
int i, sz; int i, sz;
char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" }; char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" };
Info("Philips PCA645/646 + PCVC675/680/690 + PCVC730/740/750 webcam module version " PWC_VERSION " loaded.\n"); Info("Philips webcam module version " PWC_VERSION " loaded.\n");
Info("Also supports the Askey VC010, various Logitech QuickCams, Samsung MPC-C10 and MPC-C30,\n"); Info("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n");
Info("the Creative WebCam 5, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\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");
if (fps) { if (fps) {
if (fps < 4 || fps > 30) { if (fps < 4 || fps > 30) {
...@@ -1991,7 +2058,7 @@ static int __init usb_pwc_init(void) ...@@ -1991,7 +2058,7 @@ static int __init usb_pwc_init(void)
default_fps = fps; default_fps = fps;
Info("Default framerate set to %d.\n", default_fps); Info("Default framerate set to %d.\n", default_fps);
} }
if (size) { if (size) {
/* string; try matching with array */ /* string; try matching with array */
for (sz = 0; sz < PSZ_MAX; sz++) { for (sz = 0; sz < PSZ_MAX; sz++) {
...@@ -2041,12 +2108,12 @@ static int __init usb_pwc_init(void) ...@@ -2041,12 +2108,12 @@ static int __init usb_pwc_init(void)
if (leds[1] >= 0) if (leds[1] >= 0)
led_off = leds[1]; led_off = leds[1];
/* Big device node whoopla. Basically, it allows you to assign a /* Big device node whoopla. Basicly, it allows you to assign a
device node (/dev/videoX) to a camera, based on its type device node (/dev/videoX) to a camera, based on its type
& serial number. The format is [type[.serialnumber]:]node. & serial number. The format is [type[.serialnumber]:]node.
Any camera that isn't matched by these rules gets the next Any camera that isn't matched by these rules gets the next
available free device node. available free device node.
*/ */
for (i = 0; i < MAX_DEV_HINTS; i++) { for (i = 0; i < MAX_DEV_HINTS; i++) {
char *s, *colon, *dot; char *s, *colon, *dot;
......
#ifndef PWC_IOCTL_H #ifndef PWC_IOCTL_H
#define PWC_IOCTL_H #define PWC_IOCTL_H
/* (C) 2001-2003 Nemosoft Unv. webcam@smcc.demon.nl /* (C) 2001-2004 Nemosoft Unv. webcam@smcc.demon.nl
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
...@@ -18,19 +18,24 @@ ...@@ -18,19 +18,24 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
/* This is pwc-ioctl.h belonging to PWC 8.10 */ /* This is pwc-ioctl.h belonging to PWC 8.12.1
It contains structures and defines to communicate from user space
directly to the driver.
*/
/* /*
Changes Changes
2001/08/03 Alvarado Added ioctl constants to access methods for 2001/08/03 Alvarado Added ioctl constants to access methods for
changing white balance and red/blue gains changing white balance and red/blue gains
2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE 2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE
2003/12/13 Nemosft Unv. Some modifications to make interfacing to
PWCX easier
*/ */
/* These are private ioctl() commands, specific for the Philips webcams. /* These are private ioctl() commands, specific for the Philips webcams.
They contain functions not found in other webcams, and settings not They contain functions not found in other webcams, and settings not
specified in the Video4Linux API. specified in the Video4Linux API.
The #define names are built up like follows: The #define names are built up like follows:
VIDIOC VIDeo IOCtl prefix VIDIOC VIDeo IOCtl prefix
PWC Philps WebCam PWC Philps WebCam
...@@ -40,13 +45,21 @@ ...@@ -40,13 +45,21 @@
*/ */
/* Enumeration of image sizes */
#define PSZ_SQCIF 0x00
#define PSZ_QSIF 0x01
#define PSZ_QCIF 0x02
#define PSZ_SIF 0x03
#define PSZ_CIF 0x04
#define PSZ_VGA 0x05
#define PSZ_MAX 6
/* The frame rate is encoded in the video_window.flags parameter using /* The frame rate is encoded in the video_window.flags parameter using
the upper 16 bits, since some flags are defined nowadays. The following the upper 16 bits, since some flags are defined nowadays. The following
defines provide a mask and shift to filter out this value. defines provide a mask and shift to filter out this value.
In 'Snapshot' mode the camera freezes its automatic exposure and colour In 'Snapshot' mode the camera freezes its automatic exposure and colour
balance controls. balance controls.
*/ */
#define PWC_FPS_SHIFT 16 #define PWC_FPS_SHIFT 16
...@@ -55,14 +68,26 @@ ...@@ -55,14 +68,26 @@
#define PWC_FPS_SNAPSHOT 0x00400000 #define PWC_FPS_SNAPSHOT 0x00400000
/* structure for transfering x & y coordinates */
struct pwc_coord
{
int x, y; /* guess what */
int size; /* size, or offset */
};
/* Used with VIDIOCPWCPROBE */
struct pwc_probe struct pwc_probe
{ {
char name[32]; char name[32];
int type; int type;
}; };
struct pwc_serial
{
char serial[30]; /* String with serial number. Contains terminating 0 */
};
/* pwc_whitebalance.mode values */ /* pwc_whitebalance.mode values */
#define PWC_WB_INDOOR 0 #define PWC_WB_INDOOR 0
#define PWC_WB_OUTDOOR 1 #define PWC_WB_OUTDOOR 1
...@@ -78,7 +103,6 @@ struct pwc_probe ...@@ -78,7 +103,6 @@ struct pwc_probe
otherwise undefined. otherwise undefined.
'read_red' and 'read_blue' are read-only. 'read_red' and 'read_blue' are read-only.
*/ */
struct pwc_whitebalance struct pwc_whitebalance
{ {
int mode; int mode;
...@@ -117,7 +141,7 @@ struct pwc_imagesize ...@@ -117,7 +141,7 @@ struct pwc_imagesize
#define PWC_MPT_TILT 0x02 #define PWC_MPT_TILT 0x02
#define PWC_MPT_TIMEOUT 0x04 /* for status */ #define PWC_MPT_TIMEOUT 0x04 /* for status */
/* Set angles; when absolute = 1, the angle is absolute and the /* Set angles; when absolute != 0, the angle is absolute and the
driver calculates the relative offset for you. This can only driver calculates the relative offset for you. This can only
be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns
absolute angles. absolute angles.
...@@ -127,18 +151,14 @@ struct pwc_mpt_angles ...@@ -127,18 +151,14 @@ struct pwc_mpt_angles
int absolute; /* write-only */ int absolute; /* write-only */
int pan; /* degrees * 100 */ int pan; /* degrees * 100 */
int tilt; /* degress * 100 */ int tilt; /* degress * 100 */
int zoom; /* N/A, set to -1 */
}; };
/* Range of angles of the camera, both horizontally and vertically. /* Range of angles of the camera, both horizontally and vertically.
The zoom is not used, maybe in the future...
*/ */
struct pwc_mpt_range struct pwc_mpt_range
{ {
int pan_min, pan_max; /* degrees * 100 */ int pan_min, pan_max; /* degrees * 100 */
int tilt_min, tilt_max; int tilt_min, tilt_max;
int zoom_min, zoom_max; /* -1, -1 */
}; };
struct pwc_mpt_status struct pwc_mpt_status
...@@ -149,6 +169,30 @@ struct pwc_mpt_status ...@@ -149,6 +169,30 @@ struct pwc_mpt_status
}; };
/* This is used for out-of-kernel decompression. With it, you can get
all the necessary information to initialize and use the decompressor
routines in standalone applications.
*/
struct pwc_video_command
{
int type; /* camera type (645, 675, 730, etc.) */
int release; /* release number */
int size; /* one of PSZ_* */
int alternate;
int command_len; /* length of USB video command */
unsigned char command_buf[13]; /* Actual USB video command */
int bandlength; /* >0 = compressed */
int frame_size; /* Size of one (un)compressed frame */
};
/* Flags for PWCX subroutines. Not all modules honour all flags. */
#define PWCX_FLAG_PLANAR 0x0001
#define PWCX_FLAG_BAYER 0x0008
/* IOCTL definitions */
/* Restore user settings */ /* Restore user settings */
#define VIDIOCPWCRUSER _IO('v', 192) #define VIDIOCPWCRUSER _IO('v', 192)
/* Save user settings */ /* Save user settings */
...@@ -169,16 +213,19 @@ struct pwc_mpt_status ...@@ -169,16 +213,19 @@ struct pwc_mpt_status
#define VIDIOCPWCGCQUAL _IOR('v', 195, int) #define VIDIOCPWCGCQUAL _IOR('v', 195, int)
/* Retrieve serial number of camera */
#define VIDIOCPWCGSERIAL _IOR('v', 198, struct pwc_serial)
/* This is a probe function; since so many devices are supported, it /* This is a probe function; since so many devices are supported, it
becomes difficult to include all the names in programs that want to becomes difficult to include all the names in programs that want to
check for the enhanced Philips stuff. So in stead, try this PROBE; check for the enhanced Philips stuff. So in stead, try this PROBE;
it returns a structure with the original name, and the corresponding it returns a structure with the original name, and the corresponding
Philips type. Philips type.
To use, fill the structure with zeroes, call PROBE and if that succeeds, To use, fill the structure with zeroes, call PROBE and if that succeeds,
compare the name with that returned from VIDIOCGCAP; they should be the compare the name with that returned from VIDIOCGCAP; they should be the
same. If so, you can be assured it is a Philips (OEM) cam and the type same. If so, you can be assured it is a Philips (OEM) cam and the type
is valid. is valid.
*/ */
#define VIDIOCPWCPROBE _IOR('v', 199, struct pwc_probe) #define VIDIOCPWCPROBE _IOR('v', 199, struct pwc_probe)
/* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */ /* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */
...@@ -225,5 +272,8 @@ struct pwc_mpt_status ...@@ -225,5 +272,8 @@ struct pwc_mpt_status
#define VIDIOCPWCMPTSANGLE _IOW('v', 212, struct pwc_mpt_angles) #define VIDIOCPWCMPTSANGLE _IOW('v', 212, struct pwc_mpt_angles)
#define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles) #define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles)
#define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status) #define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status)
/* Get the USB set-video command; needed for initializing libpwcx */
#define VIDIOCPWCGVIDCMD _IOR('v', 215, struct pwc_video_command)
#endif #endif
...@@ -15,13 +15,13 @@ ...@@ -15,13 +15,13 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#include <linux/slab.h> #include <linux/slab.h>
#include "pwc.h" #include "pwc.h"
struct pwc_coord pwc_image_sizes[PSZ_MAX] = struct pwc_coord pwc_image_sizes[PSZ_MAX] =
{ {
{ 128, 96, 0 }, { 128, 96, 0 },
{ 160, 120, 0 }, { 160, 120, 0 },
...@@ -36,11 +36,30 @@ int pwc_decode_size(struct pwc_device *pdev, int width, int height) ...@@ -36,11 +36,30 @@ int pwc_decode_size(struct pwc_device *pdev, int width, int height)
{ {
int i, find; int i, find;
/* Make sure we don't go beyond our max size */ /* Make sure we don't go beyond our max size.
if (width > pdev->view_max.x || height > pdev->view_max.y) NB: we have different limits for RAW and normal modes. In case
return -1; you don't have the decompressor loaded or use RAW mode,
the maximum viewable size is smaller.
*/
if (pdev->vpalette == VIDEO_PALETTE_RAW)
{
if (width > pdev->abs_max.x || height > pdev->abs_max.y)
{
Debug("VIDEO_PALETTE_RAW: going beyond abs_max.\n");
return -1;
}
}
else
{
if (width > pdev->view_max.x || height > pdev->view_max.y)
{
Debug("VIDEO_PALETTE_ not RAW: going beyond view_max.\n");
return -1;
}
}
/* Find the largest size supported by the camera that fits into the /* Find the largest size supported by the camera that fits into the
requested size. requested size.
*/ */
find = -1; find = -1;
for (i = 0; i < PSZ_MAX; i++) { for (i = 0; i < PSZ_MAX; i++) {
...@@ -62,6 +81,8 @@ void pwc_construct(struct pwc_device *pdev) ...@@ -62,6 +81,8 @@ void pwc_construct(struct pwc_device *pdev)
pdev->view_min.y = 96; pdev->view_min.y = 96;
pdev->view_max.x = 352; pdev->view_max.x = 352;
pdev->view_max.y = 288; pdev->view_max.y = 288;
pdev->abs_max.x = 352;
pdev->abs_max.y = 288;
pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QCIF | 1 << PSZ_CIF; pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QCIF | 1 << PSZ_CIF;
pdev->vcinterface = 2; pdev->vcinterface = 2;
pdev->vendpoint = 4; pdev->vendpoint = 4;
...@@ -77,13 +98,14 @@ void pwc_construct(struct pwc_device *pdev) ...@@ -77,13 +98,14 @@ void pwc_construct(struct pwc_device *pdev)
if (pdev->decompressor != NULL) { if (pdev->decompressor != NULL) {
pdev->view_max.x = 640; pdev->view_max.x = 640;
pdev->view_max.y = 480; pdev->view_max.y = 480;
pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA;
} }
else { else {
pdev->view_max.x = 352; pdev->view_max.x = 352;
pdev->view_max.y = 288; pdev->view_max.y = 288;
pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF;
} }
pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA;
pdev->abs_max.x = 640;
pdev->abs_max.y = 480;
pdev->vcinterface = 3; pdev->vcinterface = 3;
pdev->vendpoint = 4; pdev->vendpoint = 4;
pdev->frame_header_size = 0; pdev->frame_header_size = 0;
...@@ -99,24 +121,26 @@ void pwc_construct(struct pwc_device *pdev) ...@@ -99,24 +121,26 @@ void pwc_construct(struct pwc_device *pdev)
if (pdev->decompressor != NULL) { if (pdev->decompressor != NULL) {
pdev->view_max.x = 640; pdev->view_max.x = 640;
pdev->view_max.y = 480; pdev->view_max.y = 480;
pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA;
} }
else { else {
/* Tell CIF, even though SIF really is the maximum, but some tools really need CIF */ /* We use CIF, not SIF since some tools really need CIF. So we cheat a bit. */
pdev->view_max.x = 352; pdev->view_max.x = 352;
pdev->view_max.y = 288; pdev->view_max.y = 288;
pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF;
} }
pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA;
pdev->abs_max.x = 640;
pdev->abs_max.y = 480;
pdev->vcinterface = 3; pdev->vcinterface = 3;
pdev->vendpoint = 5; pdev->vendpoint = 5;
pdev->frame_header_size = TOUCAM_HEADER_SIZE; pdev->frame_header_size = TOUCAM_HEADER_SIZE;
pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE; pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE;
break; break;
} }
pdev->vpalette = VIDEO_PALETTE_YUV420P; /* default */
pdev->view_min.size = pdev->view_min.x * pdev->view_min.y; pdev->view_min.size = pdev->view_min.x * pdev->view_min.y;
pdev->view_max.size = pdev->view_max.x * pdev->view_max.y; pdev->view_max.size = pdev->view_max.x * pdev->view_max.y;
/* length of image, in YUV format */ /* length of image, in YUV format; always allocate enough memory. */
pdev->len_per_image = (pdev->view_max.size * 3) / 2; pdev->len_per_image = (pdev->abs_max.x * pdev->abs_max.y * 3) / 2;
} }
/* Linux driver for Philips webcam /* Linux driver for Philips webcam
Decompression frontend. Decompression frontend.
(C) 1999-2003 Nemosoft Unv. (webcam@smcc.demon.nl) (C) 1999-2003 Nemosoft Unv. (webcam@smcc.demon.nl)
...@@ -21,7 +21,9 @@ ...@@ -21,7 +21,9 @@
themselves. It also has a decompressor wrapper function. themselves. It also has a decompressor wrapper function.
*/ */
#include <asm/current.h>
#include <asm/types.h> #include <asm/types.h>
// #include <linux/sched.h>
#include "pwc.h" #include "pwc.h"
#include "pwc-uncompress.h" #include "pwc-uncompress.h"
...@@ -81,7 +83,6 @@ int pwc_decompress(struct pwc_device *pdev) ...@@ -81,7 +83,6 @@ int pwc_decompress(struct pwc_device *pdev)
u16 *src; u16 *src;
u16 *dsty, *dstu, *dstv; u16 *dsty, *dstu, *dstv;
if (pdev == NULL) if (pdev == NULL)
return -EFAULT; return -EFAULT;
#if defined(__KERNEL__) && defined(PWC_MAGIC) #if defined(__KERNEL__) && defined(PWC_MAGIC)
...@@ -97,16 +98,24 @@ int pwc_decompress(struct pwc_device *pdev) ...@@ -97,16 +98,24 @@ int pwc_decompress(struct pwc_device *pdev)
image = pdev->image_ptr[pdev->fill_image]; image = pdev->image_ptr[pdev->fill_image];
if (!image) if (!image)
return -EFAULT; return -EFAULT;
yuv = fbuf->data + pdev->frame_header_size; /* Skip header */ yuv = fbuf->data + pdev->frame_header_size; /* Skip header */
if (pdev->vbandlength == 0) {
/* Raw format; that's easy... */
if (pdev->vpalette == VIDEO_PALETTE_RAW)
{
memcpy(image, yuv, pdev->frame_size);
return 0;
}
if (pdev->vbandlength == 0) {
/* Uncompressed mode. We copy the data into the output buffer, /* Uncompressed mode. We copy the data into the output buffer,
using the viewport size (which may be larger than the image using the viewport size (which may be larger than the image
size). Unfortunately we have to do a bit of byte stuffing size). Unfortunately we have to do a bit of byte stuffing
to get the desired output format/size. to get the desired output format/size.
*/ */
/* /*
* We do some byte shuffling here to go from the * We do some byte shuffling here to go from the
* native format to YUV420P. * native format to YUV420P.
*/ */
src = (u16 *)yuv; src = (u16 *)yuv;
...@@ -140,15 +149,21 @@ int pwc_decompress(struct pwc_device *pdev) ...@@ -140,15 +149,21 @@ int pwc_decompress(struct pwc_device *pdev)
dstu += (stride >> 1); dstu += (stride >> 1);
} }
} }
else { else {
/* Compressed; the decompressor routines will write the data /* Compressed; the decompressor routines will write the data
in planar format immediately. in planar format immediately.
*/ */
int flags;
flags = PWCX_FLAG_PLANAR;
if (pdev->vsize == PSZ_VGA && pdev->vframes == 5 && pdev->vsnapshot)
flags |= PWCX_FLAG_BAYER;
if (pdev->decompressor) if (pdev->decompressor)
pdev->decompressor->decompress( pdev->decompressor->decompress(
&pdev->image, &pdev->view, &pdev->offset, &pdev->image, &pdev->view, &pdev->offset,
yuv, image, yuv, image,
1, flags,
pdev->decompress_data, pdev->vbandlength); pdev->decompress_data, pdev->vbandlength);
else else
return -ENXIO; /* No such device or address: missing decompressor */ return -ENXIO; /* No such device or address: missing decompressor */
......
...@@ -24,9 +24,15 @@ ...@@ -24,9 +24,15 @@
#define PWC_UNCOMPRESS_H #define PWC_UNCOMPRESS_H
#include <linux/config.h> #include <linux/config.h>
#include <linux/linkage.h>
#include <linux/list.h> #include <linux/list.h>
#include "pwc.h" #include "pwc-ioctl.h"
/* from pwc-dec.h */
#define PWCX_FLAG_PLANAR 0x0001
/* */
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
...@@ -42,10 +48,10 @@ struct pwc_decompressor ...@@ -42,10 +48,10 @@ struct pwc_decompressor
int type; /* type of camera (645, 680, etc) */ int type; /* type of camera (645, 680, etc) */
int table_size; /* memory needed */ int table_size; /* memory needed */
void (* init)(int release, void *buffer, void *table); /* Initialization routine; should be called after each set_video_mode */ asmlinkage void (* init)(int type, int release, void *buffer, void *table); /* Initialization routine; should be called after each set_video_mode */
void (* exit)(void); /* Cleanup routine */ asmlinkage void (* exit)(void); /* Cleanup routine */
void (* decompress)(struct pwc_coord *image, struct pwc_coord *view, struct pwc_coord *offset, asmlinkage void (* decompress)(struct pwc_coord *image, struct pwc_coord *view, struct pwc_coord *offset,
void *src, void *dst, int planar, void *src, void *dst, int flags,
void *table, int bandlength); void *table, int bandlength);
void (* lock)(void); /* make sure module cannot be unloaded */ void (* lock)(void); /* make sure module cannot be unloaded */
void (* unlock)(void); /* release lock on module */ void (* unlock)(void); /* release lock on module */
......
...@@ -22,15 +22,15 @@ ...@@ -22,15 +22,15 @@
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/smp_lock.h>
#include <linux/spinlock.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/spinlock.h>
#include <linux/videodev.h> #include <linux/videodev.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/smp_lock.h>
#include <asm/semaphore.h> #include <asm/semaphore.h>
#include <asm/errno.h> #include <asm/errno.h>
#include "pwc-uncompress.h"
#include "pwc-ioctl.h" #include "pwc-ioctl.h"
/* Defines and structures for the Philips webcam */ /* Defines and structures for the Philips webcam */
...@@ -65,9 +65,9 @@ ...@@ -65,9 +65,9 @@
#define FEATURE_MOTOR_PANTILT 0x0001 #define FEATURE_MOTOR_PANTILT 0x0001
/* Version block */ /* Version block */
#define PWC_MAJOR 8 #define PWC_MAJOR 9
#define PWC_MINOR 12 #define PWC_MINOR 0
#define PWC_VERSION "8.12" #define PWC_VERSION "9.0.1"
#define PWC_NAME "pwc" #define PWC_NAME "pwc"
/* Turn certain features on/off */ /* Turn certain features on/off */
...@@ -90,12 +90,6 @@ ...@@ -90,12 +90,6 @@
/* Absolute maximum number of buffers available for mmap() */ /* Absolute maximum number of buffers available for mmap() */
#define MAX_IMAGES 10 #define MAX_IMAGES 10
struct pwc_coord
{
int x, y; /* guess what */
int size; /* size, or offset */
};
/* The following structures were based on cpia.h. Why reinvent the wheel? :-) */ /* The following structures were based on cpia.h. Why reinvent the wheel? :-) */
struct pwc_iso_buf struct pwc_iso_buf
{ {
...@@ -118,7 +112,7 @@ struct pwc_frame_buf ...@@ -118,7 +112,7 @@ struct pwc_frame_buf
struct pwc_device struct pwc_device
{ {
struct video_device vdev; struct video_device *vdev;
#ifdef PWC_MAGIC #ifdef PWC_MAGIC
int magic; int magic;
#endif #endif
...@@ -128,6 +122,7 @@ struct pwc_device ...@@ -128,6 +122,7 @@ struct pwc_device
int type; /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */ int type; /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */
int release; /* release number */ int release; /* release number */
int features; /* feature bits */ int features; /* feature bits */
char serial[30]; /* serial number (string) */
int error_status; /* set when something goes wrong with the cam (unplugged, USB errors) */ int error_status; /* set when something goes wrong with the cam (unplugged, USB errors) */
int usb_init; /* set when the cam has been initialized over USB */ int usb_init; /* set when the cam has been initialized over USB */
...@@ -137,6 +132,7 @@ struct pwc_device ...@@ -137,6 +132,7 @@ struct pwc_device
int vcinterface; /* video control interface */ int vcinterface; /* video control interface */
int valternate; /* alternate interface needed */ int valternate; /* alternate interface needed */
int vframes, vsize; /* frames-per-second & size (see PSZ_*) */ int vframes, vsize; /* frames-per-second & size (see PSZ_*) */
int vpalette; /* palette: 420P, RAW or RGBBAYER */
int vframe_count; /* received frames */ int vframe_count; /* received frames */
int vframes_dumped; /* counter for dumped frames */ int vframes_dumped; /* counter for dumped frames */
int vframes_error; /* frames received in error */ int vframes_error; /* frames received in error */
...@@ -148,6 +144,9 @@ struct pwc_device ...@@ -148,6 +144,9 @@ struct pwc_device
char vsnapshot; /* snapshot mode */ char vsnapshot; /* snapshot mode */
char vsync; /* used by isoc handler */ char vsync; /* used by isoc handler */
char vmirror; /* for ToUCaM series */ char vmirror; /* for ToUCaM series */
int cmd_len;
unsigned char cmd_buf[13];
/* The image acquisition requires 3 to 4 steps: /* The image acquisition requires 3 to 4 steps:
1. data is gathered in short packets from the USB controller 1. data is gathered in short packets from the USB controller
...@@ -169,8 +168,9 @@ struct pwc_device ...@@ -169,8 +168,9 @@ struct pwc_device
struct pwc_frame_buf *full_frames, *full_frames_tail; /* all filled frames */ struct pwc_frame_buf *full_frames, *full_frames_tail; /* all filled frames */
struct pwc_frame_buf *fill_frame; /* frame currently being filled */ struct pwc_frame_buf *fill_frame; /* frame currently being filled */
struct pwc_frame_buf *read_frame; /* frame currently read by user process */ struct pwc_frame_buf *read_frame; /* frame currently read by user process */
int frame_size;
int frame_header_size, frame_trailer_size; int frame_header_size, frame_trailer_size;
int frame_size;
int frame_total_size; /* including header & trailer */
int drop_frames; int drop_frames;
#if PWC_DEBUG #if PWC_DEBUG
int sequence; /* Debugging aid */ int sequence; /* Debugging aid */
...@@ -187,7 +187,8 @@ struct pwc_device ...@@ -187,7 +187,8 @@ struct pwc_device
a gray or black border. view_min <= image <= view <= view_max; a gray or black border. view_min <= image <= view <= view_max;
*/ */
int image_mask; /* bitmask of supported sizes */ int image_mask; /* bitmask of supported sizes */
struct pwc_coord view_min, view_max; /* minimum and maximum sizes */ struct pwc_coord view_min, view_max; /* minimum and maximum viewable sizes */
struct pwc_coord abs_max; /* maximum supported size with compression */
struct pwc_coord image, view; /* image and viewport size */ struct pwc_coord image, view; /* image and viewport size */
struct pwc_coord offset; /* offset within the viewport */ struct pwc_coord offset; /* offset within the viewport */
...@@ -213,16 +214,6 @@ struct pwc_device ...@@ -213,16 +214,6 @@ struct pwc_device
#endif #endif
}; };
/* Enumeration of image sizes */
#define PSZ_SQCIF 0x00
#define PSZ_QSIF 0x01
#define PSZ_QCIF 0x02
#define PSZ_SIF 0x03
#define PSZ_CIF 0x04
#define PSZ_VGA 0x05
#define PSZ_MAX 6
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
...@@ -259,7 +250,7 @@ extern int pwc_get_saturation(struct pwc_device *pdev); ...@@ -259,7 +250,7 @@ extern int pwc_get_saturation(struct pwc_device *pdev);
extern int pwc_set_saturation(struct pwc_device *pdev, int value); extern int pwc_set_saturation(struct pwc_device *pdev, int value);
extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value); extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value);
extern int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value); extern int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value);
extern int pwc_get_cmos_sensor(struct pwc_device *pdev); extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor);
/* Power down or up the camera; not supported by all models */ /* Power down or up the camera; not supported by all models */
extern int pwc_camera_power(struct pwc_device *pdev, int power); extern int pwc_camera_power(struct pwc_device *pdev, int power);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment