2005-04-16 15:20:36 -07:00
/*
* cpia_pp CPiA Parallel Port driver
*
* Supports CPiA based parallel port Video Camera ' s .
*
* ( C ) Copyright 1999 Bas Huisman < bhuism @ cs . utwente . nl >
* ( C ) Copyright 1999 - 2000 Scott J . Bertin < sbertin @ securenym . net > ,
* ( C ) Copyright 1999 - 2000 Peter Pregler < Peter_Pregler @ email . com >
*
* 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 .
*/
/* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */
/* #define _CPIA_DEBUG_ 1 */
# include <linux/config.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/parport.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/workqueue.h>
# include <linux/smp_lock.h>
# include <linux/sched.h>
# include <linux/kmod.h>
/* #define _CPIA_DEBUG_ define for verbose debug output */
# include "cpia.h"
static int cpia_pp_open ( void * privdata ) ;
static int cpia_pp_registerCallback ( void * privdata , void ( * cb ) ( void * cbdata ) ,
void * cbdata ) ;
static int cpia_pp_transferCmd ( void * privdata , u8 * command , u8 * data ) ;
static int cpia_pp_streamStart ( void * privdata ) ;
static int cpia_pp_streamStop ( void * privdata ) ;
static int cpia_pp_streamRead ( void * privdata , u8 * buffer , int noblock ) ;
static int cpia_pp_close ( void * privdata ) ;
# define ABOUT "Parallel port driver for Vision CPiA based cameras"
# define PACKET_LENGTH 8
/* Magic numbers for defining port-device mappings */
# define PPCPIA_PARPORT_UNSPEC -4
# define PPCPIA_PARPORT_AUTO -3
# define PPCPIA_PARPORT_OFF -2
# define PPCPIA_PARPORT_NONE -1
# ifdef MODULE
static int parport_nr [ PARPORT_MAX ] = { [ 0 . . . PARPORT_MAX - 1 ] = PPCPIA_PARPORT_UNSPEC } ;
static char * parport [ PARPORT_MAX ] = { NULL , } ;
MODULE_AUTHOR ( " B. Huisman <bhuism@cs.utwente.nl> & Peter Pregler <Peter_Pregler@email.com> " ) ;
MODULE_DESCRIPTION ( " Parallel port driver for Vision CPiA based cameras " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_param_array ( parport , charp , NULL , 0 ) ;
MODULE_PARM_DESC ( parport , " 'auto' or a list of parallel port numbers. Just like lp. " ) ;
# else
static int parport_nr [ PARPORT_MAX ] __initdata =
{ [ 0 . . . PARPORT_MAX - 1 ] = PPCPIA_PARPORT_UNSPEC } ;
static int parport_ptr = 0 ;
# endif
struct pp_cam_entry {
struct pardevice * pdev ;
struct parport * port ;
struct work_struct cb_task ;
int open_count ;
wait_queue_head_t wq_stream ;
/* image state flags */
int image_ready ; /* we got an interrupt */
int image_complete ; /* we have seen 4 EOI */
int streaming ; /* we are in streaming mode */
int stream_irq ;
} ;
static struct cpia_camera_ops cpia_pp_ops =
{
cpia_pp_open ,
cpia_pp_registerCallback ,
cpia_pp_transferCmd ,
cpia_pp_streamStart ,
cpia_pp_streamStop ,
cpia_pp_streamRead ,
cpia_pp_close ,
1 ,
THIS_MODULE
} ;
static LIST_HEAD ( cam_list ) ;
static spinlock_t cam_list_lock_pp ;
/* FIXME */
static void cpia_parport_enable_irq ( struct parport * port ) {
parport_enable_irq ( port ) ;
mdelay ( 10 ) ;
return ;
}
static void cpia_parport_disable_irq ( struct parport * port ) {
parport_disable_irq ( port ) ;
mdelay ( 10 ) ;
return ;
}
/* Special CPiA PPC modes: These are invoked by using the 1284 Extensibility
* Link Flag during negotiation */
# define UPLOAD_FLAG 0x08
# define NIBBLE_TRANSFER 0x01
# define ECP_TRANSFER 0x03
# define PARPORT_CHUNK_SIZE PAGE_SIZE
/****************************************************************************
*
* CPiA - specific low - level parport functions for nibble uploads
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* CPiA nonstandard "Nibble" mode (no nDataAvail signal after each byte). */
/* The standard kernel parport_ieee1284_read_nibble() fails with the CPiA... */
static size_t cpia_read_nibble ( struct parport * port ,
void * buffer , size_t len ,
int flags )
{
/* adapted verbatim, with one change, from
parport_ieee1284_read_nibble ( ) in drivers / parport / ieee1284 - ops . c */
unsigned char * buf = buffer ;
int i ;
unsigned char byte = 0 ;
len * = 2 ; /* in nibbles */
for ( i = 0 ; i < len ; i + + ) {
unsigned char nibble ;
/* The CPiA firmware suppresses the use of nDataAvail (nFault LO)
* after every second nibble to signal that more
* data is available . ( the total number of Bytes that
* should be sent is known ; if too few are received , an error
* will be recorded after a timeout ) .
* This is incompatible with parport_ieee1284_read_nibble ( ) ,
* which expects to find nFault LO after every second nibble .
*/
/* Solution: modify cpia_read_nibble to only check for
* nDataAvail before the first nibble is sent .
*/
/* Does the error line indicate end of data? */
if ( ( ( i /*& 1*/ ) = = 0 ) & &
( parport_read_status ( port ) & PARPORT_STATUS_ERROR ) ) {
2006-01-06 00:19:44 -08:00
DBG ( " %s: No more nibble data (%d bytes) \n " ,
port - > name , i / 2 ) ;
goto end_of_data ;
2005-04-16 15:20:36 -07:00
}
/* Event 7: Set nAutoFd low. */
parport_frob_control ( port ,
PARPORT_CONTROL_AUTOFD ,
PARPORT_CONTROL_AUTOFD ) ;
/* Event 9: nAck goes low. */
port - > ieee1284 . phase = IEEE1284_PH_REV_DATA ;
if ( parport_wait_peripheral ( port ,
PARPORT_STATUS_ACK , 0 ) ) {
/* Timeout -- no more data? */
DBG ( " %s: Nibble timeout at event 9 (%d bytes) \n " ,
port - > name , i / 2 ) ;
parport_frob_control ( port , PARPORT_CONTROL_AUTOFD , 0 ) ;
break ;
}
/* Read a nibble. */
nibble = parport_read_status ( port ) > > 3 ;
nibble & = ~ 8 ;
if ( ( nibble & 0x10 ) = = 0 )
nibble | = 8 ;
nibble & = 0xf ;
/* Event 10: Set nAutoFd high. */
parport_frob_control ( port , PARPORT_CONTROL_AUTOFD , 0 ) ;
/* Event 11: nAck goes high. */
if ( parport_wait_peripheral ( port ,
PARPORT_STATUS_ACK ,
PARPORT_STATUS_ACK ) ) {
/* Timeout -- no more data? */
DBG ( " %s: Nibble timeout at event 11 \n " ,
port - > name ) ;
break ;
}
if ( i & 1 ) {
/* Second nibble */
byte | = nibble < < 4 ;
* buf + + = byte ;
} else
byte = nibble ;
}
if ( i = = len ) {
/* Read the last nibble without checking data avail. */
2006-01-06 00:19:44 -08:00
if ( parport_read_status ( port ) & PARPORT_STATUS_ERROR ) {
end_of_data :
/* Go to reverse idle phase. */
parport_frob_control ( port ,
PARPORT_CONTROL_AUTOFD ,
PARPORT_CONTROL_AUTOFD ) ;
port - > physport - > ieee1284 . phase = IEEE1284_PH_REV_IDLE ;
}
2005-04-16 15:20:36 -07:00
else
2006-01-06 00:19:44 -08:00
port - > physport - > ieee1284 . phase = IEEE1284_PH_HBUSY_DAVAIL ;
2005-04-16 15:20:36 -07:00
}
2006-01-06 00:19:44 -08:00
return i / 2 ;
2005-04-16 15:20:36 -07:00
}
/* CPiA nonstandard "Nibble Stream" mode (2 nibbles per cycle, instead of 1)
* ( See CPiA Data sheet p . 31 )
*
* " Nibble Stream " mode used by CPiA for uploads to non - ECP ports is a
* nonstandard variant of nibble mode which allows the same ( mediocre )
* data flow of 8 bits per cycle as software - enabled ECP by TRISTATE - capable
* parallel ports , but works also for non - TRISTATE - capable ports .
* ( Standard nibble mode only send 4 bits per cycle )
*
*/
static size_t cpia_read_nibble_stream ( struct parport * port ,
void * buffer , size_t len ,
int flags )
{
int i ;
unsigned char * buf = buffer ;
int endseen = 0 ;
for ( i = 0 ; i < len ; i + + ) {
unsigned char nibble [ 2 ] , byte = 0 ;
int j ;
/* Image Data is complete when 4 consecutive EOI bytes (0xff) are seen */
if ( endseen > 3 )
break ;
/* Event 7: Set nAutoFd low. */
parport_frob_control ( port ,
PARPORT_CONTROL_AUTOFD ,
PARPORT_CONTROL_AUTOFD ) ;
/* Event 9: nAck goes low. */
port - > ieee1284 . phase = IEEE1284_PH_REV_DATA ;
if ( parport_wait_peripheral ( port ,
PARPORT_STATUS_ACK , 0 ) ) {
/* Timeout -- no more data? */
DBG ( " %s: Nibble timeout at event 9 (%d bytes) \n " ,
port - > name , i / 2 ) ;
parport_frob_control ( port , PARPORT_CONTROL_AUTOFD , 0 ) ;
break ;
}
/* Read lower nibble */
nibble [ 0 ] = parport_read_status ( port ) > > 3 ;
/* Event 10: Set nAutoFd high. */
parport_frob_control ( port , PARPORT_CONTROL_AUTOFD , 0 ) ;
/* Event 11: nAck goes high. */
if ( parport_wait_peripheral ( port ,
PARPORT_STATUS_ACK ,
PARPORT_STATUS_ACK ) ) {
/* Timeout -- no more data? */
DBG ( " %s: Nibble timeout at event 11 \n " ,
port - > name ) ;
break ;
}
/* Read upper nibble */
nibble [ 1 ] = parport_read_status ( port ) > > 3 ;
/* reassemble the byte */
for ( j = 0 ; j < 2 ; j + + ) {
nibble [ j ] & = ~ 8 ;
if ( ( nibble [ j ] & 0x10 ) = = 0 )
nibble [ j ] | = 8 ;
nibble [ j ] & = 0xf ;
}
byte = ( nibble [ 0 ] | ( nibble [ 1 ] < < 4 ) ) ;
* buf + + = byte ;
if ( byte = = EOI )
endseen + + ;
else
endseen = 0 ;
}
return i ;
}
/****************************************************************************
*
* EndTransferMode
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void EndTransferMode ( struct pp_cam_entry * cam )
{
parport_negotiate ( cam - > port , IEEE1284_MODE_COMPAT ) ;
}
/****************************************************************************
*
* ForwardSetup
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int ForwardSetup ( struct pp_cam_entry * cam )
{
int retry ;
/* The CPiA uses ECP protocol for Downloads from the Host to the camera.
* This will be software - emulated if ECP hardware is not present
*/
/* the usual camera maximum response time is 10ms, but after receiving
* some commands , it needs up to 40 ms . ( Data Sheet p . 32 ) */
for ( retry = 0 ; retry < 4 ; + + retry ) {
if ( ! parport_negotiate ( cam - > port , IEEE1284_MODE_ECP ) ) {
break ;
}
mdelay ( 10 ) ;
}
if ( retry = = 4 ) {
DBG ( " Unable to negotiate IEEE1284 ECP Download mode \n " ) ;
return - 1 ;
}
return 0 ;
}
/****************************************************************************
*
* ReverseSetup
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int ReverseSetup ( struct pp_cam_entry * cam , int extensibility )
{
int retry ;
int upload_mode , mode = IEEE1284_MODE_ECP ;
int transfer_mode = ECP_TRANSFER ;
if ( ! ( cam - > port - > modes & PARPORT_MODE_ECP ) & &
! ( cam - > port - > modes & PARPORT_MODE_TRISTATE ) ) {
mode = IEEE1284_MODE_NIBBLE ;
transfer_mode = NIBBLE_TRANSFER ;
}
upload_mode = mode ;
if ( extensibility ) mode = UPLOAD_FLAG | transfer_mode | IEEE1284_EXT_LINK ;
/* the usual camera maximum response time is 10ms, but after
* receiving some commands , it needs up to 40 ms . */
for ( retry = 0 ; retry < 4 ; + + retry ) {
if ( ! parport_negotiate ( cam - > port , mode ) ) {
break ;
}
mdelay ( 10 ) ;
}
if ( retry = = 4 ) {
if ( extensibility )
DBG ( " Unable to negotiate upload extensibility mode \n " ) ;
else
DBG ( " Unable to negotiate upload mode \n " ) ;
return - 1 ;
}
if ( extensibility ) cam - > port - > ieee1284 . mode = upload_mode ;
return 0 ;
}
/****************************************************************************
*
* WritePacket
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int WritePacket ( struct pp_cam_entry * cam , const u8 * packet , size_t size )
{
int retval = 0 ;
int size_written ;
if ( packet = = NULL ) {
return - EINVAL ;
}
if ( ForwardSetup ( cam ) ) {
DBG ( " Write failed in setup \n " ) ;
return - EIO ;
}
size_written = parport_write ( cam - > port , packet , size ) ;
if ( size_written ! = size ) {
DBG ( " Write failed, wrote %d/%d \n " , size_written , size ) ;
retval = - EIO ;
}
EndTransferMode ( cam ) ;
return retval ;
}
/****************************************************************************
*
* ReadPacket
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int ReadPacket ( struct pp_cam_entry * cam , u8 * packet , size_t size )
{
int retval = 0 ;
if ( packet = = NULL ) {
return - EINVAL ;
}
if ( ReverseSetup ( cam , 0 ) ) {
return - EIO ;
}
/* support for CPiA variant nibble reads */
if ( cam - > port - > ieee1284 . mode = = IEEE1284_MODE_NIBBLE ) {
if ( cpia_read_nibble ( cam - > port , packet , size , 0 ) ! = size )
retval = - EIO ;
} else {
if ( parport_read ( cam - > port , packet , size ) ! = size )
retval = - EIO ;
}
EndTransferMode ( cam ) ;
return retval ;
}
/****************************************************************************
*
* cpia_pp_streamStart
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int cpia_pp_streamStart ( void * privdata )
{
struct pp_cam_entry * cam = privdata ;
DBG ( " \n " ) ;
cam - > streaming = 1 ;
cam - > image_ready = 0 ;
//if (ReverseSetup(cam,1)) return -EIO;
if ( cam - > stream_irq ) cpia_parport_enable_irq ( cam - > port ) ;
return 0 ;
}
/****************************************************************************
*
* cpia_pp_streamStop
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int cpia_pp_streamStop ( void * privdata )
{
struct pp_cam_entry * cam = privdata ;
DBG ( " \n " ) ;
cam - > streaming = 0 ;
cpia_parport_disable_irq ( cam - > port ) ;
//EndTransferMode(cam);
return 0 ;
}
/****************************************************************************
*
* cpia_pp_streamRead
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int cpia_pp_read ( struct parport * port , u8 * buffer , int len )
{
int bytes_read ;
/* support for CPiA variant "nibble stream" reads */
if ( port - > ieee1284 . mode = = IEEE1284_MODE_NIBBLE )
bytes_read = cpia_read_nibble_stream ( port , buffer , len , 0 ) ;
else {
int new_bytes ;
for ( bytes_read = 0 ; bytes_read < len ; bytes_read + = new_bytes ) {
new_bytes = parport_read ( port , buffer + bytes_read ,
len - bytes_read ) ;
if ( new_bytes < 0 ) break ;
}
}
return bytes_read ;
}
static int cpia_pp_streamRead ( void * privdata , u8 * buffer , int noblock )
{
struct pp_cam_entry * cam = privdata ;
int read_bytes = 0 ;
int i , endseen , block_size , new_bytes ;
if ( cam = = NULL ) {
DBG ( " Internal driver error: cam is NULL \n " ) ;
return - EINVAL ;
}
if ( buffer = = NULL ) {
DBG ( " Internal driver error: buffer is NULL \n " ) ;
return - EINVAL ;
}
//if(cam->streaming) DBG("%d / %d\n", cam->image_ready, noblock);
if ( cam - > stream_irq ) {
DBG ( " %d \n " , cam - > image_ready ) ;
cam - > image_ready - - ;
}
cam - > image_complete = 0 ;
if ( 0 /*cam->streaming*/ ) {
if ( ! cam - > image_ready ) {
if ( noblock ) return - EWOULDBLOCK ;
interruptible_sleep_on ( & cam - > wq_stream ) ;
if ( signal_pending ( current ) ) return - EINTR ;
DBG ( " %d \n " , cam - > image_ready ) ;
}
} else {
if ( ReverseSetup ( cam , 1 ) ) {
DBG ( " unable to ReverseSetup \n " ) ;
return - EIO ;
}
}
endseen = 0 ;
block_size = PARPORT_CHUNK_SIZE ;
while ( ! cam - > image_complete ) {
cond_resched ( ) ;
new_bytes = cpia_pp_read ( cam - > port , buffer , block_size ) ;
if ( new_bytes < = 0 ) {
break ;
}
i = - 1 ;
while ( + + i < new_bytes & & endseen < 4 ) {
if ( * buffer = = EOI ) {
endseen + + ;
} else {
endseen = 0 ;
}
buffer + + ;
}
read_bytes + = i ;
if ( endseen = = 4 ) {
cam - > image_complete = 1 ;
break ;
}
if ( CPIA_MAX_IMAGE_SIZE - read_bytes < = PARPORT_CHUNK_SIZE ) {
block_size = CPIA_MAX_IMAGE_SIZE - read_bytes ;
}
}
EndTransferMode ( cam ) ;
return cam - > image_complete ? read_bytes : - EIO ;
}
/****************************************************************************
*
* cpia_pp_transferCmd
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int cpia_pp_transferCmd ( void * privdata , u8 * command , u8 * data )
{
int err ;
int retval = 0 ;
int databytes ;
struct pp_cam_entry * cam = privdata ;
if ( cam = = NULL ) {
DBG ( " Internal driver error: cam is NULL \n " ) ;
return - EINVAL ;
}
if ( command = = NULL ) {
DBG ( " Internal driver error: command is NULL \n " ) ;
return - EINVAL ;
}
databytes = ( ( ( int ) command [ 7 ] ) < < 8 ) | command [ 6 ] ;
if ( ( err = WritePacket ( cam , command , PACKET_LENGTH ) ) < 0 ) {
DBG ( " Error writing command \n " ) ;
return err ;
}
if ( command [ 0 ] = = DATA_IN ) {
u8 buffer [ 8 ] ;
if ( data = = NULL ) {
DBG ( " Internal driver error: data is NULL \n " ) ;
return - EINVAL ;
}
if ( ( err = ReadPacket ( cam , buffer , 8 ) ) < 0 ) {
DBG ( " Error reading command result \n " ) ;
return err ;
}
memcpy ( data , buffer , databytes ) ;
} else if ( command [ 0 ] = = DATA_OUT ) {
if ( databytes > 0 ) {
if ( data = = NULL ) {
DBG ( " Internal driver error: data is NULL \n " ) ;
retval = - EINVAL ;
} else {
if ( ( err = WritePacket ( cam , data , databytes ) ) < 0 ) {
DBG ( " Error writing command data \n " ) ;
return err ;
}
}
}
} else {
DBG ( " Unexpected first byte of command: %x \n " , command [ 0 ] ) ;
retval = - EINVAL ;
}
return retval ;
}
/****************************************************************************
*
* cpia_pp_open
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int cpia_pp_open ( void * privdata )
{
struct pp_cam_entry * cam = ( struct pp_cam_entry * ) privdata ;
if ( cam = = NULL )
return - EINVAL ;
if ( cam - > open_count = = 0 ) {
if ( parport_claim ( cam - > pdev ) ) {
DBG ( " failed to claim the port \n " ) ;
return - EBUSY ;
}
parport_negotiate ( cam - > port , IEEE1284_MODE_COMPAT ) ;
parport_data_forward ( cam - > port ) ;
parport_write_control ( cam - > port , PARPORT_CONTROL_SELECT ) ;
udelay ( 50 ) ;
parport_write_control ( cam - > port ,
PARPORT_CONTROL_SELECT
| PARPORT_CONTROL_INIT ) ;
}
+ + cam - > open_count ;
return 0 ;
}
/****************************************************************************
*
* cpia_pp_registerCallback
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int cpia_pp_registerCallback ( void * privdata , void ( * cb ) ( void * cbdata ) , void * cbdata )
{
struct pp_cam_entry * cam = privdata ;
int retval = 0 ;
if ( cam - > port - > irq ! = PARPORT_IRQ_NONE ) {
INIT_WORK ( & cam - > cb_task , cb , cbdata ) ;
} else {
retval = - 1 ;
}
return retval ;
}
/****************************************************************************
*
* cpia_pp_close
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int cpia_pp_close ( void * privdata )
{
struct pp_cam_entry * cam = privdata ;
if ( - - cam - > open_count = = 0 ) {
parport_release ( cam - > pdev ) ;
}
return 0 ;
}
/****************************************************************************
*
* cpia_pp_register
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int cpia_pp_register ( struct parport * port )
{
struct pardevice * pdev = NULL ;
struct pp_cam_entry * cam ;
struct cam_data * cpia ;
if ( ! ( port - > modes & PARPORT_MODE_PCSPP ) ) {
LOG ( " port is not supported by CPiA driver \n " ) ;
return - ENXIO ;
}
cam = kmalloc ( sizeof ( struct pp_cam_entry ) , GFP_KERNEL ) ;
if ( cam = = NULL ) {
LOG ( " failed to allocate camera structure \n " ) ;
return - ENOMEM ;
}
memset ( cam , 0 , sizeof ( struct pp_cam_entry ) ) ;
pdev = parport_register_device ( port , " cpia_pp " , NULL , NULL ,
NULL , 0 , cam ) ;
if ( ! pdev ) {
LOG ( " failed to parport_register_device \n " ) ;
kfree ( cam ) ;
return - ENXIO ;
}
cam - > pdev = pdev ;
cam - > port = port ;
init_waitqueue_head ( & cam - > wq_stream ) ;
cam - > streaming = 0 ;
cam - > stream_irq = 0 ;
if ( ( cpia = cpia_register_camera ( & cpia_pp_ops , cam ) ) = = NULL ) {
LOG ( " failed to cpia_register_camera \n " ) ;
parport_unregister_device ( pdev ) ;
kfree ( cam ) ;
return - ENXIO ;
}
spin_lock ( & cam_list_lock_pp ) ;
list_add ( & cpia - > cam_data_list , & cam_list ) ;
spin_unlock ( & cam_list_lock_pp ) ;
return 0 ;
}
static void cpia_pp_detach ( struct parport * port )
{
struct list_head * tmp ;
struct cam_data * cpia = NULL ;
struct pp_cam_entry * cam ;
spin_lock ( & cam_list_lock_pp ) ;
list_for_each ( tmp , & cam_list ) {
cpia = list_entry ( tmp , struct cam_data , cam_data_list ) ;
cam = ( struct pp_cam_entry * ) cpia - > lowlevel_data ;
if ( cam & & cam - > port - > number = = port - > number ) {
list_del ( & cpia - > cam_data_list ) ;
break ;
}
cpia = NULL ;
}
spin_unlock ( & cam_list_lock_pp ) ;
if ( ! cpia ) {
DBG ( " cpia_pp_detach failed to find cam_data in cam_list \n " ) ;
return ;
}
cam = ( struct pp_cam_entry * ) cpia - > lowlevel_data ;
cpia_unregister_camera ( cpia ) ;
if ( cam - > open_count > 0 )
cpia_pp_close ( cam ) ;
parport_unregister_device ( cam - > pdev ) ;
cpia - > lowlevel_data = NULL ;
kfree ( cam ) ;
}
static void cpia_pp_attach ( struct parport * port )
{
unsigned int i ;
switch ( parport_nr [ 0 ] )
{
case PPCPIA_PARPORT_UNSPEC :
case PPCPIA_PARPORT_AUTO :
if ( port - > probe_info [ 0 ] . class ! = PARPORT_CLASS_MEDIA | |
port - > probe_info [ 0 ] . cmdset = = NULL | |
strncmp ( port - > probe_info [ 0 ] . cmdset , " CPIA_1 " , 6 ) ! = 0 )
return ;
cpia_pp_register ( port ) ;
break ;
default :
for ( i = 0 ; i < PARPORT_MAX ; + + i ) {
if ( port - > number = = parport_nr [ i ] ) {
cpia_pp_register ( port ) ;
break ;
}
}
break ;
}
}
static struct parport_driver cpia_pp_driver = {
. name = " cpia_pp " ,
. attach = cpia_pp_attach ,
. detach = cpia_pp_detach ,
} ;
int cpia_pp_init ( void )
{
printk ( KERN_INFO " %s v%d.%d.%d \n " , ABOUT ,
CPIA_PP_MAJ_VER , CPIA_PP_MIN_VER , CPIA_PP_PATCH_VER ) ;
if ( parport_nr [ 0 ] = = PPCPIA_PARPORT_OFF ) {
printk ( " disabled \n " ) ;
return 0 ;
}
spin_lock_init ( & cam_list_lock_pp ) ;
if ( parport_register_driver ( & cpia_pp_driver ) ) {
LOG ( " unable to register with parport \n " ) ;
return - EIO ;
}
return 0 ;
}
# ifdef MODULE
int init_module ( void )
{
if ( parport [ 0 ] ) {
/* The user gave some parameters. Let's see what they were. */
if ( ! strncmp ( parport [ 0 ] , " auto " , 4 ) ) {
parport_nr [ 0 ] = PPCPIA_PARPORT_AUTO ;
} else {
int n ;
for ( n = 0 ; n < PARPORT_MAX & & parport [ n ] ; n + + ) {
if ( ! strncmp ( parport [ n ] , " none " , 4 ) ) {
parport_nr [ n ] = PPCPIA_PARPORT_NONE ;
} else {
char * ep ;
unsigned long r = simple_strtoul ( parport [ n ] , & ep , 0 ) ;
if ( ep ! = parport [ n ] ) {
parport_nr [ n ] = r ;
} else {
LOG ( " bad port specifier `%s' \n " , parport [ n ] ) ;
return - ENODEV ;
}
}
}
}
}
return cpia_pp_init ( ) ;
}
void cleanup_module ( void )
{
parport_unregister_driver ( & cpia_pp_driver ) ;
return ;
}
# else /* !MODULE */
static int __init cpia_pp_setup ( char * str )
{
if ( ! strncmp ( str , " parport " , 7 ) ) {
int n = simple_strtoul ( str + 7 , NULL , 10 ) ;
if ( parport_ptr < PARPORT_MAX ) {
parport_nr [ parport_ptr + + ] = n ;
} else {
LOG ( " too many ports, %s ignored. \n " , str ) ;
}
} else if ( ! strcmp ( str , " auto " ) ) {
parport_nr [ 0 ] = PPCPIA_PARPORT_AUTO ;
} else if ( ! strcmp ( str , " none " ) ) {
parport_nr [ parport_ptr + + ] = PPCPIA_PARPORT_NONE ;
}
return 0 ;
}
__setup ( " cpia_pp= " , cpia_pp_setup ) ;
# endif /* !MODULE */