sane-project-backends/backend/plustek-usbscan.c

1242 wiersze
32 KiB
C

/*.............................................................................
* Project : SANE library for Plustek USB flatbed scanners.
*.............................................................................
* File: plustek-usbscan.c
*.............................................................................
*
* based on sources acquired from Plustek Inc.
* Copyright (C) 2001 Gerhard Jaeger <g.jaeger@earthling.net>
*.............................................................................
* History:
* 0.40 - starting version of the USB support
* 0.41 - minor fixes
*
*.............................................................................
*
* 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.
*/
#include <math.h>
static u_char bMaxITA;
static Bool m_fAutoPark;
static Bool m_fFirst;
static double m_dHDPIDivider;
static double m_dMCLKDivider;
static pScanParam m_pParam;
static u_char m_bLineRateColor;
static u_char m_bCM;
static u_char m_bPP;
static u_char m_bIntTimeAdjust;
static u_char m_bOldScanData;
static u_short m_wFastFeedStepSize;
static u_short m_wLineLength;
static u_short m_wStepSize;
static u_long m_dwPauseLimit;
static Bool m_fStart = SANE_FALSE;
static Bool usb_DownloadShadingData( pPlustek_Device, u_char );
/*.............................................................................
* our ESC key is the SIGUSR1 signal, sent when the cancel button is
* pressed
*/
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;
}
/*.............................................................................
*
*/
static u_short usb_SetAsicDpiX( pPlustek_Device dev, u_short xdpi )
{
pScanDef scanning = &dev->scanning;
pDCapsDef scaps = &dev->usbDev.Caps;
if( scaps->OpticDpi.x == 1200 &&
scanning->sParam.bDataType != SCANDATATYPE_Color &&
xdpi < 150 &&
scanning->sParam.bDataType == SCANDATATYPE_BW ) {
xdpi = 150;
}
m_dHDPIDivider = (double)scaps->OpticDpi.x / xdpi;
if (m_dHDPIDivider < 1.5)
{
m_dHDPIDivider = 1.0;
a_bRegs [0x09] = 0;
}
else if (m_dHDPIDivider < 2.0)
{
m_dHDPIDivider = 1.5;
a_bRegs [0x09] = 1;
}
else if (m_dHDPIDivider < 3.0)
{
m_dHDPIDivider = 2.0;
a_bRegs [0x09] = 2;
}
else if (m_dHDPIDivider < 4.0)
{
m_dHDPIDivider = 3.0;
a_bRegs [0x09] = 3;
}
else if (m_dHDPIDivider < 6.0)
{
m_dHDPIDivider = 4.0;
a_bRegs [0x09] = 4;
}
else if (m_dHDPIDivider < 8.0)
{
m_dHDPIDivider =6.0;
a_bRegs [0x09] = 5;
}
else if (m_dHDPIDivider < 12.0)
{
m_dHDPIDivider = 8.0;
a_bRegs [0x09] = 6;
}
else
{
m_dHDPIDivider = 12.0;
a_bRegs [0x09] = 7;
}
if( a_bRegs[0x0a] )
a_bRegs[0x09] -= ((a_bRegs[0x0a] >> 2) + 2);
return (u_short)((double)scaps->OpticDpi.x / m_dHDPIDivider);
}
/*.............................................................................
*
*/
static u_short usb_SetAsicDpiY( pPlustek_Device dev, u_short ydpi )
{
pScanDef scanning = &dev->scanning;
pDCapsDef sCaps = &dev->usbDev.Caps;
pHWDef hw = &dev->usbDev.HwSetting;
u_short wMinDpi, wDpi;
wMinDpi = sCaps->OpticDpi.y / sCaps->bSensorDistance;
wDpi = (ydpi + wMinDpi - 1) / wMinDpi * wMinDpi;
/*
* HEINER: added '*2'
*/
if( wDpi > sCaps->OpticDpi.y * 2 )
wDpi = sCaps->OpticDpi.y * 2;
if( hw->ScannerModel == MODEL_Tokyo600 ) {
/* return wDpi; */
} else if( sCaps->wFlags & DEVCAPSFLAG_Adf && sCaps->OpticDpi.x == 600 ) {
/* for ADF scanner color mode 300 dpi big noise */
if( scanning->sParam.bDataType == SCANDATATYPE_Color &&
scanning->sParam.bBitDepth > 8 && wDpi < 300 ) {
wDpi = 300;
}
} else if( sCaps->OpticDpi.x == 1200 ) {
if( scanning->sParam.bDataType != SCANDATATYPE_Color && wDpi < 200) {
wDpi = 200;
}
}
return wDpi;
}
/*.............................................................................
*
*/
static void usb_SetColorAndBits( pPlustek_Device dev, pScanParam pParam )
{
pHWDef hw = &dev->usbDev.HwSetting;
/* Set pixel packing, data mode and AFE operation */
switch (pParam->bDataType)
{
case SCANDATATYPE_Color:
m_bPP = 1;
m_bCM = 3;
a_bRegs[0x26] = 0;
memcpy( &a_bRegs[0x0f], hw->bReg_0x0f_Color, 10 );
break;
case SCANDATATYPE_Gray:
m_bPP = 1;
m_bCM = 1;
a_bRegs[0x26] = 0x0c; /* 1 channel gray & using green channel */
memcpy( &a_bRegs[0x0f], hw->bReg_0x0f_Mono, 10 );
break;
case SCANDATATYPE_BW:
m_bPP = 8;
m_bCM = 1;
a_bRegs[0x26] = 0x0c;
memcpy( &a_bRegs [0x0f], hw->bReg_0x0f_Mono, 10 );
break;
}
if (pParam->bBitDepth > 8)
a_bRegs[0x09] |= 0x20;
else if (pParam->bBitDepth == 8)
a_bRegs[0x09] |= 0x18;
}
/*.............................................................................
*
*/
static void usb_GetScanRect( pPlustek_Device dev, pScanParam pParam )
{
u_short wDataPixelStart, wLineEnd;
pDCapsDef sCaps = &dev->usbDev.Caps;
pHWDef hw = &dev->usbDev.HwSetting;
/* Convert pixels to physical dpi based */
pParam->Size.dwValidPixels = pParam->Size.dwPixels * pParam->PhyDpi.x / pParam->UserDpi.x;
/* HEINER: check ADF stuff... */
#if 0
if(pParam->bCalibration != PARAM_Gain && pParam->bCalibration != PARAM_Offset && ScanInf.m_fADF)
wDataPixelStart = 2550 * sCaps->OpticDpi.x / 300UL - (u_short)(m_dHDPIDivider * pParam->Size.dwValidPixels + 0.5);
else
#endif
wDataPixelStart = (u_short)((u_long) pParam->Origin.x * sCaps->OpticDpi.x / 300UL);
/* Data output from NS983X should be times of 2-byte and every line
* will append 2 status bytes
*/
if (pParam->bBitDepth == 1)
{
/* Pixels should be times of 16 */
pParam->Size.dwPhyPixels = (pParam->Size.dwValidPixels + 15UL) & 0xfffffff0UL;
pParam->Size.dwPhyBytes = pParam->Size.dwPhyPixels / 8UL + 2UL;
}
else if (pParam->bBitDepth == 8)
{
/* Pixels should be times of 2 */
pParam->Size.dwPhyPixels = (pParam->Size.dwValidPixels + 1UL) & 0xfffffffeUL;
pParam->Size.dwPhyBytes = pParam->Size.dwPhyPixels * pParam->bChannels + 2UL;
}
else /* pParam->bBitDepth == 16 */
{
pParam->Size.dwPhyPixels = pParam->Size.dwValidPixels;
pParam->Size.dwPhyBytes = pParam->Size.dwPhyPixels * 2 * pParam->bChannels + 2UL;
}
/* Compute data start pixel */
wDataPixelStart = (u_short)((u_long) pParam->Origin.x * sCaps->OpticDpi.x / 300UL);
if (pParam->bCalibration != PARAM_Gain && pParam->bCalibration != PARAM_Offset)
{
/* HEINER: check ADF stuff... */
#if 0
if(ScanInf.m_fADF)
wDataPixelStart = 2550 * sCaps->OpticDpi.x / 300UL - (u_short)(m_dHDPIDivider * pParam->Size.dwValidPixels + 0.5);
#endif
wDataPixelStart += hw->wActivePixelsStart;
}
wLineEnd = wDataPixelStart + (u_short)(m_dHDPIDivider * pParam->Size.dwPhyPixels + 0.5);
a_bRegs[0x22] = _HIBYTE( wDataPixelStart );
a_bRegs[0x23] = _LOBYTE( wDataPixelStart );
a_bRegs[0x24] = _HIBYTE( wLineEnd );
a_bRegs[0x25] = _LOBYTE( wLineEnd );
/* Y origin */
if( pParam->bCalibration == PARAM_Scan ) {
if( hw->ScannerModel == MODEL_Tokyo600 ) {
if(pParam->PhyDpi.x <= 75)
pParam->Origin.y += 20;
else if(pParam->PhyDpi.x <= 100)
{
if (pParam->bDataType == SCANDATATYPE_Color)
pParam->Origin.y += 0;
else
pParam->Origin.y -= 6;
}
else if(pParam->PhyDpi.x <= 150)
{
if (pParam->bDataType == SCANDATATYPE_Color)
pParam->Origin.y -= 0;
}
else if(pParam->PhyDpi.x <= 200)
{
if (pParam->bDataType == SCANDATATYPE_Color)
pParam->Origin.y -= 10;
else
pParam->Origin.y -= 4;
}
else if(pParam->PhyDpi.x <= 300)
{
if (pParam->bDataType == SCANDATATYPE_Color)
pParam->Origin.y += 16;
else
pParam->Origin.y -= 18;
}
else if(pParam->PhyDpi.x <= 400)
{
if (pParam->bDataType == SCANDATATYPE_Color)
pParam->Origin.y += 15;
else if(pParam->bDataType == SCANDATATYPE_BW)
pParam->Origin.y += 4;
}
else /* if(pParam->PhyDpi.x <= 600) */
{
if (pParam->bDataType == SCANDATATYPE_Gray)
pParam->Origin.y += 4;
}
}
/* Add gray mode offset (Green offset, we assume the CCD are
* always be RGB or BGR order).
*/
if (pParam->bDataType != SCANDATATYPE_Color)
pParam->Origin.y += (u_long)(300UL * sCaps->bSensorDistance / sCaps->OpticDpi.y );
}
pParam->Origin.y = (u_short)((u_long)pParam->Origin.y * hw->wMotorDpi / 300UL);
/* Something wrong, but I can not find it. */
if( hw->ScannerModel == MODEL_HuaLien && sCaps->OpticDpi.x == 600)
pParam->Origin.y = pParam->Origin.y * 297 / 298;
a_bRegs[0x4a] = _HIBYTE( pParam->Origin.y );
a_bRegs[0x4b] = _LOBYTE( pParam->Origin.y );
}
/*.............................................................................
*
*/
static void usb_GetDPD( pPlustek_Device dev )
{
int qtcnt; /* quarter speed count count reg 51 b2..3 */
int hfcnt; /* half speed count reg 51 b0..1 */
int strev; /* steps to reverse reg 50 */
int dpd; /* calculated dpd reg 52:53 */
int st; /* step size reg 46:47 */
pHWDef hw = &dev->usbDev.HwSetting;
qtcnt = (a_bRegs [0x51] & 0x30) >> 4; /* quarter speed count */
hfcnt = (a_bRegs [0x51] & 0xc0) >> 6; /* half speed count */
if( hw->fLM9831 )
strev = a_bRegs [0x50] & 0x3f; /* steps to reverse */
else /* LM9832 */
{
if (qtcnt == 3)
qtcnt = 8;
if (hfcnt == 3)
hfcnt = 8;
strev = a_bRegs[0x50]; /* steps to reverse */
}
st = a_bRegs[0x46] * 256 + a_bRegs[0x47]; /* scan step size */
if (m_wLineLength == 0)
dpd = 0;
else
{
dpd = (((qtcnt * 4) + (hfcnt * 2) + strev) * 4 * st) %
(m_wLineLength * m_bLineRateColor);
dpd = m_wLineLength * m_bLineRateColor - dpd;
}
a_bRegs[0x51] |= (u_char)((dpd >> 16) & 0x03);
a_bRegs[0x52] = (u_char)(dpd >> 8);
a_bRegs[0x53] = (u_char)(dpd & 0xFF);
}
/*.............................................................................
*
*/
static double usb_GetMCLKDivider( pPlustek_Device dev, pScanParam pParam)
{
u_short wMaxIntegrationTime;
double dMaxMCLKDivider;
pDCapsDef sCaps = &dev->usbDev.Caps;
pHWDef hw = &dev->usbDev.HwSetting;
m_dMCLKDivider = pParam->dMCLK;
if (m_dHDPIDivider*m_dMCLKDivider >= 5.3/*6*/)
m_bIntTimeAdjust = 0;
else
m_bIntTimeAdjust = ceil( 5.3/*6.0*/ / (m_dHDPIDivider*m_dMCLKDivider));
if( pParam->bCalibration == PARAM_Scan ) {
/* Compare Integration with USB speed to find the best ITA value */
if( pParam->bBitDepth > 8 ) {
while( pParam->Size.dwPhyBytes >
(m_dMCLKDivider * m_bCM * m_wLineLength / 6 * 9 / 10) *
(1 + m_bIntTimeAdjust)) {
m_bIntTimeAdjust++;
}
if( hw->ScannerModel == MODEL_HuaLien &&
sCaps->bCCD == kNEC3799 && m_bIntTimeAdjust > bMaxITA) {
m_bIntTimeAdjust = bMaxITA;
}
}
}
a_bRegs[0x08] = (u_char)((m_dMCLKDivider - 1) * 2);
a_bRegs[0x19] = m_bIntTimeAdjust;
if( m_bIntTimeAdjust != 0 ) {
m_wStepSize = (u_short)((u_long) m_wStepSize *
(m_bIntTimeAdjust + 1) / m_bIntTimeAdjust);
if (m_wStepSize < 2)
m_wStepSize = 2;
a_bRegs[0x46] = _HIBYTE(m_wStepSize);
a_bRegs[0x47] = _LOBYTE(m_wStepSize);
usb_GetDPD( dev );
}
/* Compute maximum MCLK divider base on maximum integration time for
* high lamp PWM, use equation 4
*/
wMaxIntegrationTime = hw->wIntegrationTimeHighLamp;
dMaxMCLKDivider = (double)dwCrystalFrequency*wMaxIntegrationTime /
(1000 * 8 * m_bCM * m_wLineLength);
/* Determine PWM setting */
if( m_dMCLKDivider > dMaxMCLKDivider )
{
a_bRegs[0x2A] = _HIBYTE( hw->wGreenPWMDutyCycleLow );
a_bRegs[0x2B] = _LOBYTE( hw->wGreenPWMDutyCycleLow );
}
else
{
a_bRegs[0x2A] = _HIBYTE( hw->wGreenPWMDutyCycleHigh );
a_bRegs[0x2B] = _LOBYTE( hw->wGreenPWMDutyCycleHigh );
}
return m_dMCLKDivider;
}
/*.............................................................................
*
*/
static void usb_GetStepSize( pPlustek_Device dev, pScanParam pParam )
{
pHWDef hw = &dev->usbDev.HwSetting;
/* Compute step size using equation 1 */
if (m_bIntTimeAdjust != 0)
m_wStepSize = (u_short)(((u_long) pParam->PhyDpi.y * m_wLineLength * m_bLineRateColor *
(m_bIntTimeAdjust + 1)) /
(4 * hw->wMotorDpi * m_bIntTimeAdjust));
else
m_wStepSize = (u_short)(((u_long) pParam->PhyDpi.y * m_wLineLength * m_bLineRateColor) /
(4 * hw->wMotorDpi));
if (m_wStepSize < 2)
m_wStepSize = 2;
m_wStepSize = m_wStepSize * 298 / 297;
a_bRegs[0x46] = _HIBYTE( m_wStepSize );
a_bRegs[0x47] = _LOBYTE( m_wStepSize );
}
/*.............................................................................
*
*/
static void usb_GetLineLength( pPlustek_Device dev )
{
/* [note]
* The ITA in this moment is always 0, it will be changed later when we
* calculate MCLK. This is very strange why this routine will not call
* again to get all new value after ITA was changed? If this routine
* never call again, maybe we remove all factor with ITA here.
*/
int tr;
int tpspd; /* turbo/preview mode speed reg 0a b2..3 */
int tpsel; /* turbo/preview mode select reg 0a b0..1 */
int gbnd; /* guardband duration reg 0e b4..7 */
int dur; /* pulse duration reg 0e b0..3 */
int ntr; /* number of tr pulses reg 0d b7 */
int afeop; /* scan mode, 0=pixel rate, 1=line rate, */
/* 4=1 channel mode a, 5=1 channel mode b, reg 26 b0..2 */
int ctmode; /* CIS tr timing mode reg 19 b0..1 */
int tp; /* tpspd or 1 if tpsel=0 */
int b; /* if ctmode=0, (ntr+1)*((2*gbnd)+dur+1), otherwise 1 */
int tradj; /* ITA */
int en_tradj;
pHWDef hw = &dev->usbDev.HwSetting;
tpspd = (a_bRegs[0x0a] & 0x0c) >> 2; /* turbo/preview mode speed */
tpsel = a_bRegs[0x0a] & 3; /* turbo/preview mode select */
gbnd = (a_bRegs[0x0e] & 0xf0) >> 4; /* TR fi1 guardband duration */
dur = (a_bRegs[0x0e] & 0xf); /* TR pulse duration */
ntr = a_bRegs[0x0d] / 128; /* number of tr pulses */
afeop = a_bRegs[0x26] & 7; /* afe op - 3 channel or 1 channel */
tradj = a_bRegs[0x19] & 0x7f; /* integration time adjust */
en_tradj = (tradj) ? 1 : 0;
ctmode = (a_bRegs[0x0b] >> 3) & 3; /* cis tr timing mode */
/* always, we just support 3 color pixel and 1 channel gray */
m_bLineRateColor = 1;
/* according to turbo/preview mode to set value */
if( tpsel == 0 ) {
tp = 1;
} else {
tp = tpspd + 2;
if( tp == 5 )
tp++;
}
/* We are CCD scanner, ctmode should be 0 */
b = (ntr + 1) * ((2*gbnd) + dur + 1);
b += (1 - ntr) * en_tradj;
tr = m_bLineRateColor * (hw->wLineEnd + tp * (b + 3 - ntr));
if (tradj == 0)
{
if (ctmode == 0)
tr += m_bLineRateColor;
}
else
{
int le_phi, num_byteclk, num_mclkf, tr_fast_pix, extra_pix;
/* Line color or gray mode */
if (afeop != 0)
{
le_phi = (tradj + 1) / 2 + 1 + 6;
num_byteclk = ((le_phi + 8 * hw->wLineEnd + 8 * b + 4) / (8 * tradj)) + 1;
num_mclkf = 8 * tradj * num_byteclk;
tr_fast_pix = num_byteclk;
extra_pix = (num_mclkf - le_phi) % 8;
}
else /* 3 channel pixel rate color */
{
le_phi = (tradj + 1) / 2 + 1 + 10 + 12;
num_byteclk = ((le_phi + 3 * 8 * hw->wLineEnd + 3 * 8 * b + 3 * 4) /
(3 * 8 * tradj)) + 1;
num_mclkf = 3 * 8 * tradj * num_byteclk;
tr_fast_pix = num_byteclk;
extra_pix = (num_mclkf - le_phi) % (3 * 8);
}
tr = b + hw->wLineEnd + 4 + tr_fast_pix;
if (extra_pix == 0)
tr++;
tr *= m_bLineRateColor;
}
m_wLineLength = tr / m_bLineRateColor;
}
/*.............................................................................
*
*/
static void usb_GetMotorParam( pPlustek_Device dev, pScanParam pParam )
{
pDCapsDef sCaps = &dev->usbDev.Caps;
pHWDef hw = &dev->usbDev.HwSetting;
if( sCaps->OpticDpi.x == 1200 ) {
switch( hw->ScannerModel ) {
case MODEL_HuaLien:
case MODEL_KaoHsiung:
default:
if(pParam->PhyDpi.x <= 200)
{
a_bRegs[0x56] = 1;
a_bRegs[0x57] = 48; /* 63; */
}
else if(pParam->PhyDpi.x <= 300)
{
a_bRegs[0x56] = 2; /* 8; */
a_bRegs[0x57] = 48; /* 56; */
}
else if(pParam->PhyDpi.x <= 400)
{
a_bRegs[0x56] = 8;
a_bRegs[0x57] = 48;
}
else if(pParam->PhyDpi.x <= 600)
{
a_bRegs[0x56] = 2; /* 10; */
a_bRegs[0x57] = 48; /* 56; */
}
else /* pParam->PhyDpi.x == 1200) */
{
a_bRegs[0x56] = 1; /* 8; */
a_bRegs[0x57] = 48; /* 56; */
}
break;
}
} else {
switch ( hw->ScannerModel ) {
case MODEL_Tokyo600:
a_bRegs[0x56] = 16;
a_bRegs[0x57] = 4; /* 2; */
break;
case MODEL_HuaLien:
{
if(pParam->PhyDpi.x <= 200)
{
a_bRegs[0x56] = 64; /* 24; */
a_bRegs[0x57] = 4; /* 16; */
}
else if(pParam->PhyDpi.x <= 300)
{
a_bRegs[0x56] = 64; /* 16; */
a_bRegs[0x57] = 4; /* 16; */
}
else if(pParam->PhyDpi.x <= 400)
{
a_bRegs[0x56] = 64; /* 16; */
a_bRegs[0x57] = 4; /* 16; */
}
else /* if(pParam->PhyDpi.x <= 600) */
{
/* HEINER: check ADF stuff... */
#if 0
if(ScanInf.m_fADF)
{
a_bRegs[0x56] = 8;
a_bRegs[0x57] = 48;
}
else
#endif
{
a_bRegs[0x56] = 64; /* 2; */
a_bRegs[0x57] = 4; /* 48; */
}
}
}
break;
case MODEL_KaoHsiung:
default:
if(pParam->PhyDpi.x <= 200)
{
a_bRegs[0x56] = 24;
a_bRegs[0x57] = 16;
}
else if(pParam->PhyDpi.x <= 300)
{
a_bRegs[0x56] = 16;
a_bRegs[0x57] = 16;
}
else if(pParam->PhyDpi.x <= 400)
{
a_bRegs[0x56] = 16;
a_bRegs[0x57] = 16;
}
else /* if(pParam->PhyDpi.x <= 600) */
{
a_bRegs[0x56] = 2;
a_bRegs[0x57] = 48;
}
break;
}
}
}
static u_long usb_min( u_long val1, u_long val2 )
{
if( val1 > val2 )
return val2;
return val1;
}
static u_long usb_max( u_long val1, u_long val2 )
{
if( val1 > val2 )
return val1;
return val2;
}
/*.............................................................................
*
*/
static void usb_GetPauseLimit( pPlustek_Device dev, pScanParam pParam )
{
pHWDef hw = &dev->usbDev.HwSetting;
/* Get available buffer size in KB
* for 512kb this will be 296
* for 2Mb this will be 1832
*/
m_dwPauseLimit = (u_long)(hw->wDRAMSize - (16 + 16 + 4) * 3 * 2);
m_dwPauseLimit -= (pParam->Size.dwPhyBytes / 1024 + 1);
/* If not reversing, take into account the steps to reverse */
if( a_bRegs[0x50] == 0 )
m_dwPauseLimit -= ((a_bRegs[0x54] & 7) *
pParam->Size.dwPhyBytes + 1023) / 1024;
m_dwPauseLimit = usb_max( usb_min(m_dwPauseLimit,
(u_long)ceil(pParam->Size.dwTotalBytes / 1024.0)), 2);
a_bRegs[0x4e] = (u_char)floor((m_dwPauseLimit*512.0) / (2*hw->wDRAMSize));
if( a_bRegs[0x4e] > 1 ) {
a_bRegs[0x4e]--;
if(a_bRegs[0x4e] > 1)
a_bRegs[0x4e]--;
} else
a_bRegs[0x4e] = 1;
a_bRegs[0x4f] = 1;
}
/*.............................................................................
*
*/
static void usb_GetScanLinesAndSize( pPlustek_Device dev, pScanParam pParam )
{
pDCapsDef sCaps = &dev->usbDev.Caps;
pParam->Size.dwPhyLines = (u_long)ceil((double) pParam->Size.dwLines *
pParam->PhyDpi.y / pParam->UserDpi.y);
/* Calculate color offset */
if (pParam->bCalibration == PARAM_Scan && pParam->bChannels == 3) {
dev->scanning.bLineDistance = sCaps->bSensorDistance *
pParam->PhyDpi.y / sCaps->OpticDpi.x;
pParam->Size.dwPhyLines += (dev->scanning.bLineDistance << 1);
}
else
dev->scanning.bLineDistance = 0;
pParam->Size.dwTotalBytes = pParam->Size.dwPhyBytes * pParam->Size.dwPhyLines;
}
/*.............................................................................
*
*/
static Bool usb_SetScanParameters( pPlustek_Device dev, pScanParam pParam )
{
static u_char reg8, reg38[6], reg48[2];
pScanParam pdParam = &dev->scanning.sParam;
pHWDef hw = &dev->usbDev.HwSetting;
m_pParam = pParam;
DBG( _DBG_INFO, "usb_SetScanParameters()\n" );
if( !usb_IsScannerReady(dev))
return SANE_FALSE;
if(pParam->bCalibration == PARAM_Scan && pParam->bSource == SOURCE_ADF) {
/* HEINER: dSaveMoveSpeed is only used in func EjectPaper!!!
dSaveMoveSpeed = hw->dMaxMoveSpeed;
*/
hw->dMaxMoveSpeed = 1.0;
usb_MotorSelect( dev, SANE_TRUE );
usb_MotorOn( dev->fd, SANE_TRUE );
}
pParam->PhyDpi.x = usb_SetAsicDpiX( dev, pParam->UserDpi.x );
pParam->PhyDpi.y = usb_SetAsicDpiY( dev, pParam->UserDpi.y );
usb_SetColorAndBits( dev, pParam );
usb_GetScanRect( dev, pParam );
if( dev->caps.dwFlag & SFLAG_ADF ) {
if( pParam->bCalibration == PARAM_Scan ) {
if( pdParam->bSource == SOURCE_ADF ) {
a_bRegs[0x50] = 0;
a_bRegs[0x51] = 0x40;
if( pParam->PhyDpi.x <= 300)
a_bRegs[0x54] = (a_bRegs[0x54] & ~7) | 4; /* 3; */
else
a_bRegs[0x54] = (a_bRegs[0x54] & ~7) | 5; /* 4; */
} else {
a_bRegs[0x50] = dev->usbDev.bStepsToReverse;
a_bRegs[0x51] = hw->bReg_0x51;
a_bRegs[0x54] &= ~7;
}
} else
a_bRegs[0x50] = 0;
} else {
if( pParam->bCalibration == PARAM_Scan )
a_bRegs[0x50] = dev->usbDev.bStepsToReverse;
else
a_bRegs[0x50] = 0;
}
/* Assume we will not use ITA */
a_bRegs[0x19] = m_bIntTimeAdjust = 0;
/* Initiate variables */
/* Get variables for calculation algorithms */
if(!(pParam->bCalibration == PARAM_Scan &&
pParam->bSource == SOURCE_ADF && fLastScanIsAdf )) {
usb_GetLineLength ( dev );
usb_GetStepSize ( dev, pParam );
usb_GetDPD ( dev );
usb_GetMCLKDivider( dev, pParam );
usb_GetMotorParam ( dev, pParam );
}
/* Compute fast feed step size, use equation 3 and equation 8 */
if( m_dMCLKDivider < 1.0)
m_dMCLKDivider = 1.0;
m_wFastFeedStepSize = (u_short)(dwCrystalFrequency /
(m_dMCLKDivider * 8 * m_bCM * hw->dMaxMoveSpeed *
4 * hw->wMotorDpi));
if( m_bIntTimeAdjust != 0 )
m_wFastFeedStepSize /= m_bIntTimeAdjust;
if(a_bRegs[0x0a])
m_wFastFeedStepSize *= ((a_bRegs[0x0a] >> 2) + 2);
a_bRegs[0x48] = _HIBYTE( m_wFastFeedStepSize );
a_bRegs[0x49] = _LOBYTE( m_wFastFeedStepSize );
/* Compute the number of lines to scan using actual Y resolution */
usb_GetScanLinesAndSize( dev, pParam );
/* Pause limit should be bounded by total bytes to read
* so that the chassis will not move too far.
*/
usb_GetPauseLimit( dev, pParam );
/* For ADF .... */
if(pParam->bCalibration == PARAM_Scan && pParam->bSource == SOURCE_ADF) {
if( fLastScanIsAdf ) {
a_bRegs[0x08] = reg8;
memcpy( &a_bRegs[0x38], reg38, sizeof(reg38));
memcpy( &a_bRegs[0x48], reg48, sizeof(reg48));
} else {
reg8 = a_bRegs[0x08];
memcpy( reg38, &a_bRegs[0x38], sizeof(reg38));
memcpy( reg48, &a_bRegs[0x48], sizeof(reg48));
}
usb_MotorSelect( dev, SANE_TRUE );
}
/* Reset LM983x's state machine before setting register values */
if( !usbio_WriteReg( dev->fd, 0x18, 24 ))
return SANE_FALSE;
usleep(200 * 1000); /* Need to delay at least xxx microseconds */
/* HEINER: Reset necessary ???*/
#if 1
if( !usbio_WriteReg( dev->fd, 0x07, 0x20 ))
return SANE_FALSE;
#endif
if( !usbio_WriteReg( dev->fd, 0x19, 6 ))
return SANE_FALSE;
a_bRegs[0x07] = 0;
/* Set register values */
memset( &a_bRegs[0x03], 0, 3 );
memset( &a_bRegs[0x5C], 0, 0x7F-0x5C+1 );
/* 0x08 - 0x5E */
_UIO(sanei_lm983x_write( dev->fd, 0x08, &a_bRegs[0x08], 0x5E - 0x08+1, SANE_TRUE));
/* 0x03 - 0x05 */
_UIO(sanei_lm983x_write( dev->fd, 0x03, &a_bRegs[0x03], 3, SANE_TRUE));
/* 0x5C - 0x7F */
_UIO(sanei_lm983x_write( dev->fd, 0x5C, &a_bRegs[0x5C], 0x7F - 0x5C +1, SANE_TRUE));
if( !usbio_WriteReg( dev->fd, 0x07, 0 ))
return SANE_FALSE;
DBG( _DBG_INFO, "usb_SetScanParameters() done.\n" );
return SANE_TRUE;
}
/*.............................................................................
*
*/
static Bool usb_ScanBegin( pPlustek_Device dev, Bool fAutoPark )
{
u_char value;
pHWDef hw = &dev->usbDev.HwSetting;
DBG( _DBG_INFO, "usb_ScanBegin()\n" );
/* save the request for usb_ScanEnd () */
m_fAutoPark = fAutoPark;
/* Disable home sensor during scan, or the chassis cannot move */
value = ((m_pParam->bCalibration == PARAM_Scan &&
m_pParam->bSource == SOURCE_ADF)? (a_bRegs[0x58] & ~7): 0);
if(!usbio_WriteReg( dev->fd, 0x58, value ))
return SANE_FALSE;
/* Check if scanner is ready for receiving command */
if( !usb_IsScannerReady(dev))
return SANE_FALSE;
/* Flush cache */
for(;;) {
if( SANE_TRUE == cancelRead ) {
DBG( _DBG_INFO, "ScanBegin() - Cancel detected...\n" );
return SANE_FALSE;
}
_UIO(usbio_ReadReg( dev->fd, 0x01, &m_bOldScanData ));
if( m_bOldScanData ) {
u_long dwBytesToRead = m_bOldScanData * hw->wDRAMSize * 4;
u_char *pBuffer = malloc( sizeof(u_char) * dwBytesToRead );
_UIO(sanei_lm983x_read( dev->fd, 0x00, pBuffer, dwBytesToRead, SANE_FALSE ));
free( pBuffer );
} else
break;
}
/* Download map & Shading data */
if(( m_pParam->bCalibration == PARAM_Scan &&
!usb_MapDownload( dev, m_pParam->bDataType)) ||
/* !usb_LinearMapDownload( dev )) || */
!usb_DownloadShadingData( dev, m_pParam->bCalibration ))
return SANE_FALSE;
/* Move chassis and start to read image data */
if (!usbio_WriteReg( dev->fd, 0x07, 3 ))
return SANE_FALSE;
usbio_ReadReg( dev->fd, 0x01, &m_bOldScanData );
m_bOldScanData = 0; /* No data at all */
m_fStart = m_fFirst = SANE_TRUE; /* Prepare to read */
return SANE_TRUE;
}
/*.............................................................................
*
*/
static Bool usb_ScanEnd( pPlustek_Device dev )
{
u_char value;
DBG( _DBG_INFO, "usbDev_ScanEnd(), start=%u, park=%u\n",
m_fStart, m_fAutoPark );
usbio_ReadReg( dev->fd, 0x07, &value );
if( value == 3 || value != 2 )
usbio_WriteReg( dev->fd, 0x07, 0 );
if( m_fStart ) {
m_fStart = SANE_FALSE;
if( m_fAutoPark ) {
usb_ModuleToHome( dev, SANE_FALSE );
}
}
else if( SANE_TRUE == cancelRead ) {
usb_ModuleToHome( dev, SANE_FALSE );
}
return SANE_TRUE;
}
/*.............................................................................
*
*/
static Bool usb_IsDataAvailableInDRAM( pPlustek_Device dev )
{
/* Compute polling timeout
* Height (Inches) / MaxScanSpeed (Inches/Second) = Seconds to move the
* module from top to bottom. Then convert the seconds to miliseconds
* by multiply 1000. We add extra 2 seconds to get some tolerance.
*/
u_char a_bBand[3];
long dwTicks;
struct timeval t;
DBG( _DBG_INFO, "usb_IsDataAvailableInDRAM()\n" );
gettimeofday( &t, NULL);
dwTicks = t.tv_sec + 30;
for(;;) {
_UIO( sanei_lm983x_read( dev->fd, 0x01, a_bBand, 3, SANE_FALSE ));
gettimeofday( &t, NULL);
if( t.tv_sec > dwTicks )
break;
if( usb_IsEscPressed()) {
DBG(_DBG_INFO,"usb_IsDataAvailableInDRAM() - Cancel detected...\n");
return SANE_FALSE;
}
/* It is not stable for read */
if((a_bBand[0] != a_bBand[1]) && (a_bBand[1] != a_bBand[2]))
continue;
if( a_bBand[0] > m_bOldScanData ) {
if( m_pParam->bSource != SOURCE_Reflection )
usleep(1000*(30 * a_bRegs[0x08] * dev->usbDev.Caps.OpticDpi.x / 600));
else
usleep(1000*(20 * a_bRegs[0x08] * dev->usbDev.Caps.OpticDpi.x / 600));
DBG( _DBG_INFO, "Data is available\n" );
return SANE_TRUE;
}
}
DBG( _DBG_INFO, "NO Data available\n" );
return SANE_FALSE;
}
/*.............................................................................
*
*/
static Bool usb_ScanReadImage( pPlustek_Device dev, void *pBuf, u_long dwSize )
{
static u_long dwBytes = 0;
SANE_Status res;
DBG( _DBG_READ, "usb_ScanReadImage()\n" );
if( m_fFirst ) {
dwBytes = 0;
m_fFirst = SANE_FALSE;
/* Wait for data band ready */
if (!usb_IsDataAvailableInDRAM( dev )) {
DBG( _DBG_ERROR, "Nothing to read...\n" );
return SANE_FALSE;
}
}
/* HEINER: ADF */
#if 0
if(ScanInf.m_fADF == 1 && Scanning.sParam.bCalibration == PARAM_Scan)
{
if(dwBytes)
{
DWORD dw;
BOOL fRet;
if(dwSize < dwBytes)
dw = dwSize;
else
dw = dwBytes;
fRet = ReadData(0x00, (PBYTE)pBuf, dw);
dwBytes -= dw;
if(!dwBytes)
{
WriteRegister(0x07, 0); // To stop scanning
ScanInf.m_fADF++;
if(dwSize > dw)
ScanReadImage((PBYTE)pBuf + dw, dwSize - dw);
}
return fRet;
}
else if(!Hardware.SensorPaper())
{
dwBytes = (Scanning.sParam.PhyDpi.y * 18 / 25) * Scanning.sParam.Size.dwPhyBytes;
return ScanReadImage(pBuf, dwSize);
}
}
else if(ScanInf.m_fADF > 1)
{
DWORD dw;
if(Scanning.sParam.bBitDepth > 8)
{
for(dw = 0; dw < dwSize; dw += 2)
*((PWORD)pBuf + dw) = 0xfffc;
}
else
FillMemory(pBuf, dwSize, 0xff);
return TRUE;
}
#endif
res = sanei_lm983x_read(dev->fd, 0x00, (u_char *)pBuf, dwSize, SANE_FALSE);
DBG( _DBG_READ, "usb_ScanReadImage() done, result: %d\n", res );
if( SANE_STATUS_GOOD == res )
return SANE_TRUE;
DBG( _DBG_ERROR, "usb_ScanReadImage() failed\n" );
return SANE_FALSE;
}
/*.............................................................................
*
*/
static void usb_GetImageInfo( pImgDef pInfo, pWinInfo pSize )
{
DBG( _DBG_INFO, "usb_GetImageInfo()\n" );
pSize->dwPixels = (u_long)pInfo->crArea.cx * pInfo->xyDpi.x / 300UL;
pSize->dwLines = (u_long)pInfo->crArea.cy * pInfo->xyDpi.y / 300UL;
switch( pInfo->wDataType ) {
case COLOR_TRUE48:
pSize->dwBytes = pSize->dwPixels * 6UL;
break;
case COLOR_TRUE24:
pSize->dwBytes = pSize->dwPixels * 3UL;
break;
case COLOR_GRAY16:
pSize->dwBytes = pSize->dwPixels << 1;
break;
case COLOR_256GRAY:
pSize->dwBytes = pSize->dwPixels;
break;
default:
pSize->dwBytes = (pSize->dwPixels + 7UL) >> 3;
break;
}
}
/*.............................................................................
*
*/
static void usb_SaveImageInfo( pImgDef pInfo, pScanParam pParam )
{
DBG( _DBG_INFO, "usb_SaveImageInfo()\n" );
/* Dpi & Origins */
pParam->UserDpi = pInfo->xyDpi;
pParam->Origin.x = pInfo->crArea.x;
pParam->Origin.y = pInfo->crArea.y;
/* Source & Bits */
pParam->bBitDepth = 8;
switch( pInfo->wDataType ) {
case COLOR_TRUE48:
pParam->bBitDepth = 16;
case COLOR_TRUE24:
pParam->bDataType = SCANDATATYPE_Color;
pParam->bChannels = 3;
break;
case COLOR_GRAY16:
pParam->bBitDepth = 16;
case COLOR_256GRAY:
pParam->bDataType = SCANDATATYPE_Gray;
pParam->bChannels = 1;
break;
default:
pParam->bBitDepth = 1;
pParam->bDataType = SCANDATATYPE_BW;
pParam->bChannels = 1;
}
DBG( _DBG_INFO, "dwFlag = 0x%08lx\n", pInfo->dwFlag );
if( pInfo->dwFlag & SCANDEF_Transparency )
pParam->bSource = SOURCE_Transparency;
else if( pInfo->dwFlag & SCANDEF_Negative )
pParam->bSource = SOURCE_Negative;
else if( pInfo->dwFlag & SCANDEF_Adf )
pParam->bSource = SOURCE_ADF;
else
pParam->bSource = SOURCE_Reflection;
}
/* END PLUSTEK-USBSCAN.C ....................................................*/