/** @file u12-io.c * @brief The I/O functions to the U12 backend stuff. * * Copyright (c) 2003-2004 Gerhard Jaeger * GeneSys Logic I/O stuff derived from canon630u-common.c which has * been written by Nathan Rutman * * History: * - 0.01 - initial version * - 0.02 - changed u12io_GetFifoLength() behaviour * - added delays to reset function * . *
* This file is part of the SANE package. * * 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., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. * * As a special exception, the authors of SANE give permission for * additional uses of the libraries contained in this release of SANE. * * The exception is that, if you link a SANE library with other files * to produce an executable, this does not by itself cause the * resulting executable to be covered by the GNU General Public * License. Your use of that executable is in no way restricted on * account of linking the SANE library code into it. * * This exception does not, however, invalidate any other reasons why * the executable file might be covered by the GNU General Public * License. * * If you submit changes to SANE to the maintainers to be included in * a subsequent release, you agree by submitting the changes that * those changes may be distributed with this exception intact. * * If you write modifications of your own for SANE, it is your choice * whether to permit this exception to apply to your modifications. * If you do not wish that, delete this exception notice. *
*/ /** Format: * cacheLen[0] = ASIC-ID * cacheLen[1] = SCANSTATE ? * cacheLen[2] = REG-STATUS ? * cacheLen[3] = ?? * cacheLen[4] = FIFO-LEN (RED) HiByte LW * cacheLen[5] = FIFO-LEN (RED) LoByte LW * cacheLen[6] = FIFO-LEN (RED) LoByte HW * cacheLen[7] = FIFO-LEN (GREEN) HiByte LW * cacheLen[8] = FIFO-LEN (GREEN) LoByte LW * cacheLen[9] = FIFO-LEN (GREEN) LoByte HW * cacheLen[10] = FIFO-LEN (BLUE) HiByte LW * cacheLen[11] = FIFO-LEN (BLUE) LoByte LW * cacheLen[12] = FIFO-LEN (BLUE) LoByte HW */ static SANE_Byte cacheLen[13]; /** This function is used to detect a cancel condition, * our ESC key is the SIGUSR1 signal. It is sent by the backend when the * cancel button has been pressed * * @param - none * @return the function returns SANE_TRUE if a cancel condition has been * detected, if not, it returns SANE_FALSE */ static SANE_Bool u12io_IsEscPressed( void ) { sigset_t sigs; sigpending( &sigs ); if( sigismember( &sigs, SIGUSR1 )) { DBG( _DBG_INFO, "SIGUSR1 is pending --> Cancel detected\n" ); return SANE_TRUE; } return SANE_FALSE; } /** fall asleep for some micro-seconds... */ static void u12io_udelay( unsigned long usec ) { struct timeval now, deadline; if( usec == 0 ) return; gettimeofday( &deadline, NULL ); deadline.tv_usec += usec; deadline.tv_sec += deadline.tv_usec / 1000000; deadline.tv_usec %= 1000000; do { gettimeofday( &now, NULL ); } while ((now.tv_sec < deadline.tv_sec) || (now.tv_sec == deadline.tv_sec && now.tv_usec < deadline.tv_usec)); } /** Initializes a timer. * @param timer - pointer to the timer to start * @param us - timeout value in micro-seconds */ static void u12io_StartTimer( TimerDef *timer , unsigned long us ) { struct timeval start_time; gettimeofday( &start_time, NULL ); *timer = start_time.tv_sec * 1e6 + start_time.tv_usec + us; } /** Checks if a timer has been expired or not. * @param timer - pointer to the timer to check * @return Function returns SANE_TRUE when the timer has been expired, * otherwise SANE_FALSE */ static SANE_Bool u12io_CheckTimer( TimerDef *timer ) { struct timeval current_time; gettimeofday(¤t_time, NULL); if((current_time.tv_sec * 1e6 + current_time.tv_usec) > *timer ) return SANE_TRUE; return SANE_FALSE; } /* GL640 communication functions for Genesys Logic GL640USB * USB-IEEE1284 parallel port bridge */ /* Assign status and verify a good return code */ #define CHK(A) {if( (status = A) != SANE_STATUS_GOOD ) { \ DBG( _DBG_ERROR, "Failure on line of %s: %d\n", __FILE__, \ __LINE__ ); return A; }} /** Register codes for the bridge. These are NOT the registers for the ASIC * on the other side of the bridge. */ typedef enum { GL640_BULK_SETUP = 0x82, GL640_EPP_ADDR = 0x83, GL640_EPP_DATA_READ = 0x84, GL640_EPP_DATA_WRITE = 0x85, GL640_SPP_STATUS = 0x86, GL640_SPP_CONTROL = 0x87, GL640_SPP_DATA = 0x88, GL640_GPIO_OE = 0x89, GL640_GPIO_READ = 0x8a, GL640_GPIO_WRITE = 0x8b } GL640_Request; /** for setting up bulk transfers */ static SANE_Byte bulk_setup_data[] = { 0, 0x11, 0, 0, 0, 0, 0, 0 }; /** Write to the usb-parallel port bridge. */ static SANE_Status gl640WriteControl(int fd, GL640_Request req, u_char * data, unsigned int size) { SANE_Status status; status = sanei_usb_control_msg( fd, /* rqttype */ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT /*0x40 */ , /* rqt */ (size > 1) ? 0x04 : 0x0C, /* val */ (SANE_Int) req, /* ind */ 0, /* len */ size, /* dat */ data); if( status != SANE_STATUS_GOOD ) { DBG( _DBG_ERROR, "gl640WriteControl error\n"); } return status; } /** Read from the usb-parallel port bridge. */ static SANE_Status gl640ReadControl( int fd, GL640_Request req, u_char *data, unsigned int size ) { SANE_Status status; status = sanei_usb_control_msg( fd, /* rqttype */ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN /*0xc0 */ , /* rqt */ (size > 1) ? 0x04 : 0x0C, /* val */ (SANE_Int) req, /* ind */ 0, /* len */ size, /* dat */ data); if( status != SANE_STATUS_GOOD ) { DBG( _DBG_ERROR, "gl640ReadControl error\n"); } return status; } /** Wrappers to write a single byte to the bridge */ static inline SANE_Status gl640WriteReq( int fd, GL640_Request req, u_char data ) { return gl640WriteControl( fd, req, &data, 1); } /** Wrappers to read a single byte from the bridge */ static inline SANE_Status gl640ReadReq( int fd, GL640_Request req, u_char *data ) { return gl640ReadControl( fd, req, data, 1 ); } /** Write USB bulk data * setup is an apparently scanner-specific sequence: * {(0=read, 1=write), 0x00, 0x00, 0x00, sizelo, sizehi, 0x00, 0x00} * setup[1] = 0x11 --> data to register * setup[1] = 0x01 --> data to scanner memory */ static SANE_Status gl640WriteBulk( int fd, u_char *setup, u_char *data, size_t size ) { SANE_Status status; setup[0] = 1; setup[4] = (size) & 0xFF; setup[5] = (size >> 8) & 0xFF; setup[6] = 0; CHK (gl640WriteControl (fd, GL640_BULK_SETUP, setup, 8)); status = sanei_usb_write_bulk (fd, data, &size); if( status != SANE_STATUS_GOOD ) { DBG( _DBG_ERROR, "gl640WriteBulk error\n"); } return status; } /** Read USB bulk data * setup is an apparently scanner-specific sequence: * {(0=read, 1=write), 0x00, 0x00, 0x00, sizelo, sizehi, 0x00, 0x00} * setup[1] = 0x00 --> data from scanner memory * setup[1] = 0x0c --> data from scanner fifo? */ static SANE_Status gl640ReadBulk( int fd, u_char *setup, u_char *data, size_t size, int mod ) { SANE_Byte *len_info; size_t complete, current, toget; SANE_Status status; setup[0] = 0; setup[4] = (size) & 0xFF; setup[5] = (size >> 8) & 0xFF; setup[6] = mod; CHK (gl640WriteControl (fd, GL640_BULK_SETUP, setup, 8)); len_info = NULL; toget = size; if( mod ) { toget *= mod; len_info = data + toget; toget += 13; } for( complete = 0; complete < toget; ) { current = toget - complete; status = sanei_usb_read_bulk( fd, data, ¤t ); if( status != SANE_STATUS_GOOD ) { DBG( _DBG_ERROR, "gl640ReadBulk error\n"); break; } data += current; complete += current; } if( len_info ) { memcpy( cacheLen, len_info, 13 ); } return status; } /* now the functions to access PP registers */ /** read the contents of the status register */ static SANE_Byte inb_status( int fd ) { u_char data = 0xff; gl640ReadReq( fd, GL640_SPP_STATUS, &data ); return data; } /** write a byte to the SPP data port */ static SANE_Status outb_data( int fd, u_char data ) { return gl640WriteReq( fd, GL640_SPP_DATA, data); } /** write to the parport control register */ static SANE_Status outb_ctrl( int fd, u_char data ) { return gl640WriteReq( fd, GL640_SPP_CONTROL, data); } /************************* ASIC access stuff *********************************/ /** write a register number to the ASIC */ static void u12io_RegisterToScanner( U12_Device *dev, SANE_Byte reg ) { if( dev->mode == _PP_MODE_EPP ) { gl640WriteReq( dev->fd, GL640_EPP_ADDR, reg ); } else { /* write register number to read from to SPP data-port */ outb_data( dev->fd, reg ); /* signal that to the ASIC */ outb_ctrl( dev->fd, _CTRL_SIGNAL_REGWRITE ); outb_ctrl( dev->fd, _CTRL_END_REGWRITE ); } } /** as the name says, we switch to SPP mode */ static void u12io_SwitchToSPPMode( U12_Device *dev ) { dev->mode = _PP_MODE_SPP; outb_ctrl( dev->fd, _CTRL_GENSIGNAL ); } /** as the name says, we switch to SPP mode */ static void u12io_SwitchToEPPMode( U12_Device *dev ) { u12io_RegisterToScanner( dev, REG_EPPENABLE ); dev->mode = _PP_MODE_EPP; } /** read data from SPP status port */ static SANE_Byte u12io_DataFromSPP( U12_Device *dev ) { SANE_Byte data, tmp; /* read low nibble */ tmp = inb_status( dev->fd ); outb_ctrl( dev->fd, (_CTRL_GENSIGNAL + _CTRL_STROBE)); /* read high nibble */ data = inb_status( dev->fd ); data &= 0xf0; /* combine with low nibble */ data |= (tmp >> 4); return data; } /** Read the content of specific ASIC register */ static SANE_Byte u12io_DataFromRegister( U12_Device *dev, SANE_Byte reg ) { SANE_Byte val; if( dev->mode == _PP_MODE_EPP ) { gl640WriteReq( dev->fd, GL640_EPP_ADDR, reg ); gl640ReadReq ( dev->fd, GL640_EPP_DATA_READ, &val ); } else { u12io_RegisterToScanner( dev, reg ); val = u12io_DataFromSPP( dev ); } return val; } /** */ static void u12io_CloseScanPath( U12_Device *dev ) { u12io_RegisterToScanner( dev, 0xff ); u12io_RegisterToScanner( dev, REG_SWITCHBUS ); dev->mode = _PP_MODE_SPP; } /** try to connect to scanner */ static SANE_Bool u12io_OpenScanPath( U12_Device *dev ) { u_char tmp; DBG( _DBG_IO, "u12io_OpenScanPath()\n" ); u12io_SwitchToSPPMode( dev ); outb_data( dev->fd, _ID_TO_PRINTER ); _DODELAY(20); outb_data( dev->fd, _ID1ST ); _DODELAY(5); outb_data( dev->fd, _ID2ND ); _DODELAY(5); outb_data( dev->fd, _ID3RD ); _DODELAY(5); outb_data( dev->fd, _ID4TH ); _DODELAY(5); tmp = u12io_DataFromRegister( dev, REG_ASICID ); if( ASIC_ID == tmp ) { u12io_SwitchToEPPMode( dev ); return SANE_TRUE; } DBG( _DBG_IO, "u12io_OpenScanPath() failed!\n" ); return SANE_FALSE; } /** Write data to asic (SPP mode only) */ static void u12io_DataToScanner( U12_Device *dev , SANE_Byte bValue ) { if( dev->mode != _PP_MODE_SPP ) { DBG( _DBG_ERROR, "u12io_DataToScanner() in wrong mode!\n" ); return; } /* output data */ outb_data( dev->fd, bValue ); /* notify asic there is data */ outb_ctrl( dev->fd, _CTRL_SIGNAL_DATAWRITE ); /* end write cycle */ outb_ctrl( dev->fd, _CTRL_END_DATAWRITE ); } /** Write data to specific ASIC's register */ static SANE_Status u12io_DataToRegister( U12_Device *dev, SANE_Byte reg, SANE_Byte data ) { SANE_Status status; SANE_Byte buf[2]; if( dev->mode == _PP_MODE_EPP ) { buf[0] = reg; buf[1] = data; bulk_setup_data[1] = 0x11; CHK( gl640WriteBulk ( dev->fd, bulk_setup_data, buf, 2 )); } else { u12io_RegisterToScanner( dev, reg ); u12io_DataToScanner( dev, data ); } return SANE_STATUS_GOOD; } /** Write data-buffer to specific ASIC's register * The format in the buffer is * reg(0),val(0),reg(1),val(1),..., reg(len-1),val(len-1) */ static SANE_Status u12io_DataToRegs( U12_Device *dev, SANE_Byte *buf, int len ) { SANE_Status status; if( dev->mode != _PP_MODE_EPP ) { DBG( _DBG_ERROR, "u12io_DataToRegs() in wrong mode!\n" ); return SANE_STATUS_IO_ERROR; } bulk_setup_data[1] = 0x11; CHK( gl640WriteBulk ( dev->fd, bulk_setup_data, buf, len*2 )); return SANE_STATUS_GOOD; } /** write data to the DAC */ static void u12io_DataRegisterToDAC( U12_Device *dev, SANE_Byte reg, SANE_Byte val ) { SANE_Byte buf[6]; buf[0] = REG_ADCADDR; buf[1] = reg; buf[2] = REG_ADCDATA; buf[3] = val; buf[4] = REG_ADCSERIALOUT; buf[5] = val; u12io_DataToRegs( dev, buf, 3 ); } /** write data block to scanner */ static SANE_Status u12io_MoveDataToScanner( U12_Device *dev, SANE_Byte *buf, int len ) { SANE_Status status; /* u12io_RegisterToScanner( dev, REG_INITDATAFIFO ); */ u12io_RegisterToScanner( dev, REG_WRITEDATAMODE ); bulk_setup_data[1] = 0x01; CHK( gl640WriteBulk( dev->fd, bulk_setup_data, buf, len )); bulk_setup_data[1] = 0x11; return SANE_STATUS_GOOD; } static SANE_Status u12io_ReadData( U12_Device *dev, SANE_Byte *buf, int len ) { SANE_Status status; u12io_DataToRegister( dev, REG_MODECONTROL, dev->regs.RD_ModeControl ); /* u12io_RegisterToScanner( dev, REG_INITDATAFIFO ); */ u12io_RegisterToScanner( dev, REG_READDATAMODE ); bulk_setup_data[1] = 0x00; CHK (gl640ReadBulk( dev->fd, bulk_setup_data, buf, len, 0 )); bulk_setup_data[1] = 0x11; return SANE_STATUS_GOOD; } /** perform a SW reset of ASIC P98003 */ static void u12io_SoftwareReset( U12_Device *dev ) { DBG( _DBG_INFO, "Device reset (%i)!!!\n", dev->fd ); u12io_DataToRegister( dev, REG_TESTMODE, _SW_TESTMODE ); outb_data( dev->fd, _ID_TO_PRINTER ); _DODELAY(20); outb_data( dev->fd, _RESET1ST ); _DODELAY(5); outb_data( dev->fd, _RESET2ND ); _DODELAY(5); outb_data( dev->fd, _RESET3RD ); _DODELAY(5); outb_data( dev->fd, _RESET4TH ); _DODELAY(250); } /** */ static SANE_Bool u12io_IsConnected( U12_Device *dev ) { int mode; SANE_Byte tmp; SANE_Byte buf[6]; DBG( _DBG_INFO, "u12io_IsConnected()\n" ); tmp = inb_status( dev->fd ); DBG( _DBG_INFO, "* tmp1 = 0x%02x\n", tmp ); gl640WriteReq( dev->fd, GL640_EPP_ADDR, REG_ASICID ); gl640ReadReq ( dev->fd, GL640_EPP_DATA_READ, &tmp ); DBG( _DBG_INFO, "* REG_ASICID = 0x%02x\n", tmp ); if( tmp != ASIC_ID ) { DBG( _DBG_INFO, "* Scanner is NOT connected!\n" ); /* FIXME: really needed? */ #if 1 if( dev->initialized ) { tmp = inb_status( dev->fd ); DBG( _DBG_INFO, "* tmp3 = 0x%02x\n", tmp ); gl640WriteReq( dev->fd, GL640_EPP_ADDR, REG_ASICID ); gl640ReadReq ( dev->fd, GL640_EPP_DATA_READ, &tmp ); DBG( _DBG_INFO, "* REG_ASICID = 0x%02x\n", tmp ); mode = dev->mode; dev->mode = _PP_MODE_EPP; u12io_DataToRegister( dev, REG_ADCADDR, 1 ); u12io_DataToRegister( dev, REG_ADCDATA, 0 ); u12io_DataToRegister( dev, REG_ADCSERIALOUT, 0 ); buf[0] = REG_MODECONTROL; buf[1] = 0x19; buf[2] = REG_STEPCONTROL; buf[3] = 0xff; buf[4] = REG_MOTOR0CONTROL; buf[5] = 0; u12io_DataToRegs( dev, buf, 3 ); dev->mode = mode; } #endif return SANE_FALSE; } u12io_SwitchToEPPMode( dev ); DBG( _DBG_INFO, "* Scanner is connected!\n" ); return SANE_TRUE; } /** */ static SANE_Byte u12io_GetExtendedStatus( U12_Device *dev ) { SANE_Byte b; b = u12io_DataFromRegister( dev, REG_STATUS2 ); if( b == 0xff ) return 0; return b; } /** */ static SANE_Status u12io_ReadMonoData( U12_Device *dev, SANE_Byte *buf, u_long len ) { SANE_Status status; bulk_setup_data[1] = 0x0c; bulk_setup_data[2] = ((dev->regs.RD_ModeControl >> 3) + 1); CHK (gl640ReadBulk( dev->fd, bulk_setup_data, buf, len, 1 )); bulk_setup_data[1] = 0x11; bulk_setup_data[2] = 0; return SANE_STATUS_GOOD; } /** */ static SANE_Status u12io_ReadColorData( U12_Device *dev, SANE_Byte *buf, u_long len ) { SANE_Status status; bulk_setup_data[1] = 0x0c; CHK (gl640ReadBulk( dev->fd, bulk_setup_data, buf, len, 3 )); bulk_setup_data[1] = 0x11; return SANE_STATUS_GOOD; } /** read the recent state count */ static SANE_Byte u12io_GetScanState( U12_Device *dev ) { if( cacheLen[0] == 0x83 ) { DBG( _DBG_READ, "u12io_GetScanState(cached) = 0x%02x\n", cacheLen[1] ); return cacheLen[1]; } return u12io_DataFromRegister( dev, REG_GETSCANSTATE ); } /** download a scanstate-table */ static SANE_Status u12io_DownloadScanStates( U12_Device *dev ) { SANE_Status status; TimerDef timer; u12io_RegisterToScanner( dev, REG_SCANSTATECONTROL ); bulk_setup_data[1] = 0x01; CHK( gl640WriteBulk( dev->fd, bulk_setup_data, dev->a_nbNewAdrPointer, _SCANSTATE_BYTES )); bulk_setup_data[1] = 0x11; /* FIXME: refreshState probably always FALSE */ if( dev->scan.refreshState ) { u12io_RegisterToScanner( dev, REG_REFRESHSCANSTATE ); u12io_StartTimer( &timer, (_SECOND/2)); do { if (!( u12io_GetScanState( dev ) & _SCANSTATE_STOP)) break; } while( !u12io_CheckTimer(&timer)); } return SANE_STATUS_GOOD; } /** - initializes the scan states * - sets all necessary registers * FIXME: first copy to buffer, then use u12io_DataToRegs() */ static void u12io_PutOnAllRegisters( U12_Device *dev ) { SANE_Byte *val, reg; SANE_Byte *rb, buf[100]; int c; /* setup scan states */ u12io_DownloadScanStates( dev ); c = 0; rb = buf; *(rb++) = REG_MODECONTROL; *(rb++) = dev->regs.RD_ModeControl; c++; *(rb++) = REG_STEPCONTROL; *(rb++) = dev->regs.RD_StepControl; c++; *(rb++) = REG_MOTOR0CONTROL; *(rb++) = dev->regs.RD_Motor0Control; c++; *(rb++) = REG_LINECONTROL; *(rb++) = dev->regs.RD_LineControl; c++; *(rb++) = REG_XSTEPTIME; *(rb++) = dev->regs.RD_XStepTime; c++; *(rb++) = REG_MODELCONTROL; *(rb++) = dev->regs.RD_ModelControl; c++; /* the 1st register to write */ val = (SANE_Byte*)&dev->regs.RD_Dpi; /* 0x21 - 0x28 */ for( reg = REG_DPILO; reg <= REG_THRESHOLDHI; reg++, val++ ) { *(rb++) = reg; *(rb++) = *val; c++; } u12io_DataToRegs( dev, buf, c ); u12io_RegisterToScanner( dev, REG_INITDATAFIFO ); u12io_DataToRegister( dev, REG_MODECONTROL, _ModeScan ); } /** */ static void u12io_ResetFifoLen( void ) { memset( cacheLen, 0, 13 ); } /** */ static u_long u12io_GetFifoLength( U12_Device *dev ) { SANE_Status status; size_t toget; SANE_Byte data[64]; u_long len, len_r, len_g, len_b; if( cacheLen[0] == 0x83 ) { DBG( _DBG_READ, "Using cached FIFO len\n" ); memcpy( data, cacheLen, 13 ); u12io_ResetFifoLen(); } else { memset( bulk_setup_data, 0, 8 ); bulk_setup_data[1] = 0x0c; CHK (gl640WriteControl(dev->fd, GL640_BULK_SETUP, bulk_setup_data, 8)); toget = 13; status = sanei_usb_read_bulk( dev->fd, data, &toget ); if( status != SANE_STATUS_GOOD ) { DBG( _DBG_ERROR, "ReadBulk error\n"); return SANE_FALSE; } bulk_setup_data[1] = 0x11; memcpy( cacheLen, data, 13 ); } len_r = (u_long)data[5] * 256 + (u_long)data[4]; len_g = (u_long)data[8] * 256 + (u_long)data[7]; len_b = (u_long)data[11] * 256 + (u_long)data[10]; if( dev->DataInf.wPhyDataType < COLOR_TRUE24 ) { len = len_g; } else { len = len_r; if( len_g < len ) len = len_g; if( len_b < len ) len = len_b; } DBG( _DBG_READ, "FIFO-LEN: %lu %lu %lu = %lu\n", len_r, len_g, len_b, len ); return len; } /** */ static SANE_Bool u12io_ReadOneShadingLine( U12_Device *dev, SANE_Byte *buf, u_long len ) { TimerDef timer; SANE_Status status; DBG( _DBG_READ, "u12io_ReadOneShadingLine()\n" ); u12io_StartTimer( &timer, _SECOND ); dev->scan.bFifoSelect = REG_GFIFOOFFSET; do { u12io_ResetFifoLen(); if( u12io_GetFifoLength( dev ) >= dev->regs.RD_Pixels ) { status = u12io_ReadColorData( dev, buf, len ); if( status != SANE_STATUS_GOOD ) { DBG( _DBG_ERROR, "ReadColorData error\n"); return SANE_FALSE; } DBG( _DBG_READ, "* done\n" ); return SANE_TRUE; } } while( !u12io_CheckTimer( &timer )); DBG( _DBG_ERROR, "u12io_ReadOneShadingLine() failed!\n" ); return SANE_FALSE; } /* END U12-IO.C .............................................................*/