2005-04-17 02:20:36 +04:00
/*
* Video4Linux Colour QuickCam driver
* Copyright 1997 - 2000 Philip Blundell < philb @ gnu . org >
*
* Module parameters :
*
* parport = auto - - probe all parports ( default )
* parport = 0 - - parport0 becomes qcam1
* parport = 2 , 0 , 1 - - parports 2 , 0 , 1 are tried in that order
*
* probe = 0 - - do no probing , assume camera is present
* probe = 1 - - use IEEE - 1284 autoprobe data only ( default )
* probe = 2 - - probe aggressively for cameras
*
* force_rgb = 1 - - force data format to RGB ( default is BGR )
*
* The parport parameter controls which parports will be scanned .
* Scanning all parports causes some printers to print a garbage page .
2006-03-25 15:19:53 +03:00
* - - March 14 , 1999 Billy Donahue < billy @ escape . com >
2005-04-17 02:20:36 +04:00
*
* Fixed data format to BGR , added force_rgb parameter . Added missing
* parport_unregister_driver ( ) on module removal .
* - - May 28 , 2000 Claudio Matsuoka < claudio @ conectiva . com >
*/
# include <linux/module.h>
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/fs.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/mm.h>
# include <linux/parport.h>
# include <linux/sched.h>
2006-02-07 11:49:14 +03:00
# include <linux/mutex.h>
2008-04-16 23:13:15 +04:00
# include <linux/jiffies.h>
2010-05-10 10:55:25 +04:00
# include <linux/videodev2.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
2010-05-10 10:55:25 +04:00
# include <media/v4l2-device.h>
# include <media/v4l2-common.h>
# include <media/v4l2-ioctl.h>
2012-05-08 22:20:21 +04:00
# include <media/v4l2-fh.h>
# include <media/v4l2-ctrls.h>
# include <media/v4l2-event.h>
2005-04-17 02:20:36 +04:00
2010-05-10 10:55:25 +04:00
struct qcam {
struct v4l2_device v4l2_dev ;
2005-04-17 02:20:36 +04:00
struct video_device vdev ;
2012-05-08 22:20:21 +04:00
struct v4l2_ctrl_handler hdl ;
2005-04-17 02:20:36 +04:00
struct pardevice * pdev ;
struct parport * pport ;
int width , height ;
int ccd_width , ccd_height ;
int mode ;
int contrast , brightness , whitebal ;
int top , left ;
unsigned int bidirectional ;
2006-02-07 11:49:14 +03:00
struct mutex lock ;
2005-04-17 02:20:36 +04:00
} ;
/* cameras maximum */
# define MAX_CAMS 4
/* The three possible QuickCam modes */
# define QC_MILLIONS 0x18
# define QC_BILLIONS 0x10
# define QC_THOUSANDS 0x08 /* with VIDEC compression (not supported) */
/* The three possible decimations */
# define QC_DECIMATION_1 0
# define QC_DECIMATION_2 2
# define QC_DECIMATION_4 4
2010-05-10 10:55:25 +04:00
# define BANNER "Colour QuickCam for Video4Linux v0.06"
2005-04-17 02:20:36 +04:00
static int parport [ MAX_CAMS ] = { [ 1 . . . MAX_CAMS - 1 ] = - 1 } ;
static int probe = 2 ;
2012-01-13 03:02:20 +04:00
static bool force_rgb ;
2005-04-17 02:20:36 +04:00
static int video_nr = - 1 ;
2010-05-10 10:55:25 +04:00
/* FIXME: parport=auto would never have worked, surely? --RR */
MODULE_PARM_DESC ( parport , " parport=<auto|n[,n]...> for port detection method \n "
" probe=<0|1|2> for camera detection method \n "
" force_rgb=<0|1> for RGB data format (default BGR) " ) ;
module_param_array ( parport , int , NULL , 0 ) ;
module_param ( probe , int , 0 ) ;
module_param ( force_rgb , bool , 0 ) ;
module_param ( video_nr , int , 0 ) ;
static struct qcam * qcams [ MAX_CAMS ] ;
static unsigned int num_cams ;
static inline void qcam_set_ack ( struct qcam * qcam , unsigned int i )
2005-04-17 02:20:36 +04:00
{
/* note: the QC specs refer to the PCAck pin by voltage, not
software level . PC ports have builtin inverters . */
2010-03-22 10:33:56 +03:00
parport_frob_control ( qcam - > pport , 8 , i ? 8 : 0 ) ;
2005-04-17 02:20:36 +04:00
}
2010-05-10 10:55:25 +04:00
static inline unsigned int qcam_ready1 ( struct qcam * qcam )
2005-04-17 02:20:36 +04:00
{
2010-03-22 10:33:56 +03:00
return ( parport_read_status ( qcam - > pport ) & 0x8 ) ? 1 : 0 ;
2005-04-17 02:20:36 +04:00
}
2010-05-10 10:55:25 +04:00
static inline unsigned int qcam_ready2 ( struct qcam * qcam )
2005-04-17 02:20:36 +04:00
{
2010-03-22 10:33:56 +03:00
return ( parport_read_data ( qcam - > pport ) & 0x1 ) ? 1 : 0 ;
2005-04-17 02:20:36 +04:00
}
2010-05-10 10:55:25 +04:00
static unsigned int qcam_await_ready1 ( struct qcam * qcam , int value )
2005-04-17 02:20:36 +04:00
{
2010-05-10 10:55:25 +04:00
struct v4l2_device * v4l2_dev = & qcam - > v4l2_dev ;
2005-04-17 02:20:36 +04:00
unsigned long oldjiffies = jiffies ;
unsigned int i ;
2008-04-16 23:13:15 +04:00
for ( oldjiffies = jiffies ;
2010-03-22 10:33:56 +03:00
time_before ( jiffies , oldjiffies + msecs_to_jiffies ( 40 ) ) ; )
2005-04-17 02:20:36 +04:00
if ( qcam_ready1 ( qcam ) = = value )
return 0 ;
2006-03-25 15:19:53 +03:00
/* If the camera didn't respond within 1/25 second, poll slowly
2005-04-17 02:20:36 +04:00
for a while . */
2010-03-22 10:33:56 +03:00
for ( i = 0 ; i < 50 ; i + + ) {
2005-04-17 02:20:36 +04:00
if ( qcam_ready1 ( qcam ) = = value )
return 0 ;
msleep_interruptible ( 100 ) ;
}
/* Probably somebody pulled the plug out. Not much we can do. */
2010-05-10 10:55:25 +04:00
v4l2_err ( v4l2_dev , " ready1 timeout (%d) %x %x \n " , value ,
2005-04-17 02:20:36 +04:00
parport_read_status ( qcam - > pport ) ,
parport_read_control ( qcam - > pport ) ) ;
return 1 ;
}
2010-05-10 10:55:25 +04:00
static unsigned int qcam_await_ready2 ( struct qcam * qcam , int value )
2005-04-17 02:20:36 +04:00
{
2010-05-10 10:55:25 +04:00
struct v4l2_device * v4l2_dev = & qcam - > v4l2_dev ;
2005-04-17 02:20:36 +04:00
unsigned long oldjiffies = jiffies ;
unsigned int i ;
2008-04-16 23:13:15 +04:00
for ( oldjiffies = jiffies ;
2010-03-22 10:33:56 +03:00
time_before ( jiffies , oldjiffies + msecs_to_jiffies ( 40 ) ) ; )
2005-04-17 02:20:36 +04:00
if ( qcam_ready2 ( qcam ) = = value )
return 0 ;
2006-03-25 15:19:53 +03:00
/* If the camera didn't respond within 1/25 second, poll slowly
2005-04-17 02:20:36 +04:00
for a while . */
2010-03-22 10:33:56 +03:00
for ( i = 0 ; i < 50 ; i + + ) {
2005-04-17 02:20:36 +04:00
if ( qcam_ready2 ( qcam ) = = value )
return 0 ;
msleep_interruptible ( 100 ) ;
}
/* Probably somebody pulled the plug out. Not much we can do. */
2010-05-10 10:55:25 +04:00
v4l2_err ( v4l2_dev , " ready2 timeout (%d) %x %x %x \n " , value ,
2005-04-17 02:20:36 +04:00
parport_read_status ( qcam - > pport ) ,
parport_read_control ( qcam - > pport ) ,
parport_read_data ( qcam - > pport ) ) ;
return 1 ;
}
2010-05-10 10:55:25 +04:00
static int qcam_read_data ( struct qcam * qcam )
2005-04-17 02:20:36 +04:00
{
unsigned int idata ;
2010-03-22 10:33:56 +03:00
2005-04-17 02:20:36 +04:00
qcam_set_ack ( qcam , 0 ) ;
2010-03-22 10:33:56 +03:00
if ( qcam_await_ready1 ( qcam , 1 ) )
return - 1 ;
2005-04-17 02:20:36 +04:00
idata = parport_read_status ( qcam - > pport ) & 0xf0 ;
qcam_set_ack ( qcam , 1 ) ;
2010-03-22 10:33:56 +03:00
if ( qcam_await_ready1 ( qcam , 0 ) )
return - 1 ;
idata | = parport_read_status ( qcam - > pport ) > > 4 ;
2005-04-17 02:20:36 +04:00
return idata ;
}
2010-05-10 10:55:25 +04:00
static int qcam_write_data ( struct qcam * qcam , unsigned int data )
2005-04-17 02:20:36 +04:00
{
2010-05-10 10:55:25 +04:00
struct v4l2_device * v4l2_dev = & qcam - > v4l2_dev ;
2005-04-17 02:20:36 +04:00
unsigned int idata ;
2010-03-22 10:33:56 +03:00
2005-04-17 02:20:36 +04:00
parport_write_data ( qcam - > pport , data ) ;
idata = qcam_read_data ( qcam ) ;
2010-03-22 10:33:56 +03:00
if ( data ! = idata ) {
2010-05-10 10:55:25 +04:00
v4l2_warn ( v4l2_dev , " sent %x but received %x \n " , data ,
2005-04-17 02:20:36 +04:00
idata ) ;
return 1 ;
2006-03-25 15:19:53 +03:00
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
2010-05-10 10:55:25 +04:00
static inline int qcam_set ( struct qcam * qcam , unsigned int cmd , unsigned int data )
2005-04-17 02:20:36 +04:00
{
if ( qcam_write_data ( qcam , cmd ) )
return - 1 ;
if ( qcam_write_data ( qcam , data ) )
return - 1 ;
return 0 ;
}
2010-05-10 10:55:25 +04:00
static inline int qcam_get ( struct qcam * qcam , unsigned int cmd )
2005-04-17 02:20:36 +04:00
{
if ( qcam_write_data ( qcam , cmd ) )
return - 1 ;
return qcam_read_data ( qcam ) ;
}
2010-05-10 10:55:25 +04:00
static int qc_detect ( struct qcam * qcam )
2005-04-17 02:20:36 +04:00
{
unsigned int stat , ostat , i , count = 0 ;
/* The probe routine below is not very reliable. The IEEE-1284
probe takes precedence . */
/* XXX Currently parport provides no way to distinguish between
" the IEEE probe was not done " and " the probe was done, but
no device was found " . Fix this one day. */
if ( qcam - > pport - > probe_info [ 0 ] . class = = PARPORT_CLASS_MEDIA
& & qcam - > pport - > probe_info [ 0 ] . model
2006-03-25 15:19:53 +03:00
& & ! strcmp ( qcam - > pdev - > port - > probe_info [ 0 ] . model ,
2005-04-17 02:20:36 +04:00
" Color QuickCam 2.0 " ) ) {
printk ( KERN_DEBUG " QuickCam: Found by IEEE1284 probe. \n " ) ;
return 1 ;
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( probe < 2 )
return 0 ;
parport_write_control ( qcam - > pport , 0xc ) ;
/* look for a heartbeat */
ostat = stat = parport_read_status ( qcam - > pport ) ;
2010-03-22 10:33:56 +03:00
for ( i = 0 ; i < 250 ; i + + ) {
2005-04-17 02:20:36 +04:00
mdelay ( 1 ) ;
stat = parport_read_status ( qcam - > pport ) ;
2010-03-22 10:33:56 +03:00
if ( ostat ! = stat ) {
if ( + + count > = 3 )
return 1 ;
2005-04-17 02:20:36 +04:00
ostat = stat ;
}
}
/* Reset the camera and try again */
parport_write_control ( qcam - > pport , 0xc ) ;
parport_write_control ( qcam - > pport , 0x8 ) ;
mdelay ( 1 ) ;
parport_write_control ( qcam - > pport , 0xc ) ;
mdelay ( 1 ) ;
count = 0 ;
ostat = stat = parport_read_status ( qcam - > pport ) ;
2010-03-22 10:33:56 +03:00
for ( i = 0 ; i < 250 ; i + + ) {
2005-04-17 02:20:36 +04:00
mdelay ( 1 ) ;
stat = parport_read_status ( qcam - > pport ) ;
2010-03-22 10:33:56 +03:00
if ( ostat ! = stat ) {
if ( + + count > = 3 )
return 1 ;
2005-04-17 02:20:36 +04:00
ostat = stat ;
}
}
/* no (or flatline) camera, give up */
return 0 ;
}
2010-05-10 10:55:25 +04:00
static void qc_reset ( struct qcam * qcam )
2005-04-17 02:20:36 +04:00
{
parport_write_control ( qcam - > pport , 0xc ) ;
parport_write_control ( qcam - > pport , 0x8 ) ;
mdelay ( 1 ) ;
parport_write_control ( qcam - > pport , 0xc ) ;
2006-03-25 15:19:53 +03:00
mdelay ( 1 ) ;
2005-04-17 02:20:36 +04:00
}
/* Reset the QuickCam and program for brightness, contrast,
* white - balance , and resolution . */
2010-05-10 10:55:25 +04:00
static void qc_setup ( struct qcam * qcam )
2005-04-17 02:20:36 +04:00
{
2010-05-10 10:55:25 +04:00
qc_reset ( qcam ) ;
2005-04-17 02:20:36 +04:00
2010-03-22 10:33:56 +03:00
/* Set the brightness. */
2010-05-10 10:55:25 +04:00
qcam_set ( qcam , 11 , qcam - > brightness ) ;
2005-04-17 02:20:36 +04:00
/* Set the height and width. These refer to the actual
CCD area * before * applying the selected decimation . */
2010-05-10 10:55:25 +04:00
qcam_set ( qcam , 17 , qcam - > ccd_height ) ;
qcam_set ( qcam , 19 , qcam - > ccd_width / 2 ) ;
2005-04-17 02:20:36 +04:00
/* Set top and left. */
2010-05-10 10:55:25 +04:00
qcam_set ( qcam , 0xd , qcam - > top ) ;
qcam_set ( qcam , 0xf , qcam - > left ) ;
2005-04-17 02:20:36 +04:00
/* Set contrast and white balance. */
2010-05-10 10:55:25 +04:00
qcam_set ( qcam , 0x19 , qcam - > contrast ) ;
qcam_set ( qcam , 0x1f , qcam - > whitebal ) ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
/* Set the speed. */
2010-05-10 10:55:25 +04:00
qcam_set ( qcam , 45 , 2 ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-25 15:19:53 +03:00
/* Read some bytes from the camera and put them in the buffer.
2005-04-17 02:20:36 +04:00
nbytes should be a multiple of 3 , because bidirectional mode gives
us three bytes at a time . */
2010-05-10 10:55:25 +04:00
static unsigned int qcam_read_bytes ( struct qcam * qcam , unsigned char * buf , unsigned int nbytes )
2005-04-17 02:20:36 +04:00
{
unsigned int bytes = 0 ;
2010-05-10 10:55:25 +04:00
qcam_set_ack ( qcam , 0 ) ;
if ( qcam - > bidirectional ) {
2005-04-17 02:20:36 +04:00
/* It's a bidirectional port */
2010-03-22 10:33:56 +03:00
while ( bytes < nbytes ) {
2005-04-17 02:20:36 +04:00
unsigned int lo1 , hi1 , lo2 , hi2 ;
unsigned char r , g , b ;
2010-05-10 10:55:25 +04:00
if ( qcam_await_ready2 ( qcam , 1 ) )
2010-03-22 10:33:56 +03:00
return bytes ;
2010-05-10 10:55:25 +04:00
lo1 = parport_read_data ( qcam - > pport ) > > 1 ;
hi1 = ( ( parport_read_status ( qcam - > pport ) > > 3 ) & 0x1f ) ^ 0x10 ;
qcam_set_ack ( qcam , 1 ) ;
if ( qcam_await_ready2 ( qcam , 0 ) )
2010-03-22 10:33:56 +03:00
return bytes ;
2010-05-10 10:55:25 +04:00
lo2 = parport_read_data ( qcam - > pport ) > > 1 ;
hi2 = ( ( parport_read_status ( qcam - > pport ) > > 3 ) & 0x1f ) ^ 0x10 ;
qcam_set_ack ( qcam , 0 ) ;
2010-03-22 10:33:56 +03:00
r = lo1 | ( ( hi1 & 1 ) < < 7 ) ;
g = ( ( hi1 & 0x1e ) < < 3 ) | ( ( hi2 & 0x1e ) > > 1 ) ;
b = lo2 | ( ( hi2 & 1 ) < < 7 ) ;
2005-04-17 02:20:36 +04:00
if ( force_rgb ) {
buf [ bytes + + ] = r ;
buf [ bytes + + ] = g ;
buf [ bytes + + ] = b ;
} else {
buf [ bytes + + ] = b ;
buf [ bytes + + ] = g ;
buf [ bytes + + ] = r ;
}
}
2010-03-22 10:33:56 +03:00
} else {
2005-04-17 02:20:36 +04:00
/* It's a unidirectional port */
int i = 0 , n = bytes ;
unsigned char rgb [ 3 ] ;
2010-03-22 10:33:56 +03:00
while ( bytes < nbytes ) {
2005-04-17 02:20:36 +04:00
unsigned int hi , lo ;
2010-05-10 10:55:25 +04:00
if ( qcam_await_ready1 ( qcam , 1 ) )
2010-03-22 10:33:56 +03:00
return bytes ;
2010-05-10 10:55:25 +04:00
hi = ( parport_read_status ( qcam - > pport ) & 0xf0 ) ;
qcam_set_ack ( qcam , 1 ) ;
if ( qcam_await_ready1 ( qcam , 0 ) )
2010-03-22 10:33:56 +03:00
return bytes ;
2010-05-10 10:55:25 +04:00
lo = ( parport_read_status ( qcam - > pport ) & 0xf0 ) ;
qcam_set_ack ( qcam , 0 ) ;
2005-04-17 02:20:36 +04:00
/* flip some bits */
rgb [ ( i = bytes + + % 3 ) ] = ( hi | ( lo > > 4 ) ) ^ 0x88 ;
if ( i > = 2 ) {
get_fragment :
if ( force_rgb ) {
buf [ n + + ] = rgb [ 0 ] ;
buf [ n + + ] = rgb [ 1 ] ;
buf [ n + + ] = rgb [ 2 ] ;
} else {
buf [ n + + ] = rgb [ 2 ] ;
buf [ n + + ] = rgb [ 1 ] ;
buf [ n + + ] = rgb [ 0 ] ;
}
}
}
if ( i ) {
i = 0 ;
goto get_fragment ;
}
}
return bytes ;
}
# define BUFSZ 150
2010-05-10 10:55:25 +04:00
static long qc_capture ( struct qcam * qcam , char __user * buf , unsigned long len )
2005-04-17 02:20:36 +04:00
{
2010-05-10 10:55:25 +04:00
struct v4l2_device * v4l2_dev = & qcam - > v4l2_dev ;
2012-04-20 14:30:48 +04:00
unsigned lines , pixelsperline ;
2010-05-10 10:55:25 +04:00
unsigned int is_bi_dir = qcam - > bidirectional ;
2005-04-17 02:20:36 +04:00
size_t wantlen , outptr = 0 ;
char tmpbuf [ BUFSZ ] ;
if ( ! access_ok ( VERIFY_WRITE , buf , len ) )
return - EFAULT ;
/* Wait for camera to become ready */
2010-03-22 10:33:56 +03:00
for ( ; ; ) {
2010-05-10 10:55:25 +04:00
int i = qcam_get ( qcam , 41 ) ;
2010-03-22 10:33:56 +03:00
2005-04-17 02:20:36 +04:00
if ( i = = - 1 ) {
2010-05-10 10:55:25 +04:00
qc_setup ( qcam ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
if ( ( i & 0x80 ) = = 0 )
break ;
2010-03-22 10:33:56 +03:00
schedule ( ) ;
2005-04-17 02:20:36 +04:00
}
2010-05-10 10:55:25 +04:00
if ( qcam_set ( qcam , 7 , ( qcam - > mode | ( is_bi_dir ? 1 : 0 ) ) + 1 ) )
2005-04-17 02:20:36 +04:00
return - EIO ;
2006-03-25 15:19:53 +03:00
2010-05-10 10:55:25 +04:00
lines = qcam - > height ;
pixelsperline = qcam - > width ;
2005-04-17 02:20:36 +04:00
2010-03-22 10:33:56 +03:00
if ( is_bi_dir ) {
2005-04-17 02:20:36 +04:00
/* Turn the port around */
2010-05-10 10:55:25 +04:00
parport_data_reverse ( qcam - > pport ) ;
2005-04-17 02:20:36 +04:00
mdelay ( 3 ) ;
2010-05-10 10:55:25 +04:00
qcam_set_ack ( qcam , 0 ) ;
if ( qcam_await_ready1 ( qcam , 1 ) ) {
qc_setup ( qcam ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
2010-05-10 10:55:25 +04:00
qcam_set_ack ( qcam , 1 ) ;
if ( qcam_await_ready1 ( qcam , 0 ) ) {
qc_setup ( qcam ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
}
wantlen = lines * pixelsperline * 24 / 8 ;
2010-03-22 10:33:56 +03:00
while ( wantlen ) {
2005-04-17 02:20:36 +04:00
size_t t , s ;
2010-03-22 10:33:56 +03:00
s = ( wantlen > BUFSZ ) ? BUFSZ : wantlen ;
2010-05-10 10:55:25 +04:00
t = qcam_read_bytes ( qcam , tmpbuf , s ) ;
2010-03-22 10:33:56 +03:00
if ( outptr < len ) {
2005-04-17 02:20:36 +04:00
size_t sz = len - outptr ;
2010-03-22 10:33:56 +03:00
if ( sz > t )
sz = t ;
if ( __copy_to_user ( buf + outptr , tmpbuf , sz ) )
2005-04-17 02:20:36 +04:00
break ;
outptr + = sz ;
}
wantlen - = t ;
if ( t < s )
break ;
cond_resched ( ) ;
}
len = outptr ;
2010-03-22 10:33:56 +03:00
if ( wantlen ) {
2010-05-10 10:55:25 +04:00
v4l2_err ( v4l2_dev , " short read. \n " ) ;
2005-04-17 02:20:36 +04:00
if ( is_bi_dir )
2010-05-10 10:55:25 +04:00
parport_data_forward ( qcam - > pport ) ;
qc_setup ( qcam ) ;
2005-04-17 02:20:36 +04:00
return len ;
}
2010-03-22 10:33:56 +03:00
if ( is_bi_dir ) {
2005-04-17 02:20:36 +04:00
int l ;
2010-03-22 10:33:56 +03:00
2005-04-17 02:20:36 +04:00
do {
2010-05-10 10:55:25 +04:00
l = qcam_read_bytes ( qcam , tmpbuf , 3 ) ;
2005-04-17 02:20:36 +04:00
cond_resched ( ) ;
} while ( l & & ( tmpbuf [ 0 ] = = 0x7e | | tmpbuf [ 1 ] = = 0x7e | | tmpbuf [ 2 ] = = 0x7e ) ) ;
if ( force_rgb ) {
if ( tmpbuf [ 0 ] ! = 0xe | | tmpbuf [ 1 ] ! = 0x0 | | tmpbuf [ 2 ] ! = 0xf )
2010-05-10 10:55:25 +04:00
v4l2_err ( v4l2_dev , " bad EOF \n " ) ;
2005-04-17 02:20:36 +04:00
} else {
if ( tmpbuf [ 0 ] ! = 0xf | | tmpbuf [ 1 ] ! = 0x0 | | tmpbuf [ 2 ] ! = 0xe )
2010-05-10 10:55:25 +04:00
v4l2_err ( v4l2_dev , " bad EOF \n " ) ;
2005-04-17 02:20:36 +04:00
}
2010-05-10 10:55:25 +04:00
qcam_set_ack ( qcam , 0 ) ;
if ( qcam_await_ready1 ( qcam , 1 ) ) {
v4l2_err ( v4l2_dev , " no ack after EOF \n " ) ;
parport_data_forward ( qcam - > pport ) ;
qc_setup ( qcam ) ;
2005-04-17 02:20:36 +04:00
return len ;
}
2010-05-10 10:55:25 +04:00
parport_data_forward ( qcam - > pport ) ;
2005-04-17 02:20:36 +04:00
mdelay ( 3 ) ;
2010-05-10 10:55:25 +04:00
qcam_set_ack ( qcam , 1 ) ;
if ( qcam_await_ready1 ( qcam , 0 ) ) {
v4l2_err ( v4l2_dev , " no ack to port turnaround \n " ) ;
qc_setup ( qcam ) ;
2005-04-17 02:20:36 +04:00
return len ;
}
2010-03-22 10:33:56 +03:00
} else {
2005-04-17 02:20:36 +04:00
int l ;
2010-03-22 10:33:56 +03:00
2005-04-17 02:20:36 +04:00
do {
2010-05-10 10:55:25 +04:00
l = qcam_read_bytes ( qcam , tmpbuf , 1 ) ;
2005-04-17 02:20:36 +04:00
cond_resched ( ) ;
} while ( l & & tmpbuf [ 0 ] = = 0x7e ) ;
2010-05-10 10:55:25 +04:00
l = qcam_read_bytes ( qcam , tmpbuf + 1 , 2 ) ;
2005-04-17 02:20:36 +04:00
if ( force_rgb ) {
if ( tmpbuf [ 0 ] ! = 0xe | | tmpbuf [ 1 ] ! = 0x0 | | tmpbuf [ 2 ] ! = 0xf )
2010-05-10 10:55:25 +04:00
v4l2_err ( v4l2_dev , " bad EOF \n " ) ;
2005-04-17 02:20:36 +04:00
} else {
if ( tmpbuf [ 0 ] ! = 0xf | | tmpbuf [ 1 ] ! = 0x0 | | tmpbuf [ 2 ] ! = 0xe )
2010-05-10 10:55:25 +04:00
v4l2_err ( v4l2_dev , " bad EOF \n " ) ;
2005-04-17 02:20:36 +04:00
}
}
2010-05-10 10:55:25 +04:00
qcam_write_data ( qcam , 0 ) ;
2005-04-17 02:20:36 +04:00
return len ;
}
/*
* Video4linux interfacing
*/
2010-05-10 10:55:25 +04:00
static int qcam_querycap ( struct file * file , void * priv ,
struct v4l2_capability * vcap )
2005-04-17 02:20:36 +04:00
{
2010-05-10 10:55:25 +04:00
struct qcam * qcam = video_drvdata ( file ) ;
2006-03-25 15:19:53 +03:00
2010-05-10 10:55:25 +04:00
strlcpy ( vcap - > driver , qcam - > v4l2_dev . name , sizeof ( vcap - > driver ) ) ;
strlcpy ( vcap - > card , " Color Quickcam " , sizeof ( vcap - > card ) ) ;
strlcpy ( vcap - > bus_info , " parport " , sizeof ( vcap - > bus_info ) ) ;
2012-05-08 22:20:21 +04:00
vcap - > device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE ;
vcap - > capabilities = vcap - > device_caps | V4L2_CAP_DEVICE_CAPS ;
2010-05-10 10:55:25 +04:00
return 0 ;
}
2010-03-22 10:33:56 +03:00
2010-05-10 10:55:25 +04:00
static int qcam_enum_input ( struct file * file , void * fh , struct v4l2_input * vin )
{
if ( vin - > index > 0 )
return - EINVAL ;
strlcpy ( vin - > name , " Camera " , sizeof ( vin - > name ) ) ;
vin - > type = V4L2_INPUT_TYPE_CAMERA ;
vin - > audioset = 0 ;
vin - > tuner = 0 ;
vin - > std = 0 ;
vin - > status = 0 ;
return 0 ;
}
2010-03-22 10:33:56 +03:00
2010-05-10 10:55:25 +04:00
static int qcam_g_input ( struct file * file , void * fh , unsigned int * inp )
{
* inp = 0 ;
return 0 ;
}
static int qcam_s_input ( struct file * file , void * fh , unsigned int inp )
{
return ( inp > 0 ) ? - EINVAL : 0 ;
}
static int qcam_g_fmt_vid_cap ( struct file * file , void * fh , struct v4l2_format * fmt )
{
struct qcam * qcam = video_drvdata ( file ) ;
struct v4l2_pix_format * pix = & fmt - > fmt . pix ;
pix - > width = qcam - > width ;
pix - > height = qcam - > height ;
pix - > pixelformat = V4L2_PIX_FMT_RGB24 ;
pix - > field = V4L2_FIELD_NONE ;
pix - > bytesperline = 3 * qcam - > width ;
pix - > sizeimage = 3 * qcam - > width * qcam - > height ;
/* Just a guess */
pix - > colorspace = V4L2_COLORSPACE_SRGB ;
return 0 ;
}
static int qcam_try_fmt_vid_cap ( struct file * file , void * fh , struct v4l2_format * fmt )
{
struct v4l2_pix_format * pix = & fmt - > fmt . pix ;
if ( pix - > height < 60 | | pix - > width < 80 ) {
pix - > height = 60 ;
pix - > width = 80 ;
} else if ( pix - > height < 120 | | pix - > width < 160 ) {
pix - > height = 120 ;
pix - > width = 160 ;
} else {
pix - > height = 240 ;
pix - > width = 320 ;
2010-03-22 10:33:56 +03:00
}
2010-05-10 10:55:25 +04:00
pix - > pixelformat = V4L2_PIX_FMT_RGB24 ;
pix - > field = V4L2_FIELD_NONE ;
pix - > bytesperline = 3 * pix - > width ;
pix - > sizeimage = 3 * pix - > width * pix - > height ;
/* Just a guess */
pix - > colorspace = V4L2_COLORSPACE_SRGB ;
return 0 ;
}
static int qcam_s_fmt_vid_cap ( struct file * file , void * fh , struct v4l2_format * fmt )
{
struct qcam * qcam = video_drvdata ( file ) ;
struct v4l2_pix_format * pix = & fmt - > fmt . pix ;
int ret = qcam_try_fmt_vid_cap ( file , fh , fmt ) ;
if ( ret )
return ret ;
switch ( pix - > height ) {
case 60 :
qcam - > mode = QC_DECIMATION_4 ;
break ;
case 120 :
qcam - > mode = QC_DECIMATION_2 ;
break ;
2010-03-22 10:33:56 +03:00
default :
2010-05-10 10:55:25 +04:00
qcam - > mode = QC_DECIMATION_1 ;
break ;
2005-04-17 02:20:36 +04:00
}
2010-05-10 10:55:25 +04:00
mutex_lock ( & qcam - > lock ) ;
qcam - > mode | = QC_MILLIONS ;
qcam - > height = pix - > height ;
qcam - > width = pix - > width ;
parport_claim_or_block ( qcam - > pdev ) ;
qc_setup ( qcam ) ;
parport_release ( qcam - > pdev ) ;
mutex_unlock ( & qcam - > lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2010-05-10 10:55:25 +04:00
static int qcam_enum_fmt_vid_cap ( struct file * file , void * fh , struct v4l2_fmtdesc * fmt )
2005-04-17 02:20:36 +04:00
{
2010-05-10 10:55:25 +04:00
static struct v4l2_fmtdesc formats [ ] = {
{ 0 , 0 , 0 ,
" RGB 8:8:8 " , V4L2_PIX_FMT_RGB24 ,
{ 0 , 0 , 0 , 0 }
} ,
} ;
enum v4l2_buf_type type = fmt - > type ;
if ( fmt - > index > 0 )
return - EINVAL ;
* fmt = formats [ fmt - > index ] ;
fmt - > type = type ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
static ssize_t qcam_read ( struct file * file , char __user * buf ,
size_t count , loff_t * ppos )
{
2010-05-10 10:55:25 +04:00
struct qcam * qcam = video_drvdata ( file ) ;
2005-04-17 02:20:36 +04:00
int len ;
2006-02-07 11:49:14 +03:00
mutex_lock ( & qcam - > lock ) ;
2005-04-17 02:20:36 +04:00
parport_claim_or_block ( qcam - > pdev ) ;
/* Probably should have a semaphore against multiple users */
2010-03-22 10:33:56 +03:00
len = qc_capture ( qcam , buf , count ) ;
2005-04-17 02:20:36 +04:00
parport_release ( qcam - > pdev ) ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & qcam - > lock ) ;
2005-04-17 02:20:36 +04:00
return len ;
}
2012-05-08 22:20:21 +04:00
static int qcam_s_ctrl ( struct v4l2_ctrl * ctrl )
{
struct qcam * qcam =
container_of ( ctrl - > handler , struct qcam , hdl ) ;
int ret = 0 ;
mutex_lock ( & qcam - > lock ) ;
switch ( ctrl - > id ) {
case V4L2_CID_BRIGHTNESS :
qcam - > brightness = ctrl - > val ;
break ;
case V4L2_CID_CONTRAST :
qcam - > contrast = ctrl - > val ;
break ;
case V4L2_CID_GAMMA :
qcam - > whitebal = ctrl - > val ;
break ;
default :
ret = - EINVAL ;
break ;
}
if ( ret = = 0 ) {
parport_claim_or_block ( qcam - > pdev ) ;
qc_setup ( qcam ) ;
parport_release ( qcam - > pdev ) ;
}
mutex_unlock ( & qcam - > lock ) ;
return ret ;
}
2008-12-30 12:58:20 +03:00
static const struct v4l2_file_operations qcam_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
2012-05-08 22:20:21 +04:00
. open = v4l2_fh_open ,
. release = v4l2_fh_release ,
. poll = v4l2_ctrl_poll ,
2010-11-14 16:09:38 +03:00
. unlocked_ioctl = video_ioctl2 ,
2005-04-17 02:20:36 +04:00
. read = qcam_read ,
} ;
2010-05-10 10:55:25 +04:00
static const struct v4l2_ioctl_ops qcam_ioctl_ops = {
. vidioc_querycap = qcam_querycap ,
. vidioc_g_input = qcam_g_input ,
. vidioc_s_input = qcam_s_input ,
. vidioc_enum_input = qcam_enum_input ,
2012-05-08 22:20:21 +04:00
. vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap ,
2010-05-10 10:55:25 +04:00
. vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap ,
. vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap ,
. vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap ,
2012-05-08 22:20:21 +04:00
. vidioc_log_status = v4l2_ctrl_log_status ,
. vidioc_subscribe_event = v4l2_ctrl_subscribe_event ,
. vidioc_unsubscribe_event = v4l2_event_unsubscribe ,
} ;
static const struct v4l2_ctrl_ops qcam_ctrl_ops = {
. s_ctrl = qcam_s_ctrl ,
2005-04-17 02:20:36 +04:00
} ;
/* Initialize the QuickCam driver control structure. */
2010-05-10 10:55:25 +04:00
static struct qcam * qcam_init ( struct parport * port )
2005-04-17 02:20:36 +04:00
{
2010-05-10 10:55:25 +04:00
struct qcam * qcam ;
struct v4l2_device * v4l2_dev ;
2006-03-25 15:19:53 +03:00
2010-05-10 10:55:25 +04:00
qcam = kzalloc ( sizeof ( * qcam ) , GFP_KERNEL ) ;
if ( qcam = = NULL )
2005-04-17 02:20:36 +04:00
return NULL ;
2010-05-10 10:55:25 +04:00
v4l2_dev = & qcam - > v4l2_dev ;
strlcpy ( v4l2_dev - > name , " c-qcam " , sizeof ( v4l2_dev - > name ) ) ;
if ( v4l2_device_register ( NULL , v4l2_dev ) < 0 ) {
v4l2_err ( v4l2_dev , " Could not register v4l2_device \n " ) ;
2011-07-04 18:11:42 +04:00
kfree ( qcam ) ;
2010-05-10 10:55:25 +04:00
return NULL ;
}
2012-05-08 22:20:21 +04:00
v4l2_ctrl_handler_init ( & qcam - > hdl , 3 ) ;
v4l2_ctrl_new_std ( & qcam - > hdl , & qcam_ctrl_ops ,
V4L2_CID_BRIGHTNESS , 0 , 255 , 1 , 240 ) ;
v4l2_ctrl_new_std ( & qcam - > hdl , & qcam_ctrl_ops ,
V4L2_CID_CONTRAST , 0 , 255 , 1 , 192 ) ;
v4l2_ctrl_new_std ( & qcam - > hdl , & qcam_ctrl_ops ,
V4L2_CID_GAMMA , 0 , 255 , 1 , 128 ) ;
if ( qcam - > hdl . error ) {
v4l2_err ( v4l2_dev , " couldn't register controls \n " ) ;
v4l2_ctrl_handler_free ( & qcam - > hdl ) ;
kfree ( qcam ) ;
return NULL ;
}
2010-05-10 10:55:25 +04:00
qcam - > pport = port ;
qcam - > pdev = parport_register_device ( port , " c-qcam " , NULL , NULL ,
2005-04-17 02:20:36 +04:00
NULL , 0 , NULL ) ;
2010-05-10 10:55:25 +04:00
qcam - > bidirectional = ( qcam - > pport - > modes & PARPORT_MODE_TRISTATE ) ? 1 : 0 ;
2005-04-17 02:20:36 +04:00
2010-05-10 10:55:25 +04:00
if ( qcam - > pdev = = NULL ) {
v4l2_err ( v4l2_dev , " couldn't register for %s. \n " , port - > name ) ;
2012-05-08 22:20:21 +04:00
v4l2_ctrl_handler_free ( & qcam - > hdl ) ;
2010-05-10 10:55:25 +04:00
kfree ( qcam ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
2006-03-25 15:19:53 +03:00
2010-05-10 10:55:25 +04:00
strlcpy ( qcam - > vdev . name , " Colour QuickCam " , sizeof ( qcam - > vdev . name ) ) ;
qcam - > vdev . v4l2_dev = v4l2_dev ;
qcam - > vdev . fops = & qcam_fops ;
qcam - > vdev . ioctl_ops = & qcam_ioctl_ops ;
qcam - > vdev . release = video_device_release_empty ;
2012-05-08 22:20:21 +04:00
qcam - > vdev . ctrl_handler = & qcam - > hdl ;
set_bit ( V4L2_FL_USE_FH_PRIO , & qcam - > vdev . flags ) ;
2010-05-10 10:55:25 +04:00
video_set_drvdata ( & qcam - > vdev , qcam ) ;
mutex_init ( & qcam - > lock ) ;
qcam - > width = qcam - > ccd_width = 320 ;
qcam - > height = qcam - > ccd_height = 240 ;
qcam - > mode = QC_MILLIONS | QC_DECIMATION_1 ;
qcam - > contrast = 192 ;
qcam - > brightness = 240 ;
qcam - > whitebal = 128 ;
qcam - > top = 1 ;
qcam - > left = 14 ;
return qcam ;
2005-04-17 02:20:36 +04:00
}
static int init_cqcam ( struct parport * port )
{
2010-05-10 10:55:25 +04:00
struct qcam * qcam ;
struct v4l2_device * v4l2_dev ;
2005-04-17 02:20:36 +04:00
2010-03-22 10:33:56 +03:00
if ( parport [ 0 ] ! = - 1 ) {
2005-04-17 02:20:36 +04:00
/* The user gave specific instructions */
int i , found = 0 ;
2010-03-22 10:33:56 +03:00
for ( i = 0 ; i < MAX_CAMS & & parport [ i ] ! = - 1 ; i + + ) {
2005-04-17 02:20:36 +04:00
if ( parport [ 0 ] = = port - > number )
found = 1 ;
}
if ( ! found )
return - ENODEV ;
}
if ( num_cams = = MAX_CAMS )
return - ENOSPC ;
qcam = qcam_init ( port ) ;
2010-03-22 10:33:56 +03:00
if ( qcam = = NULL )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2006-03-25 15:19:53 +03:00
2010-05-10 10:55:25 +04:00
v4l2_dev = & qcam - > v4l2_dev ;
2005-04-17 02:20:36 +04:00
parport_claim_or_block ( qcam - > pdev ) ;
qc_reset ( qcam ) ;
2006-03-25 15:19:53 +03:00
2010-03-22 10:33:56 +03:00
if ( probe & & qc_detect ( qcam ) = = 0 ) {
2005-04-17 02:20:36 +04:00
parport_release ( qcam - > pdev ) ;
parport_unregister_device ( qcam - > pdev ) ;
kfree ( qcam ) ;
return - ENODEV ;
}
qc_setup ( qcam ) ;
parport_release ( qcam - > pdev ) ;
2006-03-25 15:19:53 +03:00
2008-09-04 00:11:58 +04:00
if ( video_register_device ( & qcam - > vdev , VFL_TYPE_GRABBER , video_nr ) < 0 ) {
2010-05-10 10:55:25 +04:00
v4l2_err ( v4l2_dev , " Unable to register Colour QuickCam on %s \n " ,
2005-04-17 02:20:36 +04:00
qcam - > pport - > name ) ;
parport_unregister_device ( qcam - > pdev ) ;
kfree ( qcam ) ;
return - ENODEV ;
}
2010-05-10 10:55:25 +04:00
v4l2_info ( v4l2_dev , " %s: Colour QuickCam found on %s \n " ,
2009-11-27 19:57:15 +03:00
video_device_node_name ( & qcam - > vdev ) , qcam - > pport - > name ) ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
qcams [ num_cams + + ] = qcam ;
return 0 ;
}
2010-05-10 10:55:25 +04:00
static void close_cqcam ( struct qcam * qcam )
2005-04-17 02:20:36 +04:00
{
video_unregister_device ( & qcam - > vdev ) ;
2012-05-08 22:20:21 +04:00
v4l2_ctrl_handler_free ( & qcam - > hdl ) ;
2005-04-17 02:20:36 +04:00
parport_unregister_device ( qcam - > pdev ) ;
kfree ( qcam ) ;
}
static void cq_attach ( struct parport * port )
{
init_cqcam ( port ) ;
}
static void cq_detach ( struct parport * port )
{
/* Write this some day. */
}
static struct parport_driver cqcam_driver = {
. name = " cqcam " ,
. attach = cq_attach ,
. detach = cq_detach ,
} ;
2010-03-22 10:33:56 +03:00
static int __init cqcam_init ( void )
2005-04-17 02:20:36 +04:00
{
2010-05-10 10:55:25 +04:00
printk ( KERN_INFO BANNER " \n " ) ;
2005-04-17 02:20:36 +04:00
return parport_register_driver ( & cqcam_driver ) ;
}
2010-03-22 10:33:56 +03:00
static void __exit cqcam_cleanup ( void )
2005-04-17 02:20:36 +04:00
{
unsigned int i ;
for ( i = 0 ; i < num_cams ; i + + )
close_cqcam ( qcams [ i ] ) ;
parport_unregister_driver ( & cqcam_driver ) ;
}
MODULE_AUTHOR ( " Philip Blundell <philb@gnu.org> " ) ;
MODULE_DESCRIPTION ( BANNER ) ;
MODULE_LICENSE ( " GPL " ) ;
[media] Stop using linux/version.h on most video drivers
All the modified drivers didn't have any version increment since
Jan, 1 2011. Several of them didn't have any version increment
for a long time, even having new features and important bug fixes
happening.
As we're now filling the QUERYCAP version with the current Kernel
Release, we don't need to maintain a per-driver version control
anymore. So, let's just use the default.
In order to preserve the Kernel module version history, a
KERNEL_VERSION() macro were added to all modified drivers, and
the extraver number were incremented.
I opted to preserve the per-driver version control to a few
pwc, pvrusb2, s2255, s5p-fimc and sh_vou.
A few drivers are still using the legacy way to handle ioctl's.
So, we can't do such change on them, otherwise, they'll break.
Those are: uvc, et61x251 and sn9c102.
The rationale is that the per-driver version control seems to be
actively maintained on those.
Yet, I think that the better for them would be to just use the
default version numbering, instead of doing that by themselves.
While here, removed a few uneeded include linux/version.h
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
2011-06-24 21:45:49 +04:00
MODULE_VERSION ( " 0.0.4 " ) ;
2005-04-17 02:20:36 +04:00
module_init ( cqcam_init ) ;
module_exit ( cqcam_cleanup ) ;