Logo Search packages:      
Sourcecode: sane-backends version File versions

plustek-usbscan.c

Go to the documentation of this file.
/*.............................................................................
 * Project : SANE library for Plustek flatbed scanners.
 *.............................................................................
 */

/** @file plustek-usbscan.c
 *  @brief Scanning...
 *
 * Based on sources acquired from Plustek Inc.<br>
 * Copyright (C) 2001-2004 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
 * .
 * <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>
 */

/** array used to get motor-settings and mclk-settings
 */
00071 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 pScanParam 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
 */
00099 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
 */
00112 static u_long usb_max( u_long val1, u_long val2 )
{
      if( val1 > val2 )
            return val1;

      return val2;
}

/** 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.
 */
00129 static u_short usb_SetAsicDpiX( Plustek_Device *dev, u_short xdpi )
{
      u_short   res;
    pScanDef  scanning = &dev->scanning;
      pDCapsDef scaps    = &dev->usbDev.Caps;

      /*
     * 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;
            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;
      }

      /* adjust, if any turbo/preview mode is set, should be 0 here... */
      if( a_bRegs[0x0a] )
            a_bRegs[0x09] -= ((a_bRegs[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 -
 */
00207 static u_short usb_SetAsicDpiY( Plustek_Device *dev, u_short ydpi )
{
    pScanDef  scanning = &dev->scanning;
      pDCapsDef sCaps    = &dev->usbDev.Caps;
      pHWDef    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
 */
00259 static void usb_SetColorAndBits( Plustek_Device *dev, pScanParam pParam )
{
      pHWDef hw = &dev->usbDev.HwSetting;

      /*
     * Set pixel packing, data mode and AFE operation
     */
      switch( pParam->bDataType ) {
            case SCANDATATYPE_Color:
                  m_bCM = 3;
                  a_bRegs[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( a_bRegs[0x26] & _ONE_CH_COLOR )
                        a_bRegs[0x26] |= (_BLUE_CH | 0x01);

                  memcpy( &a_bRegs[0x0f], hw->bReg_0x0f_Color, 10 );
                  break;

            case SCANDATATYPE_Gray:
                  m_bCM = 1;
                  a_bRegs[0x26] = (hw->bReg_0x26 & 0x18) | 0x04;
                  memcpy( &a_bRegs[0x0f], hw->bReg_0x0f_Mono, 10 );
                  break;

            case SCANDATATYPE_BW:
                  m_bCM = 1;
                  a_bRegs[0x26] = (hw->bReg_0x26 & 0x18) | 0x04;
                  memcpy( &a_bRegs[0x0f], hw->bReg_0x0f_Mono, 10 );
                  break;
      }
                  
      a_bRegs[0x27] = hw->bReg_0x27;

      if( pParam->bBitDepth > 8 ) {
            a_bRegs[0x09] |= 0x20;         /* 14/16bit image data */

      } else if( pParam->bBitDepth == 8 ) {
            a_bRegs[0x09] |= 0x18;        /* 8bits/per pixel */
      }
}

/** 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
 */
00316 static void usb_GetScanRect( Plustek_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;

            if((hw->bReg_0x26 & _ONE_CH_COLOR) &&
                                                (pParam->bDataType == SCANDATATYPE_Color)) {
                  pParam->Size.dwPhyBytes *= 3;
            }
      }
      else /* pParam->bBitDepth == 16 */
      {
            pParam->Size.dwPhyPixels = pParam->Size.dwValidPixels;
            pParam->Size.dwPhyBytes =
                                    pParam->Size.dwPhyPixels * 2 * pParam->bChannels + 2UL;

            if((hw->bReg_0x26 & _ONE_CH_COLOR) &&
                                                (pParam->bDataType == SCANDATATYPE_Color)) {
                  pParam->Size.dwPhyBytes *= 3;
            }
      }

      /* Compute data start pixel */
      wDataPixelStart = (u_short)((u_long)pParam->Origin.x *
                                                      sCaps->OpticDpi.x / 300UL);

      /* during the calibration steps, we read the entire CCD data
       */
      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);

      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 );
      }

      a_bRegs[0x22] = _HIBYTE( wDataPixelStart );
      a_bRegs[0x23] = _LOBYTE( wDataPixelStart );

      /* should match: wLineEnd-wDataPixelStart%(m_dHDPIDivider*2) = 0!! */
      a_bRegs[0x24] = _HIBYTE( wLineEnd );
      a_bRegs[0x25] = _LOBYTE( wLineEnd );

      DBG( _DBG_INFO2, ">> End-Start=%u, HDPI=%.2f\n",
                                       wLineEnd-wDataPixelStart, m_dHDPIDivider);

      /* Y origin */
      if( pParam->bCalibration == PARAM_Scan ) {

            if( hw->motorModel == 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->motorModel == MODEL_HuaLien && sCaps->OpticDpi.x == 600)
            pParam->Origin.y = pParam->Origin.y * 297 / 298;

      DBG(_DBG_INFO2,"* Full Steps to Skip at Start = 0x%04x\n",pParam->Origin.y);

      a_bRegs[0x4a] = _HIBYTE( pParam->Origin.y );
      a_bRegs[0x4b] = _LOBYTE( pParam->Origin.y );
}

/** preset scan stepsize and fastfeed stepsize
 */
00484 static void usb_PresetStepSize( Plustek_Device *dev, pScanParam pParam )
{
      u_short ssize;
      double  mclkdiv = pParam->dMCLK;
      pHWDef  hw      = &dev->usbDev.HwSetting;

      ssize = (u_short)((double)dwCrystalFrequency / ( mclkdiv * 8.0 *
            (double)m_bCM * hw->dMaxMotorSpeed * 4.0 * (double)hw->wMotorDpi));

      a_bRegs[0x46] = _HIBYTE( ssize );
      a_bRegs[0x47] = _LOBYTE( ssize );
      a_bRegs[0x48] = _HIBYTE( ssize );
      a_bRegs[0x49] = _LOBYTE( ssize );

      DBG( _DBG_INFO2, "* StepSize(Preset) = %u (0x%04x)\n", ssize, ssize );
}

/** calculate default phase difference DPD
 */
00503 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                    */

      pHWDef hw = &dev->usbDev.HwSetting;

      qtcnt = (a_bRegs [0x51] & 0x30) >> 4;     /* quarter speed count */
      hfcnt = (a_bRegs [0x51] & 0xc0) >> 6;     /* half speed count    */

      if( _LM9831 == hw->chip )
            strev = a_bRegs [0x50] & 0x3f;            /* steps to reverse */
      else /* LM9832/3 */
      {
            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);
            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 );

      a_bRegs[0x51] |= (u_char)((dpd >> 16) & 0x03);
      a_bRegs[0x52] = (u_char)(dpd >> 8);
      a_bRegs[0x53] = (u_char)(dpd & 0xFF);
}

/** 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)
 */
00553 static double usb_GetMCLKDivider( Plustek_Device *dev, pScanParam pParam )
{
      double dMaxIntegrationTime;
      double dMaxMCLKDivider;

      pDCapsDef sCaps = &dev->usbDev.Caps;
      pHWDef    hw    = &dev->usbDev.HwSetting;

      DBG( _DBG_INFO, "usb_GetMCLKDivider()\n" );

      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->motorModel == MODEL_HuaLien &&
                        sCaps->bCCD == kNEC3799 && m_bIntTimeAdjust > bMaxITA) {
                        m_bIntTimeAdjust = bMaxITA;
                  }

                  if(((hw->motorModel == MODEL_HP) && (sCaps->bCCD == kNECSLIM))/* ||
                        ( a_bRegs[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 );

      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);

            DBG( _DBG_INFO2, "* Stepsize = %u, 0x46=0x%02x 0x47=0x%02x\n",
                               m_wStepSize, a_bRegs[0x46], a_bRegs[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)dwCrystalFrequency * dMaxIntegrationTime /
                                 (1000 * 8 * m_bCM * m_wLineLength);

      /* Determine lamp PWM setting */
      if( m_dMCLKDivider > dMaxMCLKDivider ) {

            DBG( _DBG_INFO2, "* Setting GreenPWMDutyCycleLow\n" );
            a_bRegs[0x2a] = _HIBYTE( hw->wGreenPWMDutyCycleLow );
            a_bRegs[0x2b] = _LOBYTE( hw->wGreenPWMDutyCycleLow );

      } else {

            DBG( _DBG_INFO2, "* Setting GreenPWMDutyCycleHigh\n" );
            a_bRegs[0x2a] = _HIBYTE( hw->wGreenPWMDutyCycleHigh );
            a_bRegs[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
 */
00647 static void usb_GetStepSize( Plustek_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 );

      DBG( _DBG_INFO2, "* Stepsize = %u, 0x46=0x%02x 0x47=0x%02x\n",
                        m_wStepSize, a_bRegs[0x46], a_bRegs[0x47] );
}

/**
 */
static void usb_GetLineLength( Plustek_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 */

      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;
      
      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;

      DBG( _DBG_INFO2, "* LineLength=%d, LineRateColor=%u\n",
                                                                  m_wLineLength, m_bLineRateColor );
}

/** usb_GetMotorParam
 * registers 0x56, 0x57
 */
00779 static void usb_GetMotorParam( Plustek_Device *dev, pScanParam pParam )
{
      int          idx, i;
      pClkMotorDef clk;
      pMDef        md;
      pDCapsDef    sCaps = &dev->usbDev.Caps;
      pHWDef       hw    = &dev->usbDev.HwSetting;

      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.x <= dpi_ranges[i] )
                        break;
                  idx++;
            }
            if( idx >= _MAX_CLK )
                  idx = _MAX_CLK - 1;

            a_bRegs[0x56] = md[idx].pwm;
            a_bRegs[0x57] = md[idx].pwm_duty;

            a_bRegs[0x43] = 0;
            a_bRegs[0x44] = 0;

            if( md[idx].scan_lines_per_line > 1 ) {

                  if((pParam->bBitDepth > 8) &&
                        (pParam->bDataType == SCANDATATYPE_Color)) {

                        a_bRegs[0x43] = 0xff;
                        a_bRegs[0x44] = md[idx].scan_lines_per_line;

                        DBG( _DBG_INFO2, "* Line Skipping : 0x43=0x%02x, 0x44=0x%02x\n",
                                                                        a_bRegs[0x43], a_bRegs[0x44] );
                  }
            }

    } else {
        if( sCaps->OpticDpi.x == 1200 ) {

            switch( hw->motorModel ) {

            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->motorModel ) {

            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;
            }
        }
      }
      
      DBG( _DBG_INFO2, "* MOTOR-Settings: PWM=0x%02x, PWM_DUTY=0x%02x\n",
                       a_bRegs[0x56], a_bRegs[0x57] );
}

