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>
# include <linux/videodev.h>
2006-06-05 17:26:32 +04:00
# include <media/v4l2-common.h>
2008-07-20 15:12:02 +04:00
# include <media/v4l2-ioctl.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>
2006-02-07 11:49:14 +03:00
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
struct qcam_device {
struct video_device vdev ;
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 ;
2008-08-23 12:31:47 +04:00
unsigned long in_use ;
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
# define BANNER "Colour QuickCam for Video4Linux v0.05"
static int parport [ MAX_CAMS ] = { [ 1 . . . MAX_CAMS - 1 ] = - 1 } ;
static int probe = 2 ;
2008-04-22 21:41:48 +04:00
static int force_rgb ;
2005-04-17 02:20:36 +04:00
static int video_nr = - 1 ;
static inline void qcam_set_ack ( struct qcam_device * qcam , unsigned int i )
{
/* note: the QC specs refer to the PCAck pin by voltage, not
software level . PC ports have builtin inverters . */
parport_frob_control ( qcam - > pport , 8 , i ? 8 : 0 ) ;
}
static inline unsigned int qcam_ready1 ( struct qcam_device * qcam )
{
return ( parport_read_status ( qcam - > pport ) & 0x8 ) ? 1 : 0 ;
}
static inline unsigned int qcam_ready2 ( struct qcam_device * qcam )
{
return ( parport_read_data ( qcam - > pport ) & 0x1 ) ? 1 : 0 ;
}
2006-03-25 15:19:53 +03:00
static unsigned int qcam_await_ready1 ( struct qcam_device * qcam ,
2005-04-17 02:20:36 +04:00
int value )
{
unsigned long oldjiffies = jiffies ;
unsigned int i ;
2008-04-16 23:13:15 +04:00
for ( oldjiffies = jiffies ;
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 . */
for ( i = 0 ; i < 50 ; i + + )
{
if ( qcam_ready1 ( qcam ) = = value )
return 0 ;
msleep_interruptible ( 100 ) ;
}
/* Probably somebody pulled the plug out. Not much we can do. */
printk ( KERN_ERR " c-qcam: ready1 timeout (%d) %x %x \n " , value ,
parport_read_status ( qcam - > pport ) ,
parport_read_control ( qcam - > pport ) ) ;
return 1 ;
}
static unsigned int qcam_await_ready2 ( struct qcam_device * qcam , int value )
{
unsigned long oldjiffies = jiffies ;
unsigned int i ;
2008-04-16 23:13:15 +04:00
for ( oldjiffies = jiffies ;
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 . */
for ( i = 0 ; i < 50 ; i + + )
{
if ( qcam_ready2 ( qcam ) = = value )
return 0 ;
msleep_interruptible ( 100 ) ;
}
/* Probably somebody pulled the plug out. Not much we can do. */
printk ( KERN_ERR " c-qcam: ready2 timeout (%d) %x %x %x \n " , value ,
parport_read_status ( qcam - > pport ) ,
parport_read_control ( qcam - > pport ) ,
parport_read_data ( qcam - > pport ) ) ;
return 1 ;
}
static int qcam_read_data ( struct qcam_device * qcam )
{
unsigned int idata ;
qcam_set_ack ( qcam , 0 ) ;
if ( qcam_await_ready1 ( qcam , 1 ) ) return - 1 ;
idata = parport_read_status ( qcam - > pport ) & 0xf0 ;
qcam_set_ack ( qcam , 1 ) ;
if ( qcam_await_ready1 ( qcam , 0 ) ) return - 1 ;
idata | = ( parport_read_status ( qcam - > pport ) > > 4 ) ;
return idata ;
}
static int qcam_write_data ( struct qcam_device * qcam , unsigned int data )
{
unsigned int idata ;
parport_write_data ( qcam - > pport , data ) ;
idata = qcam_read_data ( qcam ) ;
2006-03-25 15:19:53 +03:00
if ( data ! = idata )
2005-04-17 02:20:36 +04:00
{
2006-03-25 15:19:53 +03:00
printk ( KERN_WARNING " cqcam: 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 ;
}
static inline int qcam_set ( struct qcam_device * qcam , unsigned int cmd , unsigned int data )
{
if ( qcam_write_data ( qcam , cmd ) )
return - 1 ;
if ( qcam_write_data ( qcam , data ) )
return - 1 ;
return 0 ;
}
static inline int qcam_get ( struct qcam_device * qcam , unsigned int cmd )
{
if ( qcam_write_data ( qcam , cmd ) )
return - 1 ;
return qcam_read_data ( qcam ) ;
}
static int qc_detect ( struct qcam_device * qcam )
{
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 ) ;
2006-03-25 15:19:53 +03:00
for ( i = 0 ; i < 250 ; i + + )
2005-04-17 02:20:36 +04:00
{
mdelay ( 1 ) ;
stat = parport_read_status ( qcam - > pport ) ;
2006-03-25 15:19:53 +03:00
if ( ostat ! = stat )
2005-04-17 02:20:36 +04:00
{
if ( + + count > = 3 ) return 1 ;
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 ) ;
2006-03-25 15:19:53 +03:00
for ( i = 0 ; i < 250 ; i + + )
2005-04-17 02:20:36 +04:00
{
mdelay ( 1 ) ;
stat = parport_read_status ( qcam - > pport ) ;
2006-03-25 15:19:53 +03:00
if ( ostat ! = stat )
2005-04-17 02:20:36 +04:00
{
if ( + + count > = 3 ) return 1 ;
ostat = stat ;
}
}
/* no (or flatline) camera, give up */
return 0 ;
}
static void qc_reset ( struct qcam_device * qcam )
{
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 . */
static void qc_setup ( struct qcam_device * q )
{
qc_reset ( q ) ;
/* Set the brightness. */
2006-03-25 15:19:53 +03:00
qcam_set ( q , 11 , q - > 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 . */
qcam_set ( q , 17 , q - > ccd_height ) ;
qcam_set ( q , 19 , q - > ccd_width / 2 ) ;
/* Set top and left. */
qcam_set ( q , 0xd , q - > top ) ;
qcam_set ( q , 0xf , q - > left ) ;
/* Set contrast and white balance. */
qcam_set ( q , 0x19 , q - > contrast ) ;
qcam_set ( q , 0x1f , q - > whitebal ) ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
/* Set the speed. */
qcam_set ( q , 45 , 2 ) ;
}
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 . */
static unsigned int qcam_read_bytes ( struct qcam_device * q , unsigned char * buf , unsigned int nbytes )
{
unsigned int bytes = 0 ;
qcam_set_ack ( q , 0 ) ;
if ( q - > bidirectional )
{
/* It's a bidirectional port */
while ( bytes < nbytes )
{
unsigned int lo1 , hi1 , lo2 , hi2 ;
unsigned char r , g , b ;
if ( qcam_await_ready2 ( q , 1 ) ) return bytes ;
lo1 = parport_read_data ( q - > pport ) > > 1 ;
hi1 = ( ( parport_read_status ( q - > pport ) > > 3 ) & 0x1f ) ^ 0x10 ;
qcam_set_ack ( q , 1 ) ;
if ( qcam_await_ready2 ( q , 0 ) ) return bytes ;
lo2 = parport_read_data ( q - > pport ) > > 1 ;
hi2 = ( ( parport_read_status ( q - > pport ) > > 3 ) & 0x1f ) ^ 0x10 ;
qcam_set_ack ( q , 0 ) ;
r = ( lo1 | ( ( hi1 & 1 ) < < 7 ) ) ;
g = ( ( hi1 & 0x1e ) < < 3 ) | ( ( hi2 & 0x1e ) > > 1 ) ;
b = ( lo2 | ( ( hi2 & 1 ) < < 7 ) ) ;
if ( force_rgb ) {
buf [ bytes + + ] = r ;
buf [ bytes + + ] = g ;
buf [ bytes + + ] = b ;
} else {
buf [ bytes + + ] = b ;
buf [ bytes + + ] = g ;
buf [ bytes + + ] = r ;
}
}
}
else
{
/* It's a unidirectional port */
int i = 0 , n = bytes ;
unsigned char rgb [ 3 ] ;
while ( bytes < nbytes )
{
unsigned int hi , lo ;
if ( qcam_await_ready1 ( q , 1 ) ) return bytes ;
hi = ( parport_read_status ( q - > pport ) & 0xf0 ) ;
qcam_set_ack ( q , 1 ) ;
if ( qcam_await_ready1 ( q , 0 ) ) return bytes ;
lo = ( parport_read_status ( q - > pport ) & 0xf0 ) ;
qcam_set_ack ( q , 0 ) ;
/* 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
static long qc_capture ( struct qcam_device * q , char __user * buf , unsigned long len )
{
unsigned lines , pixelsperline , bitsperxfer ;
unsigned int is_bi_dir = q - > bidirectional ;
size_t wantlen , outptr = 0 ;
char tmpbuf [ BUFSZ ] ;
if ( ! access_ok ( VERIFY_WRITE , buf , len ) )
return - EFAULT ;
/* Wait for camera to become ready */
for ( ; ; )
{
int i = qcam_get ( q , 41 ) ;
if ( i = = - 1 ) {
qc_setup ( q ) ;
return - EIO ;
}
if ( ( i & 0x80 ) = = 0 )
break ;
else
schedule ( ) ;
}
if ( qcam_set ( q , 7 , ( q - > mode | ( is_bi_dir ? 1 : 0 ) ) + 1 ) )
return - EIO ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
lines = q - > height ;
pixelsperline = q - > width ;
bitsperxfer = ( is_bi_dir ) ? 24 : 8 ;
if ( is_bi_dir )
{
/* Turn the port around */
parport_data_reverse ( q - > pport ) ;
mdelay ( 3 ) ;
qcam_set_ack ( q , 0 ) ;
if ( qcam_await_ready1 ( q , 1 ) ) {
qc_setup ( q ) ;
return - EIO ;
}
qcam_set_ack ( q , 1 ) ;
if ( qcam_await_ready1 ( q , 0 ) ) {
qc_setup ( q ) ;
return - EIO ;
}
}
wantlen = lines * pixelsperline * 24 / 8 ;
while ( wantlen )
{
size_t t , s ;
s = ( wantlen > BUFSZ ) ? BUFSZ : wantlen ;
t = qcam_read_bytes ( q , tmpbuf , s ) ;
if ( outptr < len )
{
size_t sz = len - outptr ;
if ( sz > t ) sz = t ;
if ( __copy_to_user ( buf + outptr , tmpbuf , sz ) )
break ;
outptr + = sz ;
}
wantlen - = t ;
if ( t < s )
break ;
cond_resched ( ) ;
}
len = outptr ;
if ( wantlen )
{
printk ( " qcam: short read. \n " ) ;
if ( is_bi_dir )
parport_data_forward ( q - > pport ) ;
qc_setup ( q ) ;
return len ;
}
if ( is_bi_dir )
{
int l ;
do {
l = qcam_read_bytes ( q , tmpbuf , 3 ) ;
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 )
printk ( " qcam: bad EOF \n " ) ;
} else {
if ( tmpbuf [ 0 ] ! = 0xf | | tmpbuf [ 1 ] ! = 0x0 | | tmpbuf [ 2 ] ! = 0xe )
printk ( " qcam: bad EOF \n " ) ;
}
qcam_set_ack ( q , 0 ) ;
if ( qcam_await_ready1 ( q , 1 ) )
{
printk ( " qcam: no ack after EOF \n " ) ;
parport_data_forward ( q - > pport ) ;
qc_setup ( q ) ;
return len ;
}
parport_data_forward ( q - > pport ) ;
mdelay ( 3 ) ;
qcam_set_ack ( q , 1 ) ;
if ( qcam_await_ready1 ( q , 0 ) )
{
printk ( " qcam: no ack to port turnaround \n " ) ;
qc_setup ( q ) ;
return len ;
}
}
else
{
int l ;
do {
l = qcam_read_bytes ( q , tmpbuf , 1 ) ;
cond_resched ( ) ;
} while ( l & & tmpbuf [ 0 ] = = 0x7e ) ;
l = qcam_read_bytes ( q , tmpbuf + 1 , 2 ) ;
if ( force_rgb ) {
if ( tmpbuf [ 0 ] ! = 0xe | | tmpbuf [ 1 ] ! = 0x0 | | tmpbuf [ 2 ] ! = 0xf )
printk ( " qcam: bad EOF \n " ) ;
} else {
if ( tmpbuf [ 0 ] ! = 0xf | | tmpbuf [ 1 ] ! = 0x0 | | tmpbuf [ 2 ] ! = 0xe )
printk ( " qcam: bad EOF \n " ) ;
}
}
qcam_write_data ( q , 0 ) ;
return len ;
}
/*
* Video4linux interfacing
*/
static int qcam_do_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , void * arg )
{
struct video_device * dev = video_devdata ( file ) ;
struct qcam_device * qcam = ( struct qcam_device * ) dev ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
switch ( cmd )
{
case VIDIOCGCAP :
{
struct video_capability * b = arg ;
strcpy ( b - > name , " Quickcam " ) ;
b - > type = VID_TYPE_CAPTURE | VID_TYPE_SCALES ;
b - > channels = 1 ;
b - > audios = 0 ;
b - > maxwidth = 320 ;
b - > maxheight = 240 ;
b - > minwidth = 80 ;
b - > minheight = 60 ;
return 0 ;
}
case VIDIOCGCHAN :
{
struct video_channel * v = arg ;
if ( v - > channel ! = 0 )
return - EINVAL ;
v - > flags = 0 ;
v - > tuners = 0 ;
/* Good question.. its composite or SVHS so.. */
v - > type = VIDEO_TYPE_CAMERA ;
strcpy ( v - > name , " Camera " ) ;
return 0 ;
}
case VIDIOCSCHAN :
{
struct video_channel * v = arg ;
if ( v - > channel ! = 0 )
return - EINVAL ;
return 0 ;
}
case VIDIOCGTUNER :
{
struct video_tuner * v = arg ;
if ( v - > tuner )
return - EINVAL ;
memset ( v , 0 , sizeof ( * v ) ) ;
strcpy ( v - > name , " Format " ) ;
v - > mode = VIDEO_MODE_AUTO ;
return 0 ;
}
case VIDIOCSTUNER :
{
struct video_tuner * v = arg ;
if ( v - > tuner )
return - EINVAL ;
if ( v - > mode ! = VIDEO_MODE_AUTO )
return - EINVAL ;
return 0 ;
}
case VIDIOCGPICT :
{
struct video_picture * p = arg ;
p - > colour = 0x8000 ;
p - > hue = 0x8000 ;
p - > brightness = qcam - > brightness < < 8 ;
p - > contrast = qcam - > contrast < < 8 ;
p - > whiteness = qcam - > whitebal < < 8 ;
p - > depth = 24 ;
p - > palette = VIDEO_PALETTE_RGB24 ;
return 0 ;
}
case VIDIOCSPICT :
{
struct video_picture * p = arg ;
/*
* Sanity check args
*/
if ( p - > depth ! = 24 | | p - > palette ! = VIDEO_PALETTE_RGB24 )
return - EINVAL ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
/*
* Now load the camera .
*/
qcam - > brightness = p - > brightness > > 8 ;
qcam - > contrast = p - > contrast > > 8 ;
qcam - > whitebal = p - > whiteness > > 8 ;
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 ) ;
2006-03-25 15:19:53 +03:00
qc_setup ( qcam ) ;
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 0 ;
}
case VIDIOCSWIN :
{
struct video_window * vw = arg ;
if ( vw - > flags )
return - EINVAL ;
if ( vw - > clipcount )
return - EINVAL ;
if ( vw - > height < 60 | | vw - > height > 240 )
return - EINVAL ;
if ( vw - > width < 80 | | vw - > width > 320 )
return - EINVAL ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
qcam - > width = 80 ;
qcam - > height = 60 ;
qcam - > mode = QC_DECIMATION_4 ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
if ( vw - > width > = 160 & & vw - > height > = 120 )
{
qcam - > width = 160 ;
qcam - > height = 120 ;
qcam - > mode = QC_DECIMATION_2 ;
}
if ( vw - > width > = 320 & & vw - > height > = 240 )
{
qcam - > width = 320 ;
qcam - > height = 240 ;
qcam - > mode = QC_DECIMATION_1 ;
}
qcam - > mode | = QC_MILLIONS ;
#if 0
if ( vw - > width > = 640 & & vw - > height > = 480 )
{
qcam - > width = 640 ;
qcam - > height = 480 ;
qcam - > mode = QC_BILLIONS | QC_DECIMATION_1 ;
}
# endif
2006-03-25 15:19:53 +03:00
/* Ok we figured out what to use from our
2005-04-17 02:20:36 +04:00
wide choice */
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 ) ;
qc_setup ( qcam ) ;
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 0 ;
}
case VIDIOCGWIN :
{
struct video_window * vw = arg ;
memset ( vw , 0 , sizeof ( * vw ) ) ;
vw - > width = qcam - > width ;
vw - > height = qcam - > height ;
return 0 ;
}
case VIDIOCKEY :
return 0 ;
case VIDIOCCAPTURE :
case VIDIOCGFBUF :
case VIDIOCSFBUF :
case VIDIOCGFREQ :
case VIDIOCSFREQ :
case VIDIOCGAUDIO :
case VIDIOCSAUDIO :
return - EINVAL ;
default :
return - ENOIOCTLCMD ;
}
return 0 ;
}
static int qcam_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
return video_usercopy ( inode , file , cmd , arg , qcam_do_ioctl ) ;
}
static ssize_t qcam_read ( struct file * file , char __user * buf ,
size_t count , loff_t * ppos )
{
struct video_device * v = video_devdata ( file ) ;
struct qcam_device * qcam = ( struct qcam_device * ) v ;
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 */
2006-03-25 15:19:53 +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 ;
}
2008-08-23 12:31:47 +04:00
static int qcam_exclusive_open ( struct inode * inode , struct file * file )
{
struct video_device * dev = video_devdata ( file ) ;
struct qcam_device * qcam = ( struct qcam_device * ) dev ;
return test_and_set_bit ( 0 , & qcam - > in_use ) ? - EBUSY : 0 ;
}
static int qcam_exclusive_release ( struct inode * inode , struct file * file )
{
struct video_device * dev = video_devdata ( file ) ;
struct qcam_device * qcam = ( struct qcam_device * ) dev ;
clear_bit ( 0 , & qcam - > in_use ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
/* video device template */
2007-02-12 11:55:33 +03:00
static const struct file_operations qcam_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
2008-08-23 12:31:47 +04:00
. open = qcam_exclusive_open ,
. release = qcam_exclusive_release ,
2005-04-17 02:20:36 +04:00
. ioctl = qcam_ioctl ,
2008-04-22 21:46:11 +04:00
# ifdef CONFIG_COMPAT
2006-01-09 20:24:57 +03:00
. compat_ioctl = v4l_compat_ioctl32 ,
2008-04-22 21:46:11 +04:00
# endif
2005-04-17 02:20:36 +04:00
. read = qcam_read ,
. llseek = no_llseek ,
} ;
static struct video_device qcam_template =
{
. name = " Colour QuickCam " ,
. fops = & qcam_fops ,
2008-08-23 13:23:55 +04:00
. release = video_device_release_empty ,
2005-04-17 02:20:36 +04:00
} ;
/* Initialize the QuickCam driver control structure. */
static struct qcam_device * qcam_init ( struct parport * port )
{
struct qcam_device * q ;
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
q = kmalloc ( sizeof ( struct qcam_device ) , GFP_KERNEL ) ;
if ( q = = NULL )
return NULL ;
q - > pport = port ;
q - > pdev = parport_register_device ( port , " c-qcam " , NULL , NULL ,
NULL , 0 , NULL ) ;
q - > bidirectional = ( q - > pport - > modes & PARPORT_MODE_TRISTATE ) ? 1 : 0 ;
2006-03-25 15:19:53 +03:00
if ( q - > pdev = = NULL )
2005-04-17 02:20:36 +04:00
{
printk ( KERN_ERR " c-qcam: couldn't register for %s. \n " ,
port - > name ) ;
kfree ( q ) ;
return NULL ;
}
2006-03-25 15:19:53 +03:00
2005-04-17 02:20:36 +04:00
memcpy ( & q - > vdev , & qcam_template , sizeof ( qcam_template ) ) ;
2006-02-07 11:49:14 +03:00
mutex_init ( & q - > lock ) ;
2005-04-17 02:20:36 +04:00
q - > width = q - > ccd_width = 320 ;
q - > height = q - > ccd_height = 240 ;
q - > mode = QC_MILLIONS | QC_DECIMATION_1 ;
q - > contrast = 192 ;
q - > brightness = 240 ;
q - > whitebal = 128 ;
q - > top = 1 ;
q - > left = 14 ;
return q ;
}
static struct qcam_device * qcams [ MAX_CAMS ] ;
2008-04-22 21:41:48 +04:00
static unsigned int num_cams ;
2005-04-17 02:20:36 +04:00
static int init_cqcam ( struct parport * port )
{
struct qcam_device * qcam ;
if ( parport [ 0 ] ! = - 1 )
{
/* The user gave specific instructions */
int i , found = 0 ;
for ( i = 0 ; i < MAX_CAMS & & parport [ i ] ! = - 1 ; i + + )
{
if ( parport [ 0 ] = = port - > number )
found = 1 ;
}
if ( ! found )
return - ENODEV ;
}
if ( num_cams = = MAX_CAMS )
return - ENOSPC ;
qcam = qcam_init ( port ) ;
if ( qcam = = NULL )
return - ENODEV ;
2006-03-25 15:19:53 +03:00
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
2005-04-17 02:20:36 +04:00
if ( probe & & qc_detect ( qcam ) = = 0 )
{
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 ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " Unable to register Colour QuickCam on %s \n " ,
qcam - > pport - > name ) ;
parport_unregister_device ( qcam - > pdev ) ;
kfree ( qcam ) ;
return - ENODEV ;
}
2006-03-25 15:19:53 +03:00
printk ( KERN_INFO " video%d: Colour QuickCam found on %s \n " ,
2008-10-20 01:54:26 +04:00
qcam - > vdev . num , 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 ;
}
static void close_cqcam ( struct qcam_device * qcam )
{
video_unregister_device ( & qcam - > vdev ) ;
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 ,
} ;
static int __init cqcam_init ( void )
{
printk ( BANNER " \n " ) ;
return parport_register_driver ( & cqcam_driver ) ;
}
static void __exit cqcam_cleanup ( void )
{
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 " ) ;
/* 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 ) ;
module_init ( cqcam_init ) ;
module_exit ( cqcam_cleanup ) ;