2005-04-17 02:20:36 +04:00
/* Driver for Philips webcam
Functions that send various control messages to the webcam , including
video modes .
( C ) 1999 - 2003 Nemosoft Unv .
2006-04-24 17:29:46 +04:00
( C ) 2004 - 2006 Luc Saillard ( luc @ saillard . org )
2005-04-17 02:20:36 +04:00
NOTE : this version of pwc is an unofficial ( modified ) release of pwc & pcwx
driver and thus may have bugs that are not present in the original version .
Please send bug reports and support requests to < luc @ saillard . org > .
NOTE : this version of pwc is an unofficial ( modified ) release of pwc & pcwx
driver and thus may have bugs that are not present in the original version .
Please send bug reports and support requests to < luc @ saillard . org > .
The decompression routines have been implemented by reverse - engineering the
Nemosoft binary pwcx module . Caveat emptor .
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
/*
Changes
2006-03-25 15:19:53 +03:00
2001 / 08 / 03 Alvarado Added methods for changing white balance and
red / green gains
2005-04-17 02:20:36 +04:00
*/
/* Control functions for the cam; brightness, contrast, video mode, etc. */
# ifdef __KERNEL__
2006-03-25 15:19:53 +03:00
# include <asm/uaccess.h>
2005-04-17 02:20:36 +04:00
# endif
# include <asm/errno.h>
2006-04-24 17:29:46 +04:00
# include <linux/version.h>
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
# include "pwc.h"
# include "pwc-uncompress.h"
# include "pwc-kiara.h"
# include "pwc-timon.h"
2006-04-24 17:29:46 +04:00
# include "pwc-dec1.h"
# include "pwc-dec23.h"
2005-04-17 02:20:36 +04:00
/* Request types: video */
# define SET_LUM_CTL 0x01
# define GET_LUM_CTL 0x02
# define SET_CHROM_CTL 0x03
# define GET_CHROM_CTL 0x04
# define SET_STATUS_CTL 0x05
# define GET_STATUS_CTL 0x06
# define SET_EP_STREAM_CTL 0x07
# define GET_EP_STREAM_CTL 0x08
2006-04-24 17:29:46 +04:00
# define GET_XX_CTL 0x09
# define SET_XX_CTL 0x0A
# define GET_XY_CTL 0x0B
# define SET_XY_CTL 0x0C
2005-04-17 02:20:36 +04:00
# define SET_MPT_CTL 0x0D
# define GET_MPT_CTL 0x0E
/* Selectors for the Luminance controls [GS]ET_LUM_CTL */
# define AGC_MODE_FORMATTER 0x2000
# define PRESET_AGC_FORMATTER 0x2100
# define SHUTTER_MODE_FORMATTER 0x2200
# define PRESET_SHUTTER_FORMATTER 0x2300
# define PRESET_CONTOUR_FORMATTER 0x2400
# define AUTO_CONTOUR_FORMATTER 0x2500
# define BACK_LIGHT_COMPENSATION_FORMATTER 0x2600
# define CONTRAST_FORMATTER 0x2700
# define DYNAMIC_NOISE_CONTROL_FORMATTER 0x2800
# define FLICKERLESS_MODE_FORMATTER 0x2900
# define AE_CONTROL_SPEED 0x2A00
# define BRIGHTNESS_FORMATTER 0x2B00
# define GAMMA_FORMATTER 0x2C00
/* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */
# define WB_MODE_FORMATTER 0x1000
# define AWB_CONTROL_SPEED_FORMATTER 0x1100
# define AWB_CONTROL_DELAY_FORMATTER 0x1200
# define PRESET_MANUAL_RED_GAIN_FORMATTER 0x1300
# define PRESET_MANUAL_BLUE_GAIN_FORMATTER 0x1400
# define COLOUR_MODE_FORMATTER 0x1500
# define SATURATION_MODE_FORMATTER1 0x1600
# define SATURATION_MODE_FORMATTER2 0x1700
/* Selectors for the Status controls [GS]ET_STATUS_CTL */
# define SAVE_USER_DEFAULTS_FORMATTER 0x0200
# define RESTORE_USER_DEFAULTS_FORMATTER 0x0300
# define RESTORE_FACTORY_DEFAULTS_FORMATTER 0x0400
# define READ_AGC_FORMATTER 0x0500
# define READ_SHUTTER_FORMATTER 0x0600
# define READ_RED_GAIN_FORMATTER 0x0700
# define READ_BLUE_GAIN_FORMATTER 0x0800
2006-04-24 17:29:46 +04:00
# define GET_STATUS_B00 0x0B00
2005-04-17 02:20:36 +04:00
# define SENSOR_TYPE_FORMATTER1 0x0C00
2006-04-24 17:29:46 +04:00
# define GET_STATUS_3000 0x3000
2005-04-17 02:20:36 +04:00
# define READ_RAW_Y_MEAN_FORMATTER 0x3100
# define SET_POWER_SAVE_MODE_FORMATTER 0x3200
# define MIRROR_IMAGE_FORMATTER 0x3300
# define LED_FORMATTER 0x3400
2006-04-24 17:29:46 +04:00
# define LOWLIGHT 0x3500
# define GET_STATUS_3600 0x3600
2005-04-17 02:20:36 +04:00
# define SENSOR_TYPE_FORMATTER2 0x3700
2006-04-24 17:29:46 +04:00
# define GET_STATUS_3800 0x3800
# define GET_STATUS_4000 0x4000
# define GET_STATUS_4100 0x4100 /* Get */
# define CTL_STATUS_4200 0x4200 /* [GS] 1 */
2005-04-17 02:20:36 +04:00
/* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */
# define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100
/* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */
# define PT_RELATIVE_CONTROL_FORMATTER 0x01
# define PT_RESET_CONTROL_FORMATTER 0x02
# define PT_STATUS_FORMATTER 0x03
2005-11-29 11:43:42 +03:00
static const char * size2name [ PSZ_MAX ] =
2005-04-17 02:20:36 +04:00
{
" subQCIF " ,
" QSIF " ,
" QCIF " ,
" SIF " ,
" CIF " ,
" VGA " ,
2006-03-25 15:19:53 +03:00
} ;
2005-04-17 02:20:36 +04:00
/********/
2006-03-25 15:19:53 +03:00
/* Entries for the Nala (645/646) camera; the Nala doesn't have compression
2005-04-17 02:20:36 +04:00
preferences , so you either get compressed or non - compressed streams .
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
An alternate value of 0 means this mode is not available at all .
*/
2007-04-23 06:54:36 +04:00
# define PWC_FPS_MAX_NALA 8
2005-04-17 02:20:36 +04:00
struct Nala_table_entry {
char alternate ; /* USB alternate setting */
int compressed ; /* Compressed yes/no */
unsigned char mode [ 3 ] ; /* precomputed mode table */
} ;
2007-04-23 06:54:36 +04:00
static unsigned int Nala_fps_vector [ PWC_FPS_MAX_NALA ] = { 4 , 5 , 7 , 10 , 12 , 15 , 20 , 24 } ;
static struct Nala_table_entry Nala_table [ PSZ_MAX ] [ PWC_FPS_MAX_NALA ] =
2005-04-17 02:20:36 +04:00
{
# include "pwc-nala.h"
} ;
2006-04-24 17:29:46 +04:00
static void pwc_set_image_buffer_size ( struct pwc_device * pdev ) ;
2005-04-17 02:20:36 +04:00
/****************************************************************************/
# define SendControlMsg(request, value, buflen) \
usb_control_msg ( pdev - > udev , usb_sndctrlpipe ( pdev - > udev , 0 ) , \
request , \
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE , \
value , \
pdev - > vcinterface , \
& buf , buflen , 500 )
# define RecvControlMsg(request, value, buflen) \
usb_control_msg ( pdev - > udev , usb_rcvctrlpipe ( pdev - > udev , 0 ) , \
request , \
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE , \
value , \
pdev - > vcinterface , \
& buf , buflen , 500 )
2006-04-24 17:29:46 +04:00
static int send_video_command ( struct usb_device * udev , int index , void * buf , int buflen )
2005-04-17 02:20:36 +04:00
{
return usb_control_msg ( udev ,
usb_sndctrlpipe ( udev , 0 ) ,
SET_EP_STREAM_CTL ,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
VIDEO_OUTPUT_CONTROL_FORMATTER ,
index ,
buf , buflen , 1000 ) ;
}
2006-04-24 17:29:46 +04:00
static int set_video_mode_Nala ( struct pwc_device * pdev , int size , int frames )
2005-04-17 02:20:36 +04:00
{
unsigned char buf [ 3 ] ;
int ret , fps ;
struct Nala_table_entry * pEntry ;
int frames2frames [ 31 ] =
{ /* closest match of framerate */
0 , 0 , 0 , 0 , 4 , /* 0-4 */
5 , 5 , 7 , 7 , 10 , /* 5-9 */
2006-03-25 15:19:53 +03:00
10 , 10 , 12 , 12 , 15 , /* 10-14 */
15 , 15 , 15 , 20 , 20 , /* 15-19 */
20 , 20 , 20 , 24 , 24 , /* 20-24 */
24 , 24 , 24 , 24 , 24 , /* 25-29 */
24 /* 30 */
2005-04-17 02:20:36 +04:00
} ;
2006-03-25 15:19:53 +03:00
int frames2table [ 31 ] =
2005-04-17 02:20:36 +04:00
{ 0 , 0 , 0 , 0 , 0 , /* 0-4 */
1 , 1 , 1 , 2 , 2 , /* 5-9 */
3 , 3 , 4 , 4 , 4 , /* 10-14 */
5 , 5 , 5 , 5 , 5 , /* 15-19 */
6 , 6 , 6 , 6 , 7 , /* 20-24 */
7 , 7 , 7 , 7 , 7 , /* 25-29 */
7 /* 30 */
} ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( size < 0 | | size > PSZ_CIF | | frames < 4 | | frames > 25 )
return - EINVAL ;
frames = frames2frames [ frames ] ;
fps = frames2table [ frames ] ;
pEntry = & Nala_table [ size ] [ fps ] ;
if ( pEntry - > alternate = = 0 )
return - EINVAL ;
2006-03-25 15:19:53 +03:00
memcpy ( buf , pEntry - > mode , 3 ) ;
2005-04-17 02:20:36 +04:00
ret = send_video_command ( pdev - > udev , pdev - > vendpoint , buf , 3 ) ;
if ( ret < 0 ) {
2006-04-24 17:29:46 +04:00
PWC_DEBUG_MODULE ( " Failed to send video command... %d \n " , ret ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
if ( pEntry - > compressed & & pdev - > vpalette ! = VIDEO_PALETTE_RAW )
2006-04-24 17:29:46 +04:00
pwc_dec1_init ( pdev - > type , pdev - > release , buf , pdev - > decompress_data ) ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
pdev - > cmd_len = 3 ;
memcpy ( pdev - > cmd_buf , buf , 3 ) ;
/* Set various parameters */
pdev - > vframes = frames ;
pdev - > vsize = size ;
pdev - > valternate = pEntry - > alternate ;
pdev - > image = pwc_image_sizes [ size ] ;
pdev - > frame_size = ( pdev - > image . x * pdev - > image . y * 3 ) / 2 ;
if ( pEntry - > compressed ) {
if ( pdev - > release < 5 ) { /* 4 fold compression */
pdev - > vbandlength = 528 ;
pdev - > frame_size / = 4 ;
}
else {
pdev - > vbandlength = 704 ;
pdev - > frame_size / = 3 ;
}
}
else
pdev - > vbandlength = 0 ;
return 0 ;
}
2006-04-24 17:29:46 +04:00
static int set_video_mode_Timon ( struct pwc_device * pdev , int size , int frames , int compression , int snapshot )
2005-04-17 02:20:36 +04:00
{
unsigned char buf [ 13 ] ;
const struct Timon_table_entry * pChoose ;
int ret , fps ;
if ( size > = PSZ_MAX | | frames < 5 | | frames > 30 | | compression < 0 | | compression > 3 )
return - EINVAL ;
if ( size = = PSZ_VGA & & frames > 15 )
return - EINVAL ;
fps = ( frames / 5 ) - 1 ;
/* Find a supported framerate with progressively higher compression ratios
if the preferred ratio is not available .
*/
pChoose = NULL ;
while ( compression < = 3 ) {
pChoose = & Timon_table [ size ] [ fps ] [ compression ] ;
if ( pChoose - > alternate ! = 0 )
break ;
compression + + ;
}
if ( pChoose = = NULL | | pChoose - > alternate = = 0 )
return - ENOENT ; /* Not supported. */
memcpy ( buf , pChoose - > mode , 13 ) ;
if ( snapshot )
buf [ 0 ] | = 0x80 ;
ret = send_video_command ( pdev - > udev , pdev - > vendpoint , buf , 13 ) ;
if ( ret < 0 )
return ret ;
2006-04-24 17:29:46 +04:00
if ( pChoose - > bandlength > 0 & & pdev - > vpalette ! = VIDEO_PALETTE_RAW )
pwc_dec23_init ( pdev , pdev - > type , buf ) ;
2005-04-17 02:20:36 +04:00
pdev - > cmd_len = 13 ;
memcpy ( pdev - > cmd_buf , buf , 13 ) ;
/* Set various parameters */
pdev - > vframes = frames ;
pdev - > vsize = size ;
pdev - > vsnapshot = snapshot ;
pdev - > valternate = pChoose - > alternate ;
pdev - > image = pwc_image_sizes [ size ] ;
pdev - > vbandlength = pChoose - > bandlength ;
if ( pChoose - > bandlength > 0 )
pdev - > frame_size = ( pChoose - > bandlength * pdev - > image . y ) / 4 ;
else
pdev - > frame_size = ( pdev - > image . x * pdev - > image . y * 12 ) / 8 ;
return 0 ;
}
2006-04-24 17:29:46 +04:00
static int set_video_mode_Kiara ( struct pwc_device * pdev , int size , int frames , int compression , int snapshot )
2005-04-17 02:20:36 +04:00
{
const struct Kiara_table_entry * pChoose = NULL ;
int fps , ret ;
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 )
return - EINVAL ;
if ( size = = PSZ_VGA & & frames > 15 )
return - EINVAL ;
fps = ( frames / 5 ) - 1 ;
/* special case: VGA @ 5 fps and snapshot is raw bayer mode */
2006-04-24 17:29:46 +04:00
if ( size = = PSZ_VGA & & frames = = 5 & & snapshot & & pdev - > vpalette = = VIDEO_PALETTE_RAW )
2005-04-17 02:20:36 +04:00
{
2006-03-25 15:19:53 +03:00
/* Only available in case the raw palette is selected or
we have the decompressor available . This mode is
only available in compressed form
2005-04-17 02:20:36 +04:00
*/
2006-04-24 17:29:46 +04:00
PWC_DEBUG_SIZE ( " Choosing VGA/5 BAYER mode. \n " ) ;
pChoose = & RawEntry ;
2005-04-17 02:20:36 +04:00
}
else
{
2006-03-25 15:19:53 +03:00
/* Find a supported framerate with progressively higher compression ratios
2005-04-17 02:20:36 +04:00
if the preferred ratio is not available .
2006-03-25 15:19:53 +03:00
Skip this step when using RAW modes .
2005-04-17 02:20:36 +04:00
*/
2006-04-24 17:29:46 +04:00
snapshot = 0 ;
2005-04-17 02:20:36 +04:00
while ( compression < = 3 ) {
pChoose = & Kiara_table [ size ] [ fps ] [ compression ] ;
if ( pChoose - > alternate ! = 0 )
break ;
compression + + ;
}
}
if ( pChoose = = NULL | | pChoose - > alternate = = 0 )
return - ENOENT ; /* Not supported. */
2006-04-24 17:29:46 +04:00
PWC_TRACE ( " Using alternate setting %d. \n " , pChoose - > alternate ) ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
/* usb_control_msg won't take staticly allocated arrays as argument?? */
memcpy ( buf , pChoose - > mode , 12 ) ;
if ( snapshot )
buf [ 0 ] | = 0x80 ;
/* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */
ret = send_video_command ( pdev - > udev , 4 /* pdev->vendpoint */ , buf , 12 ) ;
if ( ret < 0 )
return ret ;
2006-04-24 17:29:46 +04:00
if ( pChoose - > bandlength > 0 & & pdev - > vpalette ! = VIDEO_PALETTE_RAW )
pwc_dec23_init ( pdev , pdev - > type , buf ) ;
2005-04-17 02:20:36 +04:00
pdev - > cmd_len = 12 ;
memcpy ( pdev - > cmd_buf , buf , 12 ) ;
/* All set and go */
pdev - > vframes = frames ;
pdev - > vsize = size ;
pdev - > vsnapshot = snapshot ;
pdev - > valternate = pChoose - > alternate ;
pdev - > image = pwc_image_sizes [ size ] ;
pdev - > vbandlength = pChoose - > bandlength ;
if ( pdev - > vbandlength > 0 )
pdev - > frame_size = ( pdev - > vbandlength * pdev - > image . y ) / 4 ;
else
pdev - > frame_size = ( pdev - > image . x * pdev - > image . y * 12 ) / 8 ;
2006-04-24 17:29:46 +04:00
PWC_TRACE ( " frame_size=%d, vframes=%d, vsize=%d, vsnapshot=%d, vbandlength=%d \n " ,
pdev - > frame_size , pdev - > vframes , pdev - > vsize , pdev - > vsnapshot , pdev - > vbandlength ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/**
@ pdev : device structure
@ width : viewport width
@ height : viewport height
@ frame : framerate , in fps
@ compression : preferred compression ratio
@ snapshot : snapshot mode or streaming
*/
int pwc_set_video_mode ( struct pwc_device * pdev , int width , int height , int frames , int compression , int snapshot )
{
2006-03-25 15:19:53 +03:00
int ret , size ;
2005-04-17 02:20:36 +04:00
2006-04-24 17:29:46 +04:00
PWC_DEBUG_FLOW ( " set_video_mode(%dx%d @ %d, palette %d). \n " , width , height , frames , pdev - > vpalette ) ;
2005-04-17 02:20:36 +04:00
size = pwc_decode_size ( pdev , width , height ) ;
if ( size < 0 ) {
2006-04-24 17:29:46 +04:00
PWC_DEBUG_MODULE ( " Could not find suitable size. \n " ) ;
2005-04-17 02:20:36 +04:00
return - ERANGE ;
}
2006-04-24 17:29:46 +04:00
PWC_TRACE ( " decode_size = %d. \n " , size ) ;
2005-04-17 02:20:36 +04:00
2006-04-24 17:29:46 +04:00
if ( DEVICE_USE_CODEC1 ( pdev - > type ) ) {
2005-04-17 02:20:36 +04:00
ret = set_video_mode_Nala ( pdev , size , frames ) ;
2006-03-25 15:19:53 +03:00
2006-04-24 17:29:46 +04:00
} else if ( DEVICE_USE_CODEC3 ( pdev - > type ) ) {
2005-04-17 02:20:36 +04:00
ret = set_video_mode_Kiara ( pdev , size , frames , compression , snapshot ) ;
2006-04-24 17:29:46 +04:00
} else {
ret = set_video_mode_Timon ( pdev , size , frames , compression , snapshot ) ;
2005-04-17 02:20:36 +04:00
}
if ( ret < 0 ) {
2006-04-24 17:29:46 +04:00
PWC_ERROR ( " Failed to set video mode %s@%d fps; return code = %d \n " , size2name [ size ] , frames , ret ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
pdev - > view . x = width ;
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 ) ;
2006-04-24 17:29:46 +04:00
PWC_DEBUG_SIZE ( " Set viewport to %dx%d, image size is %dx%d. \n " , width , height , pwc_image_sizes [ size ] . x , pwc_image_sizes [ size ] . y ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-04-23 06:54:36 +04:00
static unsigned int pwc_get_fps_Nala ( struct pwc_device * pdev , unsigned int index , unsigned int size )
{
unsigned int i ;
for ( i = 0 ; i < PWC_FPS_MAX_NALA ; i + + ) {
if ( Nala_table [ size ] [ i ] . alternate ) {
if ( index - - = = 0 ) return Nala_fps_vector [ i ] ;
}
}
return 0 ;
}
static unsigned int pwc_get_fps_Kiara ( struct pwc_device * pdev , unsigned int index , unsigned int size )
{
unsigned int i ;
for ( i = 0 ; i < PWC_FPS_MAX_KIARA ; i + + ) {
if ( Kiara_table [ size ] [ i ] [ 3 ] . alternate ) {
if ( index - - = = 0 ) return Kiara_fps_vector [ i ] ;
}
}
return 0 ;
}
static unsigned int pwc_get_fps_Timon ( struct pwc_device * pdev , unsigned int index , unsigned int size )
{
unsigned int i ;
for ( i = 0 ; i < PWC_FPS_MAX_TIMON ; i + + ) {
if ( Timon_table [ size ] [ i ] [ 3 ] . alternate ) {
if ( index - - = = 0 ) return Timon_fps_vector [ i ] ;
}
}
return 0 ;
}
unsigned int pwc_get_fps ( struct pwc_device * pdev , unsigned int index , unsigned int size )
{
unsigned int ret ;
if ( DEVICE_USE_CODEC1 ( pdev - > type ) ) {
ret = pwc_get_fps_Nala ( pdev , index , size ) ;
} else if ( DEVICE_USE_CODEC3 ( pdev - > type ) ) {
ret = pwc_get_fps_Kiara ( pdev , index , size ) ;
} else {
ret = pwc_get_fps_Timon ( pdev , index , size ) ;
}
return ret ;
}
2006-04-24 17:29:46 +04:00
# define BLACK_Y 0
# define BLACK_U 128
# define BLACK_V 128
static void pwc_set_image_buffer_size ( struct pwc_device * pdev )
{
int i , factor = 0 ;
/* for PALETTE_YUV420P */
switch ( pdev - > vpalette )
{
case VIDEO_PALETTE_YUV420P :
factor = 6 ;
break ;
case VIDEO_PALETTE_RAW :
factor = 6 ; /* can be uncompressed YUV420P */
break ;
}
/* Set sizes in bytes */
pdev - > image . size = pdev - > image . x * pdev - > image . 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
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
problem for YUV420P .
*/
pdev - > offset . x = ( ( pdev - > view . x - pdev - > image . x ) / 2 ) & 0xFFFC ;
pdev - > offset . y = ( ( pdev - > view . y - pdev - > image . y ) / 2 ) & 0xFFFE ;
/* Fill buffers with black colors */
for ( i = 0 ; i < pwc_mbufs ; i + + ) {
unsigned char * p = pdev - > image_data + pdev - > images [ i ] . offset ;
memset ( p , BLACK_Y , pdev - > view . x * pdev - > view . y ) ;
p + = pdev - > view . x * pdev - > view . y ;
memset ( p , BLACK_U , pdev - > view . x * pdev - > view . y / 4 ) ;
p + = pdev - > view . x * pdev - > view . y / 4 ;
memset ( p , BLACK_V , pdev - > view . x * pdev - > view . y / 4 ) ;
}
}
2005-04-17 02:20:36 +04:00
/* BRIGHTNESS */
int pwc_get_brightness ( struct pwc_device * pdev )
{
char buf ;
int ret ;
2006-03-25 15:19:53 +03:00
ret = RecvControlMsg ( GET_LUM_CTL , BRIGHTNESS_FORMATTER , 1 ) ;
2005-04-17 02:20:36 +04:00
if ( ret < 0 )
return ret ;
2006-04-24 17:29:46 +04:00
return buf ;
2005-04-17 02:20:36 +04:00
}
int pwc_set_brightness ( struct pwc_device * pdev , int value )
{
char buf ;
if ( value < 0 )
value = 0 ;
if ( value > 0xffff )
value = 0xffff ;
buf = ( value > > 9 ) & 0x7f ;
return SendControlMsg ( SET_LUM_CTL , BRIGHTNESS_FORMATTER , 1 ) ;
}
/* CONTRAST */
int pwc_get_contrast ( struct pwc_device * pdev )
{
char buf ;
int ret ;
ret = RecvControlMsg ( GET_LUM_CTL , CONTRAST_FORMATTER , 1 ) ;
if ( ret < 0 )
return ret ;
2006-04-24 17:29:46 +04:00
return buf ;
2005-04-17 02:20:36 +04:00
}
int pwc_set_contrast ( struct pwc_device * pdev , int value )
{
char buf ;
if ( value < 0 )
value = 0 ;
if ( value > 0xffff )
value = 0xffff ;
buf = ( value > > 10 ) & 0x3f ;
return SendControlMsg ( SET_LUM_CTL , CONTRAST_FORMATTER , 1 ) ;
}
/* GAMMA */
int pwc_get_gamma ( struct pwc_device * pdev )
{
char buf ;
int ret ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
ret = RecvControlMsg ( GET_LUM_CTL , GAMMA_FORMATTER , 1 ) ;
if ( ret < 0 )
return ret ;
2006-04-24 17:29:46 +04:00
return buf ;
2005-04-17 02:20:36 +04:00
}
int pwc_set_gamma ( struct pwc_device * pdev , int value )
{
char buf ;
if ( value < 0 )
value = 0 ;
if ( value > 0xffff )
value = 0xffff ;
buf = ( value > > 11 ) & 0x1f ;
return SendControlMsg ( SET_LUM_CTL , GAMMA_FORMATTER , 1 ) ;
}
/* SATURATION */
2006-04-24 17:29:46 +04:00
/* return a value between [-100 , 100] */
int pwc_get_saturation ( struct pwc_device * pdev , int * value )
2005-04-17 02:20:36 +04:00
{
char buf ;
2006-04-24 17:29:46 +04:00
int ret , saturation_register ;
2005-04-17 02:20:36 +04:00
if ( pdev - > type < 675 )
2006-04-24 17:29:46 +04:00
return - EINVAL ;
if ( pdev - > type < 730 )
saturation_register = SATURATION_MODE_FORMATTER2 ;
else
saturation_register = SATURATION_MODE_FORMATTER1 ;
ret = RecvControlMsg ( GET_CHROM_CTL , saturation_register , 1 ) ;
2005-04-17 02:20:36 +04:00
if ( ret < 0 )
return ret ;
2006-04-24 17:29:46 +04:00
* value = ( signed ) buf ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-04-24 17:29:46 +04:00
/* @param value saturation color between [-100 , 100] */
2005-04-17 02:20:36 +04:00
int pwc_set_saturation ( struct pwc_device * pdev , int value )
{
char buf ;
2006-04-24 17:29:46 +04:00
int saturation_register ;
2005-04-17 02:20:36 +04:00
if ( pdev - > type < 675 )
return - EINVAL ;
2006-04-24 17:29:46 +04:00
if ( value < - 100 )
value = - 100 ;
if ( value > 100 )
value = 100 ;
if ( pdev - > type < 730 )
saturation_register = SATURATION_MODE_FORMATTER2 ;
else
saturation_register = SATURATION_MODE_FORMATTER1 ;
return SendControlMsg ( SET_CHROM_CTL , saturation_register , 1 ) ;
2005-04-17 02:20:36 +04:00
}
/* AGC */
2006-04-24 17:29:46 +04:00
int pwc_set_agc ( struct pwc_device * pdev , int mode , int value )
2005-04-17 02:20:36 +04:00
{
char buf ;
int ret ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( mode )
buf = 0x0 ; /* auto */
else
buf = 0xff ; /* fixed */
ret = SendControlMsg ( SET_LUM_CTL , AGC_MODE_FORMATTER , 1 ) ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( ! mode & & ret > = 0 ) {
if ( value < 0 )
value = 0 ;
if ( value > 0xffff )
value = 0xffff ;
buf = ( value > > 10 ) & 0x3F ;
ret = SendControlMsg ( SET_LUM_CTL , PRESET_AGC_FORMATTER , 1 ) ;
}
if ( ret < 0 )
return ret ;
return 0 ;
}
2006-04-24 17:29:46 +04:00
int pwc_get_agc ( struct pwc_device * pdev , int * value )
2005-04-17 02:20:36 +04:00
{
unsigned char buf ;
int ret ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
ret = RecvControlMsg ( GET_LUM_CTL , AGC_MODE_FORMATTER , 1 ) ;
if ( ret < 0 )
return ret ;
if ( buf ! = 0 ) { /* fixed */
ret = RecvControlMsg ( GET_LUM_CTL , PRESET_AGC_FORMATTER , 1 ) ;
if ( ret < 0 )
return ret ;
if ( buf > 0x3F )
buf = 0x3F ;
2006-03-25 15:19:53 +03:00
* value = ( buf < < 10 ) ;
2005-04-17 02:20:36 +04:00
}
else { /* auto */
ret = RecvControlMsg ( GET_STATUS_CTL , READ_AGC_FORMATTER , 1 ) ;
if ( ret < 0 )
return ret ;
/* Gah... this value ranges from 0x00 ... 0x9F */
if ( buf > 0x9F )
buf = 0x9F ;
* value = - ( 48 + buf * 409 ) ;
}
return 0 ;
}
2006-04-24 17:29:46 +04:00
int pwc_set_shutter_speed ( struct pwc_device * pdev , int mode , int value )
2005-04-17 02:20:36 +04:00
{
char buf [ 2 ] ;
int speed , ret ;
if ( mode )
buf [ 0 ] = 0x0 ; /* auto */
else
buf [ 0 ] = 0xff ; /* fixed */
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
ret = SendControlMsg ( SET_LUM_CTL , SHUTTER_MODE_FORMATTER , 1 ) ;
if ( ! mode & & ret > = 0 ) {
if ( value < 0 )
value = 0 ;
if ( value > 0xffff )
value = 0xffff ;
2006-04-24 17:29:46 +04:00
if ( DEVICE_USE_CODEC2 ( pdev - > type ) ) {
2005-04-17 02:20:36 +04:00
/* speed ranges from 0x0 to 0x290 (656) */
speed = ( value / 100 ) ;
buf [ 1 ] = speed > > 8 ;
buf [ 0 ] = speed & 0xff ;
2006-04-24 17:29:46 +04:00
} else if ( DEVICE_USE_CODEC3 ( pdev - > type ) ) {
2005-04-17 02:20:36 +04:00
/* speed seems to range from 0x0 to 0xff */
buf [ 1 ] = 0 ;
buf [ 0 ] = value > > 8 ;
}
ret = SendControlMsg ( SET_LUM_CTL , PRESET_SHUTTER_FORMATTER , 2 ) ;
}
return ret ;
2006-03-25 15:19:53 +03:00
}
2005-04-17 02:20:36 +04:00
2006-04-24 17:29:46 +04:00
/* This function is not exported to v4l1, so output values between 0 -> 256 */
int pwc_get_shutter_speed ( struct pwc_device * pdev , int * value )
{
unsigned char buf [ 2 ] ;
int ret ;
ret = RecvControlMsg ( GET_STATUS_CTL , READ_SHUTTER_FORMATTER , 2 ) ;
if ( ret < 0 )
return ret ;
* value = buf [ 0 ] + ( buf [ 1 ] < < 8 ) ;
if ( DEVICE_USE_CODEC2 ( pdev - > type ) ) {
/* speed ranges from 0x0 to 0x290 (656) */
* value * = 256 / 656 ;
} else if ( DEVICE_USE_CODEC3 ( pdev - > type ) ) {
/* speed seems to range from 0x0 to 0xff */
}
return 0 ;
}
2005-04-17 02:20:36 +04:00
/* POWER */
int pwc_camera_power ( struct pwc_device * pdev , int power )
{
char buf ;
if ( pdev - > type < 675 | | ( pdev - > type < 730 & & pdev - > release < 6 ) )
return 0 ; /* Not supported by Nala or Timon < release 6 */
if ( power )
buf = 0x00 ; /* active */
else
buf = 0xFF ; /* power save */
return SendControlMsg ( SET_STATUS_CTL , SET_POWER_SAVE_MODE_FORMATTER , 1 ) ;
}
/* private calls */
2006-04-24 17:29:46 +04:00
int pwc_restore_user ( struct pwc_device * pdev )
2005-04-17 02:20:36 +04:00
{
char buf ; /* dummy */
return SendControlMsg ( SET_STATUS_CTL , RESTORE_USER_DEFAULTS_FORMATTER , 0 ) ;
}
2006-04-24 17:29:46 +04:00
int pwc_save_user ( struct pwc_device * pdev )
2005-04-17 02:20:36 +04:00
{
char buf ; /* dummy */
return SendControlMsg ( SET_STATUS_CTL , SAVE_USER_DEFAULTS_FORMATTER , 0 ) ;
}
2006-04-24 17:29:46 +04:00
int pwc_restore_factory ( struct pwc_device * pdev )
2005-04-17 02:20:36 +04:00
{
char buf ; /* dummy */
return SendControlMsg ( SET_STATUS_CTL , RESTORE_FACTORY_DEFAULTS_FORMATTER , 0 ) ;
}
/* ************************************************* */
/* Patch by Alvarado: (not in the original version */
/*
* the camera recognizes modes from 0 to 4 :
*
* 00 : indoor ( incandescant lighting )
* 01 : outdoor ( sunlight )
* 02 : fluorescent lighting
* 03 : manual
* 04 : auto
2006-03-25 15:19:53 +03:00
*/
2006-04-24 17:29:46 +04:00
int pwc_set_awb ( struct pwc_device * pdev , int mode )
2005-04-17 02:20:36 +04:00
{
char buf ;
int ret ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( mode < 0 )
mode = 0 ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( mode > 4 )
mode = 4 ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
buf = mode & 0x07 ; /* just the lowest three bits */
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
ret = SendControlMsg ( SET_CHROM_CTL , WB_MODE_FORMATTER , 1 ) ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( ret < 0 )
return ret ;
return 0 ;
}
2006-04-24 17:29:46 +04:00
int pwc_get_awb ( struct pwc_device * pdev )
2005-04-17 02:20:36 +04:00
{
unsigned char buf ;
int ret ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
ret = RecvControlMsg ( GET_CHROM_CTL , WB_MODE_FORMATTER , 1 ) ;
2006-03-25 15:19:53 +03:00
if ( ret < 0 )
2005-04-17 02:20:36 +04:00
return ret ;
return buf ;
}
2006-04-24 17:29:46 +04:00
int pwc_set_red_gain ( struct pwc_device * pdev , int value )
2005-04-17 02:20:36 +04:00
{
2006-03-25 15:19:53 +03:00
unsigned char buf ;
2005-04-17 02:20:36 +04:00
if ( value < 0 )
value = 0 ;
if ( value > 0xffff )
value = 0xffff ;
/* only the msb is considered */
buf = value > > 8 ;
return SendControlMsg ( SET_CHROM_CTL , PRESET_MANUAL_RED_GAIN_FORMATTER , 1 ) ;
}
2006-04-24 17:29:46 +04:00
int pwc_get_red_gain ( struct pwc_device * pdev , int * value )
2005-04-17 02:20:36 +04:00
{
unsigned char buf ;
int ret ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
ret = RecvControlMsg ( GET_CHROM_CTL , PRESET_MANUAL_RED_GAIN_FORMATTER , 1 ) ;
if ( ret < 0 )
return ret ;
* value = buf < < 8 ;
return 0 ;
}
2006-04-24 17:29:46 +04:00
int pwc_set_blue_gain ( struct pwc_device * pdev , int value )
2005-04-17 02:20:36 +04:00
{
unsigned char buf ;
if ( value < 0 )
value = 0 ;
if ( value > 0xffff )
value = 0xffff ;
/* only the msb is considered */
buf = value > > 8 ;
return SendControlMsg ( SET_CHROM_CTL , PRESET_MANUAL_BLUE_GAIN_FORMATTER , 1 ) ;
}
2006-04-24 17:29:46 +04:00
int pwc_get_blue_gain ( struct pwc_device * pdev , int * value )
2005-04-17 02:20:36 +04:00
{
unsigned char buf ;
int ret ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
ret = RecvControlMsg ( GET_CHROM_CTL , PRESET_MANUAL_BLUE_GAIN_FORMATTER , 1 ) ;
if ( ret < 0 )
return ret ;
* value = buf < < 8 ;
return 0 ;
}
/* The following two functions are different, since they only read the
2006-03-25 15:19:53 +03:00
internal red / blue gains , which may be different from the manual
2005-04-17 02:20:36 +04:00
gains set or read above .
2006-03-25 15:19:53 +03:00
*/
2006-04-24 17:29:46 +04:00
static int pwc_read_red_gain ( struct pwc_device * pdev , int * value )
2005-04-17 02:20:36 +04:00
{
unsigned char buf ;
int ret ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
ret = RecvControlMsg ( GET_STATUS_CTL , READ_RED_GAIN_FORMATTER , 1 ) ;
if ( ret < 0 )
return ret ;
* value = buf < < 8 ;
return 0 ;
}
2006-04-24 17:29:46 +04:00
static int pwc_read_blue_gain ( struct pwc_device * pdev , int * value )
2005-04-17 02:20:36 +04:00
{
unsigned char buf ;
int ret ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
ret = RecvControlMsg ( GET_STATUS_CTL , READ_BLUE_GAIN_FORMATTER , 1 ) ;
if ( ret < 0 )
return ret ;
* value = buf < < 8 ;
return 0 ;
}
2006-04-24 17:29:46 +04:00
static int pwc_set_wb_speed ( struct pwc_device * pdev , int speed )
2005-04-17 02:20:36 +04:00
{
unsigned char buf ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
/* useful range is 0x01..0x20 */
buf = speed / 0x7f0 ;
return SendControlMsg ( SET_CHROM_CTL , AWB_CONTROL_SPEED_FORMATTER , 1 ) ;
}
2006-04-24 17:29:46 +04:00
static int pwc_get_wb_speed ( struct pwc_device * pdev , int * value )
2005-04-17 02:20:36 +04:00
{
unsigned char buf ;
int ret ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
ret = RecvControlMsg ( GET_CHROM_CTL , AWB_CONTROL_SPEED_FORMATTER , 1 ) ;
if ( ret < 0 )
return ret ;
* value = buf * 0x7f0 ;
return 0 ;
}
2006-04-24 17:29:46 +04:00
static int pwc_set_wb_delay ( struct pwc_device * pdev , int delay )
2005-04-17 02:20:36 +04:00
{
unsigned char buf ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
/* useful range is 0x01..0x3F */
buf = ( delay > > 10 ) ;
return SendControlMsg ( SET_CHROM_CTL , AWB_CONTROL_DELAY_FORMATTER , 1 ) ;
}
2006-04-24 17:29:46 +04:00
static int pwc_get_wb_delay ( struct pwc_device * pdev , int * value )
2005-04-17 02:20:36 +04:00
{
unsigned char buf ;
int ret ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
ret = RecvControlMsg ( GET_CHROM_CTL , AWB_CONTROL_DELAY_FORMATTER , 1 ) ;
if ( ret < 0 )
return ret ;
* value = buf < < 10 ;
return 0 ;
}
int pwc_set_leds ( struct pwc_device * pdev , int on_value , int off_value )
{
unsigned char buf [ 2 ] ;
if ( pdev - > type < 730 )
return 0 ;
on_value / = 100 ;
off_value / = 100 ;
if ( on_value < 0 )
on_value = 0 ;
if ( on_value > 0xff )
on_value = 0xff ;
if ( off_value < 0 )
off_value = 0 ;
if ( off_value > 0xff )
off_value = 0xff ;
buf [ 0 ] = on_value ;
buf [ 1 ] = off_value ;
return SendControlMsg ( SET_STATUS_CTL , LED_FORMATTER , 2 ) ;
}
2006-06-23 13:49:34 +04:00
static int pwc_get_leds ( struct pwc_device * pdev , int * on_value , int * off_value )
2005-04-17 02:20:36 +04:00
{
unsigned char buf [ 2 ] ;
int ret ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( pdev - > type < 730 ) {
* on_value = - 1 ;
* off_value = - 1 ;
return 0 ;
}
ret = RecvControlMsg ( GET_STATUS_CTL , LED_FORMATTER , 2 ) ;
if ( ret < 0 )
return ret ;
* on_value = buf [ 0 ] * 100 ;
* off_value = buf [ 1 ] * 100 ;
return 0 ;
}
2006-04-24 17:29:46 +04:00
int pwc_set_contour ( struct pwc_device * pdev , int contour )
2005-04-17 02:20:36 +04:00
{
unsigned char buf ;
int ret ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( contour < 0 )
buf = 0xff ; /* auto contour on */
else
buf = 0x0 ; /* auto contour off */
ret = SendControlMsg ( SET_LUM_CTL , AUTO_CONTOUR_FORMATTER , 1 ) ;
if ( ret < 0 )
return ret ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( contour < 0 )
return 0 ;
if ( contour > 0xffff )
contour = 0xffff ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
buf = ( contour > > 10 ) ; /* contour preset is [0..3f] */
ret = SendControlMsg ( SET_LUM_CTL , PRESET_CONTOUR_FORMATTER , 1 ) ;
2006-03-25 15:19:53 +03:00
if ( ret < 0 )
return ret ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-04-24 17:29:46 +04:00
int pwc_get_contour ( struct pwc_device * pdev , int * contour )
2005-04-17 02:20:36 +04:00
{
unsigned char buf ;
int ret ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
ret = RecvControlMsg ( GET_LUM_CTL , AUTO_CONTOUR_FORMATTER , 1 ) ;
if ( ret < 0 )
return ret ;
if ( buf = = 0 ) {
/* auto mode off, query current preset value */
ret = RecvControlMsg ( GET_LUM_CTL , PRESET_CONTOUR_FORMATTER , 1 ) ;
2006-03-25 15:19:53 +03:00
if ( ret < 0 )
2005-04-17 02:20:36 +04:00
return ret ;
* contour = buf < < 10 ;
}
else
* contour = - 1 ;
return 0 ;
}
2006-04-24 17:29:46 +04:00
int pwc_set_backlight ( struct pwc_device * pdev , int backlight )
2005-04-17 02:20:36 +04:00
{
unsigned char buf ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( backlight )
buf = 0xff ;
else
buf = 0x0 ;
return SendControlMsg ( SET_LUM_CTL , BACK_LIGHT_COMPENSATION_FORMATTER , 1 ) ;
}
2006-04-24 17:29:46 +04:00
int pwc_get_backlight ( struct pwc_device * pdev , int * backlight )
2005-04-17 02:20:36 +04:00
{
int ret ;
unsigned char buf ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
ret = RecvControlMsg ( GET_LUM_CTL , BACK_LIGHT_COMPENSATION_FORMATTER , 1 ) ;
if ( ret < 0 )
return ret ;
2006-04-24 17:29:46 +04:00
* backlight = ! ! buf ;
return 0 ;
}
int pwc_set_colour_mode ( struct pwc_device * pdev , int colour )
{
unsigned char buf ;
if ( colour )
buf = 0xff ;
else
buf = 0x0 ;
return SendControlMsg ( SET_CHROM_CTL , COLOUR_MODE_FORMATTER , 1 ) ;
}
int pwc_get_colour_mode ( struct pwc_device * pdev , int * colour )
{
int ret ;
unsigned char buf ;
ret = RecvControlMsg ( GET_CHROM_CTL , COLOUR_MODE_FORMATTER , 1 ) ;
if ( ret < 0 )
return ret ;
* colour = ! ! buf ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-04-24 17:29:46 +04:00
int pwc_set_flicker ( struct pwc_device * pdev , int flicker )
2005-04-17 02:20:36 +04:00
{
unsigned char buf ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( flicker )
buf = 0xff ;
else
buf = 0x0 ;
return SendControlMsg ( SET_LUM_CTL , FLICKERLESS_MODE_FORMATTER , 1 ) ;
}
2006-04-24 17:29:46 +04:00
int pwc_get_flicker ( struct pwc_device * pdev , int * flicker )
2005-04-17 02:20:36 +04:00
{
int ret ;
unsigned char buf ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
ret = RecvControlMsg ( GET_LUM_CTL , FLICKERLESS_MODE_FORMATTER , 1 ) ;
if ( ret < 0 )
return ret ;
2006-04-24 17:29:46 +04:00
* flicker = ! ! buf ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-04-24 17:29:46 +04:00
int pwc_set_dynamic_noise ( struct pwc_device * pdev , int noise )
2005-04-17 02:20:36 +04:00
{
unsigned char buf ;
if ( noise < 0 )
noise = 0 ;
if ( noise > 3 )
noise = 3 ;
buf = noise ;
return SendControlMsg ( SET_LUM_CTL , DYNAMIC_NOISE_CONTROL_FORMATTER , 1 ) ;
}
2006-04-24 17:29:46 +04:00
int pwc_get_dynamic_noise ( struct pwc_device * pdev , int * noise )
2005-04-17 02:20:36 +04:00
{
int ret ;
unsigned char buf ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
ret = RecvControlMsg ( GET_LUM_CTL , DYNAMIC_NOISE_CONTROL_FORMATTER , 1 ) ;
if ( ret < 0 )
return ret ;
* noise = buf ;
return 0 ;
}
2006-04-24 17:29:46 +04:00
static int _pwc_mpt_reset ( struct pwc_device * pdev , int flags )
2005-04-17 02:20:36 +04:00
{
unsigned char buf ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
buf = flags & 0x03 ; // only lower two bits are currently used
return SendControlMsg ( SET_MPT_CTL , PT_RESET_CONTROL_FORMATTER , 1 ) ;
}
2006-04-24 17:29:46 +04:00
int pwc_mpt_reset ( struct pwc_device * pdev , int flags )
{
int ret ;
ret = _pwc_mpt_reset ( pdev , flags ) ;
if ( ret > = 0 ) {
pdev - > pan_angle = 0 ;
pdev - > tilt_angle = 0 ;
}
return ret ;
}
static int _pwc_mpt_set_angle ( struct pwc_device * pdev , int pan , int tilt )
2005-04-17 02:20:36 +04:00
{
unsigned char buf [ 4 ] ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
/* set new relative angle; angles are expressed in degrees * 100,
2005-05-04 05:07:24 +04:00
but cam as .5 degree resolution , hence divide by 200. Also
2005-04-17 02:20:36 +04:00
the angle must be multiplied by 64 before it ' s send to
the cam ( ? ? )
*/
pan = 64 * pan / 100 ;
tilt = - 64 * tilt / 100 ; /* positive tilt is down, which is not what the user would expect */
buf [ 0 ] = pan & 0xFF ;
buf [ 1 ] = ( pan > > 8 ) & 0xFF ;
buf [ 2 ] = tilt & 0xFF ;
buf [ 3 ] = ( tilt > > 8 ) & 0xFF ;
return SendControlMsg ( SET_MPT_CTL , PT_RELATIVE_CONTROL_FORMATTER , 4 ) ;
}
2006-04-24 17:29:46 +04:00
int pwc_mpt_set_angle ( struct pwc_device * pdev , int pan , int tilt )
{
int ret ;
/* check absolute ranges */
if ( pan < pdev - > angle_range . pan_min | |
pan > pdev - > angle_range . pan_max | |
tilt < pdev - > angle_range . tilt_min | |
tilt > pdev - > angle_range . tilt_max )
return - ERANGE ;
/* go to relative range, check again */
pan - = pdev - > pan_angle ;
tilt - = pdev - > tilt_angle ;
/* angles are specified in degrees * 100, thus the limit = 36000 */
if ( pan < - 36000 | | pan > 36000 | | tilt < - 36000 | | tilt > 36000 )
return - ERANGE ;
ret = _pwc_mpt_set_angle ( pdev , pan , tilt ) ;
if ( ret > = 0 ) {
pdev - > pan_angle + = pan ;
pdev - > tilt_angle + = tilt ;
}
if ( ret = = - EPIPE ) /* stall -> out of range */
ret = - ERANGE ;
return ret ;
}
static int pwc_mpt_get_status ( struct pwc_device * pdev , struct pwc_mpt_status * status )
2005-04-17 02:20:36 +04:00
{
int ret ;
unsigned char buf [ 5 ] ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
ret = RecvControlMsg ( GET_MPT_CTL , PT_STATUS_FORMATTER , 5 ) ;
if ( ret < 0 )
return ret ;
status - > status = buf [ 0 ] & 0x7 ; // 3 bits are used for reporting
status - > time_pan = ( buf [ 1 ] < < 8 ) + buf [ 2 ] ;
status - > time_tilt = ( buf [ 3 ] < < 8 ) + buf [ 4 ] ;
return 0 ;
}
int pwc_get_cmos_sensor ( struct pwc_device * pdev , int * sensor )
{
unsigned char buf ;
int ret = - 1 , request ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( pdev - > type < 675 )
request = SENSOR_TYPE_FORMATTER1 ;
else if ( pdev - > type < 730 )
return - 1 ; /* The Vesta series doesn't have this call */
else
request = SENSOR_TYPE_FORMATTER2 ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
ret = RecvControlMsg ( GET_STATUS_CTL , request , 1 ) ;
if ( ret < 0 )
return ret ;
if ( pdev - > type < 675 )
* sensor = buf | 0x100 ;
else
* sensor = buf ;
return 0 ;
}
/* End of Add-Ons */
/* ************************************************* */
2006-04-24 17:29:46 +04:00
/* 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 .
*/
/* 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 */
2005-04-17 02:20:36 +04:00
int pwc_ioctl ( struct pwc_device * pdev , unsigned int cmd , void * arg )
{
int ret = 0 ;
switch ( cmd ) {
case VIDIOCPWCRUSER :
{
if ( pwc_restore_user ( pdev ) )
ret = - EINVAL ;
break ;
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
case VIDIOCPWCSUSER :
{
if ( pwc_save_user ( pdev ) )
ret = - EINVAL ;
break ;
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
case VIDIOCPWCFACTORY :
{
if ( pwc_restore_factory ( pdev ) )
ret = - EINVAL ;
break ;
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
case VIDIOCPWCSCQUAL :
2006-03-25 15:19:53 +03:00
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( int , qual )
2005-04-17 02:20:36 +04:00
2006-04-24 17:29:46 +04:00
ARG_IN ( qual )
if ( ARGR ( qual ) < 0 | | ARGR ( qual ) > 3 )
2005-04-17 02:20:36 +04:00
ret = - EINVAL ;
else
2006-04-24 17:29:46 +04:00
ret = pwc_try_video_mode ( pdev , pdev - > view . x , pdev - > view . y , pdev - > vframes , ARGR ( qual ) , pdev - > vsnapshot ) ;
2005-04-17 02:20:36 +04:00
if ( ret > = 0 )
2006-04-24 17:29:46 +04:00
pdev - > vcompression = ARGR ( qual ) ;
2005-04-17 02:20:36 +04:00
break ;
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
case VIDIOCPWCGCQUAL :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( int , qual )
ARGR ( qual ) = pdev - > vcompression ;
ARG_OUT ( qual )
2005-04-17 02:20:36 +04:00
break ;
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
case VIDIOCPWCPROBE :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( struct pwc_probe , probe )
strcpy ( ARGR ( probe ) . name , pdev - > vdev - > name ) ;
ARGR ( probe ) . type = pdev - > type ;
ARG_OUT ( probe )
2005-04-17 02:20:36 +04:00
break ;
}
case VIDIOCPWCGSERIAL :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( struct pwc_serial , serial )
strcpy ( ARGR ( serial ) . serial , pdev - > serial ) ;
ARG_OUT ( serial )
2005-04-17 02:20:36 +04:00
break ;
}
case VIDIOCPWCSAGC :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( int , agc )
ARG_IN ( agc )
if ( pwc_set_agc ( pdev , ARGR ( agc ) < 0 ? 1 : 0 , ARGR ( agc ) ) )
2005-04-17 02:20:36 +04:00
ret = - EINVAL ;
break ;
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
case VIDIOCPWCGAGC :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( int , agc )
2006-03-25 15:19:53 +03:00
2006-04-24 17:29:46 +04:00
if ( pwc_get_agc ( pdev , ARGA ( agc ) ) )
2005-04-17 02:20:36 +04:00
ret = - EINVAL ;
2006-04-24 17:29:46 +04:00
ARG_OUT ( agc )
2005-04-17 02:20:36 +04:00
break ;
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
case VIDIOCPWCSSHUTTER :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( int , shutter_speed )
ARG_IN ( shutter_speed )
ret = pwc_set_shutter_speed ( pdev , ARGR ( shutter_speed ) < 0 ? 1 : 0 , ARGR ( shutter_speed ) ) ;
2005-04-17 02:20:36 +04:00
break ;
}
2006-03-25 15:19:53 +03:00
case VIDIOCPWCSAWB :
2005-04-17 02:20:36 +04:00
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( struct pwc_whitebalance , wb )
2006-03-25 15:19:53 +03:00
2006-04-24 17:29:46 +04:00
ARG_IN ( wb )
ret = pwc_set_awb ( pdev , ARGR ( wb ) . mode ) ;
if ( ret > = 0 & & ARGR ( wb ) . mode = = PWC_WB_MANUAL ) {
pwc_set_red_gain ( pdev , ARGR ( wb ) . manual_red ) ;
pwc_set_blue_gain ( pdev , ARGR ( wb ) . manual_blue ) ;
2005-04-17 02:20:36 +04:00
}
break ;
}
case VIDIOCPWCGAWB :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( struct pwc_whitebalance , wb )
2005-04-17 02:20:36 +04:00
2006-04-24 17:29:46 +04:00
memset ( ARGA ( wb ) , 0 , sizeof ( struct pwc_whitebalance ) ) ;
ARGR ( wb ) . mode = pwc_get_awb ( pdev ) ;
if ( ARGR ( wb ) . mode < 0 )
2005-04-17 02:20:36 +04:00
ret = - EINVAL ;
else {
2006-04-24 17:29:46 +04:00
if ( ARGR ( wb ) . mode = = PWC_WB_MANUAL ) {
ret = pwc_get_red_gain ( pdev , & ARGR ( wb ) . manual_red ) ;
2005-04-17 02:20:36 +04:00
if ( ret < 0 )
break ;
2006-04-24 17:29:46 +04:00
ret = pwc_get_blue_gain ( pdev , & ARGR ( wb ) . manual_blue ) ;
2005-04-17 02:20:36 +04:00
if ( ret < 0 )
break ;
}
2006-04-24 17:29:46 +04:00
if ( ARGR ( wb ) . mode = = PWC_WB_AUTO ) {
ret = pwc_read_red_gain ( pdev , & ARGR ( wb ) . read_red ) ;
2005-04-17 02:20:36 +04:00
if ( ret < 0 )
break ;
2007-04-23 06:54:36 +04:00
ret = pwc_read_blue_gain ( pdev , & ARGR ( wb ) . read_blue ) ;
2006-03-25 15:19:53 +03:00
if ( ret < 0 )
break ;
2005-04-17 02:20:36 +04:00
}
}
2006-04-24 17:29:46 +04:00
ARG_OUT ( wb )
2005-04-17 02:20:36 +04:00
break ;
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
case VIDIOCPWCSAWBSPEED :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( struct pwc_wb_speed , wbs )
2006-03-25 15:19:53 +03:00
2006-04-24 17:29:46 +04:00
if ( ARGR ( wbs ) . control_speed > 0 ) {
ret = pwc_set_wb_speed ( pdev , ARGR ( wbs ) . control_speed ) ;
2005-04-17 02:20:36 +04:00
}
2006-04-24 17:29:46 +04:00
if ( ARGR ( wbs ) . control_delay > 0 ) {
ret = pwc_set_wb_delay ( pdev , ARGR ( wbs ) . control_delay ) ;
2005-04-17 02:20:36 +04:00
}
break ;
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
case VIDIOCPWCGAWBSPEED :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( struct pwc_wb_speed , wbs )
2006-03-25 15:19:53 +03:00
2006-04-24 17:29:46 +04:00
ret = pwc_get_wb_speed ( pdev , & ARGR ( wbs ) . control_speed ) ;
2005-04-17 02:20:36 +04:00
if ( ret < 0 )
break ;
2006-04-24 17:29:46 +04:00
ret = pwc_get_wb_delay ( pdev , & ARGR ( wbs ) . control_delay ) ;
2005-04-17 02:20:36 +04:00
if ( ret < 0 )
break ;
2006-04-24 17:29:46 +04:00
ARG_OUT ( wbs )
2005-04-17 02:20:36 +04:00
break ;
}
2006-03-25 15:19:53 +03:00
case VIDIOCPWCSLED :
2005-04-17 02:20:36 +04:00
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( struct pwc_leds , leds )
ARG_IN ( leds )
ret = pwc_set_leds ( pdev , ARGR ( leds ) . led_on , ARGR ( leds ) . led_off ) ;
2006-06-20 07:30:57 +04:00
break ;
2005-04-17 02:20:36 +04:00
}
case VIDIOCPWCGLED :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( struct pwc_leds , leds )
ret = pwc_get_leds ( pdev , & ARGR ( leds ) . led_on , & ARGR ( leds ) . led_off ) ;
ARG_OUT ( leds )
2005-04-17 02:20:36 +04:00
break ;
}
case VIDIOCPWCSCONTOUR :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( int , contour )
ARG_IN ( contour )
ret = pwc_set_contour ( pdev , ARGR ( contour ) ) ;
2005-04-17 02:20:36 +04:00
break ;
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
case VIDIOCPWCGCONTOUR :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( int , contour )
ret = pwc_get_contour ( pdev , ARGA ( contour ) ) ;
ARG_OUT ( contour )
2005-04-17 02:20:36 +04:00
break ;
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
case VIDIOCPWCSBACKLIGHT :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( int , backlight )
ARG_IN ( backlight )
ret = pwc_set_backlight ( pdev , ARGR ( backlight ) ) ;
2005-04-17 02:20:36 +04:00
break ;
}
case VIDIOCPWCGBACKLIGHT :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( int , backlight )
ret = pwc_get_backlight ( pdev , ARGA ( backlight ) ) ;
ARG_OUT ( backlight )
2005-04-17 02:20:36 +04:00
break ;
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
case VIDIOCPWCSFLICKER :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( int , flicker )
ARG_IN ( flicker )
ret = pwc_set_flicker ( pdev , ARGR ( flicker ) ) ;
2005-04-17 02:20:36 +04:00
break ;
}
case VIDIOCPWCGFLICKER :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( int , flicker )
ret = pwc_get_flicker ( pdev , ARGA ( flicker ) ) ;
ARG_OUT ( flicker )
2005-04-17 02:20:36 +04:00
break ;
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
case VIDIOCPWCSDYNNOISE :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( int , dynnoise )
ARG_IN ( dynnoise )
ret = pwc_set_dynamic_noise ( pdev , ARGR ( dynnoise ) ) ;
2005-04-17 02:20:36 +04:00
break ;
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
case VIDIOCPWCGDYNNOISE :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( int , dynnoise )
ret = pwc_get_dynamic_noise ( pdev , ARGA ( dynnoise ) ) ;
ARG_OUT ( dynnoise ) ;
2005-04-17 02:20:36 +04:00
break ;
}
case VIDIOCPWCGREALSIZE :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( struct pwc_imagesize , size )
ARGR ( size ) . width = pdev - > image . x ;
ARGR ( size ) . height = pdev - > image . y ;
ARG_OUT ( size )
2005-04-17 02:20:36 +04:00
break ;
2006-03-25 15:19:53 +03:00
}
case VIDIOCPWCMPTRESET :
{
if ( pdev - > features & FEATURE_MOTOR_PANTILT )
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( int , flags )
2005-04-17 02:20:36 +04:00
2006-04-24 17:29:46 +04:00
ARG_IN ( flags )
ret = pwc_mpt_reset ( pdev , ARGR ( flags ) ) ;
2006-03-25 15:19:53 +03:00
}
else
{
ret = - ENXIO ;
}
break ;
}
case VIDIOCPWCMPTGRANGE :
{
if ( pdev - > features & FEATURE_MOTOR_PANTILT )
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( struct pwc_mpt_range , range )
ARGR ( range ) = pdev - > angle_range ;
ARG_OUT ( range )
2006-03-25 15:19:53 +03:00
}
else
{
ret = - ENXIO ;
}
break ;
}
case VIDIOCPWCMPTSANGLE :
{
int new_pan , new_tilt ;
if ( pdev - > features & FEATURE_MOTOR_PANTILT )
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( struct pwc_mpt_angles , angles )
ARG_IN ( angles )
2005-04-17 02:20:36 +04:00
/* The camera can only set relative angles, so
do some calculations when getting an absolute angle .
*/
2006-04-24 17:29:46 +04:00
if ( ARGR ( angles ) . absolute )
2005-04-17 02:20:36 +04:00
{
2006-04-24 17:29:46 +04:00
new_pan = ARGR ( angles ) . pan ;
new_tilt = ARGR ( angles ) . tilt ;
2006-03-25 15:19:53 +03:00
}
else
{
2006-04-24 17:29:46 +04:00
new_pan = pdev - > pan_angle + ARGR ( angles ) . pan ;
new_tilt = pdev - > tilt_angle + ARGR ( angles ) . tilt ;
2005-04-17 02:20:36 +04:00
}
2006-04-24 17:29:46 +04:00
ret = pwc_mpt_set_angle ( pdev , new_pan , new_tilt ) ;
2006-03-25 15:19:53 +03:00
}
else
{
ret = - ENXIO ;
}
break ;
}
case VIDIOCPWCMPTGANGLE :
{
if ( pdev - > features & FEATURE_MOTOR_PANTILT )
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( struct pwc_mpt_angles , angles )
2006-03-25 15:19:53 +03:00
2006-04-24 17:29:46 +04:00
ARGR ( angles ) . absolute = 1 ;
ARGR ( angles ) . pan = pdev - > pan_angle ;
ARGR ( angles ) . tilt = pdev - > tilt_angle ;
ARG_OUT ( angles )
2006-03-25 15:19:53 +03:00
}
else
{
ret = - ENXIO ;
}
break ;
}
case VIDIOCPWCMPTSTATUS :
{
if ( pdev - > features & FEATURE_MOTOR_PANTILT )
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( struct pwc_mpt_status , status )
ret = pwc_mpt_get_status ( pdev , ARGA ( status ) ) ;
ARG_OUT ( status )
2006-03-25 15:19:53 +03:00
}
else
{
ret = - ENXIO ;
}
break ;
2005-04-17 02:20:36 +04:00
}
case VIDIOCPWCGVIDCMD :
{
2006-04-24 17:29:46 +04:00
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 )
2005-04-17 02:20:36 +04:00
break ;
}
/*
case VIDIOCPWCGVIDTABLE :
{
2006-04-24 17:29:46 +04:00
ARG_DEF ( struct pwc_table_init_buffer , table ) ;
ARGR ( table ) . len = pdev - > cmd_len ;
memcpy ( & ARGR ( table ) . buffer , pdev - > decompress_data , pdev - > decompressor - > table_size ) ;
ARG_OUT ( table )
2005-04-17 02:20:36 +04:00
break ;
}
*/
default :
ret = - ENOIOCTLCMD ;
break ;
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( ret > 0 )
return 0 ;
return ret ;
}
2006-04-24 17:29:46 +04:00
/* vim: set cinoptions= formatoptions=croql cindent shiftwidth=8 tabstop=8: */