summaryrefslogtreecommitdiff
path: root/backend/plustek-usbscan.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/plustek-usbscan.c')
-rw-r--r--backend/plustek-usbscan.c1666
1 files changed, 1666 insertions, 0 deletions
diff --git a/backend/plustek-usbscan.c b/backend/plustek-usbscan.c
new file mode 100644
index 0000000..c8824fc
--- /dev/null
+++ b/backend/plustek-usbscan.c
@@ -0,0 +1,1666 @@
+/*.............................................................................
+ * Project : SANE library for Plustek flatbed scanners.
+ *.............................................................................
+ */
+
+/** @file plustek-usbscan.c
+ * @brief Scanning...
+ *
+ * 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 - minor fixes
+ * - 0.42 - added some stuff for CIS devices
+ * - 0.43 - no changes
+ * - 0.44 - added CIS specific settings and calculations
+ * - removed usb_IsEscPressed
+ * - 0.45 - fixed the special setting stuff for CanoScan
+ * - fixed pixel calculation for binary modes
+ * - fixed cancel hang problem
+ * - fixed CIS PhyBytes adjustment
+ * - removed CanoScan specific setting stuff
+ * - 0.46 - fixed problem in usb_SetScanParameters()
+ * - 0.47 - no changes
+ * - 0.48 - minor fixes
+ * - 0.49 - a_bRegs is now part of the device structure
+ * - using now PhyDpi.y as selector for the motor MCLK range
+ * - changed usb_MapDownload prototype
+ * - 0.50 - cleanup
+ * - 0.51 - added usb_get_res() and usb_GetPhyPixels()
+ * - 0.52 - removed stuff, that will most probably never be used
+ * .
+ * <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>
+ */
+
+#define DIVIDER 8
+
+/** array used to get motor-settings and mclk-settings
+ */
+static int dpi_ranges[] = { 75,100,150,200,300,400,600,800,1200,2400 };
+
+static u_char bMaxITA;
+
+static SANE_Bool m_fAutoPark;
+static SANE_Bool m_fFirst;
+static double m_dHDPIDivider;
+static double m_dMCLKDivider;
+static ScanParam *m_pParam;
+static u_char m_bLineRateColor;
+static u_char m_bCM;
+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 SANE_Bool m_fStart = SANE_FALSE;
+
+/* Prototype... */
+static SANE_Bool usb_DownloadShadingData( Plustek_Device*, u_char );
+
+/** returns the min of the two values val1 and val2
+ * @param val1 - first parameter
+ * @param val2 - second parameter
+ * @return val1 if val1 < val2, else val1
+ */
+static u_long
+usb_min( u_long val1, u_long val2 )
+{
+ if( val1 > val2 )
+ return val2;
+ return val1;
+}
+
+/** returns the max of the two values val1 and val2
+ * @param val1 - first parameter
+ * @param val2 - second parameter
+ * @return val1 if val1 > val2, else val2
+ */
+static u_long
+usb_max( u_long val1, u_long val2 )
+{
+ if( val1 > val2 )
+ return val1;
+ return val2;
+}
+
+/** function to get the possible resolution for a specific base resolution,
+ * according to the dividers a LM983x offers.
+ * @param base - scanners optical resolution
+ * @param idx - which divider to use
+ */
+static u_short
+usb_get_res( u_short base, u_short idx )
+{
+ double div_list[DIVIDER] = { 12.0, 8.0, 6.0, 4.0, 3.0, 2.0, 1.5, 1.0 };
+
+ if( idx == 0 || idx > DIVIDER )
+ return 0;
+
+ return (u_short)((double)base/div_list[idx-1]);
+}
+
+/** Set the horizontal DPI divider.
+ * Affected registers:<br>
+ * 0x09 - Horizontal DPI divider HDPI_DIV<br>
+ *
+ * @param dev - pointer to our device structure,
+ * it should contain all we need
+ * @param xdpi - user specified horizontal resolution
+ * @return - the function returns the "normalized" horizontal resolution.
+ */
+static u_short
+usb_SetAsicDpiX( Plustek_Device *dev, u_short xdpi )
+{
+ u_short res;
+ ScanDef *scanning = &dev->scanning;
+ DCapsDef *scaps = &dev->usbDev.Caps;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ /* limit xdpi to lower value for certain devices...
+ */
+ if( scaps->OpticDpi.x == 1200 &&
+ scanning->sParam.bDataType != SCANDATATYPE_Color &&
+ xdpi < 150 &&
+ scanning->sParam.bDataType == SCANDATATYPE_BW ) {
+ xdpi = 150;
+ DBG( _DBG_INFO2, "* LIMIT XDPI to %udpi\n", xdpi );
+ }
+
+ m_dHDPIDivider = (double)scaps->OpticDpi.x / xdpi;
+
+ if (m_dHDPIDivider < 1.5)
+ {
+ m_dHDPIDivider = 1.0;
+ regs[0x09] = 0;
+ }
+ else if (m_dHDPIDivider < 2.0)
+ {
+ m_dHDPIDivider = 1.5;
+ regs[0x09] = 1;
+ }
+ else if (m_dHDPIDivider < 3.0)
+ {
+ m_dHDPIDivider = 2.0;
+ regs[0x09] = 2;
+ }
+ else if (m_dHDPIDivider < 4.0)
+ {
+ m_dHDPIDivider = 3.0;
+ regs[0x09] = 3;
+ }
+ else if (m_dHDPIDivider < 6.0)
+ {
+ m_dHDPIDivider = 4.0;
+ regs[0x09] = 4;
+ }
+ else if (m_dHDPIDivider < 8.0)
+ {
+ m_dHDPIDivider = 6.0;
+ regs[0x09] = 5;
+ }
+ else if (m_dHDPIDivider < 12.0)
+ {
+ m_dHDPIDivider = 8.0;
+ regs[0x09] = 6;
+ }
+ else
+ {
+ m_dHDPIDivider = 12.0;
+ regs[0x09] = 7;
+ }
+
+ /* adjust, if any turbo/preview mode is set, should be 0 here... */
+ if( regs[0x0a] )
+ regs[0x09] -= ((regs[0x0a] >> 2) + 2);
+
+ DBG( _DBG_INFO2, "* HDPI: %.3f\n", m_dHDPIDivider );
+ res = (u_short)((double)scaps->OpticDpi.x / m_dHDPIDivider);
+
+ DBG( _DBG_INFO2, "* XDPI=%u, HDPI=%.3f\n", res, m_dHDPIDivider );
+ return res;
+}
+
+/**
+ * @param dev - pointer to our device structure,
+ * it should contain all we need
+ * @param ydpi - user specified vertical resolution
+ * @return -
+ */
+static u_short
+usb_SetAsicDpiY( Plustek_Device *dev, u_short ydpi )
+{
+ ScanDef *scanning = &dev->scanning;
+ DCapsDef *sCaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ u_short wMinDpi, wDpi;
+
+ if(0 != sCaps->bSensorDistance )
+ wMinDpi = sCaps->OpticDpi.y / sCaps->bSensorDistance;
+ else
+ wMinDpi = 75;
+
+ /* Here we might have to check against the MinDpi value ! */
+ wDpi = (ydpi + wMinDpi - 1) / wMinDpi * wMinDpi;
+
+ /*
+ * HEINER: added '*2'
+ */
+ if( wDpi > sCaps->OpticDpi.y * 2 )
+ wDpi = sCaps->OpticDpi.y * 2;
+
+ if( (hw->motorModel == MODEL_Tokyo600) ||
+ !_IS_PLUSTEKMOTOR(hw->motorModel)) {
+ /* 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;
+ }
+ }
+
+ DBG( _DBG_INFO2, "* YDPI=%u, MinDPIY=%u\n", wDpi, wMinDpi );
+ return wDpi;
+}
+
+/** set color mode and sensor configuration stuff, according to the data mode
+ * Affected registers:<br>
+ * 0x26 - 0x27 - Color Mode settings<br>
+ * 0x0f - 0x18 - Sensor Configuration - directly from the HwDefs<br>
+ * 0x09 - add Data Mode and Pixel Packing<br>
+ *
+ * @param dev - pointer to our device structure,
+ * it should contain all we need
+ * @param pParam - pointer to the current scan parameters
+ * @return - Nothing
+ */
+static void
+usb_SetColorAndBits( Plustek_Device *dev, ScanParam *pParam )
+{
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ /* Set pixel packing, data mode and AFE operation */
+ switch( pParam->bDataType ) {
+ case SCANDATATYPE_Color:
+ m_bCM = 3;
+ regs[0x26] = hw->bReg_0x26 & 0x7;
+
+ /* if set to one channel color, we select the blue channel
+ * as input source, this is the default, but I don't know
+ * what happens, if we deselect this
+ */
+ if( regs[0x26] & _ONE_CH_COLOR )
+ regs[0x26] |= (_BLUE_CH | 0x01);
+
+ memcpy( &regs[0x0f], hw->bReg_0x0f_Color, 10 );
+ break;
+
+ case SCANDATATYPE_Gray:
+ m_bCM = 1;
+ regs[0x26] = (hw->bReg_0x26 & 0x18) | 0x04;
+ memcpy( &regs[0x0f], hw->bReg_0x0f_Mono, 10 );
+ break;
+
+ case SCANDATATYPE_BW:
+ m_bCM = 1;
+ regs[0x26] = (hw->bReg_0x26 & 0x18) | 0x04;
+ memcpy( &regs[0x0f], hw->bReg_0x0f_Mono, 10 );
+ break;
+ }
+
+ regs[0x27] = hw->bReg_0x27;
+
+ if( pParam->bBitDepth > 8 ) {
+ regs[0x09] |= 0x20; /* 14/16bit image data */
+
+ } else if( pParam->bBitDepth == 8 ) {
+ regs[0x09] |= 0x18; /* 8bits/per pixel */
+ }
+}
+
+/**
+ * Data output from NS983X should be times of 2-byte and every line
+ * will append 2 status bytes
+ */
+static void
+usb_GetPhyPixels( Plustek_Device *dev, ScanParam *sp )
+{
+ sp->Size.dwValidPixels = sp->Size.dwPixels * sp->PhyDpi.x / sp->UserDpi.x;
+
+ if (sp->bBitDepth == 1) {
+
+ /* Pixels should be times of 16 */
+ sp->Size.dwPhyPixels =
+ (sp->Size.dwValidPixels + 15UL) & 0xfffffff0UL;
+ sp->Size.dwPhyBytes = sp->Size.dwPhyPixels / 8UL + 2UL;
+
+ } else if (sp->bBitDepth == 8) {
+
+ /* Pixels should be times of 2 */
+ sp->Size.dwPhyPixels = (sp->Size.dwValidPixels + 1UL) & 0xfffffffeUL;
+ sp->Size.dwPhyBytes = sp->Size.dwPhyPixels * sp->bChannels + 2UL;
+
+ /* need to be adjusted fo CIS devices in color mode */
+ if(usb_IsCISDevice( dev ) && (sp->bDataType == SCANDATATYPE_Color)) {
+ sp->Size.dwPhyBytes *= 3;
+ }
+ }
+ else /* sp->bBitDepth == 16 */
+ {
+ sp->Size.dwPhyPixels = sp->Size.dwValidPixels;
+ sp->Size.dwPhyBytes = sp->Size.dwPhyPixels * 2 * sp->bChannels + 2UL;
+
+ /* need to be adjusted fo CIS devices in color mode */
+ if(usb_IsCISDevice( dev ) && (sp->bDataType == SCANDATATYPE_Color)) {
+ sp->Size.dwPhyBytes *= 3;
+ }
+ }
+}
+
+/** Calculate basic image settings like the number of physical bytes per line
+ * etc...
+ * Affected registers:<br>
+ * 0x22/0x23 - Data Pixels Start<br>
+ * 0x24/0x25 - Data Pixels End<br>
+ * 0x4a/0x4b - Full Steps to Skip at Start of Scan
+ *
+ * @param dev - pointer to our device structure,
+ * it should contain all we need
+ * @param pParam - pointer to the current scan parameters
+ * @return - Nothing
+ */
+static void
+usb_GetScanRect( Plustek_Device *dev, ScanParam *sp )
+{
+ u_short wDataPixelStart, wLineEnd;
+
+ DCapsDef *sCaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ /* Convert pixels to physical dpi based */
+ usb_GetPhyPixels( dev, sp );
+
+ /* Compute data start pixel */
+#if 0
+/* HEINER: check ADF stuff... */
+ if(sp->bCalibration != PARAM_Gain &&
+ sp->bCalibration != PARAM_Offset && ScanInf.m_fADF)
+ wDataPixelStart = 2550 * sCaps->OpticDpi.x / 300UL -
+ (u_short)(m_dHDPIDivider * sp->Size.dwValidPixels + 0.5);
+ else
+#endif
+ wDataPixelStart = (u_short)((u_long)sp->Origin.x *
+ sCaps->OpticDpi.x / 300UL);
+
+ /* during the calibration steps, we read the entire CCD data
+ */
+ if((sp->bCalibration != PARAM_Gain)&&(sp->bCalibration != PARAM_Offset)) {
+/* HEINER: check ADF stuff... */
+#if 0
+ if(ScanInf.m_fADF) {
+ wDataPixelStart = 2550 * sCaps->OpticDpi.x / 300UL -
+ (u_short)(m_dHDPIDivider * sp->Size.dwValidPixels + 0.5);
+ }
+#endif
+ wDataPixelStart += hw->wActivePixelsStart;
+ }
+
+ wLineEnd = wDataPixelStart + (u_short)(m_dHDPIDivider *
+ sp->Size.dwPhyPixels + 0.5);
+
+ DBG( _DBG_INFO2, "* DataPixelStart=%u, LineEnd=%u\n",
+ wDataPixelStart, wLineEnd );
+ if( wDataPixelStart & 1 ) {
+
+ wDataPixelStart++;
+ wLineEnd++;
+
+ DBG( _DBG_INFO2, "* DataPixelStart=%u, LineEnd=%u (ADJ)\n",
+ wDataPixelStart, wLineEnd );
+ }
+
+ regs[0x22] = _HIBYTE( wDataPixelStart );
+ regs[0x23] = _LOBYTE( wDataPixelStart );
+
+ /* should match: wLineEnd-wDataPixelStart%(m_dHDPIDivider*2) = 0!! */
+ regs[0x24] = _HIBYTE( wLineEnd );
+ regs[0x25] = _LOBYTE( wLineEnd );
+
+ DBG( _DBG_INFO2, ">> End-Start=%u, HDPI=%.2f\n",
+ wLineEnd-wDataPixelStart, m_dHDPIDivider);
+
+ /* Y origin */
+ if( sp->bCalibration == PARAM_Scan ) {
+
+ if( hw->motorModel == MODEL_Tokyo600 ) {
+
+ if(sp->PhyDpi.x <= 75)
+ sp->Origin.y += 20;
+ else if(sp->PhyDpi.x <= 100)
+ {
+ if (sp->bDataType == SCANDATATYPE_Color)
+ sp->Origin.y += 0;
+ else
+ sp->Origin.y -= 6;
+ }
+ else if(sp->PhyDpi.x <= 150)
+ {
+ if (sp->bDataType == SCANDATATYPE_Color)
+ sp->Origin.y -= 0;
+ }
+ else if(sp->PhyDpi.x <= 200)
+ {
+ if (sp->bDataType == SCANDATATYPE_Color)
+ sp->Origin.y -= 10;
+ else
+ sp->Origin.y -= 4;
+ }
+ else if(sp->PhyDpi.x <= 300)
+ {
+ if (sp->bDataType == SCANDATATYPE_Color)
+ sp->Origin.y += 16;
+ else
+ sp->Origin.y -= 18;
+ }
+ else if(sp->PhyDpi.x <= 400)
+ {
+ if (sp->bDataType == SCANDATATYPE_Color)
+ sp->Origin.y += 15;
+ else if(sp->bDataType == SCANDATATYPE_BW)
+ sp->Origin.y += 4;
+ }
+ else /* if(sp->PhyDpi.x <= 600) */
+ {
+ if (sp->bDataType == SCANDATATYPE_Gray)
+ sp->Origin.y += 4;
+ }
+ }
+
+ /* Add gray mode offset (Green offset, we assume the CCD are
+ * always be RGB or BGR order).
+ */
+ if (sp->bDataType != SCANDATATYPE_Color)
+ sp->Origin.y += (u_long)(300UL *
+ sCaps->bSensorDistance / sCaps->OpticDpi.y);
+ }
+
+ sp->Origin.y=(u_short)((u_long)sp->Origin.y * hw->wMotorDpi/300UL);
+
+ /* Something wrong, but I can not find it. */
+ if( hw->motorModel == MODEL_HuaLien && sCaps->OpticDpi.x == 600)
+ sp->Origin.y = sp->Origin.y * 297 / 298;
+
+ DBG(_DBG_INFO2,"* Full Steps to Skip at Start = 0x%04x\n",
+ sp->Origin.y);
+
+ regs[0x4a] = _HIBYTE( sp->Origin.y );
+ regs[0x4b] = _LOBYTE( sp->Origin.y );
+}
+
+/** preset scan stepsize and fastfeed stepsize
+ */
+static void
+usb_PresetStepSize( Plustek_Device *dev, ScanParam *pParam )
+{
+ u_short ssize;
+ double mclkdiv = pParam->dMCLK;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ ssize = (u_short)((double)CRYSTAL_FREQ / ( mclkdiv * 8.0 *
+ (double)m_bCM * hw->dMaxMotorSpeed * 4.0 * (double)hw->wMotorDpi));
+
+ regs[0x46] = _HIBYTE( ssize );
+ regs[0x47] = _LOBYTE( ssize );
+ regs[0x48] = _HIBYTE( ssize );
+ regs[0x49] = _LOBYTE( ssize );
+
+ DBG( _DBG_INFO2, "* StepSize(Preset) = %u (0x%04x)\n", ssize, ssize );
+}
+
+/** calculate default phase difference DPD
+ */
+static void
+usb_GetDPD( Plustek_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 */
+
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ qtcnt = (regs[0x51] & 0x30) >> 4; /* quarter speed count */
+ hfcnt = (regs[0x51] & 0xc0) >> 6; /* half speed count */
+
+ if( _LM9831 == hw->chip )
+ strev = regs[0x50] & 0x3f; /* steps to reverse */
+ else /* LM9832/3 */
+ {
+ if (qtcnt == 3)
+ qtcnt = 8;
+ if (hfcnt == 3)
+ hfcnt = 8;
+ strev = regs[0x50]; /* steps to reverse */
+ }
+
+ st = regs[0x46] * 256 + regs[0x47]; /* scan step size */
+
+ if (m_wLineLength == 0)
+ dpd = 0;
+ else
+ {
+ dpd = (((qtcnt * 4) + (hfcnt * 2) + strev) * 4 * st) %
+ (m_wLineLength * m_bLineRateColor);
+ DBG( _DBG_INFO2, "* DPD =%u (0x%04x)\n", dpd, dpd );
+ dpd = m_wLineLength * m_bLineRateColor - dpd;
+ }
+
+ DBG( _DBG_INFO2, "* DPD =%u (0x%04x), step size=%u, steps2rev=%u\n",
+ dpd, dpd, st, strev);
+ DBG( _DBG_INFO2, "* llen=%u, lineRateColor=%u, qtcnt=%u, hfcnt=%u\n",
+ m_wLineLength, m_bLineRateColor, qtcnt, hfcnt );
+
+ regs[0x51] |= (u_char)((dpd >> 16) & 0x03);
+ regs[0x52] = (u_char)(dpd >> 8);
+ regs[0x53] = (u_char)(dpd & 0xFF);
+}
+
+#define MCLKDIV_SCALING 2
+#define _MIN(a,b) ((a) < (b) ? (a) : (b))
+#define _MAX(a,b) ((a) > (b) ? (a) : (b))
+
+/**
+ */
+static int
+usb_GetMCLKDiv( Plustek_Device *dev )
+{
+ int j, pixelbits, pixelsperline, r;
+ int minmclk, maxmclk, mclkdiv;
+ double hdpi, min_int_time;
+ u_char *regs = dev->usbDev.a_bRegs;
+ HWDef *hw = &dev->usbDev.HwSetting;
+
+ DBG( _DBG_INFO, "usb_GetMCLKDiv()\n" );
+
+ r = 8; /* line rate */
+ if ((regs[0x26] & 7) == 0)
+ r = 24; /* pixel rate */
+
+ /* use high or low res min integration time */
+ min_int_time = ((regs[0x9]&7) > 2 ? hw->dMinIntegrationTimeLowres:
+ hw->dMinIntegrationTimeHighres);
+
+ minmclk = (int)ceil((double) MCLKDIV_SCALING * CRYSTAL_FREQ *
+ min_int_time /((double)1000. * r * m_wLineLength));
+ minmclk = _MAX(minmclk,MCLKDIV_SCALING);
+
+ maxmclk = (int)(32.5*MCLKDIV_SCALING + .5);
+
+ DBG(_DBG_INFO2,"- lower mclkdiv limit=%f\n",(double)minmclk/MCLKDIV_SCALING);
+ DBG(_DBG_INFO2,"- upper mclkdiv limit=%f\n",(double)maxmclk/MCLKDIV_SCALING);
+
+ /* get the bits per pixel */
+ switch (regs[0x9] & 0x38) {
+ case 0: pixelbits=1; break;
+ case 0x8: pixelbits=2; break;
+ case 0x10: pixelbits=4; break;
+ case 0x18: pixelbits=8; break;
+ default: pixelbits=16;break;
+ }
+
+ /* compute the horizontal dpi (pixels per inch) */
+ j = regs[0x9] & 0x7;
+ hdpi = ((j&1)*.5+1)*(j&2?2:1)*(j&4?4:1);
+
+ pixelsperline = (int)((256*regs[0x24]+regs[0x25]-256*regs[0x22]-regs[0x23])
+ *pixelbits/(hdpi * 8));
+ mclkdiv = (int)ceil((double)MCLKDIV_SCALING * pixelsperline * CRYSTAL_FREQ
+ /((double) 8. * m_wLineLength * dev->transferRate));
+
+ DBG( _DBG_INFO2, "- hdpi = %.3f\n", hdpi );
+ DBG( _DBG_INFO2, "- pixelbits = %u\n", pixelbits );
+ DBG( _DBG_INFO2, "- pixelsperline = %u\n", pixelsperline );
+ DBG( _DBG_INFO2, "- linelen = %u\n", m_wLineLength );
+ DBG( _DBG_INFO2, "- transferrate = %lu\n", dev->transferRate );
+ DBG( _DBG_INFO2, "- MCLK Divider = %u\n", mclkdiv/MCLKDIV_SCALING );
+
+ mclkdiv = _MAX(mclkdiv,minmclk);
+ mclkdiv = _MIN(mclkdiv,maxmclk);
+ DBG( _DBG_INFO2, "- Current MCLK Divider = %u\n", mclkdiv/MCLKDIV_SCALING );
+
+ if( dev->transferRate == 2000000 ) {
+ while (mclkdiv * hdpi < 6.*MCLKDIV_SCALING) {
+ mclkdiv++;
+ }
+ DBG( _DBG_INFO2, "- HIGHSPEED MCLK Divider = %u\n",
+ mclkdiv/MCLKDIV_SCALING );
+ }
+
+ return mclkdiv;
+}
+
+/** Plusteks' poor-man MCLK calculation...
+ * at least we give the master clock divider and adjust the step size
+ * and integration time (for 14/16 bit modes)
+ */
+static double
+usb_GetMCLKDivider( Plustek_Device *dev, ScanParam *pParam )
+{
+ double dMaxIntegrationTime;
+ double dMaxMCLKDivider;
+
+ DCapsDef *sCaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ DBG( _DBG_INFO, "usb_GetMCLKDivider()\n" );
+
+ if( dev->transferRate == 2000000 ) {
+ int mclkdiv = usb_GetMCLKDiv(dev);
+ pParam->dMCLK = (double)mclkdiv/MCLKDIV_SCALING;
+ }
+
+ 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 ) {
+
+ usb_GetMCLKDiv(dev);
+
+ /* 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->motorModel == MODEL_HuaLien &&
+ sCaps->bCCD == kNEC3799 && m_bIntTimeAdjust > bMaxITA) {
+ m_bIntTimeAdjust = bMaxITA;
+ }
+
+ if(((hw->motorModel == MODEL_HP) && (sCaps->bCCD == kNECSLIM))/* ||
+ ( regs[0x26] & _ONE_CH_COLOR )*/) {
+
+ bMaxITA = (u_char)floor((m_dMCLKDivider + 1) / 2.0);
+
+ DBG( _DBG_INFO2, "* MaxITA (HP) = %u\n", bMaxITA );
+ if( m_bIntTimeAdjust > bMaxITA ) {
+ DBG( _DBG_INFO, "* ITA (%u) limited\n", m_bIntTimeAdjust );
+ m_bIntTimeAdjust = bMaxITA;
+ }
+ }
+ }
+ }
+ DBG( _DBG_INFO2, "* Integration Time Adjust = %u (HDPI=%.3f,MCLKD=%.3f)\n",
+ m_bIntTimeAdjust, m_dHDPIDivider, m_dMCLKDivider );
+
+ regs[0x08] = (u_char)((m_dMCLKDivider - 1) * 2);
+ regs[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;
+
+ regs[0x46] = _HIBYTE(m_wStepSize);
+ regs[0x47] = _LOBYTE(m_wStepSize);
+
+ DBG( _DBG_INFO2, "* Stepsize = %u, 0x46=0x%02x 0x47=0x%02x\n",
+ m_wStepSize, regs[0x46], regs[0x47] );
+ usb_GetDPD( dev );
+ }
+
+ /* Compute maximum MCLK divider base on maximum integration time for
+ * high lamp PWM, use equation 4
+ */
+ dMaxIntegrationTime = hw->dIntegrationTimeHighLamp;
+ dMaxMCLKDivider = (double)CRYSTAL_FREQ * dMaxIntegrationTime /
+ (1000 * 8 * m_bCM * m_wLineLength);
+
+ /* Determine lamp PWM setting */
+ if( m_dMCLKDivider > dMaxMCLKDivider ) {
+
+ DBG( _DBG_INFO2, "* Setting GreenPWMDutyCycleLow\n" );
+ regs[0x2a] = _HIBYTE( hw->wGreenPWMDutyCycleLow );
+ regs[0x2b] = _LOBYTE( hw->wGreenPWMDutyCycleLow );
+
+ } else {
+
+ DBG( _DBG_INFO2, "* Setting GreenPWMDutyCycleHigh\n" );
+ regs[0x2a] = _HIBYTE( hw->wGreenPWMDutyCycleHigh );
+ regs[0x2b] = _LOBYTE( hw->wGreenPWMDutyCycleHigh );
+ }
+
+ DBG( _DBG_INFO2, "* Current MCLK Divider = %f\n", m_dMCLKDivider );
+ return m_dMCLKDivider;
+}
+
+/** calculate the step size of each scan step
+ */
+static void
+usb_GetStepSize( Plustek_Device *dev, ScanParam *pParam )
+{
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ /* 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;
+
+ regs[0x46] = _HIBYTE( m_wStepSize );
+ regs[0x47] = _LOBYTE( m_wStepSize );
+
+ DBG( _DBG_INFO2, "* Stepsize = %u, 0x46=0x%02x 0x47=0x%02x\n",
+ m_wStepSize, regs[0x46], regs[0x47] );
+}
+
+/**
+ */
+static void
+usb_GetLineLength( Plustek_Device *dev, ScanParam *param )
+{
+/* [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;
+ u_short le;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ ClkMotorDef *motor = usb_GetMotorSet( hw->motorModel );
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ tpspd = (regs[0x0a] & 0x0c) >> 2; /* turbo/preview mode speed */
+ tpsel = regs[0x0a] & 3; /* turbo/preview mode select */
+
+ gbnd = (regs[0x0e] & 0xf0) >> 4; /* TR fi1 guardband duration */
+ dur = (regs[0x0e] & 0xf); /* TR pulse duration */
+
+ ntr = regs[0x0d] / 128; /* number of tr pulses */
+
+ afeop = regs[0x26] & 7; /* afe op - 3 channel or 1 channel */
+
+ tradj = regs[0x19] & 0x7f; /* integration time adjust */
+ en_tradj = (tradj) ? 1 : 0;
+
+ ctmode = (regs[0x0b] >> 3) & 3; /* cis tr timing mode */
+
+ m_bLineRateColor = 1;
+ if (afeop == 1 || afeop == 5) /* if 3 channel line or 1 channel mode b */
+ m_bLineRateColor = 3;
+
+ /* according to turbo/preview mode to set value */
+ if( tpsel == 0 ) {
+ tp = 1;
+ } else {
+ tp = tpspd + 2;
+ if( tp == 5 )
+ tp++;
+ }
+
+ b = 1;
+ if( ctmode == 0 ) { /* CCD mode scanner*/
+
+ b = (ntr + 1) * ((2 * gbnd) + dur + 1);
+ b += (1 - ntr) * en_tradj;
+ }
+ if( ctmode == 2 ) /* CIS mode scanner */
+ b = 3;
+
+ /* it might be necessary to tweak this value, to keep the
+ * MCLK constant - see some sheet-fed devices
+ */
+ le = hw->wLineEnd;
+ if (motor->dpi_thresh != 0) {
+ if( param->PhyDpi.y <= motor->dpi_thresh) {
+ le = motor->lineend;
+ DBG( _DBG_INFO2, "* Adjusting lineend: %u\n", le);
+ }
+ regs[0x20] = _HIBYTE( le );
+ regs[0x21] = _LOBYTE( le );
+ }
+
+ tr = m_bLineRateColor * (le + 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 * le + 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 * le + 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 + le + 4 + tr_fast_pix;
+ if (extra_pix == 0)
+ tr++;
+ tr *= m_bLineRateColor;
+ }
+ m_wLineLength = tr / m_bLineRateColor;
+
+ DBG( _DBG_INFO2, "* LineLength=%d, LineRateColor=%u\n",
+ m_wLineLength, m_bLineRateColor );
+}
+
+/** usb_GetMotorParam
+ * registers 0x56, 0x57
+ */
+static void
+usb_GetMotorParam( Plustek_Device *dev, ScanParam *pParam )
+{
+ int idx, i;
+ ClkMotorDef *clk;
+ MDef *md;
+ DCapsDef *sCaps = &dev->usbDev.Caps;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ if (!_IS_PLUSTEKMOTOR(hw->motorModel)) {
+
+ clk = usb_GetMotorSet( hw->motorModel );
+ md = clk->motor_sets;
+ idx = 0;
+ for( i = 0; i < _MAX_CLK; i++ ) {
+ if( pParam->PhyDpi.y <= dpi_ranges[i] )
+ break;
+ idx++;
+ }
+ if( idx >= _MAX_CLK )
+ idx = _MAX_CLK - 1;
+
+ regs[0x56] = md[idx].pwm;
+ regs[0x57] = md[idx].pwm_duty;
+
+ regs[0x43] = 0;
+ regs[0x44] = 0;
+
+ if( md[idx].scan_lines_per_line > 1 ) {
+
+ if((pParam->bBitDepth > 8) &&
+ (pParam->bDataType == SCANDATATYPE_Color)) {
+
+ regs[0x43] = 0xff;
+ regs[0x44] = md[idx].scan_lines_per_line;
+
+ DBG( _DBG_INFO2, "* Line Skipping : 0x43=0x%02x, 0x44=0x%02x\n",
+ regs[0x43], regs[0x44] );
+ }
+ }
+ } else {
+
+ if( sCaps->OpticDpi.x == 1200 ) {
+
+ switch( hw->motorModel ) {
+
+ case MODEL_HuaLien:
+ case MODEL_KaoHsiung:
+ default:
+ if(pParam->PhyDpi.x <= 200)
+ {
+ regs[0x56] = 1;
+ regs[0x57] = 48; /* 63; */
+ }
+ else if(pParam->PhyDpi.x <= 300)
+ {
+ regs[0x56] = 2; /* 8; */
+ regs[0x57] = 48; /* 56; */
+ }
+ else if(pParam->PhyDpi.x <= 400)
+ {
+ regs[0x56] = 8;
+ regs[0x57] = 48;
+ }
+ else if(pParam->PhyDpi.x <= 600)
+ {
+ regs[0x56] = 2; /* 10; */
+ regs[0x57] = 48; /* 56; */
+ }
+ else /* pParam->PhyDpi.x == 1200) */
+ {
+ regs[0x56] = 1; /* 8; */
+ regs[0x57] = 48; /* 56; */
+ }
+ break;
+ }
+ } else {
+ switch ( hw->motorModel ) {
+
+ case MODEL_Tokyo600:
+ regs[0x56] = 16;
+ regs[0x57] = 4; /* 2; */
+ break;
+ case MODEL_HuaLien:
+ {
+ if(pParam->PhyDpi.x <= 200)
+ {
+ regs[0x56] = 64; /* 24; */
+ regs[0x57] = 4; /* 16; */
+ }
+ else if(pParam->PhyDpi.x <= 300)
+ {
+ regs[0x56] = 64; /* 16; */
+ regs[0x57] = 4; /* 16; */
+ }
+ else if(pParam->PhyDpi.x <= 400)
+ {
+ regs[0x56] = 64; /* 16; */
+ regs[0x57] = 4; /* 16; */
+ }
+ else /* if(pParam->PhyDpi.x <= 600) */
+ {
+/* HEINER: check ADF stuff... */
+#if 0
+ if(ScanInf.m_fADF)
+ {
+ regs[0x56] = 8;
+ regs[0x57] = 48;
+ }
+ else
+#endif
+ {
+ regs[0x56] = 64; /* 2; */
+ regs[0x57] = 4; /* 48; */
+ }
+ }
+ }
+ break;
+ case MODEL_KaoHsiung:
+ default:
+ if(pParam->PhyDpi.x <= 200)
+ {
+ regs[0x56] = 24;
+ regs[0x57] = 16;
+ }
+ else if(pParam->PhyDpi.x <= 300)
+ {
+ regs[0x56] = 16;
+ regs[0x57] = 16;
+ }
+ else if(pParam->PhyDpi.x <= 400)
+ {
+ regs[0x56] = 16;
+ regs[0x57] = 16;
+ }
+ else /* if(pParam->PhyDpi.x <= 600) */
+ {
+ regs[0x56] = 2;
+ regs[0x57] = 48;
+ }
+ break;
+ }
+ }
+ }
+
+ DBG( _DBG_INFO2, "* MOTOR-Settings: PWM=0x%02x, PWM_DUTY=0x%02x\n",
+ regs[0x56], regs[0x57] );
+}
+
+/**
+ */
+static void
+usb_GetPauseLimit( Plustek_Device *dev, ScanParam *pParam )
+{
+ int coeffsize, scaler;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ scaler = 1;
+ if( hw->bReg_0x26 & _ONE_CH_COLOR ) {
+ if( pParam->bDataType == SCANDATATYPE_Color ) {
+ scaler = 3;
+ }
+ }
+
+ /* compute size of coefficient ram */
+ coeffsize = 4 + 16 + 16; /* gamma and shading and offset */
+
+ /* if 16 bit, then not all is used */
+ if( regs[0x09] & 0x20 ) {
+ coeffsize = 16 + 16; /* no gamma */
+ }
+ coeffsize *= (2*3); /* 3 colors and 2 bytes/word */
+
+
+ /* Get available buffer size in KB
+ * for 512kb this will be 296
+ * for 2Mb this will be 1832
+ */
+ m_dwPauseLimit = (u_long)(hw->wDRAMSize - (u_long)(coeffsize));
+ m_dwPauseLimit -= ((pParam->Size.dwPhyBytes*scaler) / 1024 + 1);
+
+ /* If not reversing, take into account the steps to reverse */
+ if( regs[0x50] == 0 )
+ m_dwPauseLimit -= ((regs[0x54] & 7) *
+ (pParam->Size.dwPhyBytes * scaler) + 1023) / 1024;
+
+ DBG( _DBG_INFO2, "* PL=%lu, coeffsize=%u, scaler=%u\n",
+ m_dwPauseLimit, coeffsize, scaler );
+
+ m_dwPauseLimit = usb_max( usb_min(m_dwPauseLimit,
+ (u_long)ceil(pParam->Size.dwTotalBytes / 1024.0)), 2);
+
+ regs[0x4e] = (u_char)floor((m_dwPauseLimit*512.0)/(2.0*hw->wDRAMSize));
+
+ if( regs[0x4e] > 1 ) {
+ regs[0x4e]--;
+ if(regs[0x4e] > 1)
+ regs[0x4e]--;
+ } else
+ regs[0x4e] = 1;
+
+ /* resume, when buffer is 2/8 kbytes full (512k/2M memory)
+ */
+ regs[0x4f] = 1;
+
+ DBG( _DBG_INFO2, "* PauseLimit = %lu, [0x4e] = 0x%02x, [0x4f] = 0x%02x\n",
+ m_dwPauseLimit, regs[0x4e], regs[0x4f] );
+}
+
+/** usb_GetScanLinesAndSize
+ */
+static void
+usb_GetScanLinesAndSize( Plustek_Device *dev, ScanParam *pParam )
+{
+ DCapsDef *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;
+
+ DBG( _DBG_INFO, "* PhyBytes = %lu\n", pParam->Size.dwPhyBytes );
+ DBG( _DBG_INFO, "* PhyLines = %lu\n", pParam->Size.dwPhyLines );
+ DBG( _DBG_INFO, "* TotalBytes = %lu\n", pParam->Size.dwTotalBytes );
+}
+
+/** function to preset/reset the merlin registers
+ */
+static SANE_Bool
+usb_SetScanParameters( Plustek_Device *dev, ScanParam *pParam )
+{
+ static u_char reg8, reg38[6], reg48[2];
+
+ ScanDef *scan = &dev->scanning;
+ ScanParam *pdParam = &dev->scanning.sParam;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ 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, SANE_TRUE );
+ }
+
+ /*
+ * calculate the basic settings...
+ */
+ 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 );
+
+ usb_PresetStepSize( dev, pParam );
+
+ if( dev->caps.dwFlag & SFLAG_ADF ) {
+
+ if( pParam->bCalibration == PARAM_Scan ) {
+
+ if( pdParam->bSource == SOURCE_ADF ) {
+ regs[0x50] = 0;
+ regs[0x51] = 0x40;
+ if( pParam->PhyDpi.x <= 300)
+ regs[0x54] = (regs[0x54] & ~7) | 4; /* 3; */
+ else
+ regs[0x54] = (regs[0x54] & ~7) | 5; /* 4; */
+ } else {
+ regs[0x50] = hw->bStepsToReverse;
+ regs[0x51] = hw->bReg_0x51;
+ regs[0x54] &= ~7;
+ }
+ } else
+ regs[0x50] = 0;
+ } else {
+ if( pParam->bCalibration == PARAM_Scan )
+ regs[0x50] = hw->bStepsToReverse;
+ else
+ regs[0x50] = 0;
+ }
+
+ /* Assume we will not use ITA */
+ regs[0x19] = m_bIntTimeAdjust = 0;
+
+ /* Get variables from calculation algorithms */
+ if(!(pParam->bCalibration == PARAM_Scan &&
+ pParam->bSource == SOURCE_ADF && dev->usbDev.fLastScanIsAdf )) {
+
+ DBG( _DBG_INFO2, "* Scan calculations...\n" );
+ usb_GetLineLength ( dev, pParam );
+ 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)(CRYSTAL_FREQ /
+ (m_dMCLKDivider * 8 * m_bCM * hw->dMaxMoveSpeed *
+ 4 * hw->wMotorDpi));
+ /* CIS special ;-) */
+ if((hw->bReg_0x26 & _ONE_CH_COLOR) && (m_bCM == 1)) {
+ DBG( _DBG_INFO2, "* CIS FFStep-Speedup\n" );
+ m_wFastFeedStepSize /= 3;
+ }
+
+ if( m_bIntTimeAdjust != 0 )
+ m_wFastFeedStepSize /= m_bIntTimeAdjust;
+
+ if(regs[0x0a])
+ m_wFastFeedStepSize *= ((regs[0x0a] >> 2) + 2);
+ regs[0x48] = _HIBYTE( m_wFastFeedStepSize );
+ regs[0x49] = _LOBYTE( m_wFastFeedStepSize );
+
+ DBG( _DBG_INFO2, "* FFStepSize = %u, [0x48] = 0x%02x, [0x49] = 0x%02x\n",
+ m_wFastFeedStepSize, regs[0x48], regs[0x49] );
+
+ /* 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( dev->usbDev.fLastScanIsAdf ) {
+
+ regs[0x08] = reg8;
+ memcpy( &regs[0x38], reg38, sizeof(reg38));
+ memcpy( &regs[0x48], reg48, sizeof(reg48));
+
+ } else {
+
+ reg8 = regs[0x08];
+ memcpy( reg38, &regs[0x38], sizeof(reg38));
+ memcpy( reg48, &regs[0x48], sizeof(reg48));
+ }
+ usb_MotorSelect( dev, SANE_TRUE );
+ }
+
+ /* Reset LM983x's state machine before setting register values */
+ if( !usbio_WriteReg( dev->fd, 0x18, 0x18 ))
+ return SANE_FALSE;
+
+ usleep(200 * 1000); /* Need to delay at least xxx microseconds */
+
+ if( !usbio_WriteReg( dev->fd, 0x07, 0x20 ))
+ return SANE_FALSE;
+
+ if( !usbio_WriteReg( dev->fd, 0x19, 6 ))
+ return SANE_FALSE;
+
+ regs[0x07] = 0;
+ regs[0x28] = 0;
+
+ /* set unused registers to 0 */
+ memset( &regs[0x03], 0, 3 );
+ memset( &regs[0x5f], 0, 0x7f-0x5f+1 );
+
+ if( !usb_IsSheetFedDevice(dev)) {
+ /* we limit the possible scansteps to avoid, that the sensors bumps
+ * against the scanbed - not necessary for sheet-fed devices
+ */
+ if(pParam->bCalibration==PARAM_Scan && pParam->bSource!=SOURCE_ADF) {
+
+ u_long lines = pParam->Size.dwPhyLines + scan->bLinesToSkip +
+ scan->dwLinesDiscard + 5;
+ u_short scansteps = (u_short)ceil((double)lines*
+ hw->wMotorDpi / pParam->PhyDpi.y);
+ DBG( _DBG_INFO, "* Scansteps=%u (%lu*%u/%u)\n", scansteps, lines,
+ hw->wMotorDpi, pParam->PhyDpi.y );
+ regs[0x4c] = _HIBYTE(scansteps);
+ regs[0x4d] = _LOBYTE(scansteps);
+ }
+ }
+
+ /* set the merlin registers */
+ _UIO(sanei_lm983x_write( dev->fd, 0x03, &regs[0x03], 3, SANE_TRUE));
+ _UIO(sanei_lm983x_write( dev->fd, 0x08, &regs[0x08], 0x7f - 0x08+1, SANE_TRUE));
+
+ usleep(100);
+
+ if( !usbio_WriteReg( dev->fd, 0x07, 0 ))
+ return SANE_FALSE;
+
+ DBG( _DBG_INFO, "usb_SetScanParameters() done.\n" );
+ return SANE_TRUE;
+}
+
+/**
+ */
+static SANE_Bool
+usb_ScanBegin( Plustek_Device *dev, SANE_Bool auto_park )
+{
+ u_char value;
+ u_short inches;
+ HWDef *hw = &dev->usbDev.HwSetting;
+ DCapsDef *sc = &dev->usbDev.Caps;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ DBG( _DBG_INFO, "usb_ScanBegin()\n" );
+
+ if( !usb_Wait4ScanSample( dev ))
+ return SANE_FALSE;
+
+ /* save the request for usb_ScanEnd () */
+ m_fAutoPark = auto_park;
+
+ /* Disable home sensor during scan, or the chassis cannot move */
+ value = ((m_pParam->bCalibration == PARAM_Scan &&
+ m_pParam->bSource == SOURCE_ADF)? (regs[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 - only LM9831 (Source: National Semiconductors) */
+ if( _LM9831 == hw->chip ) {
+
+ 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 );
+
+ DBG(_DBG_INFO,"Flushing cache - %lu bytes (bOldScanData=%u)\n",
+ dwBytesToRead, m_bOldScanData );
+
+ _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 )) ||
+ !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 */
+
+ DBG( _DBG_DREGS, "Register Dump before reading data:\n" );
+ dumpregs( dev->fd, NULL );
+
+ inches = (u_short)((m_pParam->Origin.y *300UL)/hw->wMotorDpi);
+ DBG( _DBG_INFO2, ">>> INCH=%u, DOY=%u\n", inches, sc->Normal.DataOrigin.y );
+ if( inches > sc->Normal.DataOrigin.y )
+ usb_WaitPos( dev, 150, SANE_FALSE );
+
+ DBG( _DBG_INFO, "usb_ScanBegin() done.\n" );
+ return SANE_TRUE;
+}
+
+/** usb_ScanEnd
+ * stop all the processing stuff and reposition sensor back home
+ */
+static SANE_Bool
+usb_ScanEnd( Plustek_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 SANE_Bool
+usb_IsDataAvailableInDRAM( Plustek_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;
+ u_char *regs = dev->usbDev.a_bRegs;
+
+ 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 * regs[0x08] * dev->usbDev.Caps.OpticDpi.x / 600));
+ else
+ usleep(1000*(20 * regs[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 SANE_Bool
+usb_ScanReadImage( Plustek_Device *dev, void *pBuf, u_long dwSize )
+{
+ u_char *regs = dev->usbDev.a_bRegs;
+ SANE_Status res;
+
+ DBG( _DBG_READ, "usb_ScanReadImage(%lu)\n", dwSize );
+
+ if( m_fFirst ) {
+
+ m_fFirst = SANE_FALSE;
+
+ /* Wait for data band ready */
+ if (!usb_IsDataAvailableInDRAM( dev )) {
+ DBG( _DBG_ERROR, "Nothing to read...\n" );
+ return SANE_FALSE;
+ }
+
+ /* restore the fast forward stepsize...*/
+ sanei_lm983x_write(dev->fd, 0x48, &regs[0x48], 2, SANE_TRUE);
+ }
+ res = sanei_lm983x_read(dev->fd, 0x00, (u_char *)pBuf, dwSize, SANE_FALSE);
+
+ /* check for pressed ESC button, as sanei_lm983x_read() may take some time
+ */
+ if( usb_IsEscPressed()) {
+ DBG(_DBG_INFO,"usb_ScanReadImage() - Cancel detected...\n");
+ return 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;
+}
+
+/** calculate the number of pixels per line and lines out of a given
+ * crop-area. The size of the area is given on a 300dpi base!
+ */
+static void
+usb_GetImageInfo( Plustek_Device *dev, ImgDef *pInfo, WinInfo *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;
+
+ DBG( _DBG_INFO2,"Area: cx=%u, cy=%u\n",pInfo->crArea.cx,pInfo->crArea.cy);
+
+ switch( pInfo->wDataType ) {
+
+ case COLOR_TRUE48:
+ pSize->dwBytes = pSize->dwPixels * 6UL;
+ break;
+
+ case COLOR_TRUE24:
+ if( dev->scanning.fGrayFromColor > 7 ){
+ pSize->dwBytes = (pSize->dwPixels + 7UL) >> 3;
+ pSize->dwPixels = pSize->dwBytes * 8;
+ } else {
+ 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;
+ pSize->dwPixels = pSize->dwBytes * 8;
+ break;
+ }
+}
+
+/**
+ */
+static void
+usb_SaveImageInfo( Plustek_Device *dev, ImgDef *pInfo )
+{
+ HWDef *hw = &dev->usbDev.HwSetting;
+ ScanParam *pParam = &dev->scanning.sParam;
+
+ 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;
+ /* fall through... */
+
+ case COLOR_TRUE24:
+ pParam->bDataType = SCANDATATYPE_Color;
+
+ /* AFE operation: one or 3 channels ! */
+ if( hw->bReg_0x26 & _ONE_CH_COLOR )
+ pParam->bChannels = 1;
+ else
+ pParam->bChannels = 3;
+ break;
+
+ case COLOR_GRAY16:
+ pParam->bBitDepth = 16;
+ /* fall through... */
+
+ 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;
+
+ /* it seems, that we need to adjust the Origin.x when we have a
+ * sheetfed device to avoid stripes in the resulting pictures
+ */
+ if( usb_IsSheetFedDevice(dev)) {
+
+ int step, div, org, xdpi;
+
+ xdpi = usb_SetAsicDpiX( dev, pParam->UserDpi.x );
+
+ if ((xdpi * 2) <= 300)
+ div = 300;
+ else if ((xdpi * 2) <= 600)
+ div = 600;
+ else if ((xdpi * 2) <= 1200)
+ div = 1200;
+ else
+ div = 2400;
+
+ step = div / xdpi;
+ org = pParam->Origin.x;
+
+ pParam->Origin.x = (pParam->Origin.x / step) * step;
+
+ if (org != pParam->Origin.x)
+ DBG(_DBG_INFO, "* Origin.x adjusted: %i -> %i\n",
+ org, pParam->Origin.x);
+ }
+}
+
+/* END PLUSTEK-USBSCAN.C ....................................................*/