diff options
Diffstat (limited to 'backend/plustek-usbhw.c')
-rw-r--r-- | backend/plustek-usbhw.c | 1848 |
1 files changed, 1848 insertions, 0 deletions
diff --git a/backend/plustek-usbhw.c b/backend/plustek-usbhw.c new file mode 100644 index 0000000..a1dae68 --- /dev/null +++ b/backend/plustek-usbhw.c @@ -0,0 +1,1848 @@ +/*............................................................................. + * Project : SANE library for Plustek flatbed scanners. + *............................................................................. + */ + +/** @file plustek-usbhw.c + * @brief Functions to control the scanner hardware. + * + * Based on sources acquired from Plustek Inc.<br> + * Copyright (C) 2001-2013 Gerhard Jaeger <gerhard@gjaeger.de> + * + * History: + * - 0.40 - starting version of the USB support + * - 0.41 - added EPSON1250 specific stuff + * - added alternative usb_IsScannerReady function + * - 0.42 - added warmup stuff + * - added UMAX 3400 stuff + * - fixed problem with minimum wait time... + * - 0.43 - added usb_switchLamp for non-Plustek devices + * - 0.44 - added bStepsToReverse and active Pixelstart values + * - to resetRegister function + * - modified getLampStatus function for CIS devices + * - added usb_Wait4Warmup() + * - moved usb_IsEscPressed to this file + * - added usb_switchLampX + * - do now not reinitialized MISC I/O pins upon reset registers + * - 0.45 - added function usb_AdjustLamps() to tweak CIS lamp settings + * - fixed NULL pointer problem in lamp-off ISR + * - added usb_AdjustCISLampSettings() + * - skipping warmup for CIS devices + * - 0.46 - fixed problem in usb_GetLampStatus for CIS devices, as we + * read back reg[0x29] to wrong position + * made it compile without itimer definitions + * - 0.47 - moved usb_HostSwap() and usb_Swap() to this file. + * - fixed lampOff timer for systems w/o setitimer + * - added lamp off adjustment for CIS devices + * - 0.48 - added usb_IsCISDevice() + * - added usb_HasTPA() + * - changed usb_Wait4Warmup() + * - added usb_WaitPos() + * - added usb_FillLampRegs() - sets also PWMDutyCylce now + * - added UMAX3450 TPA autodetection + * - 0.49 - a_bRegs is now part of the device structure + * - fixed problem in backtracking, when speedup is enabled + * - added usb_UpdateButtonStatus() + * - 0.50 - added button support for Plustek/Genius devices + * - changed behaviour of usb_IsScannerReady + * - added special misc I/O setup for CIS devices (usb_ResetRegisters) + * - 0.51 - change usb_AdjustLamps() and use it now in usb_switchLamp() + * - added usb_Wait4ScanSample() and usb_InCalibrationMode() + * - tweaked EjectPaper to work correctly with the supported sheet-fed + * devices + * - fixed button handling for Plustek/Genius devices and added + * some more debug output to that code path + * - 0.52 - changed DCapsDef, lamp -> misc_io + * - hammer in output bit, when using misc io pins for lamp switching + * - increased wait time for sheet-fed scanner (needed for Q-SCAN A6, + * patch by Hiroshi Miura) + * . + * <hr> + * 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. + * <hr> + */ +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#define DEV_LampReflection 1 +#define DEV_LampTPA 2 +#define DEV_LampAll 3 +#define DEV_LampPositive 4 +#define DEV_LampNegative 5 + +#define WAIT_TIME_FOR_SCAN_SAMPLE 20 /* 20 seconds maximum wait time */ + + +/** the NatSemi 983x is a big endian chip, and the line protocol data all + * arrives big-endian. This determines if we need to swap to host-order + */ +static SANE_Bool +usb_HostSwap( void ) +{ + u_short pattern = 0xfeed; /* deadbeef */ + unsigned char *bytewise = (unsigned char *)&pattern; + + if ( bytewise[0] == 0xfe ) { + DBG( _DBG_READ, "We're big-endian! No need to swap!\n" ); + return 0; + } + DBG( _DBG_READ, "We're little-endian! NatSemi LM983x is big!\n" ); + DBG( _DBG_READ, "--> Must swap data!\n" ); + return 1; +} + +/** as the name says.. + */ +static void +usb_Swap( u_short *pw, u_long dwBytes ) +{ + for( dwBytes /= 2; dwBytes--; pw++ ) + _SWAP(((u_char*) pw)[0], ((u_char*)pw)[1]); +} + +/** + * 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 +usb_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; +} + +/** usb_GetMotorSet + * according to the model, the function returns the address of + * the corresponding entry of the Motor table + */ +static ClkMotorDef* +usb_GetMotorSet( eModelDef model ) +{ + int i; + + for( i = 0; i < MODEL_LAST; i++ ) { + if( model == Motors[i].motorModel ) { + return &(Motors[i]); + } + } + + return NULL; +} + +/** switch motor on or off + * @param handle - handle to open USB device + * @param fOn - SANE_TRUE means motor on, SANE_FALSE means motor off + * @return always SANE_TRUE + */ +static SANE_Bool +usb_MotorOn( Plustek_Device *dev, SANE_Bool fOn ) +{ + if( fOn ) + dev->usbDev.a_bRegs[0x45] |= 0x10; + else + dev->usbDev.a_bRegs[0x45] &= ~0x10; + + usbio_WriteReg( dev->fd, 0x45, dev->usbDev.a_bRegs[0x45] ); + return SANE_TRUE; +} + +/** + */ +static SANE_Bool +usb_IsCISDevice( Plustek_Device *dev ) +{ + return ( dev->usbDev.HwSetting.bReg_0x26 & _ONE_CH_COLOR ); +} + +/** + */ +static SANE_Bool +usb_IsSheetFedDevice( Plustek_Device *dev ) +{ + return ( dev->usbDev.Caps.wFlags & DEVCAPSFLAG_SheetFed ); +} + +/** + */ +static SANE_Bool +usb_InCalibrationMode( Plustek_Device *dev ) +{ + if((dev->scanning.dwFlag & SCANFLAG_Calibration) == 0) + return SANE_FALSE; + + return SANE_TRUE; +} + +/** check if scanner is ready + */ +static SANE_Bool +usb_IsScannerReady( Plustek_Device *dev ) +{ + u_char value; + double len; + long timeout; + struct timeval t; + SANE_Status res; + + /* time in s = 1000*scanner length in inches/max step speed/in */ + len = (dev->usbDev.Caps.Normal.Size.y/(double)_MEASURE_BASE) + 5; + len = (1000.0 * len)/dev->usbDev.HwSetting.dMaxMoveSpeed; + len /= 1000.0; + + /* wait at least 10 seconds... */ + if( len < 10 ) + len = 10; + + gettimeofday( &t, NULL); + timeout = t.tv_sec + len; + + do { + res = usbio_ReadReg( dev->fd, 7, &value); + if( res != SANE_STATUS_GOOD ) { + sleep(1); + } else { + if( value == 0 ) { + _UIO( usbio_ResetLM983x( dev )); + return SANE_TRUE; + } + + if((value == 0) || (value >= 0x20) || (value == 0x03)) { + + if( !usbio_WriteReg( dev->fd, 0x07, 0 )) { + DBG( _DBG_ERROR, "Scanner not ready!!!\n" ); + return SANE_FALSE; + } + else { + return SANE_TRUE; + } + } + } + gettimeofday( &t, NULL); + + } while( t.tv_sec < timeout ); + + DBG( _DBG_ERROR, "Scanner not ready!!!\n" ); + return SANE_FALSE; +} + +/** + */ +static SANE_Bool +usb_SensorAdf( Plustek_Device *dev ) +{ + u_char value; + + if( usb_IsSheetFedDevice(dev)) + return SANE_FALSE; + + usbio_ReadReg( dev->fd, 0x02, &value ); + + return (value & 0x20); +} + +/** + */ +static SANE_Bool +usb_SensorPaper( Plustek_Device *dev ) +{ + DCapsDef *sc = &dev->usbDev.Caps; + u_char val, mask = 0x02; + + usbio_ReadReg( dev->fd, 0x02, &val ); + + if( usb_IsSheetFedDevice(dev)) + mask = _GET_PAPERSENSE_PORT(sc->misc_io); + + return (val & mask); +} + +/** function for sheet-fed devices, to make sure, that there's + * something to scan + */ +static SANE_Bool +usb_Wait4ScanSample( Plustek_Device *dev ) +{ + struct timeval start_time, t2; + + if( !usb_IsSheetFedDevice(dev)) + return SANE_TRUE; + + DBG( _DBG_INFO2, "Waiting for something to scan...\n" ); + gettimeofday( &start_time, NULL ); + do { + + gettimeofday( &t2, NULL ); + if( t2.tv_sec > start_time.tv_sec + WAIT_TIME_FOR_SCAN_SAMPLE ) { + DBG( _DBG_ERROR, "Nothing to scan!!!\n" ); + return SANE_FALSE; + } + if( usb_IsEscPressed()) + return SANE_FALSE; + } + while( !usb_SensorPaper( dev )); + + /* just a little delay, to make sure the paper is taken by the scanner */ + usleep(100* 1000); + DBG( _DBG_INFO2, "... okay, scanning now!\n" ); + return SANE_TRUE; +} + +/** function to move the sensor and to speed it up to a certain speed until + * the position is reached + */ +static SANE_Bool +usb_WaitPos( Plustek_Device *dev, u_long to, SANE_Bool stay ) +{ + SANE_Bool retval; + u_char value, mclk_div, mch; + u_char r[2]; + u_short ffs, step, min_ffs; + long dwTicks; + double maxf, fac, speed; + struct timeval start_time, t2; + + HWDef *hw = &dev->usbDev.HwSetting; + u_char *regs = dev->usbDev.a_bRegs; + + /* get current master clock divider */ + usbio_ReadReg( dev->fd, 0x08, &value ); + mclk_div = (value >> 1) + 1; + + /* get current channel mode */ + usbio_ReadReg( dev->fd, 0x26, &value ); + mch = ((value & 0x07) > 1) ? 1:3; + + /* calculate the current speed */ + ffs = regs[0x48] * 256 + regs[0x49]; + speed = ((double)CRYSTAL_FREQ) /(double)((u_long)mclk_div * 32UL * + (u_long)mch * (u_long)ffs * hw->wMotorDpi); + + /* disabled ? */ + if((hw->dHighSpeed == 0.0) || (dev->adj.disableSpeedup != 0)) { + min_ffs = 0xffff; + maxf = 0.0; + if( !stay ) + return SANE_TRUE; + + } else { + min_ffs = (u_short)(CRYSTAL_FREQ /((u_long)mclk_div * 32UL * + (u_long)mch * hw->dHighSpeed * hw->wMotorDpi)); + maxf = (ffs - min_ffs)/4; + if( maxf > 100.0 ) + maxf = 100.0; + if( maxf < 5.0 ) + maxf = 5.0; + DBG( _DBG_INFO2, ">>>> CURRENT MCLK_DIV = %u\n", mclk_div ); + DBG( _DBG_INFO2, ">>>> MCH = %u\n", mch ); + DBG( _DBG_INFO2, ">>>> FFS = %u\n", ffs ); + DBG( _DBG_INFO2, ">>>> HIGH-SPEED = %.3f (%.3f)\n", + speed, hw->dHighSpeed); + DBG( _DBG_INFO2, ">>>> MIN_FFS = %u (%.3f)\n", min_ffs, maxf); + } + + gettimeofday( &start_time, NULL ); + dwTicks = start_time.tv_sec + to; + step = 1; + retval = SANE_FALSE; + + for(;;) { + + usleep( 1000 ); + _UIO( usbio_ReadReg( dev->fd, 0x07, &value )); + + if (!value) + return SANE_TRUE; + + gettimeofday(&t2, NULL); + if( t2.tv_sec > dwTicks ) + break; + + if( min_ffs != 0xffff ) { + + fac = maxf/step; + if( ffs ) { + if((u_short)fac < ffs ) { + ffs -= fac; + if( ffs < min_ffs ) + ffs = min_ffs; + } else { + if(ffs != min_ffs ) + ffs = min_ffs; + else + ffs = 0; + } + } + + if( ffs >= min_ffs ) { + if((int)fac > 25 ) + usleep( 150 * 1000 ); + + r[0] = (u_char)(ffs >> 8); + r[1] = (u_char)(ffs & 0xFF); + sanei_lm983x_write(dev->fd, 0x48, r, 2, SANE_TRUE); + if(ffs == min_ffs ) + ffs = 0; + } else { + + if( !stay ) { + retval = SANE_TRUE; + break; + } + } + step++; + } + } + return retval; +} + +/** function to move the sensor or if sheet-fed device, to move the paper. + * In moving backward-mode, the home sensor is always turned on. + * @param action - what to do + * @param steps - steps to move, based on 300dpi, 0 means move forever + */ +static SANE_Bool +usb_ModuleMove( Plustek_Device *dev, u_char action, u_long dwStep ) +{ + SANE_Bool retval, ejected; + u_char bReg2, reg7, mclk_div; + u_short wFastFeedStepSize; + double dMaxMoveSpeed; + ClkMotorDef *clk; + HWDef *hw = &dev->usbDev.HwSetting; + u_char *regs = dev->usbDev.a_bRegs; + + if( action != MOVE_ToPaperSensor && + action != MOVE_EjectAllPapers && + action != MOVE_SkipPaperSensor && + action != MOVE_ToShading && !dwStep ) { + + return SANE_TRUE; + } + + if( !usb_IsScannerReady( dev )) { + DBG( _DBG_ERROR, "Sensor-position NOT reached\n" ); + return SANE_FALSE; + } + + if( action == MOVE_EjectAllPapers ) { + + double d = hw->dMaxMoveSpeed; + + /* FIXME */ + if (hw->motorModel == MODEL_QSCAN_A6){ + DBG( _DBG_INFO2, "Q-SCAN-A6 may not be able to detect ejected papers\n"); + return SANE_TRUE; + } + + hw->dMaxMoveSpeed += 0.8; /* was 0.6 */ + + DBG( _DBG_INFO2, "Ejecting paper...\n" ); + retval = SANE_TRUE; + ejected = SANE_FALSE; + do { + if( usb_SensorPaper(dev)) { + if (!usb_ModuleMove(dev,MOVE_SkipPaperSensor, 0 )) { + hw->dMaxMoveSpeed = d; + return SANE_FALSE; + } + ejected = SANE_TRUE; + } + + if( usb_SensorAdf(dev) && + !usb_ModuleMove(dev,MOVE_ToPaperSensor, 0 )) { + hw->dMaxMoveSpeed = d; + return SANE_FALSE; + } + + if( usb_IsEscPressed()) { + retval = SANE_FALSE; + break; + } + } while( usb_SensorPaper(dev)); + + /* when the paper is beyond the sensor, we move another 300 steps + * to make sure, that the scanned sheet is out of the scanner + * BUT: not at startup + */ + if (dev->initialized >= 0 || ejected) { + DBG(_DBG_INFO2, "... MORE EJECT...\n"); + if(!usb_ModuleMove( dev, MOVE_Forward, 300 /* *3 */)) { + hw->dMaxMoveSpeed = d; + return SANE_FALSE; + } + } + + usbio_WriteReg( dev->fd, 0x07, 0); + usbio_WriteReg( dev->fd, 0x58, regs[0x58]); + + usbio_ReadReg( dev->fd, 0x02, &bReg2 ); + hw->dMaxMoveSpeed = d; + DBG( _DBG_INFO2, "...done\n" ); + return retval; + } + + usbio_WriteReg( dev->fd, 0x0a, 0 ); + + /* Compute fast feed step size, use equation 3 and equation 8 */ + dMaxMoveSpeed = hw->dMaxMoveSpeed; + + if( action == MOVE_ToShading ) { + if( hw->dMaxMoveSpeed > 0.5 ) + dMaxMoveSpeed = hw->dMaxMoveSpeed - 0.5; + } + + clk = usb_GetMotorSet( hw->motorModel ); + + mclk_div = clk->mclk_fast; + + wFastFeedStepSize = (u_short)(CRYSTAL_FREQ / + ((u_long)mclk_div * 8UL * 1 * + dMaxMoveSpeed * 4 * hw->wMotorDpi)); + + regs[0x48] = (u_char)(wFastFeedStepSize >> 8); + regs[0x49] = (u_char)(wFastFeedStepSize & 0xFF); + + dwStep = dwStep * hw->wMotorDpi / 300UL; + regs[0x4a] = _HIBYTE(_LOWORD(dwStep)); + regs[0x4b] = _LOBYTE(_LOWORD(dwStep)); + + regs[0x45] |= 0x10; + + DBG( _DBG_INFO2,"MotorDPI=%u, MaxMoveSpeed=%.3f, " + "FFStepSize=%u, Steps=%lu\n", hw->wMotorDpi, + hw->dMaxMoveSpeed, wFastFeedStepSize, dwStep ); + DBG( _DBG_INFO2, "MOTOR: " + "PWM=0x%02x, PWM_DUTY=0x%02x 0x45=0x%02x " + "0x48=0x%02x, 0x49=0x%02x \n", + regs[0x56], regs[0x57], regs[0x45], + regs[0x48], regs[0x49] ); + + DBG( _DBG_INFO2,"MCLK_FFW = %u --> 0x%02x\n", mclk_div, (mclk_div-1)*2 ); + + /* The setting for chassis moving is: + * MCLK divider = 6, 8 bits/pixel, HDPI divider = 12, + * no integration time adjustment and 1 channel grayscale + */ + + /* MCLK divider = 6 */ + if( !usbio_WriteReg(dev->fd, 0x08, (mclk_div-1)*2 /*0x0A*/)) + return SANE_FALSE; + + /* 8 bits/pixel, HDPI divider = 12 */ + if( !usbio_WriteReg(dev->fd, 0x09, 0x1F)) + return SANE_FALSE; + + /* Turn off integration time adjustment */ + if( !usbio_WriteReg(dev->fd, 0x19, 0)) + return SANE_FALSE; + + /* 1 channel grayscale, green channel */ + if( !usbio_WriteReg(dev->fd, 0x26, 0x0C)) + return SANE_FALSE; + + _UIO(sanei_lm983x_write(dev->fd, 0x48, ®s[0x48], 2, SANE_TRUE)); + _UIO(sanei_lm983x_write(dev->fd, 0x4A, ®s[0x4A], 2, SANE_TRUE)); + + /* disable home */ + if( !usbio_WriteReg(dev->fd, 0x58, regs[0x58] & ~7)) + return SANE_FALSE; + + if( !usbio_WriteReg(dev->fd, 0x45, regs[0x45] )) + return SANE_FALSE; + + if( action == MOVE_Forward || action == MOVE_ToShading ) + reg7 = 5; + else if( action == MOVE_Backward ) + reg7 = 6; + else if( action == MOVE_ToPaperSensor || action == MOVE_EjectAllPapers || + action == MOVE_SkipPaperSensor ) { + reg7 = 1; + } else { + return SANE_TRUE; + } + + retval = SANE_FALSE; + + /* start the sensor... */ + if( usbio_WriteReg( dev->fd, 0x07, reg7 )) { + + long secs; + struct timeval start_time, t2; + + /* at least we move 20 seconds before timeout... */ + gettimeofday( &start_time, NULL ); + secs = start_time.tv_sec + 20; + + if( action == MOVE_ToPaperSensor ) { + + for(;;) { + + if( usb_SensorPaper( dev )) { + usbio_WriteReg( dev->fd, 0x07, 0 ); + usbio_WriteReg( dev->fd, 0x58, regs[0x58] ); + usbio_ReadReg ( dev->fd, 0x02, &bReg2 ); + return SANE_TRUE; + } + + gettimeofday(&t2, NULL); + if( t2.tv_sec > secs ) + break; + } + } else if( action == MOVE_SkipPaperSensor ) { + + for(;;) { + + if( !usb_SensorPaper( dev )) { + usbio_WriteReg( dev->fd, 0x07, 0 ); + usbio_WriteReg( dev->fd, 0x58, regs[0x58] ); + usbio_ReadReg ( dev->fd, 0x02, &bReg2 ); + return SANE_TRUE; + } + + gettimeofday(&t2, NULL); + if( t2.tv_sec > secs ) + break; + } + } else { + + retval = usb_WaitPos( dev, 200, SANE_TRUE ); + } + + usbio_WriteReg( dev->fd, 0x58, regs[0x58] ); + usbio_ReadReg ( dev->fd, 0x02, &bReg2 ); + } + + if( !retval ) + DBG( _DBG_ERROR, "Position NOT reached\n" ); + return retval; +} + +/** + */ +static SANE_Bool +usb_ModuleToHome( Plustek_Device *dev, SANE_Bool fWait ) +{ + u_char mclk_div; + u_char value; + DCapsDef *scaps = &dev->usbDev.Caps; + HWDef *hw = &dev->usbDev.HwSetting; + u_char *regs = dev->usbDev.a_bRegs; + + if( usb_IsSheetFedDevice(dev)) { + return usb_ModuleMove( dev, MOVE_EjectAllPapers, 0 ); + } + + /* Check if merlin is ready for setting command */ + usbio_WriteReg( dev->fd, 0x58, hw->bReg_0x58 ); + usbio_ReadReg ( dev->fd, 2, &value ); + if( value & 1 ) { + dev->usbDev.fModFirstHome = SANE_FALSE; + return SANE_TRUE; + } + + _UIO( usbio_ReadReg( dev->fd, 0x07, &value )); + + if( dev->usbDev.fModFirstHome ) { + dev->usbDev.fModFirstHome = SANE_FALSE; + if( hw->motorModel != MODEL_Tokyo600 ) + usb_ModuleMove( dev, MOVE_Forward, hw->wMotorDpi / 2); + } + + /* if not homing, do it... */ + if( value != 2 ) { + + u_short wFastFeedStepSize; + + if( hw->motorModel == MODEL_Tokyo600 ) { + usbio_WriteReg( dev->fd, 0x07, 0 ); + } else { + _UIO( usbio_ResetLM983x( dev )); + usleep(200*1000); + } + + if(!_IS_PLUSTEKMOTOR(hw->motorModel)) { + + ClkMotorDef *clk; + + clk = usb_GetMotorSet( hw->motorModel ); + + regs[0x56] = clk->pwm_fast; + regs[0x57] = clk->pwm_duty_fast; + mclk_div = clk->mclk_fast; + + } else { + + mclk_div = 6; + + if( scaps->OpticDpi.x == 1200 || scaps->bPCB == 2) { + switch( hw->motorModel ) { + + case MODEL_KaoHsiung: + case MODEL_HuaLien: + default: + regs[0x56] = 1; + regs[0x57] = 63; + break; + } + } else { /* if(Device.Caps.OpticDpi.x == 600) */ + + switch( hw->motorModel ) { + + case MODEL_Tokyo600: + regs[0x56] = 4; + regs[0x57] = 4; /* 2; */ + break; + case MODEL_HuaLien: + if( dev->caps.dwFlag & SFLAG_ADF ) { + regs[0x56] = 64; /* 32; */ + regs[0x57] = 4; /* 16; */ + } else { + regs[0x56] = 32; + regs[0x57] = 16; + } + break; + + case MODEL_KaoHsiung: + default: + regs[0x56] = 64; + regs[0x57] = 20; + break; + } + } + } + + /* Compute fast feed step size, use equation 3 and equation 8 + * assumptions: MCLK = 6, Lineratemode (CM=1) + */ + wFastFeedStepSize = (u_short)(CRYSTAL_FREQ / (mclk_div * 8 * 1 * + hw->dMaxMotorSpeed * 4 * hw->wMotorDpi)); + regs[0x48] = (u_char)(wFastFeedStepSize >> 8); + regs[0x49] = (u_char)(wFastFeedStepSize & 0xFF); + regs[0x4a] = 0; + regs[0x4b] = 0; + + regs[0x45] |= 0x10; + + DBG( _DBG_INFO2,"MotorDPI=%u, MaxMotorSpeed=%.3f, FFStepSize=%u\n", + hw->wMotorDpi, hw->dMaxMotorSpeed, wFastFeedStepSize ); + DBG( _DBG_INFO, "MOTOR: " + "PWM=0x%02x, PWM_DUTY=0x%02x 0x45=0x%02x " + "0x48=0x%02x, 0x49=0x%02x\n", + regs[0x56], regs[0x57], + regs[0x45], regs[0x48], regs[0x49] ); + + /* The setting for chassis moving is: + * MCLK divider = 6, 8 bits/pixel, HDPI divider = 12, + * no integration time adjustment and 1 channel grayscale + */ + /* MCLK divider = 6 */ + value = (u_char)((mclk_div-1) * 2); + + DBG( _DBG_INFO, "MCLK_FFW = %u --> 0x%02x\n", mclk_div, value ); + + if( !usbio_WriteReg(dev->fd, 0x08, value)) + return SANE_FALSE; + + /* 8 bits/pixel, HDPI divider = 12 */ + if( !usbio_WriteReg(dev->fd, 0x09, 0x1F)) + return SANE_FALSE; + + /* Turn off integration time adjustment */ + if( !usbio_WriteReg(dev->fd, 0x19, 0)) + return SANE_FALSE; + + /* 1 channel grayscale, green channel */ + if( !usbio_WriteReg(dev->fd, 0x26, 0x8C)) + return SANE_FALSE; + + _UIO(sanei_lm983x_write(dev->fd, 0x48, ®s[0x48], 4, SANE_TRUE)); + _UIO(sanei_lm983x_write(dev->fd, 0x56, ®s[0x56], 3, SANE_TRUE)); + + if( !usbio_WriteReg(dev->fd, 0x45, regs[0x45])) + return SANE_FALSE; + + usbio_WriteReg(dev->fd, 0x0a, 0); + + if( hw->motorModel == MODEL_HuaLien && scaps->OpticDpi.x == 600 ) + usleep(100 * 1000); + + if( !usbio_WriteReg(dev->fd, 0x07, 2)) + return SANE_FALSE; + +#if 0 + if( hw->motorModel == MODEL_Tokyo600) { + + u_long dwSpeedUp = GetTickCount () + 250; + + /* while(GetTickCount () < dwSpeedUp) */ + while((int)(dwSpeedUp - GetTickCount ()) > 0) + { + Sleep (10); + if (!ReadRegister (0x07, &value)) + return FALSE; + if (!value) + return TRUE; + } + wFastFeedStepSize = (WORD)(CRYSTAL_FREQ / + (6UL * 8UL * 1 * Device.HwSetting.dMaxMotorSpeed * 4 * + Device.HwSetting.wMotorDpi) * 60 / 78); + regs[0x48] = (u_char)(wFastFeedStepSize >> 8); + regs[0x49] = (u_char)(wFastFeedStepSize & 0xFF); + WriteRegisters(0x48, ®s[0x48], 2); + } +#endif + } + return usb_WaitPos( dev, 150, fWait ); +} + +/** + */ +static SANE_Bool +usb_MotorSelect( Plustek_Device *dev, SANE_Bool fADF ) +{ + DCapsDef *sCaps = &dev->usbDev.Caps; + HWDef *hw = &dev->usbDev.HwSetting; + u_char *regs = dev->usbDev.a_bRegs; + + if(!_IS_PLUSTEKMOTOR(hw->motorModel)) { + return SANE_TRUE; + } + + if( fADF ) { + + if( sCaps->bCCD == kNEC3778 ) { + + hw->wMotorDpi = 300; + hw->dMaxMotorSpeed = 1.5; + hw->dMaxMoveSpeed = 1.5; + sCaps->OpticDpi.y = 600; + } + regs[0x5b] |= 0x80; + + } else { + + if( sCaps->bCCD == kNEC3778 ) { + + hw->wMotorDpi = 600; + hw->dMaxMotorSpeed = 1.1; + hw->dMaxMoveSpeed = 0.9; + sCaps->OpticDpi.y = 1200; + } + regs[0x5b] &= ~0x80; + } + + /* To stop the motor moving */ + usbio_WriteReg( dev->fd, 0x07, 0 ); + usleep(10 * 1000); + + usbio_WriteReg( dev->fd, 0x5b, regs[0x5b] ); + return SANE_TRUE; +} + +/** function to adjust the lamp settings of a CIS device without tweaking + * the driver-device settings + * @param dev - our almitghty device structure + * @param on - switch the lamp on or off + */ +static SANE_Bool +usb_AdjustLamps( Plustek_Device *dev, SANE_Bool on ) +{ + HWDef *hw = &dev->usbDev.HwSetting; + u_char *regs = dev->usbDev.a_bRegs; + + if( !usb_IsCISDevice(dev)) + return SANE_TRUE; + + DBG(_DBG_INFO2, "usb_AdjustLamps(%u)\n", on ); + + if( on ) { + regs[0x2c] = _HIBYTE(hw->red_lamp_on); + regs[0x2d] = _LOBYTE(hw->red_lamp_on); + regs[0x2e] = _HIBYTE(hw->red_lamp_off); + regs[0x2f] = _LOBYTE(hw->red_lamp_off); + + regs[0x30] = _HIBYTE(hw->green_lamp_on); + regs[0x31] = _LOBYTE(hw->green_lamp_on); + regs[0x32] = _HIBYTE(hw->green_lamp_off); + regs[0x33] = _LOBYTE(hw->green_lamp_off); + + regs[0x34] = _HIBYTE(hw->blue_lamp_on); + regs[0x35] = _LOBYTE(hw->blue_lamp_on); + regs[0x36] = _HIBYTE(hw->blue_lamp_off); + regs[0x37] = _LOBYTE(hw->blue_lamp_off); + + } else { + memset( ®s[0x2c], 0, 12 ); + + regs[0x2c] = 0x3f; + regs[0x2d] = 0xff; + regs[0x30] = 0x3f; + regs[0x31] = 0xff; + regs[0x34] = 0x3f; + regs[0x35] = 0xff; + } + + return sanei_lm983x_write( dev->fd, 0x2c, + ®s[0x2c], 0x37-0x2c+1, SANE_TRUE ); +} + +/** + */ +static void +usb_AdjustCISLampSettings( Plustek_Device *dev, SANE_Bool on ) +{ + HWDef *hw = &dev->usbDev.HwSetting; + + if( !usb_IsCISDevice(dev)) + return; + + DBG( _DBG_INFO2, "AdjustCISLamps(%u)\n", on ); + + if((dev->scanning.sParam.bDataType == SCANDATATYPE_Gray) || + (dev->scanning.sParam.bDataType == SCANDATATYPE_BW)) { + + DBG( _DBG_INFO2, " * setting mono mode\n" ); + hw->bReg_0x29 = hw->illu_mono.mode; + + memcpy( &hw->red_lamp_on, + &hw->illu_mono.red_lamp_on, sizeof(u_short) * 6 ); + + } else { + + DBG( _DBG_INFO2, " * setting color mode\n" ); + hw->bReg_0x29 = hw->illu_color.mode; + + memcpy( &hw->red_lamp_on, + &hw->illu_color.red_lamp_on, sizeof(u_short) * 6 ); + } + + if( !on ) { + + hw->red_lamp_on = 0x3fff; + hw->red_lamp_off = 0; + hw->green_lamp_on = 0x3fff; + hw->green_lamp_off = 0; + hw->blue_lamp_on = 0x3fff; + hw->blue_lamp_off = 0; + } else { + + if( dev->adj.rlampoff > 0 ) { + hw->red_lamp_off = dev->adj.rlampoff; + + if( hw->red_lamp_off > 0x3fff ) + hw->red_lamp_off = 0x3fff; + DBG( _DBG_INFO2, + " * red_lamp_off adjusted: %u\n", hw->red_lamp_off ); + } + + if( dev->adj.glampoff > 0 ) { + hw->green_lamp_off = dev->adj.glampoff; + + if( hw->green_lamp_off > 0x3fff ) + hw->green_lamp_off = 0x3fff; + DBG( _DBG_INFO2, + " * green_lamp_off adjusted: %u\n", hw->green_lamp_off ); + } + + if( dev->adj.blampoff > 0 ) { + hw->blue_lamp_off = dev->adj.blampoff; + + if( hw->blue_lamp_off > 0x3fff ) + hw->blue_lamp_off = 0x3fff; + DBG( _DBG_INFO2, + " * blue_lamp_off adjusted: %u\n", hw->blue_lamp_off ); + } + } + + dev->usbDev.a_bRegs[0x29] = hw->bReg_0x29; + usb_AdjustLamps( dev, on ); +} + +/** according to the flag field, we return the register and + * it's mask to turn on/off the lamp. + * @param flag - field to check + * @param reg - pointer to a var to receive the register value + * @param msk - pointer to a var to receive the mask value + * @return Nothing + */ +static void +usb_GetLampRegAndMask( u_long flag, SANE_Byte *reg, SANE_Byte *msk ) +{ + if( _MIO6 == ( _MIO6 & flag )) { + *reg = 0x5b; + *msk = 0x80; + + } else if( _MIO5 == ( _MIO5 & flag )) { + *reg = 0x5b; + *msk = 0x08; + + } else if( _MIO4 == ( _MIO4 & flag )) { + *reg = 0x5a; + *msk = 0x80; + + } else if( _MIO3 == ( _MIO3 & flag )) { + *reg = 0x5a; + *msk = 0x08; + + } else if( _MIO2 == ( _MIO2 & flag )) { + *reg = 0x59; + *msk = 0x80; + + } else if( _MIO1 == ( _MIO1 & flag )) { + *reg = 0x59; + *msk = 0x08; + + } else { + *reg = 0; + *msk = 0; + } +} + +/** usb_GetLampStatus + * This function returns the current lamp in use. + * For non Plustek devices, it always returns DEV_LampReflection. + * @param dev - pointer to our device structure, + * it should contain all we need + * @return - 0 if the scanner hasn't been used before, DEV_LampReflection + * for the normal lamp, or DEV_LampTPA for negative/transparency + * lamp + */ +static int +usb_GetLampStatus( Plustek_Device *dev ) +{ + int iLampStatus = 0; + u_char *regs = dev->usbDev.a_bRegs; + HWDef *hw = &dev->usbDev.HwSetting; + DCapsDef *sc = &dev->usbDev.Caps; + SANE_Byte reg, msk, val; + + if( NULL == hw ) { + DBG( _DBG_ERROR, "NULL-Pointer detected: usb_GetLampStatus()\n" ); + return -1; + } + + /* do we use the misc I/O pins for switching the lamp ? */ + if( _WAF_MISC_IO_LAMPS & sc->workaroundFlag ) { + + usb_GetLampRegAndMask( sc->misc_io, ®, &msk ); + + if( 0 == reg ) { + usbio_ReadReg( dev->fd, 0x29, ® ); + if( reg & 3 ) + iLampStatus |= DEV_LampReflection; + + } else { + + /* check if the lamp is on */ + usbio_ReadReg( dev->fd, reg, &val ); + + DBG( _DBG_INFO2, "LAMP-REG[0x%02x] = 0x%02x (msk=0x%02x)\n", + reg,val,msk); + if( val & msk ) + iLampStatus |= DEV_LampReflection; + + /* if the device supports a TPA, we check this here */ + if( sc->wFlags & DEVCAPSFLAG_TPA ) { + + usb_GetLampRegAndMask( _GET_TPALAMP(sc->misc_io), ®, &msk ); + if (reg) { + usbio_ReadReg( dev->fd, reg, &val ); + DBG( _DBG_INFO2, "TPA-REG[0x%02x] = 0x%02x (msk=0x%02x)\n", + reg,val,msk); + if( val & msk ) + iLampStatus |= DEV_LampTPA; + } + } + + /* CanoScan D660U extra vaganza... */ + if((dev->usbDev.vendor == 0x04A9) && (dev->usbDev.product==0x2208)) { + sanei_lm983x_read( dev->fd, 0x29, ®s[0x29], 3, SANE_TRUE ); + DBG( _DBG_INFO, "[29]=0x%02x, [2A]=0x%02x, [2B]=0x%02x\n", + regs[0x29], regs[0x2a], regs[0x2b] ); + } + } + } else { + sanei_lm983x_read(dev->fd, 0x29,®s[0x29],0x37-0x29+1,SANE_TRUE); + + if((regs[0x29] & 3) == 1) { + +/* HEINER: BETTER define register to check ! */ + + if(!_IS_PLUSTEKMOTOR(hw->motorModel)) { + iLampStatus |= DEV_LampReflection; + + } else { + + if((regs[0x2e] * 256 + regs[0x2f]) > hw->wLineEnd ) + iLampStatus |= DEV_LampReflection; + + if((regs[0x36] * 256 + regs[0x37]) > hw->wLineEnd ) + iLampStatus |= DEV_LampTPA; + } + } + } + + DBG( _DBG_INFO, "LAMP-STATUS: 0x%08x (%s)\n", + iLampStatus, iLampStatus?"on":"off" ); + return iLampStatus; +} + +/** usb_switchLampX + * used for all devices that use some misc I/O pins to switch the lamp + */ +static SANE_Bool +usb_switchLampX( Plustek_Device *dev, SANE_Bool on, SANE_Bool tpa ) +{ + SANE_Byte reg, msk; + DCapsDef *sc = &dev->usbDev.Caps; + u_char *regs = dev->usbDev.a_bRegs; + + if( tpa ) + usb_GetLampRegAndMask( _GET_TPALAMP(sc->misc_io), ®, &msk ); + else + usb_GetLampRegAndMask( sc->misc_io, ®, &msk ); + + if( 0 == reg ) + return SANE_FALSE; /* no need to switch something */ + + DBG( _DBG_INFO, "usb_switchLampX(ON=%u,TPA=%u)\n", on, tpa ); + + if( on ) { + /* in fact the output bit should be set by the default settings + * but we make sure, that it is set anyway... + */ + if (msk & 0x08) + msk |= 0x01; + else + msk |= 0x10; + regs[reg] |= msk; + } else { + regs[reg] &= ~msk; + } + + DBG( _DBG_INFO, "Switch Lamp: %u, regs[0x%02x] = 0x%02x\n", + on, reg, regs[reg] ); + usbio_WriteReg( dev->fd, reg, regs[reg] ); + return SANE_TRUE; +} + +/** usb_switchLamp + * used for all devices that use some misc I/O pins to switch the lamp + */ +static SANE_Bool +usb_switchLamp( Plustek_Device *dev, SANE_Bool on ) +{ + SANE_Bool result; + + if((dev->scanning.sParam.bSource == SOURCE_Negative) || + (dev->scanning.sParam.bSource == SOURCE_Transparency)) { + result = usb_switchLampX( dev, on, SANE_TRUE ); + } else { + result = usb_switchLampX( dev, on, SANE_FALSE ); + } + + /* to switch off CIS, we need to tweak the lampoff/on regs */ + usb_AdjustLamps( dev, on ); + return result; +} + +/** usb_LedOn + */ +static void +usb_LedOn( Plustek_Device *dev, SANE_Bool fOn ) +{ + u_char value; + + if( dev->usbDev.HwSetting.motorModel != MODEL_HuaLien ) + return; + + value = dev->usbDev.a_bRegs[0x0d]; + + if( fOn ) + value |= 0x10; + else + value &= ~0x10; + + dev->usbDev.a_bRegs[0x0d] = value; + usbio_WriteReg( dev->fd, 0x0d, value ); +} + +/** usb_FillLampRegs + * set all the registers controlling the lamps + */ +static void +usb_FillLampRegs( Plustek_Device *dev ) +{ + HWDef *hw = &dev->usbDev.HwSetting; + u_char *regs = dev->usbDev.a_bRegs; + + regs[0x2a] = _HIBYTE( hw->wGreenPWMDutyCycleLow ); + regs[0x2b] = _LOBYTE( hw->wGreenPWMDutyCycleLow ); + + regs[0x2c] = _HIBYTE( hw->red_lamp_on ); + regs[0x2d] = _LOBYTE( hw->red_lamp_on ); + regs[0x2e] = _HIBYTE( hw->red_lamp_off); + regs[0x2f] = _LOBYTE( hw->red_lamp_off); + + regs[0x30] = _HIBYTE( hw->green_lamp_on ); + regs[0x31] = _LOBYTE( hw->green_lamp_on ); + regs[0x32] = _HIBYTE( hw->green_lamp_off); + regs[0x33] = _LOBYTE( hw->green_lamp_off); + + regs[0x34] = _HIBYTE( hw->blue_lamp_on ); + regs[0x35] = _LOBYTE( hw->blue_lamp_on ); + regs[0x36] = _HIBYTE( hw->blue_lamp_off); + regs[0x37] = _LOBYTE( hw->blue_lamp_off); +} + +/** usb_LampOn + */ +static SANE_Bool +usb_LampOn( Plustek_Device *dev, SANE_Bool fOn, SANE_Bool fResetTimer ) +{ + DCapsDef *sc = &dev->usbDev.Caps; + ScanDef *scanning = &dev->scanning; + HWDef *hw = &dev->usbDev.HwSetting; + u_char *regs = dev->usbDev.a_bRegs; + int iLampStatus = usb_GetLampStatus( dev ); + int lampId = -1; + struct timeval t; + + if( NULL == scanning ) { + DBG( _DBG_ERROR, "NULL-Pointer detected: usb_LampOn()\n" ); + return SANE_FALSE; + } + + switch( scanning->sParam.bSource ) { + + case SOURCE_Reflection: + case SOURCE_ADF: + lampId = DEV_LampReflection; + break; + + case SOURCE_Transparency: + case SOURCE_Negative: + lampId = DEV_LampTPA; + break; + } + + if( fOn ) { + + if( iLampStatus != lampId ) { + + DBG( _DBG_INFO, "Switching Lamp on\n" ); + +/* here we might have to switch off the TPA/Main lamp before + * using the other one + */ + if( lampId != dev->usbDev.currentLamp ) { + if( dev->usbDev.currentLamp == DEV_LampReflection ) + usb_switchLampX( dev, SANE_FALSE, SANE_FALSE ); + else + usb_switchLampX( dev, SANE_FALSE, SANE_TRUE ); + } + + memset( ®s[0x29], 0, (0x37-0x29+1)); + + regs[0x29] = hw->bReg_0x29; + + if( !usb_switchLamp(dev, SANE_TRUE )) { + + if( lampId == DEV_LampReflection ) { + regs[0x2e] = 16383 / 256; + regs[0x2f] = 16383 % 256; + } + + if( lampId == DEV_LampTPA ) { + regs[0x36] = 16383 / 256; + regs[0x37] = 16383 % 256; + } + } + + if( _WAF_MISC_IO_LAMPS & sc->workaroundFlag ) + usb_FillLampRegs( dev ); + + sanei_lm983x_write( dev->fd, 0x29, + ®s[0x29], 0x37-0x29+1, SANE_TRUE ); + if( lampId != dev->usbDev.currentLamp ) { + + dev->usbDev.currentLamp = lampId; + + if( fResetTimer ) { + + gettimeofday( &t, NULL ); + dev->usbDev.dwTicksLampOn = t.tv_sec; + DBG( _DBG_INFO, "Warmup-Timer started\n" ); + } + } + } + + } else { + + int iStatusChange = iLampStatus & ~lampId; + + if( iStatusChange != iLampStatus ) { + + DBG( _DBG_INFO, "Switching Lamp off\n" ); + + memset( ®s[0x29], 0, 0x37-0x29+1 ); + if( !usb_switchLamp(dev, SANE_FALSE )) { + + if( iStatusChange & DEV_LampReflection ) { + regs[0x2e] = 16383 / 256; + regs[0x2f] = 16383 % 256; + } + + if( iStatusChange & DEV_LampTPA ) { + regs[0x36] = 16383 / 256; + regs[0x37] = 16383 % 256; + } + } + + if( _WAF_MISC_IO_LAMPS & sc->workaroundFlag ) + usb_FillLampRegs( dev ); + + sanei_lm983x_write( dev->fd, 0x29, + ®s[0x29], 0x37-0x29+1, SANE_TRUE ); + } + } + if( usb_GetLampStatus(dev)) + usb_LedOn( dev, SANE_TRUE ); + else + usb_LedOn( dev, SANE_FALSE ); + return SANE_TRUE; +} + +/** Function to preset the registers for the specific device, which + * should never change during the whole operation + * Affected registers:<br> + * 0x0b - 0x0e - Sensor settings - directly from the HWDef<br> + * 0x0f - 0x18 - Sensor Configuration - directly from the HwDef<br> + * 0x1a - 0x1b - Stepper Phase Correction<br> + * 0x20 - 0x21 - Line End<br> + * 0x21 - 0x22 - Data Pixel start<br> + * 0x23 - 0x24 - Data Pixel end<br> + * 0x45 - Stepper Motor Mode<br> + * 0x4c - 0x4d - Full Steps to Scan after PAPER SENSE 2 trips<br> + * 0x50 - Steps to reverse when buffer is full<br> + * 0x51 - Acceleration Profile<br> + * 0x54 - 0x5e - Motor Settings, Paper-Sense Settings and Misc I/O<br> + * + * @param dev - pointer to our device structure, + * it should contain all we need + * @return - Nothing + */ +static void +usb_ResetRegisters( Plustek_Device *dev ) +{ + int linend; + + HWDef *hw = &dev->usbDev.HwSetting; + u_char *regs = dev->usbDev.a_bRegs; + + DBG( _DBG_INFO, "RESETTING REGISTERS(%i) - 0x%02x\n", + dev->initialized, (int) sizeof(dev->usbDev.a_bRegs)); + memset( regs, 0, sizeof(dev->usbDev.a_bRegs)); + + memcpy( regs+0x0b, &hw->bSensorConfiguration, 4 ); + memcpy( regs+0x0f, &hw->bReg_0x0f_Color, 10 ); + regs[0x1a] = _HIBYTE( hw->StepperPhaseCorrection ); + regs[0x1b] = _LOBYTE( hw->StepperPhaseCorrection ); + +/* HEINER: CHECK WHY THIS has been disabled*/ +#if 0 + regs[0x1c] = hw->bOpticBlackStart; + regs[0x1d] = hw->bOpticBlackEnd; + + regs[0x1e] = _HIBYTE( hw->wActivePixelsStart ); + regs[0x1f] = _LOBYTE( hw->wActivePixelsStart ); +#endif + regs[0x20] = _HIBYTE( hw->wLineEnd ); + regs[0x21] = _LOBYTE( hw->wLineEnd ); + + regs[0x22] = _HIBYTE( hw->bOpticBlackStart ); + regs[0x23] = _LOBYTE( hw->bOpticBlackStart ); + + linend = hw->bOpticBlackStart + hw->wLineEnd; + if( linend < (hw->wLineEnd-20)) + linend = hw->wLineEnd-20; + + regs[0x24] = _HIBYTE( linend ); + regs[0x25] = _LOBYTE( linend ); + + regs[0x2a] = _HIBYTE( hw->wGreenPWMDutyCycleHigh ); + regs[0x2b] = _LOBYTE( hw->wGreenPWMDutyCycleHigh ); + + regs[0x45] = hw->bReg_0x45; + regs[0x4c] = _HIBYTE( hw->wStepsAfterPaperSensor2 ); + regs[0x4d] = _LOBYTE( hw->wStepsAfterPaperSensor2 ); + regs[0x50] = hw->bStepsToReverse; + regs[0x51] = hw->bReg_0x51; + + /* if already initialized, we ignore the MISC I/O settings as + * they are used to determine the current lamp settings... + */ + if( dev->initialized >= 0 ) { + + DBG( _DBG_INFO2, "USING MISC I/O settings\n" ); + memcpy( regs+0x54, &hw->bReg_0x54, 0x58 - 0x54 + 1 ); + regs[0x5c] = hw->bReg_0x5c; + regs[0x5d] = hw->bReg_0x5d; + regs[0x5e] = hw->bReg_0x5e; + sanei_lm983x_read( dev->fd, 0x59, ®s[0x59], 3, SANE_TRUE ); + + } else { + + DBG( _DBG_INFO2, "SETTING THE MISC I/Os\n" ); + memcpy( regs+0x54, &hw->bReg_0x54, 0x5e - 0x54 + 1 ); + + if( usb_IsCISDevice( dev )) { + + /* this sequence seems to be needed at least for the + * CanoScan devices to work properly after power-up + */ + sanei_lm983x_write_byte( dev->fd, 0x5b, regs[0x5b] ); + sanei_lm983x_write_byte( dev->fd, 0x59, regs[0x59] ); + sanei_lm983x_write_byte( dev->fd, 0x5a, regs[0x5a] ); + } else { + sanei_lm983x_write( dev->fd, 0x59, ®s[0x59], 3, SANE_TRUE ); + } + } + DBG( _DBG_INFO, "MISC I/O after RESET: 0x%02x, 0x%02x, 0x%02x\n", + regs[0x59], regs[0x5a], regs[0x5b] ); +} + +/** function which checks if we are already in home position or not. + * + */ +static SANE_Bool +usb_SensorStatus( Plustek_Device *dev ) +{ + u_char value; + HWDef *hw = &dev->usbDev.HwSetting; + +/* HEINER: Maybe needed to avoid recalibration!!! */ +#if 0 + if( dev->scanning.fCalibrated ) + return SANE_TRUE; +#endif + + /* read the status register */ + _UIO( usbio_ReadReg( dev->fd, 2, &value )); + if( value & 1 ) { + + _UIO( usbio_ReadReg( dev->fd, 0x7, &value)); + if( value ) { + + usbio_WriteReg( dev->fd, 0x07, 0 ); + usbio_WriteReg( dev->fd, 0x07, 0x20 ); + usbio_WriteReg( dev->fd, 0x07, 0 ); + + sanei_lm983x_write( dev->fd, 0x58, + &hw->bReg_0x58, 0x5b-0x58+1, SANE_TRUE ); + usbio_ReadReg( dev->fd, 2, &value ); + usbio_ReadReg( dev->fd, 2, &value ); + } + + usb_MotorOn( dev, SANE_FALSE ); + return SANE_TRUE; + } + + _UIO( usbio_ReadReg( dev->fd, 0x7, &value )); + + if( !(value & 2)) { + usb_ModuleToHome( dev, SANE_FALSE ); + } + + return SANE_FALSE; +} + +/** + */ +static void +usb_LampSwitch( Plustek_Device *dev, SANE_Bool sw ) +{ + int handle = -1; + + if( -1 == dev->fd ) { + + if( SANE_STATUS_GOOD == sanei_usb_open(dev->sane.name, &handle)) { + dev->fd = handle; + } + } + + /* needs to be recalibrated */ + dev->scanning.fCalibrated = SANE_FALSE; + + if( -1 != dev->fd ) + usb_LampOn( dev, sw, SANE_FALSE ); + + if( -1 != handle ) { + dev->fd = -1; + sanei_usb_close( handle ); + } +} + +/* HEINER: replace!!! */ +static Plustek_Device *dev_xxx = NULL; + +/** ISR to switch lamp off after time has elapsed + */ +static void +usb_LampTimerIrq( int sig ) +{ + if( NULL == dev_xxx ) + return; + + _VAR_NOT_USED( sig ); + DBG( _DBG_INFO, "LAMP OFF!!!\n" ); + + usb_LampSwitch( dev_xxx, SANE_FALSE ); +} + +/** usb_StartLampTimer + */ +static void +usb_StartLampTimer( Plustek_Device *dev ) +{ + sigset_t block, pause_mask; + struct sigaction s; +#ifdef HAVE_SETITIMER + struct itimerval interval; +#endif + /* block SIGALRM */ + sigemptyset( &block ); + sigaddset ( &block, SIGALRM ); + sigprocmask( SIG_BLOCK, &block, &pause_mask ); + + /* setup handler */ + sigemptyset( &s.sa_mask ); + sigaddset ( &s.sa_mask, SIGALRM ); + s.sa_flags = 0; + s.sa_handler = usb_LampTimerIrq; + + if( sigaction( SIGALRM, &s, NULL ) < 0 ) + DBG( _DBG_ERROR, "Can't setup timer-irq handler\n" ); + + sigprocmask( SIG_UNBLOCK, &block, &pause_mask ); + +#ifdef HAVE_SETITIMER + /* define a one-shot timer */ + interval.it_value.tv_usec = 0; + interval.it_value.tv_sec = dev->usbDev.dwLampOnPeriod; + interval.it_interval.tv_usec = 0; + interval.it_interval.tv_sec = 0; + + if( 0 != dev->usbDev.dwLampOnPeriod ) { + dev_xxx = dev; + setitimer( ITIMER_REAL, &interval, &dev->saveSettings ); + DBG( _DBG_INFO, "Lamp-Timer started (using ITIMER)\n" ); + } +#else + if( 0 != dev->usbDev.dwLampOnPeriod ) { + dev_xxx = dev; + alarm( dev->usbDev.dwLampOnPeriod ); + DBG( _DBG_INFO, "Lamp-Timer started (using ALARM)\n" ); + } +#endif +} + +/** usb_StopLampTimer + */ +static void +usb_StopLampTimer( Plustek_Device *dev ) +{ + sigset_t block, pause_mask; + + /* block SIGALRM */ + sigemptyset( &block ); + sigaddset ( &block, SIGALRM ); + sigprocmask( SIG_BLOCK, &block, &pause_mask ); + + dev_xxx = NULL; + +#ifdef HAVE_SETITIMER + if( 0 != dev->usbDev.dwLampOnPeriod ) + setitimer( ITIMER_REAL, &dev->saveSettings, NULL ); +#else + _VAR_NOT_USED( dev ); + alarm( 0 ); +#endif + DBG( _DBG_INFO, "Lamp-Timer stopped\n" ); +} + +/** wait until warmup has been done + */ +static SANE_Bool +usb_Wait4Warmup( Plustek_Device *dev ) +{ + u_long dw; + struct timeval t; + + if( usb_IsCISDevice(dev)) { + DBG(_DBG_INFO,"Warmup: skipped for CIS devices\n" ); + return SANE_TRUE; + } + + if( dev->adj.warmup < 0 ) + return SANE_TRUE; + + /* + * wait until warmup period has been elapsed + */ + gettimeofday( &t, NULL); + dw = t.tv_sec - dev->usbDev.dwTicksLampOn; + if( dw < (u_long)dev->adj.warmup ) + DBG(_DBG_INFO,"Warmup: Waiting %d seconds\n", dev->adj.warmup ); + + do { + + gettimeofday( &t, NULL); + + dw = t.tv_sec - dev->usbDev.dwTicksLampOn; + + if( usb_IsEscPressed()) { + return SANE_FALSE; + } + + } while( dw < (u_long)dev->adj.warmup ); + + return SANE_TRUE; +} + +/** function for TPA autodection (EPSON & UMAX devices) + */ +static SANE_Bool +usb_HasTPA( Plustek_Device *dev ) +{ + static char model[] = { "3450" }; + u_char val; + + if( dev->usbDev.vendor == 0x04B8 ) { /* the EPSON section */ + + usb_switchLampX( dev, SANE_FALSE, SANE_TRUE ); + usbio_WriteReg ( dev->fd, 0x58, 0x1d ); + usbio_WriteReg ( dev->fd, 0x59, 0x49 ); + usbio_ReadReg ( dev->fd, 0x02, &val ); + usbio_WriteReg ( dev->fd, 0x58, dev->usbDev.HwSetting.bReg_0x58 ); + usbio_WriteReg ( dev->fd, 0x59, dev->usbDev.HwSetting.bReg_0x59 ); + + DBG( _DBG_INFO, "REG[0x02] = 0x%02x\n", val ); + + if( val & 0x02 ) { + DBG( _DBG_INFO, "EPSON-TPA detected\n" ); + return SANE_TRUE; + } else + DBG( _DBG_INFO, "EPSON-TPA NOT detected\n" ); + + if( dev->adj.enableTpa ) { + DBG( _DBG_INFO, "EPSON-TPA usage forced\n" ); + return SANE_TRUE; + } + + } else if( dev->usbDev.vendor == 0x1606 ) { /* the UMAX section */ + + if((dev->usbDev.product == 0x0050) || (dev->usbDev.product == 0x0060)) { + + usbio_ReadReg ( dev->fd, 0x02, &val ); + DBG( _DBG_INFO, "REG[0x02] = 0x%02x\n", val ); + + usbio_WriteReg ( dev->fd, 0x58, dev->usbDev.HwSetting.bReg_0x58 ); + usbio_WriteReg ( dev->fd, 0x5a, dev->usbDev.HwSetting.bReg_0x5a ); + usbio_WriteReg ( dev->fd, 0x5b, dev->usbDev.HwSetting.bReg_0x5b ); + + usbio_ReadReg ( dev->fd, 0x02, &val ); + DBG( _DBG_INFO, "REG[0x02] = 0x%02x\n", val ); + + if( val & 0x02 ) { + DBG( _DBG_INFO, "UMAX-TPA detected\n" ); + dev->usbDev.ModelStr = model; + return SANE_TRUE; + } else + DBG( _DBG_INFO, "UMAX-TPA NOT detected\n" ); + + if( dev->adj.enableTpa ) { + DBG( _DBG_INFO, "UMAX-TPA usage forced\n" ); + dev->usbDev.ModelStr = model; + return SANE_TRUE; + } + } + } + return SANE_FALSE; +} + +/** function for reading the button states + */ +static SANE_Bool +usb_UpdateButtonStatus( Plustek_Scanner *s ) +{ + u_char mio[3]; + SANE_Byte val, mask; + int i, j, bc; + int handle = -1; + SANE_Status status; + Plustek_Device *dev = s->hw; + DCapsDef *caps = &dev->usbDev.Caps; + + if (caps->bButtons == 0) + return SANE_FALSE; + + status = sanei_access_lock( dev->sane.name, 3 ); + if( SANE_STATUS_GOOD != status ) + return SANE_FALSE; + + if( -1 == dev->fd ) { + + status = sanei_usb_open(dev->sane.name, &handle); + if( SANE_STATUS_GOOD != status ) { + sanei_access_unlock( dev->sane.name ); + return SANE_FALSE; + } + dev->fd = handle; + } + + /* we have to check all 6 misc I/O ports for input configuration*/ + mio[0] = dev->usbDev.HwSetting.bReg_0x59; + mio[1] = dev->usbDev.HwSetting.bReg_0x5a; + mio[2] = dev->usbDev.HwSetting.bReg_0x5b; + + usbio_ReadReg( dev->fd, 0x07, &val ); + if( val == 0 ) { + + /* first read clears the status... */ + usbio_ReadReg( dev->fd, 0x02, &val ); + + /* Plustek and KYE/Genius use altnernative button handling */ + if((dev->usbDev.vendor == 0x07B3) || (dev->usbDev.vendor == 0x0458)) { + + DBG( _DBG_INFO2, "Button Value=0x%02x\n", val ); + + /* no button pressed so far */ + for( i = 0; i < caps->bButtons; i++ ) + s->val[OPT_BUTTON_0 + i].w = 0; + + if (caps->bButtons == 2 || caps->bButtons == 5) { + val >>= 2; + val &= 0x07; + DBG( _DBG_INFO2, "Button Value=0x%02x (2/5)\n", val ); + + switch( val ) { + case 1: s->val[OPT_BUTTON_1].w = 1; break; + case 2: s->val[OPT_BUTTON_0].w = 1; break; + case 3: s->val[OPT_BUTTON_2].w = 1; break; + case 4: s->val[OPT_BUTTON_3].w = 1; break; + case 6: s->val[OPT_BUTTON_4].w = 1; break; + } + } else if (caps->bButtons == 4 ) { + val >>= 5; + val &= 0x07; + DBG( _DBG_INFO2, "Button Value=0x%02x (4)\n", val ); + + switch( val ) { + case 1: s->val[OPT_BUTTON_0].w = 1; break; + case 2: s->val[OPT_BUTTON_1].w = 1; break; + case 4: s->val[OPT_BUTTON_2].w = 1; break; + case 6: s->val[OPT_BUTTON_3].w = 1; break; + } + } else { + DBG( _DBG_INFO2, "Hmm, could not handle this!\n" ); + } + + } else { + + /* the generic section... */ + val >>= 2; + bc = 0; + + /* only use the "valid" ports, that reflect a button */ + if( _WAF_MISC_IO_BUTTONS & caps->workaroundFlag ) { + if((caps->misc_io & _PORT0) == 0) + mio[0] = 0xff; + if((caps->misc_io & _PORT1) == 0) + mio[1] = 0xff; + if((caps->misc_io & _PORT2) == 0) + mio[2] = 0xff; + } + + for( i = 0; i < 3; i++ ) { + + DBG( _DBG_INFO2, "Checking MISC IO[%u]=0x%02x\n", i, mio[i] ); + mask = 0x01; + + for( j = 0; j < 2; j++ ) { + + if((mio[i] & mask) == 0) { + DBG( _DBG_INFO2, "* port %u configured as input," + " status: %s (%u)\n", (i*2)+j+1, + ((val & 1)?"PRESSED":"RELEASED"), (OPT_BUTTON_0 + bc)); + s->val[OPT_BUTTON_0 + bc].w = val & 1; + bc++; + } + val >>= 1; + mask <<= 4; + } + } + } + } else { + DBG( _DBG_INFO2, "Scanner NOT idle: 0x%02x\n", val ); + } + + if( -1 != handle ) { + dev->fd = -1; + sanei_usb_close( handle ); + } + + sanei_access_unlock( dev->sane.name ); + return SANE_TRUE; +} + +/* END PLUSTEK-USBHW.C ......................................................*/ |