/* * cpia_pp CPiA Parallel Port driver * * Supports CPiA based parallel port Video Camera's. * * (C) Copyright 1999 Bas Huisman * (C) Copyright 1999-2000 Scott J. Bertin , * (C) Copyright 1999-2000 Peter Pregler * * 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. */ #include #include #include #include #include #include #include #include #include #include /* #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" /* IEEE 1284 Compatiblity Mode signal names */ #define nStrobe PARPORT_CONTROL_STROBE /* inverted */ #define nAutoFd PARPORT_CONTROL_AUTOFD /* inverted */ #define nInit PARPORT_CONTROL_INIT #define nSelectIn PARPORT_CONTROL_SELECT #define IntrEnable PARPORT_CONTROL_INTEN /* normally zero for no IRQ */ #define DirBit PARPORT_CONTROL_DIRECTION /* 0 = Forward, 1 = Reverse */ #define nFault PARPORT_STATUS_ERROR #define Select PARPORT_STATUS_SELECT #define PError PARPORT_STATUS_PAPEROUT #define nAck PARPORT_STATUS_ACK #define Busy PARPORT_STATUS_BUSY /* inverted */ /* some more */ #define HostClk nStrobe #define HostAck nAutoFd #define nReverseRequest nInit #define Active_1284 nSelectIn #define nPeriphRequest nFault #define XFlag Select #define nAckReverse PError #define PeriphClk nAck #define PeriphAck Busy /* these can be used to correct for the inversion on some bits */ #define STATUS_INVERSION_MASK (Busy) #define CONTROL_INVERSION_MASK (nStrobe|nAutoFd|nSelectIn) #define ECR_empty 0x01 #define ECR_full 0x02 #define ECR_serviceIntr 0x04 #define ECR_dmaEn 0x08 #define ECR_nErrIntrEn 0x10 #define ECR_mode_mask 0xE0 #define ECR_SPP_mode 0x00 #define ECR_PS2_mode 0x20 #define ECR_FIFO_mode 0x40 #define ECR_ECP_mode 0x60 #define ECP_FIFO_SIZE 16 #define DMA_BUFFER_SIZE PAGE_SIZE /* for 16bit DMA make sure DMA_BUFFER_SIZE is 16 bit aligned */ #define PARPORT_CHUNK_SIZE PAGE_SIZE/* >=2.3.x */ /* we read this many bytes at once */ #define GetECRMasked(port,mask) (parport_read_econtrol(port) & (mask)) #define GetStatus(port) ((parport_read_status(port)^STATUS_INVERSION_MASK)&(0xf8)) #define SetStatus(port,val) parport_write_status(port,(val)^STATUS_INVERSION_MASK) #define GetControl(port) ((parport_read_control(port)^CONTROL_INVERSION_MASK)&(0x3f)) #define SetControl(port,val) parport_write_control(port,(val)^CONTROL_INVERSION_MASK) #define GetStatusMasked(port,mask) (GetStatus(port) & (mask)) #define GetControlMasked(port,mask) (GetControl(port) & (mask)) #define SetControlMasked(port,mask) SetControl(port,GetControl(port) | (mask)); #define ClearControlMasked(port,mask) SetControl(port,GetControl(port)&~(mask)); #define FrobControlBit(port,mask,value) SetControl(port,(GetControl(port)&~(mask))|((value)&(mask))); #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 & Peter Pregler "); MODULE_DESCRIPTION("Parallel port driver for Vision CPiA based cameras"); MODULE_LICENSE("GPL"); MODULE_PARM(parport, "1-" __MODULE_STRING(PARPORT_MAX) "s"); 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 tq_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 }; static struct cam_data *cam_list; static spinlock_t cam_list_lock_pp; #ifdef _CPIA_DEBUG_ #define DEB_PORT(port) { \ u8 controll = GetControl(port); \ u8 statusss = GetStatus(port); \ DBG("nsel %c per %c naut %c nstrob %c nak %c busy %c nfaul %c sel %c init %c dir %c\n",\ ((controll & nSelectIn) ? 'U' : 'D'), \ ((statusss & PError) ? 'U' : 'D'), \ ((controll & nAutoFd) ? 'U' : 'D'), \ ((controll & nStrobe) ? 'U' : 'D'), \ ((statusss & nAck) ? 'U' : 'D'), \ ((statusss & Busy) ? 'U' : 'D'), \ ((statusss & nFault) ? 'U' : 'D'), \ ((statusss & Select) ? 'U' : 'D'), \ ((controll & nInit) ? 'U' : 'D'), \ ((controll & DirBit) ? 'R' : 'F') \ ); } #else #define DEB_PORT(port) {} #endif #define WHILE_OUT_TIMEOUT (HZ/10) #define DMA_TIMEOUT 10*HZ /* 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; } /**************************************************************************** * * 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; /* After some commands the camera needs extra time before * it will respond again, so we try up to 3 times */ for(retry=0; retry<3; ++retry) { if(!parport_negotiate(cam->port, IEEE1284_MODE_ECP)) { break; } } if(retry == 3) { DBG("Unable to negotiate ECP mode\n"); return -1; } return 0; } /**************************************************************************** * * ReverseSetup * ***************************************************************************/ static int ReverseSetup(struct pp_cam_entry *cam, int extensibility) { int retry; int mode = IEEE1284_MODE_ECP; if(extensibility) mode = 8|3|IEEE1284_EXT_LINK; /* After some commands the camera needs extra time before * it will respond again, so we try up to 3 times */ for(retry=0; retry<3; ++retry) { if(!parport_negotiate(cam->port, mode)) { break; } } if(retry == 3) { if(extensibility) DBG("Unable to negotiate extensibility mode\n"); else DBG("Unable to negotiate ECP mode\n"); return -1; } if(extensibility) cam->port->ieee1284.mode = IEEE1284_MODE_ECP; 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; } 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; } static int cpia_pp_read(struct parport *port, u8 *buffer, int len) { int bytes_read, new_bytes; for(bytes_read=0; bytes_readstreaming) 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 ) { if(current->need_resched) schedule(); new_bytes = cpia_pp_read(cam->port, buffer, block_size ); if( new_bytes <= 0 ) { break; } i=-1; while(++iimage_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) { return err; DBG("Error reading command result\n"); } 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) { cam->cb_task.routine = cb; cam->cb_task.data = 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_ECP) && !(port->modes & PARPORT_MODE_TRISTATE)) { LOG("port is not ECP capable\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 ); cpia_add_to_list(cam_list, cpia); spin_unlock( &cam_list_lock_pp ); return 0; } static void cpia_pp_detach (struct parport *port) { struct cam_data *cpia; spin_lock( &cam_list_lock_pp ); for(cpia = cam_list; cpia != NULL; cpia = cpia->next) { struct pp_cam_entry *cam = cpia->lowlevel_data; if (cam && cam->port->number == port->number) { cpia_remove_from_list(cpia); spin_unlock( &cam_list_lock_pp ); cpia_unregister_camera(cpia); if(cam->open_count > 0) { cpia_pp_close(cam); } parport_unregister_device(cam->pdev); kfree(cam); cpia->lowlevel_data = NULL; break; } } } 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 = { "cpia_pp", cpia_pp_attach, cpia_pp_detach, NULL }; 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; } if (parport_register_driver (&cpia_pp_driver)) { LOG ("unable to register with parport\n"); return -EIO; } cam_list = NULL; spin_lock_init( &cam_list_lock_pp ); 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; } } } } } #if defined(CONFIG_KMOD) && defined(CONFIG_PNP_PARPORT_MODULE) if(parport_enumerate() && !parport_enumerate()->probe_info.model) { request_module("parport_probe"); } #endif 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 0 /* Is this only a 2.2ism? -jerdfelt */ if (!str) { if (ints[0] == 0 || ints[1] == 0) { /* disable driver on "cpia_pp=" or "cpia_pp=0" */ parport_nr[0] = PPCPIA_PARPORT_OFF; } } else #endif 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 */