|  | /* | 
|  | * 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 02139, 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)) { | 
|  | DBG("%s: No more nibble data (%d bytes)\n", | 
|  | port->name, i/2); | 
|  | goto end_of_data; | 
|  | } | 
|  |  | 
|  | /* 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. */ | 
|  | 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; | 
|  | } | 
|  | else | 
|  | port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL; | 
|  | } | 
|  |  | 
|  | return i/2; | 
|  | } | 
|  |  | 
|  | /* 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 40ms. (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 40ms. */ | 
|  |  | 
|  | 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 = kzalloc(sizeof(struct pp_cam_entry), GFP_KERNEL); | 
|  | if (cam == NULL) { | 
|  | LOG("failed to allocate camera structure\n"); | 
|  | return -ENOMEM; | 
|  | } | 
|  |  | 
|  | 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 */ |