/**
 */
static void usb_GetPauseLimit( Plustek_Device *dev, pScanParam pParam )
{
      int    coeffsize, scaler;
      pHWDef hw = &dev->usbDev.HwSetting;

      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( a_bRegs[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( a_bRegs[0x50] == 0 )
            m_dwPauseLimit -= ((a_bRegs[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);

      a_bRegs[0x4e] = (u_char)floor((m_dwPauseLimit*512.0)/(2.0*hw->wDRAMSize));

      if( a_bRegs[0x4e] > 1 ) {
            a_bRegs[0x4e]--;
            if(a_bRegs[0x4e] > 1)
                  a_bRegs[0x4e]--;
      } else
            a_bRegs[0x4e] = 1;

      /*
       * resume, when buffer is 2/8 kbytes full (512k/2M memory)
       */
      a_bRegs[0x4f] = 1;

      DBG( _DBG_INFO2, "* PauseLimit = %lu, [0x4e] = 0x%02x, [0x4f] = 0x%02x\n",
                        m_dwPauseLimit, a_bRegs[0x4e], a_bRegs[0x4f] );
}

/** usb_GetScanLinesAndSize
 */
00989 static void usb_GetScanLinesAndSize( Plustek_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;

      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
 */
01015 static SANE_Bool usb_SetScanParameters( Plustek_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 );
      }

      /*
     * 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 ) {
                        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] = hw->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] = hw->bStepsToReverse;
            else
                  a_bRegs[0x50] = 0;
      }

      /* Assume we will not use ITA */
      a_bRegs[0x19] = m_bIntTimeAdjust = 0;

      /* Get variables from calculation algorithms */
      if(!(pParam->bCalibration == PARAM_Scan &&
         pParam->bSource == SOURCE_ADF && fLastScanIsAdf )) {

            DBG( _DBG_INFO2, "* Scan calculations...\n" );
            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));
      /* 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(a_bRegs[0x0a])
            m_wFastFeedStepSize *= ((a_bRegs[0x0a] >> 2) + 2);
      a_bRegs[0x48] = _HIBYTE( m_wFastFeedStepSize );
      a_bRegs[0x49] = _LOBYTE( m_wFastFeedStepSize );

      DBG( _DBG_INFO2, "* FFStepSize = %u, [0x48] = 0x%02x, [0x49] = 0x%02x\n",
                             m_wFastFeedStepSize, a_bRegs[0x48], a_bRegs[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( 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, 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;

      a_bRegs[0x07] = 0;
      a_bRegs[0x28] = 0;

      /* set unused registers to 0 */
      memset( &a_bRegs[0x03], 0, 3 );
      memset( &a_bRegs[0x5f], 0, 0x7f-0x5f+1 );

      /* set the merlin registers */
      _UIO(sanei_lm983x_write( dev->fd, 0x03, &a_bRegs[0x03], 3, SANE_TRUE));
      _UIO(sanei_lm983x_write( dev->fd, 0x08, &a_bRegs[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;
      pHWDef       hw = &dev->usbDev.HwSetting;
      pDCapsDef    sc = &dev->usbDev.Caps;

      DBG( _DBG_INFO, "usb_ScanBegin()\n" );

      /* 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)? (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 - 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, m_pParam->bDataType)) ||
            !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, ">>> INC=%u, DOY=%u\n", inches, sc->Normal.DataOrigin.y );
      if( inches > sc->Normal.DataOrigin.y )
            usb_WaitPos( dev, 150, SANE_FALSE );
      return SANE_TRUE;
}

/** usb_ScanEnd
 * stop all the processing stuff and reposition sensor back home
 */
01252 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;

      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 SANE_Bool usb_ScanReadImage( Plustek_Device *dev,
                                    void *pBuf, u_long dwSize )
{
      static u_long dwBytes = 0;
      SANE_Status res;

      DBG( _DBG_READ, "usb_ScanReadImage(%lu)\n", dwSize );

      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);

      /* 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;
}

/**
 */
static void usb_GetImageInfo( Plustek_Device *dev, 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:
                  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, pImgDef 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;
}

/* END PLUSTEK-USBSCAN.C ....................................................*/

Generated by  Doxygen 1.6.0   Back to index