2005-04-17 02:20:36 +04:00
/*
* STV0680 USB Camera Driver , by Kevin Sisson ( kjsisson @ bellsouth . net )
*
* Thanks to STMicroelectronics for information on the usb commands , and
* to Steve Miller at STM for his help and encouragement while I was
* writing this driver .
*
* This driver is based heavily on the
* Endpoints ( formerly known as AOX ) se401 USB Camera Driver
* Copyright ( c ) 2000 Jeroen B . Vreeken ( pe1rxq @ amsat . org )
*
* Still somewhat based on the Linux ov511 driver .
*
* 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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
* History :
* ver 0.1 October , 2001. Initial attempt .
*
* ver 0.2 November , 2001. Fixed asbility to resize , added brightness
* function , made more stable ( ? )
*
* ver 0.21 Nov , 2001. Added gamma correction and white balance ,
* due to Alexander Schwartz . Still trying to
* improve stablility . Moved stuff into stv680 . h
*
* ver 0.22 Nov , 2001. Added sharpen function ( by Michael Sweet ,
* mike @ easysw . com ) from GIMP , also used in pencam .
* Simple , fast , good integer math routine .
*
* ver 0.23 Dec , 2001 ( gkh )
* Took out sharpen function , ran code through
* Lindent , and did other minor tweaks to get
* things to work properly with 2.5 .1
*
* ver 0.24 Jan , 2002 ( kjs )
* Fixed the problem with webcam crashing after
* two pictures . Changed the way pic is halved to
* improve quality . Got rid of green line around
* frame . Fix brightness reset when changing size
* bug . Adjusted gamma filters slightly .
*
* ver 0.25 Jan , 2002 ( kjs )
* Fixed a bug in which the driver sometimes attempted
* to set to a non - supported size . This allowed
* gnomemeeting to work .
* Fixed proc entry removal bug .
*/
# include <linux/config.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/vmalloc.h>
# include <linux/slab.h>
# include <linux/pagemap.h>
# include <linux/errno.h>
# include <linux/videodev.h>
# include <linux/usb.h>
# include "stv680.h"
static int video_nr = - 1 ;
static int swapRGB = 0 ; /* default for auto sleect */
static int swapRGB_on = 0 ; /* default to allow auto select; -1=swap never, +1= swap always */
static unsigned int debug = 0 ;
# define PDEBUG(level, fmt, args...) \
do { \
if ( debug > = level ) \
info ( " [%s:%d] " fmt , __FUNCTION__ , __LINE__ , # # args ) ; \
} while ( 0 )
/*
* Version Information
*/
# define DRIVER_VERSION "v0.25"
# define DRIVER_AUTHOR "Kevin Sisson <kjsisson@bellsouth.net>"
# define DRIVER_DESC "STV0680 USB Camera Driver"
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
module_param ( debug , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( debug , " Debug enabled or not " ) ;
module_param ( swapRGB_on , int , 0 ) ;
MODULE_PARM_DESC ( swapRGB_on , " Red/blue swap: 1=always, 0=auto, -1=never " ) ;
module_param ( video_nr , int , 0 ) ;
/********************************************************************
*
* Memory management
*
* This is a shameless copy from the USB - cpia driver ( linux kernel
* version 2.3 .29 or so , I have no idea what this code actually does ; ) .
* Actually it seems to be a copy of a shameless copy of the bttv - driver .
* Or that is a copy of a shameless copy of . . . ( To the powers : is there
* no generic kernel - function to do this sort of stuff ? )
*
* Yes , it was a shameless copy from the bttv - driver . IIRC , Alan says
* there will be one , but apparentely not yet - jerdfelt
*
* So I copied it again for the ov511 driver - claudio
*
* Same for the se401 driver - Jeroen
*
* And the STV0680 driver - Kevin
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void * rvmalloc ( unsigned long size )
{
void * mem ;
unsigned long adr ;
size = PAGE_ALIGN ( size ) ;
mem = vmalloc_32 ( size ) ;
if ( ! mem )
return NULL ;
memset ( mem , 0 , size ) ; /* Clear the ram out, no junk to the user */
adr = ( unsigned long ) mem ;
while ( size > 0 ) {
SetPageReserved ( vmalloc_to_page ( ( void * ) adr ) ) ;
adr + = PAGE_SIZE ;
size - = PAGE_SIZE ;
}
return mem ;
}
static void rvfree ( void * mem , unsigned long size )
{
unsigned long adr ;
if ( ! mem )
return ;
adr = ( unsigned long ) mem ;
while ( ( long ) size > 0 ) {
ClearPageReserved ( vmalloc_to_page ( ( void * ) adr ) ) ;
adr + = PAGE_SIZE ;
size - = PAGE_SIZE ;
}
vfree ( mem ) ;
}
/*********************************************************************
* pencam read / write functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int stv_sndctrl ( int set , struct usb_stv * stv680 , unsigned short req , unsigned short value , unsigned char * buffer , int size )
{
int ret = - 1 ;
switch ( set ) {
case 0 : /* 0xc1 */
ret = usb_control_msg ( stv680 - > udev ,
usb_rcvctrlpipe ( stv680 - > udev , 0 ) ,
req ,
( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT ) ,
value , 0 , buffer , size , PENCAM_TIMEOUT ) ;
break ;
case 1 : /* 0x41 */
ret = usb_control_msg ( stv680 - > udev ,
usb_sndctrlpipe ( stv680 - > udev , 0 ) ,
req ,
( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT ) ,
value , 0 , buffer , size , PENCAM_TIMEOUT ) ;
break ;
case 2 : /* 0x80 */
ret = usb_control_msg ( stv680 - > udev ,
usb_rcvctrlpipe ( stv680 - > udev , 0 ) ,
req ,
( USB_DIR_IN | USB_RECIP_DEVICE ) ,
value , 0 , buffer , size , PENCAM_TIMEOUT ) ;
break ;
case 3 : /* 0x40 */
ret = usb_control_msg ( stv680 - > udev ,
usb_sndctrlpipe ( stv680 - > udev , 0 ) ,
req ,
( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE ) ,
value , 0 , buffer , size , PENCAM_TIMEOUT ) ;
break ;
}
if ( ( ret < 0 ) & & ( req ! = 0x0a ) ) {
PDEBUG ( 1 , " STV(e): usb_control_msg error %i, request = 0x%x, error = %i " , set , req , ret ) ;
}
return ret ;
}
static int stv_set_config ( struct usb_stv * dev , int configuration , int interface , int alternate )
{
if ( configuration ! = dev - > udev - > actconfig - > desc . bConfigurationValue
| | usb_reset_configuration ( dev - > udev ) < 0 ) {
PDEBUG ( 1 , " STV(e): FAILED to reset configuration %i " , configuration ) ;
return - 1 ;
}
if ( usb_set_interface ( dev - > udev , interface , alternate ) < 0 ) {
PDEBUG ( 1 , " STV(e): FAILED to set alternate interface %i " , alternate ) ;
return - 1 ;
}
return 0 ;
}
static int stv_stop_video ( struct usb_stv * dev )
{
int i ;
unsigned char * buf ;
buf = kmalloc ( 40 , GFP_KERNEL ) ;
if ( buf = = NULL ) {
PDEBUG ( 0 , " STV(e): Out of (small buf) memory " ) ;
return - 1 ;
}
/* this is a high priority command; it stops all lower order commands */
if ( ( i = stv_sndctrl ( 1 , dev , 0x04 , 0x0000 , buf , 0x0 ) ) < 0 ) {
i = stv_sndctrl ( 0 , dev , 0x80 , 0 , buf , 0x02 ) ; /* Get Last Error; 2 = busy */
PDEBUG ( 1 , " STV(i): last error: %i, command = 0x%x " , buf [ 0 ] , buf [ 1 ] ) ;
} else {
PDEBUG ( 1 , " STV(i): Camera reset to idle mode. " ) ;
}
if ( ( i = stv_set_config ( dev , 1 , 0 , 0 ) ) < 0 )
PDEBUG ( 1 , " STV(e): Reset config during exit failed " ) ;
/* get current mode */
buf [ 0 ] = 0xf0 ;
if ( ( i = stv_sndctrl ( 0 , dev , 0x87 , 0 , buf , 0x08 ) ) ! = 0x08 ) /* get mode */
PDEBUG ( 0 , " STV(e): Stop_video: problem setting original mode " ) ;
if ( dev - > origMode ! = buf [ 0 ] ) {
memset ( buf , 0 , 8 ) ;
buf [ 0 ] = ( unsigned char ) dev - > origMode ;
if ( ( i = stv_sndctrl ( 3 , dev , 0x07 , 0x0100 , buf , 0x08 ) ) ! = 0x08 ) {
PDEBUG ( 0 , " STV(e): Stop_video: Set_Camera_Mode failed " ) ;
i = - 1 ;
}
buf [ 0 ] = 0xf0 ;
i = stv_sndctrl ( 0 , dev , 0x87 , 0 , buf , 0x08 ) ;
if ( ( i ! = 0x08 ) | | ( buf [ 0 ] ! = dev - > origMode ) ) {
PDEBUG ( 0 , " STV(e): camera NOT set to original resolution. " ) ;
i = - 1 ;
} else
PDEBUG ( 0 , " STV(i): Camera set to original resolution " ) ;
}
/* origMode */
kfree ( buf ) ;
return i ;
}
static int stv_set_video_mode ( struct usb_stv * dev )
{
int i , stop_video = 1 ;
unsigned char * buf ;
buf = kmalloc ( 40 , GFP_KERNEL ) ;
if ( buf = = NULL ) {
PDEBUG ( 0 , " STV(e): Out of (small buf) memory " ) ;
return - 1 ;
}
if ( ( i = stv_set_config ( dev , 1 , 0 , 0 ) ) < 0 ) {
kfree ( buf ) ;
return i ;
}
i = stv_sndctrl ( 2 , dev , 0x06 , 0x0100 , buf , 0x12 ) ;
if ( ! ( i > 0 ) & & ( buf [ 8 ] = = 0x53 ) & & ( buf [ 9 ] = = 0x05 ) ) {
PDEBUG ( 1 , " STV(e): Could not get descriptor 0100. " ) ;
goto error ;
}
/* set alternate interface 1 */
if ( ( i = stv_set_config ( dev , 1 , 0 , 1 ) ) < 0 )
goto error ;
if ( ( i = stv_sndctrl ( 0 , dev , 0x85 , 0 , buf , 0x10 ) ) ! = 0x10 )
goto error ;
PDEBUG ( 1 , " STV(i): Setting video mode. " ) ;
/* Switch to Video mode: 0x0100 = VGA (640x480), 0x0000 = CIF (352x288) 0x0300 = QVGA (320x240) */
if ( ( i = stv_sndctrl ( 1 , dev , 0x09 , dev - > VideoMode , buf , 0x0 ) ) < 0 ) {
stop_video = 0 ;
goto error ;
}
goto exit ;
error :
kfree ( buf ) ;
if ( stop_video = = 1 )
stv_stop_video ( dev ) ;
return - 1 ;
exit :
kfree ( buf ) ;
return 0 ;
}
static int stv_init ( struct usb_stv * stv680 )
{
int i = 0 ;
unsigned char * buffer ;
unsigned long int bufsize ;
buffer = kmalloc ( 40 , GFP_KERNEL ) ;
if ( buffer = = NULL ) {
PDEBUG ( 0 , " STV(e): Out of (small buf) memory " ) ;
return - 1 ;
}
memset ( buffer , 0 , 40 ) ;
udelay ( 100 ) ;
/* set config 1, interface 0, alternate 0 */
if ( ( i = stv_set_config ( stv680 , 1 , 0 , 0 ) ) < 0 ) {
kfree ( buffer ) ;
PDEBUG ( 0 , " STV(e): set config 1,0,0 failed " ) ;
return - 1 ;
}
/* ping camera to be sure STV0680 is present */
if ( ( i = stv_sndctrl ( 0 , stv680 , 0x88 , 0x5678 , buffer , 0x02 ) ) ! = 0x02 )
goto error ;
if ( ( buffer [ 0 ] ! = 0x56 ) | | ( buffer [ 1 ] ! = 0x78 ) ) {
PDEBUG ( 1 , " STV(e): camera ping failed!! " ) ;
goto error ;
}
/* get camera descriptor */
if ( ( i = stv_sndctrl ( 2 , stv680 , 0x06 , 0x0200 , buffer , 0x09 ) ) ! = 0x09 )
goto error ;
i = stv_sndctrl ( 2 , stv680 , 0x06 , 0x0200 , buffer , 0x22 ) ;
if ( ! ( i > = 0 ) & & ( buffer [ 7 ] = = 0xa0 ) & & ( buffer [ 8 ] = = 0x23 ) ) {
PDEBUG ( 1 , " STV(e): Could not get descriptor 0200. " ) ;
goto error ;
}
if ( ( i = stv_sndctrl ( 0 , stv680 , 0x8a , 0 , buffer , 0x02 ) ) ! = 0x02 )
goto error ;
if ( ( i = stv_sndctrl ( 0 , stv680 , 0x8b , 0 , buffer , 0x24 ) ) ! = 0x24 )
goto error ;
if ( ( i = stv_sndctrl ( 0 , stv680 , 0x85 , 0 , buffer , 0x10 ) ) ! = 0x10 )
goto error ;
stv680 - > SupportedModes = buffer [ 7 ] ;
i = stv680 - > SupportedModes ;
stv680 - > CIF = 0 ;
stv680 - > VGA = 0 ;
stv680 - > QVGA = 0 ;
if ( i & 1 )
stv680 - > CIF = 1 ;
if ( i & 2 )
stv680 - > VGA = 1 ;
if ( i & 8 )
stv680 - > QVGA = 1 ;
if ( stv680 - > SupportedModes = = 0 ) {
PDEBUG ( 0 , " STV(e): There are NO supported STV680 modes!! " ) ;
i = - 1 ;
goto error ;
} else {
if ( stv680 - > CIF )
PDEBUG ( 0 , " STV(i): CIF is supported " ) ;
if ( stv680 - > QVGA )
PDEBUG ( 0 , " STV(i): QVGA is supported " ) ;
}
/* FW rev, ASIC rev, sensor ID */
PDEBUG ( 1 , " STV(i): Firmware rev is %i.%i " , buffer [ 0 ] , buffer [ 1 ] ) ;
PDEBUG ( 1 , " STV(i): ASIC rev is %i.%i " , buffer [ 2 ] , buffer [ 3 ] ) ;
PDEBUG ( 1 , " STV(i): Sensor ID is %i " , ( buffer [ 4 ] * 16 ) + ( buffer [ 5 ] > > 4 ) ) ;
/* set alternate interface 1 */
if ( ( i = stv_set_config ( stv680 , 1 , 0 , 1 ) ) < 0 )
goto error ;
if ( ( i = stv_sndctrl ( 0 , stv680 , 0x85 , 0 , buffer , 0x10 ) ) ! = 0x10 )
goto error ;
if ( ( i = stv_sndctrl ( 0 , stv680 , 0x8d , 0 , buffer , 0x08 ) ) ! = 0x08 )
goto error ;
i = buffer [ 3 ] ;
PDEBUG ( 0 , " STV(i): Camera has %i pictures. " , i ) ;
/* get current mode */
if ( ( i = stv_sndctrl ( 0 , stv680 , 0x87 , 0 , buffer , 0x08 ) ) ! = 0x08 )
goto error ;
stv680 - > origMode = buffer [ 0 ] ; /* 01 = VGA, 03 = QVGA, 00 = CIF */
/* This will attemp CIF mode, if supported. If not, set to QVGA */
memset ( buffer , 0 , 8 ) ;
if ( stv680 - > CIF )
buffer [ 0 ] = 0x00 ;
else if ( stv680 - > QVGA )
buffer [ 0 ] = 0x03 ;
if ( ( i = stv_sndctrl ( 3 , stv680 , 0x07 , 0x0100 , buffer , 0x08 ) ) ! = 0x08 ) {
PDEBUG ( 0 , " STV(i): Set_Camera_Mode failed " ) ;
i = - 1 ;
goto error ;
}
buffer [ 0 ] = 0xf0 ;
stv_sndctrl ( 0 , stv680 , 0x87 , 0 , buffer , 0x08 ) ;
if ( ( ( stv680 - > CIF = = 1 ) & & ( buffer [ 0 ] ! = 0x00 ) ) | | ( ( stv680 - > QVGA = = 1 ) & & ( buffer [ 0 ] ! = 0x03 ) ) ) {
PDEBUG ( 0 , " STV(e): Error setting camera video mode! " ) ;
i = - 1 ;
goto error ;
} else {
if ( buffer [ 0 ] = = 0 ) {
stv680 - > VideoMode = 0x0000 ;
PDEBUG ( 0 , " STV(i): Video Mode set to CIF " ) ;
}
if ( buffer [ 0 ] = = 0x03 ) {
stv680 - > VideoMode = 0x0300 ;
PDEBUG ( 0 , " STV(i): Video Mode set to QVGA " ) ;
}
}
if ( ( i = stv_sndctrl ( 0 , stv680 , 0x8f , 0 , buffer , 0x10 ) ) ! = 0x10 )
goto error ;
bufsize = ( buffer [ 0 ] < < 24 ) | ( buffer [ 1 ] < < 16 ) | ( buffer [ 2 ] < < 8 ) | ( buffer [ 3 ] ) ;
stv680 - > cwidth = ( buffer [ 4 ] < < 8 ) | ( buffer [ 5 ] ) ; /* ->camera = 322, 356, 644 */
stv680 - > cheight = ( buffer [ 6 ] < < 8 ) | ( buffer [ 7 ] ) ; /* ->camera = 242, 292, 484 */
stv680 - > origGain = buffer [ 12 ] ;
goto exit ;
error :
i = stv_sndctrl ( 0 , stv680 , 0x80 , 0 , buffer , 0x02 ) ; /* Get Last Error */
PDEBUG ( 1 , " STV(i): last error: %i, command = 0x%x " , buffer [ 0 ] , buffer [ 1 ] ) ;
kfree ( buffer ) ;
return - 1 ;
exit :
kfree ( buffer ) ;
/* video = 320x240, 352x288 */
if ( stv680 - > CIF = = 1 ) {
stv680 - > maxwidth = 352 ;
stv680 - > maxheight = 288 ;
stv680 - > vwidth = 352 ;
stv680 - > vheight = 288 ;
}
if ( stv680 - > QVGA = = 1 ) {
stv680 - > maxwidth = 320 ;
stv680 - > maxheight = 240 ;
stv680 - > vwidth = 320 ;
stv680 - > vheight = 240 ;
}
stv680 - > rawbufsize = bufsize ; /* must be ./. by 8 */
stv680 - > maxframesize = bufsize * 3 ; /* RGB size */
PDEBUG ( 2 , " STV(i): cwidth = %i, cheight = %i " , stv680 - > cwidth , stv680 - > cheight ) ;
PDEBUG ( 1 , " STV(i): width = %i, height = %i, rawbufsize = %li " , stv680 - > vwidth , stv680 - > vheight , stv680 - > rawbufsize ) ;
/* some default values */
stv680 - > bulk_in_endpointAddr = 0x82 ;
stv680 - > dropped = 0 ;
stv680 - > error = 0 ;
stv680 - > framecount = 0 ;
stv680 - > readcount = 0 ;
stv680 - > streaming = 0 ;
/* bright, white, colour, hue, contrast are set by software, not in stv0680 */
stv680 - > brightness = 32767 ;
stv680 - > chgbright = 0 ;
stv680 - > whiteness = 0 ; /* only for greyscale */
stv680 - > colour = 32767 ;
stv680 - > contrast = 32767 ;
stv680 - > hue = 32767 ;
stv680 - > palette = STV_VIDEO_PALETTE ;
stv680 - > depth = 24 ; /* rgb24 bits */
if ( ( swapRGB_on = = 0 ) & & ( swapRGB = = 0 ) )
PDEBUG ( 1 , " STV(i): swapRGB is (auto) OFF " ) ;
else if ( ( swapRGB_on = = 0 ) & & ( swapRGB = = 1 ) )
PDEBUG ( 1 , " STV(i): swapRGB is (auto) ON " ) ;
else if ( swapRGB_on = = 1 )
PDEBUG ( 1 , " STV(i): swapRGB is (forced) ON " ) ;
else if ( swapRGB_on = = - 1 )
PDEBUG ( 1 , " STV(i): swapRGB is (forced) OFF " ) ;
if ( stv_set_video_mode ( stv680 ) < 0 ) {
PDEBUG ( 0 , " STV(e): Could not set video mode in stv_init " ) ;
return - 1 ;
}
return 0 ;
}
/***************** last of pencam routines *******************/
/****************************************************************************
* sysfs
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# define stv680_file(name, variable, field) \
static ssize_t show_ # # name ( struct class_device * class_dev , char * buf ) \
{ \
struct video_device * vdev = to_video_device ( class_dev ) ; \
struct usb_stv * stv = video_get_drvdata ( vdev ) ; \
return sprintf ( buf , field , stv - > variable ) ; \
} \
static CLASS_DEVICE_ATTR ( name , S_IRUGO , show_ # # name , NULL ) ;
stv680_file ( model , camera_name , " %s \n " ) ;
stv680_file ( in_use , user , " %d \n " ) ;
stv680_file ( streaming , streaming , " %d \n " ) ;
stv680_file ( palette , palette , " %i \n " ) ;
stv680_file ( frames_total , readcount , " %d \n " ) ;
stv680_file ( frames_read , framecount , " %d \n " ) ;
stv680_file ( packets_dropped , dropped , " %d \n " ) ;
stv680_file ( decoding_errors , error , " %d \n " ) ;
static void stv680_create_sysfs_files ( struct video_device * vdev )
{
video_device_create_file ( vdev , & class_device_attr_model ) ;
video_device_create_file ( vdev , & class_device_attr_in_use ) ;
video_device_create_file ( vdev , & class_device_attr_streaming ) ;
video_device_create_file ( vdev , & class_device_attr_palette ) ;
video_device_create_file ( vdev , & class_device_attr_frames_total ) ;
video_device_create_file ( vdev , & class_device_attr_frames_read ) ;
video_device_create_file ( vdev , & class_device_attr_packets_dropped ) ;
video_device_create_file ( vdev , & class_device_attr_decoding_errors ) ;
}
static void stv680_remove_sysfs_files ( struct video_device * vdev )
{
video_device_remove_file ( vdev , & class_device_attr_model ) ;
video_device_remove_file ( vdev , & class_device_attr_in_use ) ;
video_device_remove_file ( vdev , & class_device_attr_streaming ) ;
video_device_remove_file ( vdev , & class_device_attr_palette ) ;
video_device_remove_file ( vdev , & class_device_attr_frames_total ) ;
video_device_remove_file ( vdev , & class_device_attr_frames_read ) ;
video_device_remove_file ( vdev , & class_device_attr_packets_dropped ) ;
video_device_remove_file ( vdev , & class_device_attr_decoding_errors ) ;
}
/********************************************************************
* Camera control
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int stv680_get_pict ( struct usb_stv * stv680 , struct video_picture * p )
{
/* This sets values for v4l interface. max/min = 65535/0 */
p - > brightness = stv680 - > brightness ;
p - > whiteness = stv680 - > whiteness ; /* greyscale */
p - > colour = stv680 - > colour ;
p - > contrast = stv680 - > contrast ;
p - > hue = stv680 - > hue ;
p - > palette = stv680 - > palette ;
p - > depth = stv680 - > depth ;
return 0 ;
}
static int stv680_set_pict ( struct usb_stv * stv680 , struct video_picture * p )
{
/* See above stv680_get_pict */
if ( p - > palette ! = STV_VIDEO_PALETTE ) {
PDEBUG ( 2 , " STV(e): Palette set error in _set_pic " ) ;
return 1 ;
}
if ( stv680 - > brightness ! = p - > brightness ) {
stv680 - > chgbright = 1 ;
stv680 - > brightness = p - > brightness ;
}
stv680 - > whiteness = p - > whiteness ; /* greyscale */
stv680 - > colour = p - > colour ;
stv680 - > contrast = p - > contrast ;
stv680 - > hue = p - > hue ;
stv680 - > palette = p - > palette ;
stv680 - > depth = p - > depth ;
return 0 ;
}
static void stv680_video_irq ( struct urb * urb , struct pt_regs * regs )
{
struct usb_stv * stv680 = urb - > context ;
int length = urb - > actual_length ;
if ( length < stv680 - > rawbufsize )
PDEBUG ( 2 , " STV(i): Lost data in transfer: exp %li, got %i " , stv680 - > rawbufsize , length ) ;
/* ohoh... */
if ( ! stv680 - > streaming )
return ;
if ( ! stv680 - > udev ) {
PDEBUG ( 0 , " STV(e): device vapourished in video_irq " ) ;
return ;
}
/* 0 sized packets happen if we are to fast, but sometimes the camera
keeps sending them forever . . .
*/
if ( length & & ! urb - > status ) {
stv680 - > nullpackets = 0 ;
switch ( stv680 - > scratch [ stv680 - > scratch_next ] . state ) {
case BUFFER_READY :
case BUFFER_BUSY :
stv680 - > dropped + + ;
break ;
case BUFFER_UNUSED :
memcpy ( stv680 - > scratch [ stv680 - > scratch_next ] . data ,
( unsigned char * ) urb - > transfer_buffer , length ) ;
stv680 - > scratch [ stv680 - > scratch_next ] . state = BUFFER_READY ;
stv680 - > scratch [ stv680 - > scratch_next ] . length = length ;
if ( waitqueue_active ( & stv680 - > wq ) ) {
wake_up_interruptible ( & stv680 - > wq ) ;
}
stv680 - > scratch_overflow = 0 ;
stv680 - > scratch_next + + ;
if ( stv680 - > scratch_next > = STV680_NUMSCRATCH )
stv680 - > scratch_next = 0 ;
break ;
} /* switch */
} else {
stv680 - > nullpackets + + ;
if ( stv680 - > nullpackets > STV680_MAX_NULLPACKETS ) {
if ( waitqueue_active ( & stv680 - > wq ) ) {
wake_up_interruptible ( & stv680 - > wq ) ;
}
}
} /* if - else */
/* Resubmit urb for new data */
urb - > status = 0 ;
urb - > dev = stv680 - > udev ;
if ( usb_submit_urb ( urb , GFP_ATOMIC ) )
PDEBUG ( 0 , " STV(e): urb burned down in video irq " ) ;
return ;
} /* _video_irq */
static int stv680_start_stream ( struct usb_stv * stv680 )
{
struct urb * urb ;
int err = 0 , i ;
stv680 - > streaming = 1 ;
/* Do some memory allocation */
for ( i = 0 ; i < STV680_NUMFRAMES ; i + + ) {
stv680 - > frame [ i ] . data = stv680 - > fbuf + i * stv680 - > maxframesize ;
stv680 - > frame [ i ] . curpix = 0 ;
}
/* packet size = 4096 */
for ( i = 0 ; i < STV680_NUMSBUF ; i + + ) {
stv680 - > sbuf [ i ] . data = kmalloc ( stv680 - > rawbufsize , GFP_KERNEL ) ;
if ( stv680 - > sbuf [ i ] . data = = NULL ) {
PDEBUG ( 0 , " STV(e): Could not kmalloc raw data buffer %i " , i ) ;
return - 1 ;
}
}
stv680 - > scratch_next = 0 ;
stv680 - > scratch_use = 0 ;
stv680 - > scratch_overflow = 0 ;
for ( i = 0 ; i < STV680_NUMSCRATCH ; i + + ) {
stv680 - > scratch [ i ] . data = kmalloc ( stv680 - > rawbufsize , GFP_KERNEL ) ;
if ( stv680 - > scratch [ i ] . data = = NULL ) {
PDEBUG ( 0 , " STV(e): Could not kmalloc raw scratch buffer %i " , i ) ;
return - 1 ;
}
stv680 - > scratch [ i ] . state = BUFFER_UNUSED ;
}
for ( i = 0 ; i < STV680_NUMSBUF ; i + + ) {
urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! urb )
return - ENOMEM ;
/* sbuf is urb->transfer_buffer, later gets memcpyed to scratch */
usb_fill_bulk_urb ( urb , stv680 - > udev ,
usb_rcvbulkpipe ( stv680 - > udev , stv680 - > bulk_in_endpointAddr ) ,
stv680 - > sbuf [ i ] . data , stv680 - > rawbufsize ,
stv680_video_irq , stv680 ) ;
stv680 - > urb [ i ] = urb ;
err = usb_submit_urb ( stv680 - > urb [ i ] , GFP_KERNEL ) ;
if ( err )
PDEBUG ( 0 , " STV(e): urb burned down in start stream " ) ;
} /* i STV680_NUMSBUF */
stv680 - > framecount = 0 ;
return 0 ;
}
static int stv680_stop_stream ( struct usb_stv * stv680 )
{
int i ;
if ( ! stv680 - > streaming | | ! stv680 - > udev )
return 1 ;
stv680 - > streaming = 0 ;
for ( i = 0 ; i < STV680_NUMSBUF ; i + + )
if ( stv680 - > urb [ i ] ) {
usb_kill_urb ( stv680 - > urb [ i ] ) ;
usb_free_urb ( stv680 - > urb [ i ] ) ;
stv680 - > urb [ i ] = NULL ;
kfree ( stv680 - > sbuf [ i ] . data ) ;
}
for ( i = 0 ; i < STV680_NUMSCRATCH ; i + + ) {
kfree ( stv680 - > scratch [ i ] . data ) ;
stv680 - > scratch [ i ] . data = NULL ;
}
return 0 ;
}
static int stv680_set_size ( struct usb_stv * stv680 , int width , int height )
{
int wasstreaming = stv680 - > streaming ;
/* Check to see if we need to change */
if ( ( stv680 - > vwidth = = width ) & & ( stv680 - > vheight = = height ) )
return 0 ;
PDEBUG ( 1 , " STV(i): size request for %i x %i " , width , height ) ;
/* Check for a valid mode */
if ( ( ! width | | ! height ) | | ( ( width & 1 ) | | ( height & 1 ) ) ) {
PDEBUG ( 1 , " STV(e): set_size error: request: v.width = %i, v.height = %i actual: stv.width = %i, stv.height = %i " , width , height , stv680 - > vwidth , stv680 - > vheight ) ;
return 1 ;
}
if ( ( width < ( stv680 - > maxwidth / 2 ) ) | | ( height < ( stv680 - > maxheight / 2 ) ) ) {
width = stv680 - > maxwidth / 2 ;
height = stv680 - > maxheight / 2 ;
} else if ( ( width > = 158 ) & & ( width < = 166 ) & & ( stv680 - > QVGA = = 1 ) ) {
width = 160 ;
height = 120 ;
} else if ( ( width > = 172 ) & & ( width < = 180 ) & & ( stv680 - > CIF = = 1 ) ) {
width = 176 ;
height = 144 ;
} else if ( ( width > = 318 ) & & ( width < = 350 ) & & ( stv680 - > QVGA = = 1 ) ) {
width = 320 ;
height = 240 ;
} else if ( ( width > = 350 ) & & ( width < = 358 ) & & ( stv680 - > CIF = = 1 ) ) {
width = 352 ;
height = 288 ;
} else {
PDEBUG ( 1 , " STV(e): request for non-supported size: request: v.width = %i, v.height = %i actual: stv.width = %i, stv.height = %i " , width , height , stv680 - > vwidth , stv680 - > vheight ) ;
return 1 ;
}
/* Stop a current stream and start it again at the new size */
if ( wasstreaming )
stv680_stop_stream ( stv680 ) ;
stv680 - > vwidth = width ;
stv680 - > vheight = height ;
PDEBUG ( 1 , " STV(i): size set to %i x %i " , stv680 - > vwidth , stv680 - > vheight ) ;
if ( wasstreaming )
stv680_start_stream ( stv680 ) ;
return 0 ;
}
/**********************************************************************
* Video Decoding
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/******* routines from the pencam program; hey, they work! ********/
/*
* STV0680 Vision Camera Chipset Driver
* Copyright ( C ) 2000 Adam Harrison < adam @ antispin . org >
*/
# define RED 0
# define GREEN 1
# define BLUE 2
# define AD(x, y, w) (((y)*(w)+(x))*3)
static void bayer_unshuffle ( struct usb_stv * stv680 , struct stv680_scratch * buffer )
{
int x , y , i ;
int w = stv680 - > cwidth ;
int vw = stv680 - > cwidth , vh = stv680 - > cheight ;
unsigned int p = 0 ;
int colour = 0 , bayer = 0 ;
unsigned char * raw = buffer - > data ;
struct stv680_frame * frame = & stv680 - > frame [ stv680 - > curframe ] ;
unsigned char * output = frame - > data ;
unsigned char * temp = frame - > data ;
int offset = buffer - > offset ;
if ( frame - > curpix = = 0 ) {
if ( frame - > grabstate = = FRAME_READY ) {
frame - > grabstate = FRAME_GRABBING ;
}
}
if ( offset ! = frame - > curpix ) { /* Regard frame as lost :( */
frame - > curpix = 0 ;
stv680 - > error + + ;
return ;
}
if ( ( stv680 - > vwidth = = 320 ) | | ( stv680 - > vwidth = = 160 ) ) {
vw = 320 ;
vh = 240 ;
}
if ( ( stv680 - > vwidth = = 352 ) | | ( stv680 - > vwidth = = 176 ) ) {
vw = 352 ;
vh = 288 ;
}
memset ( output , 0 , 3 * vw * vh ) ; /* clear output matrix. */
for ( y = 0 ; y < vh ; y + + ) {
for ( x = 0 ; x < vw ; x + + ) {
if ( x & 1 )
p = * ( raw + y * w + ( x > > 1 ) ) ;
else
p = * ( raw + y * w + ( x > > 1 ) + ( w > > 1 ) ) ;
if ( y & 1 )
bayer = 2 ;
else
bayer = 0 ;
if ( x & 1 )
bayer + + ;
switch ( bayer ) {
case 0 :
case 3 :
colour = 1 ;
break ;
case 1 :
colour = 0 ;
break ;
case 2 :
colour = 2 ;
break ;
}
i = ( y * vw + x ) * 3 ;
* ( output + i + colour ) = ( unsigned char ) p ;
} /* for x */
} /* for y */
/****** gamma correction plus hardcoded white balance */
/* Thanks to Alexander Schwartx <alexander.schwartx@gmx.net> for this code.
Correction values red [ ] , green [ ] , blue [ ] , are generated by
( pow ( i / 256.0 , GAMMA ) * 255.0 ) * white balanceRGB where GAMMA = 0.55 , 1 < i < 255.
White balance ( RGB ) = 1.0 , 1.17 , 1.48 . Values are calculated as double float and
converted to unsigned char . Values are in stv680 . h */
for ( y = 0 ; y < vh ; y + + ) {
for ( x = 0 ; x < vw ; x + + ) {
i = ( y * vw + x ) * 3 ;
* ( output + i ) = red [ * ( output + i ) ] ;
* ( output + i + 1 ) = green [ * ( output + i + 1 ) ] ;
* ( output + i + 2 ) = blue [ * ( output + i + 2 ) ] ;
}
}
/****** bayer demosaic ******/
for ( y = 1 ; y < ( vh - 1 ) ; y + + ) {
for ( x = 1 ; x < ( vw - 1 ) ; x + + ) { /* work out pixel type */
if ( y & 1 )
bayer = 0 ;
else
bayer = 2 ;
if ( ! ( x & 1 ) )
bayer + + ;
switch ( bayer ) {
case 0 : /* green. blue lr, red tb */
* ( output + AD ( x , y , vw ) + BLUE ) = ( ( int ) * ( output + AD ( x - 1 , y , vw ) + BLUE ) + ( int ) * ( output + AD ( x + 1 , y , vw ) + BLUE ) ) > > 1 ;
* ( output + AD ( x , y , vw ) + RED ) = ( ( int ) * ( output + AD ( x , y - 1 , vw ) + RED ) + ( int ) * ( output + AD ( x , y + 1 , vw ) + RED ) ) > > 1 ;
break ;
case 1 : /* blue. green lrtb, red diagonals */
* ( output + AD ( x , y , vw ) + GREEN ) = ( ( int ) * ( output + AD ( x - 1 , y , vw ) + GREEN ) + ( int ) * ( output + AD ( x + 1 , y , vw ) + GREEN ) + ( int ) * ( output + AD ( x , y - 1 , vw ) + GREEN ) + ( int ) * ( output + AD ( x , y + 1 , vw ) + GREEN ) ) > > 2 ;
* ( output + AD ( x , y , vw ) + RED ) = ( ( int ) * ( output + AD ( x - 1 , y - 1 , vw ) + RED ) + ( int ) * ( output + AD ( x - 1 , y + 1 , vw ) + RED ) + ( int ) * ( output + AD ( x + 1 , y - 1 , vw ) + RED ) + ( int ) * ( output + AD ( x + 1 , y + 1 , vw ) + RED ) ) > > 2 ;
break ;
case 2 : /* red. green lrtb, blue diagonals */
* ( output + AD ( x , y , vw ) + GREEN ) = ( ( int ) * ( output + AD ( x - 1 , y , vw ) + GREEN ) + ( int ) * ( output + AD ( x + 1 , y , vw ) + GREEN ) + ( int ) * ( output + AD ( x , y - 1 , vw ) + GREEN ) + ( int ) * ( output + AD ( x , y + 1 , vw ) + GREEN ) ) > > 2 ;
* ( output + AD ( x , y , vw ) + BLUE ) = ( ( int ) * ( output + AD ( x - 1 , y - 1 , vw ) + BLUE ) + ( int ) * ( output + AD ( x + 1 , y - 1 , vw ) + BLUE ) + ( int ) * ( output + AD ( x - 1 , y + 1 , vw ) + BLUE ) + ( int ) * ( output + AD ( x + 1 , y + 1 , vw ) + BLUE ) ) > > 2 ;
break ;
case 3 : /* green. red lr, blue tb */
* ( output + AD ( x , y , vw ) + RED ) = ( ( int ) * ( output + AD ( x - 1 , y , vw ) + RED ) + ( int ) * ( output + AD ( x + 1 , y , vw ) + RED ) ) > > 1 ;
* ( output + AD ( x , y , vw ) + BLUE ) = ( ( int ) * ( output + AD ( x , y - 1 , vw ) + BLUE ) + ( int ) * ( output + AD ( x , y + 1 , vw ) + BLUE ) ) > > 1 ;
break ;
} /* switch */
} /* for x */
} /* for y - end demosaic */
/* fix top and bottom row, left and right side */
i = vw * 3 ;
memcpy ( output , ( output + i ) , i ) ;
memcpy ( ( output + ( vh * i ) ) , ( output + ( ( vh - 1 ) * i ) ) , i ) ;
for ( y = 0 ; y < vh ; y + + ) {
i = y * vw * 3 ;
memcpy ( ( output + i ) , ( output + i + 3 ) , 3 ) ;
memcpy ( ( output + i + ( vw * 3 ) ) , ( output + i + ( vw - 1 ) * 3 ) , 3 ) ;
}
/* process all raw data, then trim to size if necessary */
if ( ( stv680 - > vwidth = = 160 ) | | ( stv680 - > vwidth = = 176 ) ) {
i = 0 ;
for ( y = 0 ; y < vh ; y + + ) {
if ( ! ( y & 1 ) ) {
for ( x = 0 ; x < vw ; x + + ) {
p = ( y * vw + x ) * 3 ;
if ( ! ( x & 1 ) ) {
* ( output + i ) = * ( output + p ) ;
* ( output + i + 1 ) = * ( output + p + 1 ) ;
* ( output + i + 2 ) = * ( output + p + 2 ) ;
i + = 3 ;
}
} /* for x */
}
} /* for y */
}
/* reset to proper width */
if ( ( stv680 - > vwidth = = 160 ) ) {
vw = 160 ;
vh = 120 ;
}
if ( ( stv680 - > vwidth = = 176 ) ) {
vw = 176 ;
vh = 144 ;
}
/* output is RGB; some programs want BGR */
/* swapRGB_on=0 -> program decides; swapRGB_on=1, always swap */
/* swapRGB_on=-1, never swap */
if ( ( ( swapRGB = = 1 ) & & ( swapRGB_on ! = - 1 ) ) | | ( swapRGB_on = = 1 ) ) {
for ( y = 0 ; y < vh ; y + + ) {
for ( x = 0 ; x < vw ; x + + ) {
i = ( y * vw + x ) * 3 ;
* ( temp ) = * ( output + i ) ;
* ( output + i ) = * ( output + i + 2 ) ;
* ( output + i + 2 ) = * ( temp ) ;
}
}
}
/* brightness */
if ( stv680 - > chgbright = = 1 ) {
if ( stv680 - > brightness > = 32767 ) {
p = ( stv680 - > brightness - 32767 ) / 256 ;
for ( x = 0 ; x < ( vw * vh * 3 ) ; x + + ) {
if ( ( * ( output + x ) + ( unsigned char ) p ) > 255 )
* ( output + x ) = 255 ;
else
* ( output + x ) + = ( unsigned char ) p ;
} /* for */
} else {
p = ( 32767 - stv680 - > brightness ) / 256 ;
for ( x = 0 ; x < ( vw * vh * 3 ) ; x + + ) {
if ( ( unsigned char ) p > * ( output + x ) )
* ( output + x ) = 0 ;
else
* ( output + x ) - = ( unsigned char ) p ;
} /* for */
} /* else */
}
/* if */
frame - > curpix = 0 ;
frame - > curlinepix = 0 ;
frame - > grabstate = FRAME_DONE ;
stv680 - > framecount + + ;
stv680 - > readcount + + ;
if ( stv680 - > frame [ ( stv680 - > curframe + 1 ) & ( STV680_NUMFRAMES - 1 ) ] . grabstate = = FRAME_READY ) {
stv680 - > curframe = ( stv680 - > curframe + 1 ) & ( STV680_NUMFRAMES - 1 ) ;
}
} /* bayer_unshuffle */
/******* end routines from the pencam program *********/
static int stv680_newframe ( struct usb_stv * stv680 , int framenr )
{
int errors = 0 ;
while ( stv680 - > streaming & & ( stv680 - > frame [ framenr ] . grabstate = = FRAME_READY | | stv680 - > frame [ framenr ] . grabstate = = FRAME_GRABBING ) ) {
if ( ! stv680 - > frame [ framenr ] . curpix ) {
errors + + ;
}
wait_event_interruptible ( stv680 - > wq , ( stv680 - > scratch [ stv680 - > scratch_use ] . state = = BUFFER_READY ) ) ;
if ( stv680 - > nullpackets > STV680_MAX_NULLPACKETS ) {
stv680 - > nullpackets = 0 ;
PDEBUG ( 2 , " STV(i): too many null length packets, restarting capture " ) ;
stv680_stop_stream ( stv680 ) ;
stv680_start_stream ( stv680 ) ;
} else {
if ( stv680 - > scratch [ stv680 - > scratch_use ] . state ! = BUFFER_READY ) {
stv680 - > frame [ framenr ] . grabstate = FRAME_ERROR ;
PDEBUG ( 2 , " STV(e): FRAME_ERROR in _newframe " ) ;
return - EIO ;
}
stv680 - > scratch [ stv680 - > scratch_use ] . state = BUFFER_BUSY ;
bayer_unshuffle ( stv680 , & stv680 - > scratch [ stv680 - > scratch_use ] ) ;
stv680 - > scratch [ stv680 - > scratch_use ] . state = BUFFER_UNUSED ;
stv680 - > scratch_use + + ;
if ( stv680 - > scratch_use > = STV680_NUMSCRATCH )
stv680 - > scratch_use = 0 ;
if ( errors > STV680_MAX_ERRORS ) {
errors = 0 ;
PDEBUG ( 2 , " STV(i): too many errors, restarting capture " ) ;
stv680_stop_stream ( stv680 ) ;
stv680_start_stream ( stv680 ) ;
}
} /* else */
} /* while */
return 0 ;
}
/*********************************************************************
* Video4Linux
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int stv_open ( struct inode * inode , struct file * file )
{
struct video_device * dev = video_devdata ( file ) ;
struct usb_stv * stv680 = video_get_drvdata ( dev ) ;
int err = 0 ;
/* we are called with the BKL held */
stv680 - > user = 1 ;
err = stv_init ( stv680 ) ; /* main initialization routine for camera */
if ( err > = 0 ) {
stv680 - > fbuf = rvmalloc ( stv680 - > maxframesize * STV680_NUMFRAMES ) ;
if ( ! stv680 - > fbuf ) {
PDEBUG ( 0 , " STV(e): Could not rvmalloc frame bufer " ) ;
err = - ENOMEM ;
}
file - > private_data = dev ;
}
if ( err )
stv680 - > user = 0 ;
return err ;
}
static int stv_close ( struct inode * inode , struct file * file )
{
struct video_device * dev = file - > private_data ;
struct usb_stv * stv680 = video_get_drvdata ( dev ) ;
int i ;
for ( i = 0 ; i < STV680_NUMFRAMES ; i + + )
stv680 - > frame [ i ] . grabstate = FRAME_UNUSED ;
if ( stv680 - > streaming )
stv680_stop_stream ( stv680 ) ;
if ( ( i = stv_stop_video ( stv680 ) ) < 0 )
PDEBUG ( 1 , " STV(e): stop_video failed in stv_close " ) ;
rvfree ( stv680 - > fbuf , stv680 - > maxframesize * STV680_NUMFRAMES ) ;
stv680 - > user = 0 ;
if ( stv680 - > removed ) {
kfree ( stv680 ) ;
stv680 = NULL ;
PDEBUG ( 0 , " STV(i): device unregistered " ) ;
}
file - > private_data = NULL ;
return 0 ;
}
static int stv680_do_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , void * arg )
{
struct video_device * vdev = file - > private_data ;
struct usb_stv * stv680 = video_get_drvdata ( vdev ) ;
if ( ! stv680 - > udev )
return - EIO ;
switch ( cmd ) {
case VIDIOCGCAP : {
struct video_capability * b = arg ;
strcpy ( b - > name , stv680 - > camera_name ) ;
b - > type = VID_TYPE_CAPTURE ;
b - > channels = 1 ;
b - > audios = 0 ;
b - > maxwidth = stv680 - > maxwidth ;
b - > maxheight = stv680 - > maxheight ;
b - > minwidth = stv680 - > maxwidth / 2 ;
b - > minheight = stv680 - > maxheight / 2 ;
return 0 ;
}
case VIDIOCGCHAN : {
struct video_channel * v = arg ;
if ( v - > channel ! = 0 )
return - EINVAL ;
v - > flags = 0 ;
v - > tuners = 0 ;
v - > type = VIDEO_TYPE_CAMERA ;
strcpy ( v - > name , " STV Camera " ) ;
return 0 ;
}
case VIDIOCSCHAN : {
struct video_channel * v = arg ;
if ( v - > channel ! = 0 )
return - EINVAL ;
return 0 ;
}
case VIDIOCGPICT : {
struct video_picture * p = arg ;
stv680_get_pict ( stv680 , p ) ;
return 0 ;
}
case VIDIOCSPICT : {
struct video_picture * p = arg ;
if ( stv680_set_pict ( stv680 , p ) )
return - EINVAL ;
return 0 ;
}
case VIDIOCSWIN : {
struct video_window * vw = arg ;
if ( vw - > flags )
return - EINVAL ;
if ( vw - > clipcount )
return - EINVAL ;
if ( vw - > width ! = stv680 - > vwidth ) {
if ( stv680_set_size ( stv680 , vw - > width , vw - > height ) ) {
PDEBUG ( 2 , " STV(e): failed (from user) set size in VIDIOCSWIN " ) ;
return - EINVAL ;
}
}
return 0 ;
}
case VIDIOCGWIN : {
struct video_window * vw = arg ;
vw - > x = 0 ; /* FIXME */
vw - > y = 0 ;
vw - > chromakey = 0 ;
vw - > flags = 0 ;
vw - > clipcount = 0 ;
vw - > width = stv680 - > vwidth ;
vw - > height = stv680 - > vheight ;
return 0 ;
}
case VIDIOCGMBUF : {
struct video_mbuf * vm = arg ;
int i ;
memset ( vm , 0 , sizeof ( * vm ) ) ;
vm - > size = STV680_NUMFRAMES * stv680 - > maxframesize ;
vm - > frames = STV680_NUMFRAMES ;
for ( i = 0 ; i < STV680_NUMFRAMES ; i + + )
vm - > offsets [ i ] = stv680 - > maxframesize * i ;
return 0 ;
}
case VIDIOCMCAPTURE : {
struct video_mmap * vm = arg ;
if ( vm - > format ! = STV_VIDEO_PALETTE ) {
PDEBUG ( 2 , " STV(i): VIDIOCMCAPTURE vm.format (%i) != VIDEO_PALETTE (%i) " ,
vm - > format , STV_VIDEO_PALETTE ) ;
if ( ( vm - > format = = 3 ) & & ( swapRGB_on = = 0 ) ) {
PDEBUG ( 2 , " STV(i): VIDIOCMCAPTURE swapRGB is (auto) ON " ) ;
/* this may fix those apps (e.g., xawtv) that want BGR */
swapRGB = 1 ;
}
return - EINVAL ;
}
if ( vm - > frame > = STV680_NUMFRAMES ) {
PDEBUG ( 2 , " STV(e): VIDIOCMCAPTURE vm.frame > NUMFRAMES " ) ;
return - EINVAL ;
}
if ( ( stv680 - > frame [ vm - > frame ] . grabstate = = FRAME_ERROR )
| | ( stv680 - > frame [ vm - > frame ] . grabstate = = FRAME_GRABBING ) ) {
PDEBUG ( 2 , " STV(e): VIDIOCMCAPTURE grabstate (%i) error " ,
stv680 - > frame [ vm - > frame ] . grabstate ) ;
return - EBUSY ;
}
/* Is this according to the v4l spec??? */
if ( stv680 - > vwidth ! = vm - > width ) {
if ( stv680_set_size ( stv680 , vm - > width , vm - > height ) ) {
PDEBUG ( 2 , " STV(e): VIDIOCMCAPTURE set_size failed " ) ;
return - EINVAL ;
}
}
stv680 - > frame [ vm - > frame ] . grabstate = FRAME_READY ;
if ( ! stv680 - > streaming )
stv680_start_stream ( stv680 ) ;
return 0 ;
}
case VIDIOCSYNC : {
int * frame = arg ;
int ret = 0 ;
if ( * frame < 0 | | * frame > = STV680_NUMFRAMES ) {
PDEBUG ( 2 , " STV(e): Bad frame # in VIDIOCSYNC " ) ;
return - EINVAL ;
}
ret = stv680_newframe ( stv680 , * frame ) ;
stv680 - > frame [ * frame ] . grabstate = FRAME_UNUSED ;
return ret ;
}
case VIDIOCGFBUF : {
struct video_buffer * vb = arg ;
memset ( vb , 0 , sizeof ( * vb ) ) ;
return 0 ;
}
case VIDIOCKEY :
return 0 ;
case VIDIOCCAPTURE :
{
PDEBUG ( 2 , " STV(e): VIDIOCCAPTURE failed " ) ;
return - EINVAL ;
}
case VIDIOCSFBUF :
case VIDIOCGTUNER :
case VIDIOCSTUNER :
case VIDIOCGFREQ :
case VIDIOCSFREQ :
case VIDIOCGAUDIO :
case VIDIOCSAUDIO :
return - EINVAL ;
default :
return - ENOIOCTLCMD ;
} /* end switch */
return 0 ;
}
static int stv680_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
return video_usercopy ( inode , file , cmd , arg , stv680_do_ioctl ) ;
}
static int stv680_mmap ( struct file * file , struct vm_area_struct * vma )
{
struct video_device * dev = file - > private_data ;
struct usb_stv * stv680 = video_get_drvdata ( dev ) ;
unsigned long start = vma - > vm_start ;
unsigned long size = vma - > vm_end - vma - > vm_start ;
unsigned long page , pos ;
down ( & stv680 - > lock ) ;
if ( stv680 - > udev = = NULL ) {
up ( & stv680 - > lock ) ;
return - EIO ;
}
if ( size > ( ( ( STV680_NUMFRAMES * stv680 - > maxframesize ) + PAGE_SIZE - 1 )
& ~ ( PAGE_SIZE - 1 ) ) ) {
up ( & stv680 - > lock ) ;
return - EINVAL ;
}
pos = ( unsigned long ) stv680 - > fbuf ;
while ( size > 0 ) {
page = vmalloc_to_pfn ( ( void * ) pos ) ;
if ( remap_pfn_range ( vma , start , page , PAGE_SIZE , PAGE_SHARED ) ) {
up ( & stv680 - > lock ) ;
return - EAGAIN ;
}
start + = PAGE_SIZE ;
pos + = PAGE_SIZE ;
if ( size > PAGE_SIZE )
size - = PAGE_SIZE ;
else
size = 0 ;
}
up ( & stv680 - > lock ) ;
return 0 ;
}
static ssize_t stv680_read ( struct file * file , char __user * buf ,
size_t count , loff_t * ppos )
{
struct video_device * dev = file - > private_data ;
unsigned long int realcount = count ;
int ret = 0 ;
struct usb_stv * stv680 = video_get_drvdata ( dev ) ;
unsigned long int i ;
if ( STV680_NUMFRAMES ! = 2 ) {
PDEBUG ( 0 , " STV(e): STV680_NUMFRAMES needs to be 2! " ) ;
return - 1 ;
}
if ( stv680 - > udev = = NULL )
return - EIO ;
if ( realcount > ( stv680 - > vwidth * stv680 - > vheight * 3 ) )
realcount = stv680 - > vwidth * stv680 - > vheight * 3 ;
/* Shouldn't happen: */
if ( stv680 - > frame [ 0 ] . grabstate = = FRAME_GRABBING ) {
PDEBUG ( 2 , " STV(e): FRAME_GRABBING in stv680_read " ) ;
return - EBUSY ;
}
stv680 - > frame [ 0 ] . grabstate = FRAME_READY ;
stv680 - > frame [ 1 ] . grabstate = FRAME_UNUSED ;
stv680 - > curframe = 0 ;
if ( ! stv680 - > streaming )
stv680_start_stream ( stv680 ) ;
if ( ! stv680 - > streaming ) {
ret = stv680_newframe ( stv680 , 0 ) ; /* ret should = 0 */
}
ret = stv680_newframe ( stv680 , 0 ) ;
if ( ! ret ) {
if ( ( i = copy_to_user ( buf , stv680 - > frame [ 0 ] . data , realcount ) ) ! = 0 ) {
PDEBUG ( 2 , " STV(e): copy_to_user frame 0 failed, ret count = %li " , i ) ;
return - EFAULT ;
}
} else {
realcount = ret ;
}
stv680 - > frame [ 0 ] . grabstate = FRAME_UNUSED ;
return realcount ;
} /* stv680_read */
static struct file_operations stv680_fops = {
. owner = THIS_MODULE ,
. open = stv_open ,
. release = stv_close ,
. read = stv680_read ,
. mmap = stv680_mmap ,
. ioctl = stv680_ioctl ,
. llseek = no_llseek ,
} ;
static struct video_device stv680_template = {
. owner = THIS_MODULE ,
. name = " STV0680 USB camera " ,
. type = VID_TYPE_CAPTURE ,
. hardware = VID_HARDWARE_SE401 ,
. fops = & stv680_fops ,
. release = video_device_release ,
. minor = - 1 ,
} ;
static int stv680_probe ( struct usb_interface * intf , const struct usb_device_id * id )
{
struct usb_device * dev = interface_to_usbdev ( intf ) ;
struct usb_host_interface * interface ;
struct usb_stv * stv680 = NULL ;
char * camera_name = NULL ;
int retval = 0 ;
/* We don't handle multi-config cameras */
if ( dev - > descriptor . bNumConfigurations ! = 1 ) {
PDEBUG ( 0 , " STV(e): Number of Configurations != 1 " ) ;
return - ENODEV ;
}
interface = & intf - > altsetting [ 0 ] ;
/* Is it a STV680? */
if ( ( le16_to_cpu ( dev - > descriptor . idVendor ) = = USB_PENCAM_VENDOR_ID ) & &
( le16_to_cpu ( dev - > descriptor . idProduct ) = = USB_PENCAM_PRODUCT_ID ) ) {
camera_name = " STV0680 " ;
PDEBUG ( 0 , " STV(i): STV0680 camera found. " ) ;
2005-06-05 02:52:33 +04:00
} else if ( ( le16_to_cpu ( dev - > descriptor . idVendor ) = = USB_CREATIVEGOMINI_VENDOR_ID ) & &
( le16_to_cpu ( dev - > descriptor . idProduct ) = = USB_CREATIVEGOMINI_PRODUCT_ID ) ) {
camera_name = " Creative WebCam Go Mini " ;
PDEBUG ( 0 , " STV(i): Creative WebCam Go Mini found. " ) ;
2005-04-17 02:20:36 +04:00
} else {
2005-06-05 02:52:33 +04:00
PDEBUG ( 0 , " STV(e): Vendor/Product ID do not match STV0680 or Creative WebCam Go Mini values. " ) ;
PDEBUG ( 0 , " STV(e): Check that the STV0680 or Creative WebCam Go Mini camera is connected to the computer. " ) ;
2005-04-17 02:20:36 +04:00
retval = - ENODEV ;
goto error ;
}
/* We found one */
if ( ( stv680 = kmalloc ( sizeof ( * stv680 ) , GFP_KERNEL ) ) = = NULL ) {
PDEBUG ( 0 , " STV(e): couldn't kmalloc stv680 struct. " ) ;
retval = - ENOMEM ;
goto error ;
}
memset ( stv680 , 0 , sizeof ( * stv680 ) ) ;
stv680 - > udev = dev ;
stv680 - > camera_name = camera_name ;
stv680 - > vdev = video_device_alloc ( ) ;
if ( ! stv680 - > vdev ) {
retval = - ENOMEM ;
goto error ;
}
memcpy ( stv680 - > vdev , & stv680_template , sizeof ( stv680_template ) ) ;
stv680 - > vdev - > dev = & intf - > dev ;
video_set_drvdata ( stv680 - > vdev , stv680 ) ;
memcpy ( stv680 - > vdev - > name , stv680 - > camera_name , strlen ( stv680 - > camera_name ) ) ;
init_waitqueue_head ( & stv680 - > wq ) ;
init_MUTEX ( & stv680 - > lock ) ;
wmb ( ) ;
if ( video_register_device ( stv680 - > vdev , VFL_TYPE_GRABBER , video_nr ) = = - 1 ) {
PDEBUG ( 0 , " STV(e): video_register_device failed " ) ;
retval = - EIO ;
goto error_vdev ;
}
PDEBUG ( 0 , " STV(i): registered new video device: video%d " , stv680 - > vdev - > minor ) ;
usb_set_intfdata ( intf , stv680 ) ;
stv680_create_sysfs_files ( stv680 - > vdev ) ;
return 0 ;
error_vdev :
video_device_release ( stv680 - > vdev ) ;
error :
kfree ( stv680 ) ;
return retval ;
}
static inline void usb_stv680_remove_disconnected ( struct usb_stv * stv680 )
{
int i ;
stv680 - > udev = NULL ;
stv680 - > frame [ 0 ] . grabstate = FRAME_ERROR ;
stv680 - > frame [ 1 ] . grabstate = FRAME_ERROR ;
stv680 - > streaming = 0 ;
wake_up_interruptible ( & stv680 - > wq ) ;
for ( i = 0 ; i < STV680_NUMSBUF ; i + + )
if ( stv680 - > urb [ i ] ) {
usb_kill_urb ( stv680 - > urb [ i ] ) ;
usb_free_urb ( stv680 - > urb [ i ] ) ;
stv680 - > urb [ i ] = NULL ;
kfree ( stv680 - > sbuf [ i ] . data ) ;
}
for ( i = 0 ; i < STV680_NUMSCRATCH ; i + + )
kfree ( stv680 - > scratch [ i ] . data ) ;
PDEBUG ( 0 , " STV(i): %s disconnected " , stv680 - > camera_name ) ;
/* Free the memory */
kfree ( stv680 ) ;
}
static void stv680_disconnect ( struct usb_interface * intf )
{
struct usb_stv * stv680 = usb_get_intfdata ( intf ) ;
usb_set_intfdata ( intf , NULL ) ;
if ( stv680 ) {
/* We don't want people trying to open up the device */
if ( stv680 - > vdev ) {
stv680_remove_sysfs_files ( stv680 - > vdev ) ;
video_unregister_device ( stv680 - > vdev ) ;
stv680 - > vdev = NULL ;
}
if ( ! stv680 - > user ) {
usb_stv680_remove_disconnected ( stv680 ) ;
} else {
stv680 - > removed = 1 ;
}
}
}
static struct usb_driver stv680_driver = {
. owner = THIS_MODULE ,
. name = " stv680 " ,
. probe = stv680_probe ,
. disconnect = stv680_disconnect ,
. id_table = device_table
} ;
/********************************************************************
* Module routines
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int __init usb_stv680_init ( void )
{
if ( usb_register ( & stv680_driver ) < 0 ) {
PDEBUG ( 0 , " STV(e): Could not setup STV0680 driver " ) ;
return - 1 ;
}
PDEBUG ( 0 , " STV(i): usb camera driver version %s registering " , DRIVER_VERSION ) ;
info ( DRIVER_DESC " " DRIVER_VERSION ) ;
return 0 ;
}
static void __exit usb_stv680_exit ( void )
{
usb_deregister ( & stv680_driver ) ;
PDEBUG ( 0 , " STV(i): driver deregistered " ) ;
}
module_init ( usb_stv680_init ) ;
module_exit ( usb_stv680_exit ) ;