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

plustek-pp_motor.c

/* @file plustek-pp_motor.c
 * @brief all functions for motor control
 *
 * based on sources acquired from Plustek Inc.
 * Copyright (C) 1998 Plustek Inc.
 * Copyright (C) 2000-2004 Gerhard Jaeger <gerhard@gjaeger.de>
 * also based on the work done by Rick Bronson
 *
 * History:
 * - 0.30 - initial version
 * - 0.31 - no changes
 * - 0.32 - slight cosmetic changes
 *        - added function MotorToHomePosition()
 * - 0.33 - added additional debug-messages
 *        - increased speed for returning to homeposition for Models >= 9630
 *          (and ASIC 96003)
 * - 0.34 - added FIFO overflow check in motorP96SetSpeed
 *        - removed WaitBack() function from pScanData structure
 *        - removed wStayMaxStep from pScanData structure
 * - 0.35 - changed motorP96UpdateDataCurrentReadLine() to handle proper
 *        - work of ASIC96003 based 600dpi models
 * - 0.36 - merged motorP96WaitBack and motorP98WaitBack to motorWaitBack
 *        - merged motorP96SetSpeed and motorP98SetSpedd to motorSetSpeed
 *        - added Sensor-Check in function MotorToHomePosition
 *        - reduced number of forward steps for MotorToHomePosition
 * - 0.37 - removed function motorP96GetStartStopGap() - no longer used
 *        - removed a_bStepDown1Table and a_bStepUp1Table
 *        - removed // comments
 *        - added A3I stuff
 * - 0.38 - cosmetic changes
 *        - added P12 stuff
 * - 0.39 - Did some finetuning in MotorP98003ModuleForwardBackward()
 *        - Fixed a problem, that could cause the driver to throw a
 *          kernel exception
 * - 0.40 - changed back to build 0.39-3 (disabled A3I stuff)
 * - 0.41 - no changes
 * - 0.42 - changed include names
 * - 0.43 - no changes
 * .
 * <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>
 */
#include "plustek-pp_scan.h"

/*************************** some definitons *********************************/

/* #define _A3I_EN */

/*
 * adjustments for scanning in negative and tranparency mode
 */
#define _NEG_SCANNINGPOS          770
#define _POS_SCANNINGPOS          660        /* original value was 710 */

#define _P98_BACKMOVES              0x3d
#define _P98_FORWARDMOVES           0x3b        /* Origin = 3c */

#define _P98003_BACKSTEPS         120
#define _P98003_FORWARDSTEPS    120

#define _P96_BACKMOVES              130
#define _P96_FORWARDMOVES           87   /* 95 */
#define _P96_FIFOOVERFLOWTHRESH     180


#define _COLORRUNTABLE_RED          0x11
#define _COLORRUNTABLE_GREEN  0x22
#define _COLORRUNTABLE_BLUE         0x44

#define     _BW_ORIGIN                    0x0D
#define     _GRAY_ORIGIN                  0x0B
#define     _COLOR_ORIGIN                 0x0B

#define _P98003_YOFFSET             300

/**************************** local vars *************************************/

static TimerDef p98003MotorTimer;

static UShort     a_wMoveStepTable [_NUMBER_OF_SCANSTEPS];
static Byte       a_bScanStateTable[_SCANSTATE_TABLE_SIZE];
static Byte       a_bHalfStepTable [_NUMBER_OF_SCANSTEPS];
static Byte       a_bColorByteTable[_NUMBER_OF_SCANSTEPS];
static Byte       a_bColorsSum[8] = {0, 1, 1, 2, 1, 2, 2, 3};

static pUShort    pwEndMoveStepTable  = a_wMoveStepTable  + _NUMBER_OF_SCANSTEPS;
static pUChar     pbEndColorByteTable = a_bColorByteTable + _NUMBER_OF_SCANSTEPS;
static pUChar     pbEndHalfStepTable  = a_bHalfStepTable  + _NUMBER_OF_SCANSTEPS;

/*
 * for the 96001/3 based units
 */
static UShort wP96BaseDpi = 0;

static Byte a_bStepDown1Table[20]  = {3,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
static Byte a_bStepUp1Table[20]    = {4,3,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
static Byte a_bMotorDown2Table[20] = {0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2};

#ifndef _A3I_EN
static Byte a_bHalfStep2Table[32] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
                                                       1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
static Byte a_bHalfStep4Table[16] = {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2};
static Byte a_bHalfStep6Table[12] = {3,3,3,3,3,3,3,3,3,3,3,3};
static Byte a_bHalfStep8Table[8]  = {4,4,4,4,4,4,4,4};
static Byte a_bHalfStep10Table[8] = {5,5,5,5,5,5,5,5};
static Byte a_bHalfStep12Table[6] = {6,6,6,6,6,6};
static Byte a_bHalfStep14Table[6] = {7,7,7,7,7,7};
static Byte a_bHalfStep16Table[4] = {8,8,8,8};
static Byte a_bHalfStep18Table[4] = {9,9,9,9};
static Byte a_bHalfStep20Table[4] = {10,10,10,10};
static Byte a_bHalfStep22Table[4] = {11,11,11,11};
static Byte a_bHalfStep24Table[4] = {12,12,12,12};
static Byte a_bHalfStep26Table[4] = {13,13,13,13};
static Byte a_bHalfStep28Table[4] = {14,14,14,14};
static Byte a_bHalfStep30Table[4] = {15,15,15,15};
static Byte a_bHalfStep32Table[2] = {16,16};
static Byte a_bHalfStep34Table[2] = {17,17};
static Byte a_bHalfStep36Table[2] = {18,18};
static Byte a_bHalfStep38Table[2] = {19,19};
static Byte a_bHalfStep40Table[2] = {20,20};


static pUChar a_pbHalfStepTables[20] = {
      a_bHalfStep2Table,  a_bHalfStep4Table,
      a_bHalfStep6Table,  a_bHalfStep8Table,
      a_bHalfStep10Table, a_bHalfStep12Table,
      a_bHalfStep14Table, a_bHalfStep16Table,
      a_bHalfStep18Table, a_bHalfStep20Table,
      a_bHalfStep22Table, a_bHalfStep24Table,
      a_bHalfStep26Table, a_bHalfStep28Table,
      a_bHalfStep30Table, a_bHalfStep32Table,
      a_bHalfStep34Table, a_bHalfStep36Table,
      a_bHalfStep38Table, a_bHalfStep40Table
};
#endif

/*************************** local functions *********************************/

/*.............................................................................
 *
 */
static void motorP96GetStartStopGap( pScanData ps, Bool fCheckState )
{
      UChar bMotorCountDownIndex;

    if( fCheckState ) {

            ps->bMotorStepTableNo = 0xff;
            if( ps->Scan.bModuleState == _MotorInNormalState )
            return;
    }

    bMotorCountDownIndex = ps->bMotorSpeedData / 2;

    if( ps->bCurrentSpeed == 4 && ps->AsicReg.RD_Dpi < 80 )
            ps->bMotorStepTableNo = 4;
    else
            if( ps->Scan.bModuleState == _MotorGoBackward )
                ps->bMotorStepTableNo = a_bStepUp1Table[bMotorCountDownIndex];
            else
            ps->bMotorStepTableNo = a_bStepDown1Table[bMotorCountDownIndex];
}



/*.............................................................................
 * wait for the ScanState stop or ScanState reachs the dwScanStateCount
 */
static Bool motorCheckMotorPresetLength( pScanData ps )
{
    Byte     bScanState;
      TimerDef timer;

      MiscStartTimer( &timer, (_SECOND * 4));
    do {

            bScanState = IOGetScanState( ps, _FALSE );

            if (ps->fFullLength) {
                if (!(bScanState & _SCANSTATE_STOP)) /* still running */
                        if ((ULong)(bScanState & _SCANSTATE_MASK) != ps->dwScanStateCount )
                            continue;
                return ps->fFullLength;
            }

            if (bScanState & _SCANSTATE_STOP)
                break;

            /*
             * the code may work for all units
             */
            if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {

                  if (bScanState < ps->bOldStateCount)
                      bScanState += _NUMBER_OF_SCANSTEPS;

                  bScanState -= ps->bOldStateCount;

                  if (bScanState >= 40)
                      return ps->fFullLength;
            }

    } while ( !MiscCheckTimer( &timer ));

      _DODELAY(1);                   /* delay one ms */

    return ps->fFullLength;
}

/*.............................................................................
 * 1) Keep the valid content of a_bColorByteTable, and fill others to 0:
 * BeginFill = ((bCurrentLineCount + DL) < 64) ? bCurrentLineCount + DL :
 *                                    bCurrentLineCount + DL - 64;
 *    FillLength = 64 - DL
 *    [NOTE] Keep the content of a_bColorByteTable that begin at
 *           bCurrentLineCount and in DL bytes
 * 2) Keep the valid content of a_bHalfStepTable, and fill the others to 0:
 * BeginFill = ((bCurrentLineCount + bCurrentSpeed / 2 + 1) < 64) ?
 *                bCurrentLineCount + bCurrentSpeed / 2 + 1 :
 *                bCurrentLineCount + bCurrentSpeed / 2 + 1 - 64;
 *    FillLength = 64 - (bMotorSpeedData / 2 + 1);
 */
static void motorClearColorByteTableLoop0( pScanData ps, Byte bColors )
{
      ULong  dw;
      pUChar pb;

    if ((ps->bCurrentLineCount + bColors) >= _NUMBER_OF_SCANSTEPS) {
            pb = a_bColorByteTable + (ULong)(ps->bCurrentLineCount + bColors -
                                           _NUMBER_OF_SCANSTEPS);
      } else {
            pb = a_bColorByteTable + (ULong)(ps->bCurrentLineCount + bColors);
      }

    for (dw = _NUMBER_OF_SCANSTEPS - bColors; dw; dw--) {

            *pb++ = 0;
            if (pb >= pbEndColorByteTable)
            pb = a_bColorByteTable;
    }

    if ((ps->bCurrentLineCount+ps->bCurrentSpeed/2+1) >= _NUMBER_OF_SCANSTEPS) {

            pb = a_bHalfStepTable + (ULong)(ps->bCurrentLineCount +
                                                ps->bCurrentSpeed / 2 + 1 - _NUMBER_OF_SCANSTEPS);
      } else {
            pb = a_bHalfStepTable +
                   (ULong)(ps->bCurrentLineCount + ps->bCurrentSpeed / 2 + 1);
      }

    for (dw = _NUMBER_OF_SCANSTEPS - ps->bMotorSpeedData / 2 - 1; dw; dw--) {
            *pb++ = 0;
            if (pb >= pbEndHalfStepTable)
            pb = a_bHalfStepTable;
    }
}

/*.............................................................................
 * motorClearColorByteTableLoop1 ()
 *   1) Adjust bNewGap:
 *    bNewGap = (bNewGap <= bNewCurrentLineCountGap) ?
 *                0 : bNewGap - bNewCurrentLineCount - 1;
 *   2) Fill the 0 to a_bColorByteTable:
 *    FillIndex = ((bCurrentLineCount + bNewGap + 1) < 64) ?
 *                       bCurrentLineCount + bNewGap + 1 :
 *                       bCurrentLineCount + bNewGap + 1 - 64;
 *    FillCount = 64 - bNewGap - 1;
 *   3) Adjust bNewGap:
 *    bNewGap = (bCurrentLineCount <= bNewCurrentLineCountGap) ?
 *                0 : bNewGap - bNewCurrentLineCount - 1;
 *   4) Fill the a_bHalfStepTable:
 *    FillIndex = ((bCurrentLineCount + bNewGap + 1) < 64) ?
 *                       bCurrentLineCount + bNewGap + 1 :
 *                       bCurrentLineCount + bNewGap + 1 - 64;
 *    FillCount = 64 - bNewGap - 1;
 */
static void motorClearColorByteTableLoop1( pScanData ps )
{
      ULong  dw = _NUMBER_OF_SCANSTEPS - 1;
    pUChar pb;

    if (ps->bNewGap > ps->bNewCurrentLineCountGap) {
            ps->bNewGap = ps->bNewGap - ps->bNewCurrentLineCountGap - 1;
            dw -= (ULong)ps->bNewGap;
    } else {
            ps->bNewGap = 0;
      }

    if ((ps->bCurrentLineCount + ps->bNewGap + 1) >= _NUMBER_OF_SCANSTEPS) {
            pb = a_bColorByteTable +
                        (ULong)(ps->bCurrentLineCount+ps->bNewGap+1-_NUMBER_OF_SCANSTEPS);
    } else {
            pb = a_bColorByteTable +
                                          (ULong)(ps->bCurrentLineCount + ps->bNewGap + 1);
      }

    for (; dw; dw--) {
            *pb++ = 0;
            if (pb >= pbEndColorByteTable)
            pb = a_bColorByteTable;
    }

      if (ps->bCurrentSpeed > ps->bNewCurrentLineCountGap) {
            ps->bNewGap = ps->bCurrentSpeed - ps->bNewCurrentLineCountGap;
            dw = _NUMBER_OF_SCANSTEPS - 1 - (ULong)ps->bNewGap;
    } else {
            dw = _NUMBER_OF_SCANSTEPS - 1;
            ps->bNewGap = 0;
    }

      if ((ps->bCurrentLineCount + ps->bNewGap + 1) >= _NUMBER_OF_SCANSTEPS) {
            pb = a_bHalfStepTable + (ULong)(ps->bCurrentLineCount +
                                                ps->bNewGap + 1 - _NUMBER_OF_SCANSTEPS);
      } else {
            pb = a_bHalfStepTable + (ULong)(ps->bCurrentLineCount+ps->bNewGap +1);
      }

    for (; dw; dw--) {
            *pb++ = 0;
            if (pb >= pbEndHalfStepTable)
            pb = a_bHalfStepTable;
    }
}

/*.............................................................................
 * According the flag to set motor direction
 */
static void motorSetRunPositionRegister( pScanData ps )
{
      Byte bData;

      if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {
          if( ps->Scan.fMotorBackward ) {
                  bData = ps->AsicReg.RD_Motor0Control & ~_MotorDirForward;
            } else {
                  bData = ps->AsicReg.RD_Motor0Control | _MotorDirForward;
            }

          IOCmdRegisterToScanner( ps, ps->RegMotor0Control, bData );

      } else {

            if( ps->Scan.fMotorBackward ) {
                  bData = ps->Asic96Reg.RD_MotorControl & ~_MotorDirForward;
            } else {
                  bData = ps->Asic96Reg.RD_MotorControl | _MotorDirForward;
            }

          IOCmdRegisterToScanner( ps, ps->RegMotorControl, bData );
      }
}

/*.............................................................................
 *
 */
static void motorPauseColorMotorRunStates( pScanData ps )
{
      memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES);

      if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {

          ps->a_nbNewAdrPointer[2] = 0x77;      /* Read color at the same time */

      } else {
          ps->a_nbNewAdrPointer[2] = 1;
          ps->a_nbNewAdrPointer[3] = 3;
      ps->a_nbNewAdrPointer[4] = 2;
      }

      MotorSetConstantMove( ps, 0 );
}

/*.............................................................................
 * Setup the a_nbNewAdrPointer for ASIC stepping register
 */
static void motorP98FillDataToColorTable( pScanData ps,
                                                              Byte bIndex, ULong dwSteps)
{
      pUChar      pb;
    pUShort pw;
    Byte    bColor;
    UShort  w;

    for ( pb = &a_bColorByteTable[bIndex],
                                    pw = &a_wMoveStepTable[bIndex]; dwSteps; dwSteps-- ) {

            if (*pw) {                    /* valid state */

                  if( *pw >= ps->BufferForColorRunTable ) {
                        DBG( DBG_LOW, "*pw = %u > %lu !!\n",
                                    *pw, ps->BufferForColorRunTable );
                  } else {
                      bColor = ps->pColorRunTable[*pw];           /* get the colors        */
                      if (a_bColorsSum[bColor & 7])             /* need to read data */
                              *pb = bColor & 7;
                  }
            }

            if (++pw >= pwEndMoveStepTable)     {
                pw = a_wMoveStepTable;
                pb = a_bColorByteTable;
            } else
                pb++;
    }

    /* ToCondense */
    pb = a_bColorByteTable;

    for (w = 0; w < _SCANSTATE_BYTES; w++, pb += 2)
            ps->a_nbNewAdrPointer[w] = (Byte)((*pb & 7) + ((*(pb + 1) & 7) << 4));

    /* ToCondenseMotor */
    for (pb = a_bHalfStepTable, w = 0; w < _SCANSTATE_BYTES; w++) {
            if (*pb++)
                  ps->a_nbNewAdrPointer [w] |= 8;

            if (*pb++)
                ps->a_nbNewAdrPointer [w] |= 0x80;
    }
}

/*.............................................................................
 *
 */
static void motorP98FillHalfStepTable( pScanData ps )
{
      pUChar       pbHalfStepTbl, pb;
    pUShort  pwMoveStep;
    DataType Data;
    ULong    dw;

    if (1 == ps->bMotorSpeedData) {
            for (dw = 0; dw < _NUMBER_OF_SCANSTEPS; dw++)
                a_bHalfStepTable [dw] =
                                    (a_wMoveStepTable [dw] > ps->wMaxMoveStep) ? 0: 1;
    } else {
            pwMoveStep    = &a_wMoveStepTable[ps->bCurrentLineCount];
            pbHalfStepTbl = &a_bHalfStepTable[ps->bCurrentLineCount];

            if (ps->DataInf.wAppDataType >= COLOR_TRUE24)
                Data.dwValue = _NUMBER_OF_SCANSTEPS - 1;
            else
                Data.dwValue = _NUMBER_OF_SCANSTEPS;

            for (; Data.dwValue; Data.dwValue--, pbHalfStepTbl++, pwMoveStep++ ) {

                if (pwMoveStep >= pwEndMoveStepTable) {
                        pbHalfStepTbl = a_bHalfStepTable;
                        pwMoveStep    = a_wMoveStepTable;
                }

                if (*pwMoveStep) {        /* need to exposure */

                        dw = (ULong)ps->bMotorSpeedData;
                        if (Data.bValue < ps->bMotorSpeedData)
                            *pwMoveStep = 0;
                        else {
                            *pbHalfStepTbl = 1;

                            if (ps->dwFullStateSpeed) {
                                    dw -= ps->dwFullStateSpeed;
                                    for (pb = pbHalfStepTbl; dw;
                                                                        dw -= ps->dwFullStateSpeed) {
                                        pb += ps->dwFullStateSpeed;
                                        if (pb >= pbEndHalfStepTable)
                                                pb -= _NUMBER_OF_SCANSTEPS;
                                    *pb = 1;
                                    }
                            }
                        }
                }
            }
    }
}

/*.............................................................................
 *
 */
static void motorP98FillBackColorDataTable( pScanData ps )
{
      Byte bIndex;

    if ((bIndex = ps->bCurrentLineCount + ps->bNewCurrentLineCountGap + 1) >=
                                                                       _NUMBER_OF_SCANSTEPS) {
            bIndex -= _NUMBER_OF_SCANSTEPS;
      }

      motorP98FillDataToColorTable( ps, bIndex, (ULong)(_NUMBER_OF_SCANSTEPS -
                                                  ps->bNewCurrentLineCountGap));
}

/*.............................................................................
 * i/p:
 *    pScanStep = pColorRunTable if forward, a_bScanStateTable if backward
 *    dwState = how many states is requested to process
 * NOTE:
 *    The content of pScanStep contain:
 *  0: Idle state
 *  0xff: End mark
 *  others: The motor speed value
 */
static void motorP98FillBackLoop( pScanData ps,
                                                  pUChar pScanStep, ULong dwStates )
{
    for (ps->fFullLength = _FALSE; dwStates; dwStates--) {

            if (*pScanStep == 0xff ) {

                ULong dw = ps->dwScanStateCount;

                for (; dwStates; dwStates--) {
                        ps->a_nbNewAdrPointer [dw / 2] &= ((dw & 1) ? 0x7f: 0xf7);
                        dw = (dw + 1U) & _SCANSTATE_MASK;
                }
                if (!ps->dwScanStateCount)
                        ps->dwScanStateCount = _NUMBER_OF_SCANSTEPS;

                ps->dwScanStateCount--;
                ps->fFullLength = _TRUE;
            break;
            } else {
                ps->a_nbNewAdrPointer [ps->dwScanStateCount / 2] |=
                                     ((ps->dwScanStateCount & 1) ? 0x80 : 0x08);
            if (++ps->dwScanStateCount == _NUMBER_OF_SCANSTEPS)
                        ps->dwScanStateCount = 0;     /* reset to begin */

                  pScanStep++;
            }
    }
    IOSetToMotorStepCount( ps );          /* put all scan states to ASIC */
}

/*.............................................................................
 *
 */
static void motorP98SetRunFullStep( pScanData ps )
{
    ps->OpenScanPath( ps );

      ps->AsicReg.RD_StepControl = _MOTOR0_SCANSTATE;
    IODataToRegister( ps, ps->RegStepControl,
                                ps->AsicReg.RD_StepControl );
    IODataToRegister( ps, ps->RegLineControl, 96 );

    if ( ps->bFastMoveFlag == _FastMove_Low_C75_G150_Back ) {
            IODataToRegister( ps, ps->RegMotor0Control,
                                      (_MotorHQuarterStep + _MotorOn + _MotorDirBackward));
      } else {
            IODataToRegister( ps, ps->RegMotor0Control,
                                      (_MotorHQuarterStep + _MotorOn + _MotorDirForward));
      }

    if (ps->bFastMoveFlag == _FastMove_Film_150) {
            ps->AsicReg.RD_XStepTime = 12;
      } else {
            if (ps->bFastMoveFlag == _FastMove_Fast_C50_G100) {
                ps->AsicReg.RD_XStepTime =
                        ((ps->DataInf.wPhyDataType >= COLOR_TRUE24) ? 4 : 8);
            } else {
                ps->AsicReg.RD_XStepTime =
                        ((ps->DataInf.wPhyDataType >= COLOR_TRUE24) ? 6 : 12);
            }
      }

      DBG( DBG_LOW, "XStepTime = %u\n", ps->AsicReg.RD_XStepTime );
      IODataToRegister( ps, ps->RegXStepTime, ps->AsicReg.RD_XStepTime);
    ps->CloseScanPath( ps );
}

/*.............................................................................
 * moves the sensor back home...
 */
static int motorP98BackToHomeSensor( pScanData ps )
{
      int          result = _OK;
      TimerDef timer;

    MotorSetConstantMove( ps, 1 );

      ps->OpenScanPath( ps );

      ps->AsicReg.RD_StepControl =
                         (_MOTOR_FREERUN + _MOTOR0_SCANSTATE+ _MOTOR0_ONESTEP);
    IODataToRegister( ps, ps->RegStepControl, ps->AsicReg.RD_StepControl);

    ps->AsicReg.RD_ModeControl = _ModeScan;
    IODataToRegister( ps, ps->RegModeControl, ps->AsicReg.RD_ModeControl );

    ps->AsicReg.RD_Motor0Control = _MotorHQuarterStep +
                                                                        _MotorOn + _MotorDirBackward;
    IODataToRegister( ps, ps->RegMotor0Control, ps->AsicReg.RD_Motor0Control );


    if( ps->DataInf.wPhyDataType >= COLOR_TRUE24) {
            ps->AsicReg.RD_XStepTime = ps->bSpeed24;
      } else {
            ps->AsicReg.RD_XStepTime = ps->bSpeed12;
      }

      DBG( DBG_HIGH, "XStepTime = %u\n", ps->AsicReg.RD_XStepTime );

      IODataToRegister( ps, ps->RegXStepTime, ps->AsicReg.RD_XStepTime );
    IORegisterToScanner( ps, ps->RegRefreshScanState );

      /* CHANGE: We allow up to 25 seconds for returning (org. val was 10) */
      MiscStartTimer( &timer, _SECOND * 25 );

    do {
            if (IODataFromRegister( ps, ps->RegStatus) & _FLAG_P98_PAPER ) {
                IODataToRegister( ps, ps->RegModelControl,
                              (Byte)(ps->AsicReg.RD_ModelControl | _HOME_SENSOR_POLARITY));
                if(!(IODataFromRegister(ps, ps->RegStatus) & _FLAG_P98_PAPER))
                        break;
            }
            _DODELAY( 10 );               /* delay 10 ms */

    } while ( !(result = MiscCheckTimer( &timer )));

    ps->CloseScanPath( ps );

      if( _OK != result )
            return result;

    memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );

      IOSetToMotorRegister( ps );

      return _OK;
}

/*.............................................................................
 * 1) Clear scan states
 * 2) Adjust the new scan state
 */
static void motorP98FillRunNewAdrPointer1( pScanData ps )
{
    ScanState sState;
    Byte      bTemp;

    IOGetCurrentStateCount( ps, &sState);
    bTemp = sState.bStep;
    if (sState.bStep < ps->bOldStateCount) {
            sState.bStep += _NUMBER_OF_SCANSTEPS;/* over table (table just can       */
      }                                                            /* holds 64 step, then reset to */
                                                                   /* 0, so if less than means over*/
                                                                   /* the table)                   */
    sState.bStep   -= ps->bOldStateCount;  /* how many states passed     */
    ps->pScanState += sState.bStep;

    /*
       * if current state != no stepped or stepped a cycle, fill the table with
     * 1 in NOT STEPPED length. (1 means to this state has to be processing).
       */
      ps->bOldStateCount   = bTemp;
      ps->dwScanStateCount = (ULong)((bTemp + 1) & _SCANSTATE_MASK);

      motorP98FillBackLoop( ps, ps->pScanState, _NUMBER_OF_SCANSTEPS );
}

/*.............................................................................
 *
 */
static void motorP98FillRunNewAdrPointer( pScanData ps )
{
    memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );

    motorP98FillRunNewAdrPointer1( ps );
}

/*.............................................................................
 * move the sensor to a specific Y-position
 */
static void motorP98PositionYProc( pScanData ps, ULong dwStates )
{
    ScanState sState;

    memset( ps->pColorRunTable, 1, dwStates );
    memset( ps->pColorRunTable + dwStates, 0xff, (3800UL - dwStates));

      IOGetCurrentStateCount( ps, &sState);

      ps->bOldStateCount = sState.bStep;

    ps->OpenScanPath( ps );
    IODataToRegister( ps, ps->RegMotor0Control,
                                (Byte)(_MotorOn + _MotorHEightStep +(ps->Scan.fMotorBackward)?
                                    _MotorDirBackward :  _MotorDirForward));

      DBG( DBG_LOW, "XStepTime = %u\n", ps->bSpeed6 );
    IODataToRegister( ps, ps->RegXStepTime, ps->bSpeed6 );
      ps->CloseScanPath( ps );

    ps->pScanState = ps->pColorRunTable;

    ps->FillRunNewAdrPointer( ps );

    while(!motorCheckMotorPresetLength( ps ))
            motorP98FillRunNewAdrPointer1( ps );
}

/*.............................................................................
 * checks if the sensor is in itīs home position and moves it back if necessary
 */
static int motorP98CheckSensorInHome( pScanData ps )
{
      int result;

    if (!(IODataRegisterFromScanner(ps,ps->RegStatus) & _FLAG_P98_PAPER)){

            MotorSetConstantMove( ps, 1 );
            ps->Scan.fMotorBackward  = _FALSE;
            ps->bExtraMotorCtrl = 0;
            motorP98PositionYProc( ps, 20 );

            result = motorP98BackToHomeSensor( ps );
            if( _OK != result )
                  return result;

            _DODELAY( 250 );
    }

      return _OK;
}

/*.............................................................................
 * move the sensor to the scan-start position
 */
static void motorP98WaitForPositionY( pScanData ps )
{
      ULong dw;
    ULong dwBX, dwDX;

    if( ps->DataInf.dwScanFlag & SCANDEF_TPA ) {

            motorP98BackToHomeSensor( ps );
            _DODELAY( 100 );

/* CHECK do we need this block ? was test code in the original source code */
            ps->OpenScanPath( ps );
            IODataToRegister( ps, ps->RegModelControl, ps->AsicReg.RD_ModelControl);
            IODataToRegister( ps, ps->RegStepControl, (Byte)(_MOTOR_FREERUN +
                                         _MOTOR0_SCANSTATE + _MOTOR0_ONESTEP));
            IODataToRegister( ps, ps->RegMotor0Control, (Byte)(_MotorOn +
                                      _MotorHQuarterStep + _MotorDirForward));
            ps->CloseScanPath( ps );

            for (dw=1000; dw; dw--) {
            if (IODataRegisterFromScanner( ps, ps->RegStatus) & _FLAG_P98_PAPER) {
                        IORegisterDirectToScanner( ps, ps->RegForceStep );
                        _DODELAY( 1000 / 400 );
            }
            }
/*-*/
            ps->AsicReg.RD_ModeControl = _ModeScan;
            IOCmdRegisterToScanner( ps, ps->RegModeControl,
                                                ps->AsicReg.RD_ModeControl );

            memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );

            ps->Scan.fMotorBackward = _FALSE;
            ps->bExtraMotorCtrl = 0;
            ps->bFastMoveFlag       = _FastMove_Film_150;

            if (ps->DataInf.dwScanFlag & SCANDEF_Negative) {
            MotorP98GoFullStep(ps, (ps->DataInf.crImage.y+_NEG_SCANNINGPOS)/2);
            } else {
                MotorP98GoFullStep(ps, (ps->DataInf.crImage.y+_POS_SCANNINGPOS)/2);
            }

            return;
    }

    ps->AsicReg.RD_ModeControl = _ModeScan;

    IOCmdRegisterToScanner( ps, ps->RegModeControl,
                                          ps->AsicReg.RD_ModeControl );

    memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );

    ps->Scan.fMotorBackward = _FALSE;
    ps->bExtraMotorCtrl     = 0;

    /* SetStartPoint */
      dw = ps->wInitialStep + ps->DataInf.crImage.y;

      /*
       * CHANGE: when checking out the values from the NT registry
       *             I found that the values are NOT 0
       */
    switch (ps->DataInf.wPhyDataType) {
            case COLOR_BW:          dw += _BW_ORIGIN;    break;
            case COLOR_256GRAY:     dw += _GRAY_ORIGIN;      break;
            default:                dw += _COLOR_ORIGIN; break;
    }

    if (dw & 0x80000000)
            dw = 0;           /* negative */

    if (dw > 180) {
            if (ps->bSetScanModeFlag & _ScanMode_Mono) {
                dwBX = 90;                      /* go via 150 dpi, so 180 / 2 = 90 */
                dwDX = (dw -180) % 3;
            dw   = (dw -180) / 3;   /* 100 dpi */
            } else {
                dwBX = 45;                      /* go via 75 dpi, so 180 / 4 = 45 */
                dwDX = (dw -180) % 6;
            dw     = (dw -180) / 6;       /* 50 dpi */
            }

            dwDX = (dwDX * 3 + 1) / 2 + dwBX;

            /*
             * 100/50 dpi lines is 3/2 times of 150/75
             * eax = (remainder * 3 + 1) / 2 + 180 / (2 or 4) lines
             */
            ps->bFastMoveFlag = _FastMove_Low_C75_G150;
            MotorP98GoFullStep( ps, dwDX);

            if (dw) {
                  DBG( DBG_LOW, "FAST MOVE MODE !!!\n" );
                ps->bFastMoveFlag = _FastMove_Fast_C50_G100;
            MotorP98GoFullStep( ps, dw);
            }
    } else {
            dwBX = ((ps->bSetScanModeFlag & _ScanMode_Mono) ? 2: 4);
            dw     = (dw + dwBX/2) / dwBX;
            ps->bFastMoveFlag = _FastMove_Low_C75_G150;

            MotorP98GoFullStep(ps, dw);
    }
}

/*.............................................................................
 * PreMove/EndMove
 * PreMove is only in ADF and CFB mode
 * In ADF version, there is a distance gap between Paper flag and Real initial
 * position and it need premove to real initial position and turn the motor
 * Inverse and start Fast move to start scan position
 * In CFB version , PreMove 1 mm to avoid bouncing of PaperFlag sensor then
 * fast move
 * In CIS and CSP although there have not PreMove but there have End move
 * when paper out there still have several mm need to read,
 * So it need EndMove 2mm and set Inverse Paper
 */
static Bool motorP98GotoShadingPosition( pScanData ps )
{
      int result;

      DBG( DBG_LOW, "motorP98GotoShadingPosition()\n" );

      /* Modify Lamp Back to Home step for Scan twice in short time */
      result =  motorP98CheckSensorInHome( ps );

      if( _OK != result )
            return _FALSE;

    MotorSetConstantMove( ps, 0 );        /* clear scan states */

    IOCmdRegisterToScanner( ps, ps->RegModelControl,
                                          ps->AsicReg.RD_ModelControl );

      ps->Scan.fMotorBackward = _FALSE;   /* forward */
    ps->bExtraMotorCtrl     = 0;

    if( ps->DataInf.dwScanFlag & SCANDEF_TPA ) {

            ps->bFastMoveFlag = _FastMove_Low_C75_G150;
            MotorP98GoFullStep( ps, 0x40 );

            ps->bFastMoveFlag = _FastMove_Middle_C75_G150;
            MotorP98GoFullStep( ps, ps->Device.dwModelOriginY );
    }

      memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
    IOSetToMotorRegister( ps );

    return _TRUE;
}

/*.............................................................................
 * round to the next physical available value
 */
static void motorP98SetMaxDpiAndLength( pScanData ps,
                                                          pUShort wLengthY, pUShort wBaseDpi )
{
    if (ps->DataInf.xyAppDpi.y > 600)
            *wLengthY = ps->LensInf.rExtentY.wMax * 4 + 200;
    else
            *wLengthY = ps->LensInf.rExtentY.wMax * 2 + 200;

    if ((ps->DataInf.wPhyDataType >= COLOR_TRUE24) &&
                                               (ps->DataInf.xyAppDpi.y <= ps->wMinCmpDpi)) {
            *wBaseDpi = ps->wMinCmpDpi;
      } else {
            if ((ps->DataInf.wPhyDataType < COLOR_TRUE24) &&
                                                                  (ps->DataInf.xyAppDpi.y <= 75)) {
            *wBaseDpi = 75;
            } else {
                if (ps->DataInf.xyAppDpi.y <= 150) {
                        *wBaseDpi = 150;
                  } else {
                        if (ps->DataInf.xyAppDpi.y <= 300) {
                            *wBaseDpi = 300;
                        } else {
                            if (ps->DataInf.xyAppDpi.y <= 600)
                                    *wBaseDpi = 600;
                        else
                                    *wBaseDpi = 1200;
                        }
                  }
            }
      }

      DBG( DBG_LOW, "wBaseDPI = %u, %u\n", *wBaseDpi, ps->wMinCmpDpi );
}

/*.............................................................................
 *
 */
static void motorP98FillGBColorRunTable( pScanData ps, pUChar pTable,
                                                           Byte bHi, Byte bLo, UShort wBaseDpi )
{

    if( ps->Device.f0_8_16 ) {

            if (wBaseDpi == ps->wMinCmpDpi) {
                *pTable |= bHi;
                *(pTable + 1) |= bLo;
            } else {
                  switch (wBaseDpi) {
                case 150:
                        *(pTable + 2) |= bHi;
                        *(pTable + 4) |= bLo;
                        break;

                case 300:
                        *(pTable + 4) |= bHi;
                        *(pTable + 8) |= bLo;
                        break;

                case 600:
                        *(pTable + 8) |= bHi;
                        *(pTable + 16) |= bLo;
                        break;

                default:
                        *(pTable + 16) |= bHi;
                        *(pTable + 32) |= bLo;
                        break;
                  }
            }
    } else {

            if (wBaseDpi == ps->wMinCmpDpi) {
                *pTable |= bHi;
                *(pTable + 1) |= bLo;
            } else {
                switch(wBaseDpi) {

                  case 150:
                      *(pTable + 1) |= bHi;
                      *(pTable + 2) |= bLo;
                  break;

                  case 300:
                      *(pTable + 2) |= bHi;
                      *(pTable + 4) |= bLo;
                      break;

                  case 600:
                      *(pTable + 4) |= bHi;
                      *(pTable + 8) |= bLo;
                  break;

                  default:
                      *(pTable + 8) |= bHi;
                      *(pTable + 16) |= bLo;
                      break;
                }
            }
    }
}

/*.............................................................................
 *
 */
static void motorP98SetupRunTable( pScanData ps )
{
      UShort wDpi, w, wBaseDpi, wLengthY;
      pUChar pTable;

      motorP98SetMaxDpiAndLength( ps, &wLengthY, &wBaseDpi );

      /*ClearColorRunTable(); */
    memset( ps->pColorRunTable, 0, ps->BufferForColorRunTable );

    wDpi = wBaseDpi;
    w        = wLengthY + 1000;
    pTable = ps->pColorRunTable + (_NUMBER_OF_SCANSTEPS / 4);

    if( ps->DataInf.wPhyDataType >= COLOR_TRUE24) {

            for(; w; w--, pTable++) {
                if((short)(wDpi -= ps->DataInf.xyPhyDpi.y) <= 0) {
                        wDpi += wBaseDpi;
                        *pTable |= 0x44;
                        motorP98FillGBColorRunTable( ps, pTable, 0x22, 0x11, wBaseDpi );
                }
            }
    } else {
            for(; w; w--, pTable++) {
                if((short)(wDpi -= ps->DataInf.xyPhyDpi.y) <= 0) {
                        wDpi += wBaseDpi;
                        *pTable = 0x22;
                }
            }
    }
      ps->dwColorRunIndex = 0;
}

/*.............................................................................
 *
 */
static void motorP98UpdateDataCurrentReadLine( pScanData ps )
{
    if(!(ps->Scan.bNowScanState & _SCANSTATE_STOP)) {

            Byte b;

            if (ps->Scan.bNowScanState >= ps->bCurrentLineCount)
                b = ps->Scan.bNowScanState - ps->bCurrentLineCount;
            else
            b = ps->Scan.bNowScanState + _NUMBER_OF_SCANSTEPS - ps->bCurrentLineCount;

            if (b < 40)
                return;
    }

      ps->SetMotorSpeed( ps, ps->bCurrentSpeed, _TRUE );
    IOSetToMotorRegister( ps );

    ps->Scan.bModuleState = _MotorAdvancing;
}

/*.............................................................................
 *    Byte - Scan State Index (0-63) and StopStep bit
 *    pScanState->bStep - Scan State Index (0-63)
 *    pScanState->bStatus - Scanner Status Register value
 */
static void motorP96GetScanStateAndStatus( pScanData ps, pScanState pScanStep )
{
    ps->OpenScanPath( ps );

    pScanStep->bStep   = IOGetScanState(ps, _TRUE);
      pScanStep->bStep  &= _SCANSTATE_MASK;           /* org was. ~_ScanStateStop; */
    pScanStep->bStatus = IODataFromRegister( ps, ps->RegStatus );

    ps->CloseScanPath( ps );
}

/*.............................................................................
 * Capture the image data and average them.
 */
static Byte motorP96ReadDarkData( pScanData ps )
{
      Byte   bFifoOffset;
    UShort   wSum, w;
    TimerDef timer;

      MiscStartTimer( &timer, _SECOND/2);

    do {

            bFifoOffset = IODataRegisterFromScanner( ps, ps->RegFifoOffset );

            /* stepped 1 block */
            if( bFifoOffset ) {

                  /* read data */
                  IOReadScannerImageData( ps, ps->pScanBuffer1, 512UL);

                /* 320 = 192 + 128 (128 is size to fetch data) */
                for (w = 192, wSum = 0; w < 320; w++)
                        wSum += (UShort)ps->pScanBuffer1[w];/* average data from      */
                                                                              /* offset 192 and size 128*/
                return (Byte)(wSum >> 7);                   /* divided by 128               */
            }

    } while (!MiscCheckTimer(&timer));

    return 0xff;                          /* timed-out */
}

/*.............................................................................
 * move the sensor to a specific Y-position
 */
static void motorP96PositionYProc( pScanData ps, ULong dwStates )
{
      ScanState sState;

      memset( ps->pColorRunTable, 1, dwStates );

#ifdef DEBUG
      if( dwStates > 800UL )
            DBG( DBG_HIGH, "!!!!! RUNTABLE OVERFLOW !!!!!\n" );
#endif
      memset( ps->pColorRunTable + dwStates, 0xff, 800UL - dwStates );

      IOGetCurrentStateCount( ps, &sState );
    ps->bOldStateCount = sState.bStep;

    /* TurnOnMotorAndSetDirection (); */
    if( ps->Scan.fMotorBackward ) {
            IOCmdRegisterToScanner( ps, ps->RegMotorControl,
                                                            (Byte)(ps->IgnorePF | ps->MotorOn));
      } else {
            IOCmdRegisterToScanner( ps, ps->RegMotorControl,
                              (Byte)(ps->IgnorePF | ps->MotorOn | _MotorDirForward));
      }

    ps->pScanState = ps->pColorRunTable;
    do {
            ps->FillRunNewAdrPointer( ps );

    } while (!motorCheckMotorPresetLength( ps ));
}

/*.............................................................................
 * move the sensor to the scan-start position
 */
static void motorP96WaitForPositionY( pScanData ps )
{
/* scheint OKAY zu sein fuer OP4830 */
#ifdef _A3I_EN
#warning "compiling for A3I"
      ULong     dw;
      ScanState sState;

      memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );

    ps->Asic96Reg.RD_MotorControl = ps->IgnorePF|ps->MotorOn|_MotorDirForward;
    ps->Scan.fMotorBackward = _FALSE;
    ps->bExtraMotorCtrl     = ps->IgnorePF;

    if( ps->DataInf.xyAppDpi.y <= ps->PhysicalDpi )
            dw = 30UL;
    else
            dw = 46UL;

    dw = (dw + ps->DataInf.crImage.y) * 4 / 3;

    if( ps->DataInf.wPhyDataType == COLOR_TRUE24 )
            dw += 99; /* dwStepsForColor; */

    else if( ps->DataInf.wPhyDataType == COLOR_256GRAY )
          dw += 99; /* dwStepsForGray; */
      else
          dw += 99; /* dwStepsForBW; */

    if( dw >= 130UL ) {

            dw -= 100UL;
            dw <<= 1;
          /* GoFullStep (dw); */

            memset( ps->pColorRunTable, 1, dw );
            memset( ps->pColorRunTable + dw, 0xff, ps->BufferForColorRunTable - dw );

            IOGetCurrentStateCount( ps, &sState );
            ps->bOldStateCount = sState.bStep;

            /* AdjustMotorTime () */
            IOCmdRegisterToScanner( ps, ps->RegLineControl, 31 );

            /* SetRunHalfStep () */
            if( ps->Scan.fMotorBackward )
                IOCmdRegisterToScanner( ps, ps->RegMotorControl, _Motor1FullStep |
                                                    ps->IgnorePF | ps->MotorOn );
            else
                IOCmdRegisterToScanner( ps, ps->RegMotorControl, ps->IgnorePF |
                                                                ps->MotorOn | _MotorDirForward );

            ps->pScanState = ps->pColorRunTable;

            do {
                  ps->FillRunNewAdrPointer( ps );

            } while (!motorCheckMotorPresetLength(ps));

            /* RestoreMotorTime () */
            IOCmdRegisterToScanner( ps, ps->RegLineControl,
                                                ps->AsicReg.RD_LineControl );

            dw = 100UL;
    }

    if( ps->DataInf.wPhyDataType != COLOR_TRUE24 )
            dw += 20;

      motorP96PositionYProc( ps, dw );

#else

      ULong   dw;
    ScanState sState;

      TimerDef  timer;

      MiscStartTimer( &timer, _SECOND / 4);
    while (!MiscCheckTimer( &timer ));

      memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );

    ps->Asic96Reg.RD_MotorControl = ps->IgnorePF|ps->MotorOn|_MotorDirForward;
    ps->Scan.fMotorBackward = _FALSE;
    ps->bExtraMotorCtrl     = ps->IgnorePF;

    if ((ps->DataInf.wPhyDataType >= COLOR_TRUE24) ||
                                                                  (ps->DataInf.xyAppDpi.y <= 300)) {
            dw = 6UL;

    } else {

            if (ps->DataInf.xyAppDpi.y <= 600) {
                  /* 50 is from 6/300 */
                dw = (ULong)ps->DataInf.xyAppDpi.y / 50UL + 3UL;
            } else
                dw = 15;      /* 6UL * 600UL / 300UL + 3; */
    }

    dw += ps->DataInf.crImage.y;

    if (dw >= 180UL) {

            dw -= 180UL;
      /* GoFullStep (ps, dw);----------------------------------------------*/
            memset( ps->pColorRunTable, 1, dw );
#ifdef DEBUG
            if( dw > 8000UL )
                  DBG( DBG_HIGH, "!!!!! RUNTABLE OVERFLOW !!!!!\n" );
#endif
            memset( ps->pColorRunTable + dw, 0xff, 8000UL - dw );

            IOGetCurrentStateCount( ps, &sState );
            ps->bOldStateCount = sState.bStep;

            /* SetRunFullStep (ps) */
            if( ps->Scan.fMotorBackward ) {
                IOCmdRegisterToScanner( ps, ps->RegMotorControl,
                                      (Byte)(ps->FullStep | ps->IgnorePF | ps->MotorOn));
            } else {
                IOCmdRegisterToScanner( ps, ps->RegMotorControl,
                                      (Byte)(ps->FullStep | ps->IgnorePF | ps->MotorOn |
                                                                                          _MotorDirForward));
            }

            ps->pScanState = ps->pColorRunTable;

            do {
                  ps->FillRunNewAdrPointer (ps);

            } while (!motorCheckMotorPresetLength(ps));

            /*-------------------------------------------------------------------*/

            dw = 180UL;
    }

      if (ps->DataInf.wPhyDataType != COLOR_TRUE24)
            dw = dw * 2 + 16;
    else
            dw *= 2;

      motorP96PositionYProc( ps, dw );
#endif
}

/*.............................................................................
 * Position Scan Module to specified line number (Forward or Backward & wait
 * for paper flag ON)
 */
static void motorP96ConstantMoveProc1( pScanData ps, ULong dwLines )
{
      Byte    bRemainder, bLastState;
    UShort    wQuotient;
    ULong     dwDelayMaxTime;
    ScanState StateStatus;
      TimerDef  timer;
      Bool    fTimeout = _FALSE;

      /* state cycles */
    wQuotient  = (UShort)(dwLines / _NUMBER_OF_SCANSTEPS);
    bRemainder = (Byte)(dwLines % _NUMBER_OF_SCANSTEPS);

      /* 3.3 ms per line */
#ifdef _A3I_EN
    dwDelayMaxTime = dwLines * _MOTOR_ONE_LINE_TIME + _SECOND * 2;
#else
    dwDelayMaxTime = dwLines * _MOTOR_ONE_LINE_TIME + _SECOND * 20;
#endif

      /* step every time */
      MotorSetConstantMove( ps, 1 );

    ps->OpenScanPath( ps );

    ps->AsicReg.RD_ModeControl = _ModeScan;
    IODataToRegister( ps, ps->RegModeControl, _ModeScan );

    ps->Asic96Reg.RD_MotorControl = ps->MotorFreeRun |
                                                                  ps->MotorOn | _MotorDirForward;
      IODataToRegister( ps, ps->RegMotorControl, ps->Asic96Reg.RD_MotorControl );

    ps->CloseScanPath( ps );

    bLastState = 0;

      MiscStartTimer( &timer, dwDelayMaxTime );

    do {

            /* GetStatusAndScanStateAddr () */
            motorP96GetScanStateAndStatus( ps, &StateStatus );
            if (StateStatus.bStatus & _FLAG_P96_PAPER ) {
                if (wQuotient)  {

                        if (StateStatus.bStep != bLastState) {
                            bLastState = StateStatus.bStep;

                            if (!bLastState)
                                    wQuotient--;
                        }
                } else
                        if (StateStatus.bStep >= bRemainder)
                            break;
                  } else
                      break;
    } while (!(fTimeout = MiscCheckTimer( &timer )));

    if (!fTimeout) {
            memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
            IOSetToMotorRegister( ps );
    }
}

/*.............................................................................
 * PreMove/EndMove
 * PreMove is only in ADF and CFB mode
 * In ADF version, there is a distance gap between Paper flag and Real initial
 *   position and it need premove to real initial position and turn the motor
 *   Inverse and start Fast move to start scan position
 * In CFB version , PreMove 1 mm to avoid bouncing of PaperFlag sensor then
 *   fast move
 * In CIS and CSP although there have not PreMove but there have End move
 *   when paper out there still have several mm need to read,
 *   So it need EndMove 2mm and set Inverse Paper
 */
static Bool motorP96GotoShadingPosition( pScanData ps )
{
      DBG( DBG_LOW, "motorP96GotoShadingPosition()\n" );

      MotorSetConstantMove( ps, 0 );            /* clear scan states */
    ps->Scan.fMotorBackward = _FALSE;     /* forward               */
    ps->bExtraMotorCtrl     = ps->IgnorePF;

      MotorP96ConstantMoveProc( ps, 15 * 12 );  /* forward 180 lines */

    if (IODataRegisterFromScanner(ps, ps->RegStatus) & _FLAG_P96_PAPER ) {
            ps->Asic96Reg.RD_MotorControl = 0;
            IOCmdRegisterToScanner( ps, ps->RegMotorControl, 0 );

            DBG( DBG_LOW, "motorP96GotoShadingPosition() failed\n" );
            return _FALSE;
    }
    ps->Scan.fMotorBackward = _TRUE;      /* backward                          */
    ps->bExtraMotorCtrl     = 0;        /* no extra action for motor */

      /* backward a few thousand lines to touch sensor */
      MotorP96ConstantMoveProc( ps, ps->BackwardSteps );

      _DODELAY( 250 );

      IOCmdRegisterToScanner( ps, ps->RegModelControl,
                                (Byte)(ps->AsicReg.RD_ModelControl | _ModelInvertPF));

    ps->Scan.fMotorBackward = _FALSE;                       /* forward              */
    motorP96ConstantMoveProc1( ps, 14 * 12 * 2);      /* ahead 336 lines      */

      if( MODEL_OP_A3I == ps->sCaps.Model ) {

            motorP96PositionYProc( ps, 80 );

      } else {
            /* forward 24 + pScanData->wOverBlue lines */
      if (!ps->fColorMoreRedFlag)
                  motorP96PositionYProc( ps, ps->wOverBlue + 12 * 2);
      }

    if( ps->DataInf.dwScanFlag & SCANDEF_TPA ) {
            ps->Scan.fMotorBackward = _FALSE;
            ps->bExtraMotorCtrl     = ps->IgnorePF;
            MotorP96ConstantMoveProc( ps, 1200 );
    }

      IOCmdRegisterToScanner( ps, ps->RegModelControl,
                                                                        ps->AsicReg.RD_ModelControl );
    return _TRUE;
}

/*.............................................................................
 *
 */
static void motorP96FillHalfStepTable( pScanData ps )
{
#ifdef _A3I_EN
      if ( ps->Scan.bModuleState == _MotorInStopState ) {

            /* clear the table and get the step value */
            memset( a_bHalfStepTable, 0, _NUMBER_OF_SCANSTEPS );
            ps->bMotorStepTableNo = a_bMotorDown2Table[(ps->bMotorSpeedData-1)/2];
    }

      /* the fastest stepping */
    if( ps->bMotorSpeedData & 1 ) {

            memset( a_bHalfStepTable,
                   ((ps->Scan.bModuleState != _MotorInStopState) ? 1 : 0),
                                                                                    _NUMBER_OF_SCANSTEPS );
      } else {

            pUChar   pbHalfStepTbl;
            Byte     bHalfSteps;
            pUShort      pwMoveStep;
            DataType Data;

            bHalfSteps    = ps->bMotorSpeedData / 2;
            pwMoveStep    = &a_wMoveStepTable[ps->bCurrentLineCount];
            pbHalfStepTbl = &a_bHalfStepTable[ps->bCurrentLineCount];

            if( ps->DataInf.wAppDataType == COLOR_TRUE24)
                Data.dwValue = _NUMBER_OF_SCANSTEPS - 1;
            else
                Data.dwValue = _NUMBER_OF_SCANSTEPS;

            /* FillDataToHalfStepTable */
            for(; Data.dwValue; Data.dwValue-- ) {

                if( *pwMoveStep ) {       /* need to exposure */

                        if( Data.bValue >= bHalfSteps ) {

                            pUChar pb = pbHalfStepTbl + bHalfSteps;

                            /* AdjustHalfStepStart */
                            if( ps->DataInf.wAppDataType == COLOR_TRUE24 ) {

                                    if (bHalfSteps >= 2)
                                        pb -= (bHalfSteps - 1);
                            }
                            if( pb >= pbEndHalfStepTable )
                                    pb -= _NUMBER_OF_SCANSTEPS;

                            if( wP96BaseDpi <= ps->PhysicalDpi && *pwMoveStep != 2 )
                                    *pb = 1;

                        pb += bHalfSteps;

                            if( pb >= pbEndHalfStepTable )
                                    pb -= _NUMBER_OF_SCANSTEPS;

                            *pb = 1;
                        }
                        else
                            *pwMoveStep = 0;          /* idle state */
                }
                if( ++pwMoveStep >= pwEndMoveStepTable ) {
                        pwMoveStep = a_wMoveStepTable;

                        pbHalfStepTbl = a_bHalfStepTable;
                }
                else
                        pbHalfStepTbl++;
            }
    }

#else

#ifdef DEBUG
      if( 0 == wP96BaseDpi )
            DBG( DBG_HIGH, "!!!! WARNING - motorP96FillHalfStepTable(), "
                                 "wP96BaseDpi == 0 !!!!\n" );
#endif

      if ( ps->Scan.bModuleState == _MotorInStopState ) {

            /* clear the table and get the step value */
            memset( a_bHalfStepTable, 0, _NUMBER_OF_SCANSTEPS );
            ps->bMotorStepTableNo = a_bMotorDown2Table[(ps->bMotorSpeedData-1)/2];
    }

      /* the fastest stepping */
    if( ps->bMotorSpeedData & 1 ) {

            memset( a_bHalfStepTable,
                   ((ps->Scan.bModuleState != _MotorInStopState) ? 1 : 0),
                                                                                    _NUMBER_OF_SCANSTEPS );
      } else {

            pUChar   pbHalfStepTbl, pbHalfStepContent;
            pUShort      pwMoveStep;
            DataType Data;

            pbHalfStepContent = a_pbHalfStepTables[ps->bMotorSpeedData / 2 - 1];

            pwMoveStep    = &a_wMoveStepTable[ps->bCurrentLineCount];
            pbHalfStepTbl = &a_bHalfStepTable[ps->bCurrentLineCount];

            if (ps->DataInf.wAppDataType == COLOR_TRUE24)
                Data.dwValue = _NUMBER_OF_SCANSTEPS - 1;
            else
                Data.dwValue = _NUMBER_OF_SCANSTEPS;

            /* FillDataToHalfStepTable */
            for (; Data.dwValue; Data.dwValue--) {

                if (*pwMoveStep) {        /* need to exposure */

                        if (Data.bValue >= *pbHalfStepContent) {

                            pUChar pb = pbHalfStepTbl + *pbHalfStepContent;

                              if (pb >= pbEndHalfStepTable)
                                    pb -= _NUMBER_OF_SCANSTEPS;

                            /* JudgeStep1 () */
                            if ((wP96BaseDpi != 600) && (*pwMoveStep != 2)) {
                                    if (ps->Scan.bModuleState != _MotorInStopState) {
                                        *pb = 1;
                                    } else {
                                        if (ps->bMotorStepTableNo) {
                                                ps->bMotorStepTableNo--;
                                                *pb = 1;
                                    }
                                    }
                              }

                            pb += *pbHalfStepContent;
                            if (pb >= pbEndHalfStepTable)
                                    pb -= _NUMBER_OF_SCANSTEPS;

                            /* JudgeStep2 () */
                            if (ps->Scan.bModuleState == _MotorInStopState) {
                                    if (ps->bMotorStepTableNo) {
                                        ps->bMotorStepTableNo--;
                                        *pb = 1;
                                    }
                            } else {
                                    *pb = 1;
                              }

                              pbHalfStepContent++;
                        } else {
                            *pwMoveStep = 0;          /* idle state */
                        }
                  }

                if (++pwMoveStep >= pwEndMoveStepTable) {
                        pwMoveStep = a_wMoveStepTable;
                        pbHalfStepTbl = a_bHalfStepTable;
                } else {
                        pbHalfStepTbl++;
                  }
            }
    }
#endif
}

/*.............................................................................
 *
 */
static void motorP96FillDataToColorTable( pScanData ps,
                                                              Byte bIndex, ULong dwSteps)
{
    Byte     bColor, bColors;
      pUChar       pb, pb1;
      pUShort      pw;
    DataType Data;

    for (pb = &a_bColorByteTable[bIndex],
             pw = &a_wMoveStepTable[bIndex]; dwSteps; dwSteps--) {

            if (*pw) {                    /* valid state */

                  if( *pw >= ps->BufferForColorRunTable ) {
                        DBG( DBG_LOW, "*pw = %u > %lu !!\n",
                                    *pw, ps->BufferForColorRunTable );
                  } else {

                      bColor  = ps->pColorRunTable [*pw];   /* get the colors */
                      bColors = a_bColorsSum [bColor & 7];/* number of colors */

                      if (bColors) {                                    /* need to read data */
                              if (dwSteps >= bColors) {           /* enough room           */

                              /* separate the colors to byte */
                                  pb1 = pb;
                              if (bColor & ps->b1stColor) {

                                          *pb1 = ps->b1stColorByte;
                                          if (++pb1 >= pbEndColorByteTable)
                                              pb1 = a_bColorByteTable;
                                  }

                                  if (bColor & ps->b2ndColor) {

                                          *pb1 = ps->b2ndColorByte;
                                          if (++pb1 >= pbEndColorByteTable)
                                              pb1 = a_bColorByteTable;
                                  }

                              if (bColor & ps->b3rdColor)
                                          *pb1 = ps->b3rdColorByte;
                              } else
                                  *pw = 0;
                  }
                  }
            }

            if (++pw >= pwEndMoveStepTable) {
                pw = a_wMoveStepTable;
                pb = a_bColorByteTable;
            } else
                pb++;
    }

/*     ps->bOldSpeed = ps->bMotorRunStatus; non functional */

    /* ToCondense, CondenseColorByteTable */
    for (dwSteps = _SCANSTATE_BYTES, pw = (pUShort)a_bColorByteTable,
             pb = ps->a_nbNewAdrPointer; dwSteps; dwSteps--, pw++, pb++) {

            Data.wValue = *pw & 0x0303;
            *pb = Data.wOverlap.b1st | (Data.wOverlap.b2nd << 4);
    }

    /* ToCondenseMotor */
    for (dwSteps = _SCANSTATE_BYTES, pb1 = a_bHalfStepTable,
             pb = ps->a_nbNewAdrPointer; dwSteps; dwSteps--, pb1++, pb++) {

            if (*pb1++)
                *pb |= 4;

            if (*pb1)
                *pb |= 0x40;
    }
}

/*.............................................................................
 *
 */
static void motorP96FillBackColorDataTable( pScanData ps )
{
      Byte  bIndex;
      ULong dw;

    if ((ps->bCurrentLineCount + ps->bNewCurrentLineCountGap + 1) >=
                                                                              _NUMBER_OF_SCANSTEPS){
            bIndex = ps->bCurrentLineCount + ps->bNewCurrentLineCountGap + 1 -
                         _NUMBER_OF_SCANSTEPS;
      } else {
            bIndex = ps->bCurrentLineCount + ps->bNewCurrentLineCountGap + 1;
      }
    dw = _NUMBER_OF_SCANSTEPS - ps->bNewCurrentLineCountGap;

    motorP96FillDataToColorTable( ps, bIndex, dw );
}

/*.............................................................................
 * i/p:
 *    pScanStep = pScanData->pColorRunTable if forward, a_bScanStateTable if backward
 *    dwState = how many states is requested to process
 * NOTE:
 *    The content of pScanStep contain:
 *    0: Idle state
 *    0xff: End mark
 *    others: The motor speed value
 */
static void motorP96FillBackLoop( pScanData ps,
                                                  pUChar pScanStep, ULong dwStates )
{
    for (; dwStates; dwStates--) {

            if (*pScanStep == 0xff)
                break;                          /* end of states */

            if (*pScanStep) {
                if (*pScanStep == 1) {    /* speed == 1, this state has to step */

                        if (ps->dwScanStateCount & 1)
                            ps->a_nbNewAdrPointer[ps->dwScanStateCount / 2] |= 0x40;
                        else
                            ps->a_nbNewAdrPointer[ps->dwScanStateCount / 2] |= 0x04;
                  }

                *pScanStep -= 1;          /* speed decrease by 1 */

                if (!(*pScanStep))
                        pScanStep++;            /* state processed */
            } else
                pScanStep++;              /* skip this state */

            if (++ps->dwScanStateCount == _NUMBER_OF_SCANSTEPS)
                ps->dwScanStateCount = 0;                         /* reset to begin */
      }

    if (*pScanStep != 0xff)
            ps->fFullLength = _FALSE;
    else
            ps->fFullLength = _TRUE;

    IOSetToMotorStepCount( ps );          /* put all scan states to ASIC */
}

/*.............................................................................
 * 1) Clear scan states
 * 2) Adjust the new scan state
 */
static void motorP96FillRunNewAdrPointer( pScanData ps )
{
      ScanState sState;

      memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES);

      IOGetCurrentStateCount( ps, &sState );

    if (sState.bStep < ps->bOldStateCount )
            sState.bStep += _NUMBER_OF_SCANSTEPS;/* over table (table just can   */
                                                                   /* holds 64 step, then reset to */
                                                                   /* 0, so if less than means over*/
                                                                   /* the table)                            */

      sState.bStep -= ps->bOldStateCount;        /* how many states passed */
    ps->pScanState += sState.bStep;

    /*
       * if current state != no stepped or stepped a cycle, fill the table with
       * 1 in NOT STEPPED length. (1 means to this state has to be processing).
       */
    if (sState.bStep && sState.bStep != (_NUMBER_OF_SCANSTEPS - 1))
            memset( ps->pScanState, 1, _NUMBER_OF_SCANSTEPS - sState.bStep - 1 );

      IOGetCurrentStateCount( ps, &sState);
    ps->bOldStateCount   = sState.bStep;              /* update current state */
    ps->dwScanStateCount = (ULong)((sState.bStep + 1) & (_NUMBER_OF_SCANSTEPS - 1));

      /* fill begin at next step */
      motorP96FillBackLoop( ps, ps->pScanState, (_NUMBER_OF_SCANSTEPS - 1));
}

/*.............................................................................
 *
 */
static void motorP96SetupRunTable( pScanData ps )
{
      Short       siSum;
      UShort            wLoop;
      UShort            wLengthY;
      DataPointer p;

      DBG( DBG_LOW, "motorP96SetupRunTable()\n" );

    /* SetMaxDpiAndLength (ps) */
#ifdef _A3I_EN
    if( ps->DataInf.xyPhyDpi.y > ps->PhysicalDpi ) {

            wLengthY    = 6800 * 2;
            wP96BaseDpi = ps->PhysicalDpi *2;
    } else {
            wLengthY    = 6800;
            wP96BaseDpi = ps->LensInf.rDpiY.wPhyMax >> 1;
    }
#else
    if( ps->DataInf.xyPhyDpi.y > (ps->LensInf.rDpiY.wPhyMax / 2)) {

            wLengthY    = ps->LensInf.rExtentY.wMax << 1;
            wP96BaseDpi = ps->LensInf.rDpiY.wPhyMax;
    } else {
            wLengthY    = ps->LensInf.rExtentY.wMax;
            wP96BaseDpi = ps->LensInf.rDpiY.wPhyMax >> 1;
    }
#endif

      DBG( DBG_LOW, "wLengthY = %u, wP96BaseDpi = %u\n", wLengthY, wP96BaseDpi );

    /* ClearColorRunTable (ps) */
      memset( ps->pColorRunTable, 0, ps->BufferForColorRunTable ); /*wLengthY + 0x60 ); */

    p.pb = ps->pColorRunTable + _SCANSTATE_BYTES;
#ifdef _A3I_EN
    wLoop = wLengthY + 200;
#else
    wLoop = wLengthY + 0x20;
#endif
    siSum = (Short)wP96BaseDpi;

    if (ps->DataInf.wPhyDataType != COLOR_TRUE24) {
            for (; wLoop; wLoop--, p.pb++)      {
                if ((siSum -= (Short)ps->DataInf.xyPhyDpi.y) <= 0) {
                        siSum += (Short)wP96BaseDpi;
                        *p.pb = _COLORRUNTABLE_GREEN;
            }
            }

#ifdef _A3I_EN
            memset( ps->pColorRunTable + _NUMBER_OF_SCANSTEPS / 8 + wLengthY,
                                                                                                0x77, 0x100 );
#endif
    } else {
            /* CalColorRunTable */
            DataType Data;

            if (ps->fSonyCCD) {

                  if((ps->sCaps.Model == MODEL_OP_12000P) ||
                                           (ps->sCaps.Model == MODEL_OP_A3I)) {
                        Data.wValue = (_COLORRUNTABLE_RED << 8) | _COLORRUNTABLE_BLUE;
                  } else {
                        Data.wValue = (_COLORRUNTABLE_GREEN << 8) | _COLORRUNTABLE_BLUE;
                  }
            } else {
                  Data.wValue = (_COLORRUNTABLE_BLUE << 8) | _COLORRUNTABLE_GREEN;
            }

            for (; wLoop; wLoop--, p.pb++)      {

                if ((siSum -= (Short)ps->DataInf.xyPhyDpi.y) <= 0) {
                        siSum += (Short)wP96BaseDpi;

                  if((ps->sCaps.Model == MODEL_OP_12000P)|| 
                                           (ps->sCaps.Model == MODEL_OP_A3I)) {
                              *p.pb |= _COLORRUNTABLE_GREEN;
                } else {
                              *p.pb |= _COLORRUNTABLE_RED;
                }

                        /* Sony:Green,Toshiba:Blue */
                        *(p.pb + 8)  |= Data.wOverlap.b2nd;
                        *(p.pb + 16) |= Data.wOverlap.b1st;
                }
            }

#ifdef _A3I_EN
            memset( ps->pColorRunTable + _NUMBER_OF_SCANSTEPS / 8 + wLengthY,
                                                                                                0x77, 0x100 );
#endif
            if (ps->DataInf.xyPhyDpi.y < 100) {

                Byte bColor;

                /* CheckColorTable () */
                if (ps->fSonyCCD)
                        Data.wValue = 0xdd22;
                else
                        Data.wValue = 0xbb44;

                for (wLoop = wLengthY - _SCANSTATE_BYTES,
                         p.pb = ps->pColorRunTable + _SCANSTATE_BYTES;
                                                                         wLoop; wLoop--, p.pb++) {
                        bColor = 0;

                        switch (a_bColorsSum[*p.pb & 0x0f]) {

                      case 3:
                              if (*(p.pb + 2))
                                  bColor = 1;
                      case 2:
                              if (*(p.pb + 1))
                              bColor++;
                              if (bColor == 2) {
                                  *p.pb &= ~_COLORRUNTABLE_RED;
                                  *(p.pb - 2) = _COLORRUNTABLE_RED;
                              }

                              if (bColor) {
                              if (*p.pb & ps->RedDataReady) {
                                          *p.pb &= ~_COLORRUNTABLE_RED;
                                          *(p.pb - 1) = _COLORRUNTABLE_RED;
                                  } else {
                                          *p.pb &= Data.wOverlap.b2nd;
                                          *(p.pb - 1) = Data.wOverlap.b1st;
                              }
                              }
                        }
            }
            }
      }
}

/*.............................................................................
 *
 */
static void motorP96UpdateDataCurrentReadLine( pScanData ps )
{
      ScanState   State1, State2;
      TimerDef    timer;

      IOGetCurrentStateCount( ps, &State1 );
      IOGetCurrentStateCount( ps, &State2 );

    if (State1.bStatus == State2.bStatus) {

            if (!(State2.bStatus & _SCANSTATE_STOP)) {

                /* motor still running */
                if (State2.bStep < ps->bCurrentLineCount) {
                        State2.bStep = State2.bStep + _NUMBER_OF_SCANSTEPS -
                                                                               ps->bCurrentLineCount;
                  } else
                        State2.bStep -= ps->bCurrentLineCount;

                if (State2.bStep >= (_NUMBER_OF_SCANSTEPS - 3)) {

                        MiscStartTimer( &timer, _SECOND );

                        do {
                            State2.bStatus = IOGetScanState( ps, _FALSE );

                        } while (!(State2.bStatus & _SCANSTATE_STOP) &&
                                     !MiscCheckTimer( &timer ));
                } else
                        if (State2.bStep < 40)
                            return;
            }

#ifdef _A3I_EN
            if( ps->bFifoCount >= 140) {
#else
            if( ps->bFifoCount >= 20) {
#endif
                if( 1 == ps->bCurrentSpeed ) {
                        ps->bCurrentSpeed *= 2;
                } else {
                        if( COLOR_TRUE24 == ps->DataInf.wPhyDataType )
                        ps->bCurrentSpeed += 4;
                        else
                            ps->bCurrentSpeed += 2;
                  }

            MotorP96AdjustCurrentSpeed( ps, ps->bCurrentSpeed );
            }

            /*
             * when using the full-step speed on 600 dpi models, then set
             * the motor into half-step mode, to avoid that the scanner hits
             * the back of its bed
             */
/* HEINER:A3I */
#if 1
      if((600 == ps->PhysicalDpi) && (1 == ps->bCurrentSpeed)) {

                  if( ps->Asic96Reg.RD_MotorControl & ps->FullStep ) {
                        ps->Asic96Reg.RD_MotorControl &= ~ps->FullStep;
                  IOCmdRegisterToScanner( ps, ps->RegMotorControl,
                                                                     ps->Asic96Reg.RD_MotorControl );
                  }
            }
#endif
            ps->SetMotorSpeed( ps, ps->bCurrentSpeed, _TRUE );

            IOSetToMotorRegister( ps);
    }
}

/*.............................................................................
 * 1) Save the current scan state
 * 2) Set the motor direction
 */
static void motorGoHalfStep1( pScanData ps )
{
      ScanState sState;

    IOGetCurrentStateCount( ps, &sState );

      ps->bOldStateCount = sState.bStep;

      motorSetRunPositionRegister(ps);
      ps->pScanState = a_bScanStateTable;

      if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {

            ps->FillRunNewAdrPointer( ps );

            while (!motorCheckMotorPresetLength(ps))
                  motorP98FillRunNewAdrPointer1( ps );
      } else {

          while (!motorCheckMotorPresetLength( ps ))
                  ps->FillRunNewAdrPointer( ps );
      }
}

/*.............................................................................
 * when loosing data, we use this function to go back some lines and read them
 * again...
 */
static void motorP96WaitBack( pScanData ps )
{
      DataPointer p;
      DataType    Data;
      ULong       dw;
      UShort            w;
      UShort            wStayMaxStep;

    /* FindMaxMoveStepIndex () */

    p.pw = a_wMoveStepTable;

    for( Data.dwValue = _NUMBER_OF_SCANSTEPS, wStayMaxStep = 1; Data.dwValue;
                                                                               Data.dwValue--, p.pw++ )
            if( *p.pw > wStayMaxStep )
                wStayMaxStep = *p.pw;     /* save the largest step number */

    if( ps->DataInf.xyPhyDpi.y > ps->PhysicalDpi )
            wStayMaxStep -= 40;
    else
            wStayMaxStep -= 20;

    IORegisterDirectToScanner( ps, ps->RegInitDataFifo );

    memset( a_bScanStateTable, 1, _P96_BACKMOVES );
    memset(&a_bScanStateTable[_P96_BACKMOVES], 0xff, 250 - _P96_BACKMOVES );

    ps->Scan.fMotorBackward = _TRUE;
    motorGoHalfStep1( ps );               /* backward 130 lines */

    _DODELAY(200);                  /* let the motor stable */

    if( ps->DataInf.xyPhyDpi.y <= ps->PhysicalDpi ) {
            if( ps->DataInf.wPhyDataType == COLOR_TRUE24 ) {
                dw = _P96_FORWARDMOVES - 1;
            } else {
            dw = _P96_FORWARDMOVES - 2;
            }
    } else {
            dw = _P96_FORWARDMOVES;
      }
      
      memset( a_bScanStateTable, 1, dw );
    memset(&a_bScanStateTable[dw], 0xff, 250 - dw );

    ps->Scan.fMotorBackward = _FALSE;
    motorGoHalfStep1( ps );               /* move forward */

    /* GetNowStepTable () */
    ps->bCurrentLineCount = IOGetScanState( ps, _FALSE ) & _SCANSTATE_MASK;
    ps->bNewCurrentLineCountGap = 0;

    /* ClearColorByteTable () */
    memset( a_bColorByteTable, 0, _NUMBER_OF_SCANSTEPS );

    /* ClearHalfStepTable () */
    memset( a_bHalfStepTable, 0, _NUMBER_OF_SCANSTEPS );

    /* FillWaitMoveStepTable () */
    p.pw = &a_wMoveStepTable[((ps->bCurrentLineCount + 1) & 0x3f)];
    *p.pw = 1;
    p.pw++;

    for(w = wStayMaxStep, Data.bValue = ps->bMotorSpeedData, dw = 60;dw;dw--) {

            if( p.pw >= pwEndMoveStepTable ) /* make sure pointer in range */
                p.pw = a_wMoveStepTable;

            if (--Data.bValue)
                *p.pw = 0;                                  /* don't step */
            else {
                Data.bValue = ps->bMotorSpeedData;  /* speed value                   */
                *p.pw = w;                                  /* the ptr to pColorRunTable */
                w++;                                        /* pointer++                         */
            }

            p.pw++;                                         /* to next entry */
    }
      motorP96FillHalfStepTable( ps );
      motorP96FillBackColorDataTable( ps );
}

/*.............................................................................
 * when loosing data, we use this function to go back some lines and read them
 * again...
 */
static void motorP98WaitBack( pScanData ps )
{
      DataPointer p;
      DataType    Data;
      ULong       dw;
      UShort            w;
      UShort            wStayMaxStep;
      UShort            back, forward;

    p.pw = &a_wMoveStepTable[ps->bCurrentLineCount];

    if (0 == *p.pw) {

            for (w = _NUMBER_OF_SCANSTEPS; w && !*p.pw; w--) {
                p.pw--;
                if (p.pw < a_wMoveStepTable)
                        p.pw = &a_wMoveStepTable[_NUMBER_OF_SCANSTEPS - 1];
            }
            wStayMaxStep = *p.pw + 1;
    } else {
            wStayMaxStep = *p.pw;       /* save the largest step number */
      }

      if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {

            forward = _P98_FORWARDMOVES;
            back  = _P98_BACKMOVES;
      } else {
            forward = _P96_FORWARDMOVES;
            back  = _P96_BACKMOVES;
      }

      /*
       * Off to avoid the problem of block data re-read/lost
       */
      memset( a_bScanStateTable, 1, back );
      memset( &a_bScanStateTable[back], 0xff, (_SCANSTATE_TABLE_SIZE - back));
      ps->Scan.fMotorBackward = _TRUE;
    motorGoHalfStep1( ps );

    _DODELAY(200);                  /* let the motor stable */

    memset(a_bScanStateTable, 1, forward );
    memset(&a_bScanStateTable[forward], 0xff, (_SCANSTATE_TABLE_SIZE-forward));
      ps->Scan.fMotorBackward = _FALSE;
      motorGoHalfStep1( ps );

    ps->bNewCurrentLineCountGap = 0;

    memset( a_bColorByteTable, 0, _NUMBER_OF_SCANSTEPS );
      memset( a_bHalfStepTable,  0, _NUMBER_OF_SCANSTEPS );

      ps->bCurrentLineCount = (ps->bCurrentLineCount + 1) & 0x3f;

    p.pw = &a_wMoveStepTable[ps->bCurrentLineCount];

    for (w = wStayMaxStep, Data.bValue = ps->bMotorSpeedData,
                                                            dw = _NUMBER_OF_SCANSTEPS; dw; dw--) {
            if (--Data.bValue) {
                *p.pw = 0;                      /* don't step */
            } else {

                  /* speed value */
                Data.bValue = ps->bMotorSpeedData;
            *p.pw = w;                    /* the pointer to pColorRunTable*/
                w++;                                  /* pointer++                    */
      }
      /* make sure pointer in range */
      if (++p.pw >= pwEndMoveStepTable)
          p.pw = a_wMoveStepTable;
    }

      if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {
            motorP98FillHalfStepTable( ps );
            motorP98FillBackColorDataTable( ps );
      } else {
            motorP96FillHalfStepTable( ps );
            motorP96FillBackColorDataTable( ps );
      }
}

/*.............................................................................
 *
 */
static void motorFillMoveStepTable( pScanData ps,
                                                    UShort wIndex, Byte bStep, pUShort pw )
{
    UShort w;
    Byte   b;

    if (++pw >= pwEndMoveStepTable )
            pw = a_wMoveStepTable;

    wIndex++;

    b = ps->bMotorSpeedData;

    for (w = _NUMBER_OF_SCANSTEPS - bStep; w; w--) {
            if (b == 1) {
                b = ps->bMotorSpeedData;
                *pw = wIndex;
            wIndex++;
            } else {
                b--;
                *pw = 0;
            }
            if (++pw >= pwEndMoveStepTable)
            pw = a_wMoveStepTable;
    }

      if( _ASIC_IS_98001 == ps->sCaps.AsicID )
            motorP98FillHalfStepTable( ps );
      else
            motorP96FillHalfStepTable( ps );

    if ((ps->bCurrentLineCount + 1) >= _NUMBER_OF_SCANSTEPS) {
            b = ps->bCurrentLineCount + 1 - _NUMBER_OF_SCANSTEPS;
    } else {
            b = ps->bCurrentLineCount + 1;
      }

      if( _ASIC_IS_98001 == ps->sCaps.AsicID )
            motorP98FillDataToColorTable( ps, b, _NUMBER_OF_SCANSTEPS - 1);
      else
            motorP96FillDataToColorTable( ps, b, _NUMBER_OF_SCANSTEPS - 1);
}

/*.............................................................................
 *
 */
static void noMotorRunStatusStop( pScanData ps, Byte bScanState )
{
    Byte    b, b1, bCur;
    pUShort pw;
    pByte   pb;
    UShort  w;

    ps->bCurrentLineCount = (bScanState & _SCANSTATE_MASK);

    ps->Scan.fRefreshState  = _FALSE;

    IORegisterDirectToScanner( ps, ps->RegRefreshScanState );

    bCur = ps->bCurrentLineCount;
    pw       = &a_wMoveStepTable[bCur];
    pb       = ps->pColorRunTable;
      b    = 0;
    b1       = 0;
    w        = _NUMBER_OF_SCANSTEPS;

    if (*pw) {
            b = a_bColorsSum[pb [*pw] >> 4];
            if (b) {
                motorClearColorByteTableLoop0( ps, b );
                ps->bNewCurrentLineCountGap = b;

            motorFillMoveStepTable( ps, *pw, 1, pw );
                return;
            }
            b1++;
            bCur--;

            if (--pw < a_wMoveStepTable) {
                pw = &a_wMoveStepTable [_NUMBER_OF_SCANSTEPS - 1];
                bCur = _NUMBER_OF_SCANSTEPS - 1;
            }
    }

    for(; w; w--) {
            if (*pw) {
                if (*pw < _SCANSTATE_BYTES) {
                        b = 0;
                        break;
                } else
                        if ((b = a_bColorsSum [pb [*pw] >> 4]))
                            break;
            }
            b1++;
            bCur--;

            if (--pw < a_wMoveStepTable) {
                pw = &a_wMoveStepTable [_NUMBER_OF_SCANSTEPS - 1];
                  bCur = _NUMBER_OF_SCANSTEPS - 1;
            }
      }

      if (b1 == _NUMBER_OF_SCANSTEPS) {
            ps->bNewCurrentLineCountGap = 0;
            ps->bNewGap = 0;
      } else {
            ps->bNewCurrentLineCountGap = b1;
            ps->bNewGap = b;
      }

    motorClearColorByteTableLoop1( ps );

      motorFillMoveStepTable( ps, *pw, 0, pw);
}

/*.............................................................................
 *
 */
static void motorP96SetSpeed( pScanData ps, Byte bSpeed, Bool fSetRunState )
{
#if 0
    PUCHAR  pb;
    Byte     bScanState;
#endif
      Byte     bState, bData;
      UShort   wMoveStep;
      pUShort  pw;
      ULong    dw;
      TimerDef timer;

    if( fSetRunState )
            ps->Scan.bModuleState = _MotorInNormalState;

    ps->bMotorSpeedData = bSpeed;

    if( ps->bMoveDataOutFlag == _DataAfterRefreshState) {

            ps->bMoveDataOutFlag = _DataInNormalState;

            MiscStartTimer( &timer, (_SECOND /2));

            while (!MiscCheckTimer(&timer)) {
                if ((bState = IOGetScanState( ps, _FALSE)) & _SCANSTATE_STOP) {
                        ps->bCurrentLineCount = bState & ~_SCANSTATE_STOP;
                        motorP96WaitBack( ps );
                        return;
            }
            }
    }

    bState = IOGetScanState( ps, _FALSE );

    if((ps->Scan.bModuleState != _MotorInStopState) ||
                                                                        !(bState & _SCANSTATE_STOP)) {

            /* Try to find the available step for all rest steps.
             * 1) if current step is valid (with data request), fill all steps
             *    after it
             * 2) if current step is NULL (for delay purpose), backward search the
             *    valid entry, then fill all steps after it
             * 3) if no step is valid, fill all entries
       */
            Byte   bColors = 0;
            UShort w;

            /* NoMotorRunStatusStop () */
            ps->bCurrentLineCount  = (bState &= _SCANSTATE_MASK);
            ps->Scan.fRefreshState = _TRUE;

            IORegisterDirectToScanner( ps, ps->RegRefreshScanState );

            pw     = &a_wMoveStepTable[bState];
            bData  = 0;
            bState = ps->bCurrentLineCount;
            dw     = _NUMBER_OF_SCANSTEPS;

            if( (wMoveStep = *pw) ) {

                bColors = a_bColorsSum[ ps->pColorRunTable[*pw] / 16];

            if( bColors ) {

                        motorClearColorByteTableLoop0( ps, bColors );
                        ps->bNewCurrentLineCountGap = bColors;
                        bColors = 1;
                        goto FillMoveStepTable;

                } else {

                        bData++;
                        dw--;
                        if( --pw < a_wMoveStepTable ) {
                            pw     = a_wMoveStepTable + _NUMBER_OF_SCANSTEPS - 1;
                            bState = _NUMBER_OF_SCANSTEPS - 1;
                        }
                        else
                            bState--;
                }
            }

            /* FindNextStep */
            while( dw-- ) {

                if( (wMoveStep = *pw) ) {
                        if (wMoveStep < (_NUMBER_OF_SCANSTEPS / 2)) {
                            bColors = 0;
                            break;
                        }
                        if((bColors = a_bColorsSum [ps->pColorRunTable[wMoveStep] / 16]))
                            break;
                }

                bData++;
            if( --pw < a_wMoveStepTable ) {
                        pw     = a_wMoveStepTable + _NUMBER_OF_SCANSTEPS - 1;
                        bState = _NUMBER_OF_SCANSTEPS - 1;
                }
            else
                        bState--;
            }

            if (bData == _NUMBER_OF_SCANSTEPS )
                bData = bColors = 0;

            ps->bNewCurrentLineCountGap = bData;
            ps->bNewGap = bColors;
            motorClearColorByteTableLoop1( ps );
            bColors = 0;

            /* use pw (new pointer) and new speed to recreate MoveStepTable
             * wMoveStep = Index number from a_wMoveStepTable ([esi])
             * bColors = number of steps should be reserved
             * pw = where to fill
             */

FillMoveStepTable:

            motorP96GetStartStopGap( ps, _TRUE );

            if( !ps->bMotorStepTableNo )
                ps->bMotorStepTableNo = 1;

            if( ps->bMotorStepTableNo != 0xff && ps->IO.portMode == _PORT_SPP &&
                                                                                  ps->DataInf.xyPhyDpi.y <= 200) {
            ps->bMotorStepTableNo++;
            }

            if (++pw >= pwEndMoveStepTable)
                pw = a_wMoveStepTable;

            for( dw = _NUMBER_OF_SCANSTEPS - bColors, wMoveStep++,
                                                     bData = ps->bMotorSpeedData; dw; dw-- ) {
                if( bData == 1 ) {

                        bData = ps->bMotorSpeedData;
                        if( ps->bMotorStepTableNo ) {

                              ps->bMotorStepTableNo--;
                            w = wMoveStep;
                            wMoveStep++;

                        } else {
                            bData--;
                            w = 0;
                        }
                } else {
                        bData--;
                        w = 0;
                }
                *pw = w;

                if (++pw >= pwEndMoveStepTable)
                        pw = a_wMoveStepTable;
            }

            motorP96FillHalfStepTable( ps );

            /* FillColorBytesTable */
            if((ps->bCurrentLineCount + 1) < _NUMBER_OF_SCANSTEPS )
                bState = ps->bCurrentLineCount + 1;
            else
                bState = ps->bCurrentLineCount + 1 - _NUMBER_OF_SCANSTEPS;

            motorP96FillDataToColorTable( ps, bState, _NUMBER_OF_SCANSTEPS - 1);
      }
}

/*.............................................................................
 *
 */
static void motorP98SetSpeed( pScanData ps, Byte bSpeed, Bool fSetRunState )
{
      static Byte lastFifoState = 0;

      Bool overflow;
      Byte bOld1ScanState, bData;

    if( fSetRunState )
            ps->Scan.bModuleState = _MotorInNormalState;

    ps->bMotorSpeedData  = bSpeed;
      overflow                 = _FALSE;

      if( _ASIC_IS_98001 != ps->sCaps.AsicID ) {
            ps->bMoveDataOutFlag = _DataInNormalState;

            bData = IODataRegisterFromScanner( ps, ps->RegFifoOffset );

            if((lastFifoState > _P96_FIFOOVERFLOWTHRESH) &&
                                                                              (bData < lastFifoState)) {
                  DBG( DBG_HIGH, "FIFO OVERFLOW, loosing data !!\n" );
                  overflow = _TRUE;
        }
        lastFifoState = bData;
      }

    bOld1ScanState = IOGetScanState( ps, _FALSE );

    if(!(bOld1ScanState & _SCANSTATE_STOP) && !overflow)
            noMotorRunStatusStop( ps, bOld1ScanState );
    else {
            ps->bCurrentLineCount = (bOld1ScanState & 0x3f);
            ps->Scan.bModuleState = _MotorGoBackward;

            motorP98WaitBack( ps );

            if( overflow )
              lastFifoState = 0;

            if( _ASIC_IS_98001 != ps->sCaps.AsicID )
                  ps->bMoveDataOutFlag = _DataFromStopState;
    }
}

/*.............................................................................
 *
 */
static void motorP98003ModuleFreeRun( pScanData ps, ULong steps )
{
    IODataToRegister( ps, ps->RegMotorFreeRunCount1, (_HIBYTE(steps)));
    IODataToRegister( ps, ps->RegMotorFreeRunCount0, (_LOBYTE(steps)));
    IORegisterToScanner( ps, ps->RegMotorFreeRunTrigger );
}

/*.............................................................................
 *
 */
static void motorP98003ModuleToHome( pScanData ps )
{
    if(!(IODataFromRegister( ps, ps->RegStatus ) & _FLAG_P98_PAPER)) {

      IODataToRegister( ps, ps->RegMotor0Control,
                              (Byte)(ps->AsicReg.RD_Motor0Control|_MotorDirForward));

      MotorP98003PositionYProc( ps, 40 );
      MotorP98003BackToHomeSensor( ps );
          _DODELAY( 250 );
    }
}

/*.............................................................................
 *
 */
static void motorP98003DownloadNullScanStates( pScanData ps )
{
    memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
    IODownloadScanStates( ps );
}

/*.............................................................................
 *
 */
static void motorP98003Force16Steps( pScanData ps )
{
    ULong dw;

    IODataToRegister( ps, ps->RegStepControl, _MOTOR0_ONESTEP);
    IODataToRegister( ps, ps->RegMotor0Control, _FORWARD_MOTOR );

    for(dw = 16; dw; dw--) {
      IORegisterToScanner( ps, ps->RegForceStep );
      _DODELAY( 10 );
    }

    IODataToRegister( ps, ps->RegStepControl, _MOTOR0_SCANSTATE );
}

/*.............................................................................
 *
 */
static void motorP98003WaitForPositionY( pScanData ps )
{
    Byte  bXStep;
    ULong dwBeginY;

    dwBeginY = (ULong)ps->DataInf.crImage.y * 4 + ps->Scan.dwScanOrigin;

    if( ps->DataInf.wPhyDataType <= COLOR_256GRAY ) {
      if( ps->Device.f0_8_16 )
              dwBeginY += 16;
      else
              dwBeginY += 8;
    }

    bXStep = (Byte)((ps->DataInf.wPhyDataType <= COLOR_256GRAY) ?
                              ps->Device.XStepMono : ps->Device.XStepColor);

    if( ps->Shade.bIntermediate & _ScanMode_AverageOut )
      bXStep = 8;

    motorP98003Force16Steps( ps);
    dwBeginY -= 16;

    if (dwBeginY > (_RFT_SCANNING_ORG + _P98003_YOFFSET) &&
                                                  bXStep < ps->AsicReg.RD_XStepTime) {

      IODataToRegister( ps, ps->RegMotorDriverType, ps->Scan.motorPower );
        _DODELAY( 12 );
      IODataToRegister( ps, ps->RegXStepTime, bXStep);
          IODataToRegister( ps, ps->RegExtendedXStep, 0 );
      IODataToRegister( ps, ps->RegScanControl1,
                              (UChar)(ps->AsicReg.RD_ScanControl1 & ~_MFRC_RUNSCANSTATE));
      MotorP98003PositionYProc( ps, dwBeginY - 64 );
          dwBeginY = 64;
    }

    IODataToRegister( ps, ps->RegFifoFullLength0, _LOBYTE(ps->AsicReg.RD_BufFullSize));
    IODataToRegister( ps, ps->RegFifoFullLength1, _HIBYTE(ps->AsicReg.RD_BufFullSize));
    IODataToRegister( ps, ps->RegFifoFullLength2, _LOBYTE(_HIWORD(ps->AsicReg.RD_BufFullSize)));

    IODataToRegister( ps, ps->RegMotorDriverType, ps->AsicReg.RD_MotorDriverType);
    _DODELAY( 12 );

    if(!ps->Device.f2003 || (ps->Shade.bIntermediate & _ScanMode_AverageOut) ||
                (  ps->DataInf.xyAppDpi.y <= 75 &&
                                  ps->DataInf.wPhyDataType <= COLOR_256GRAY)) {
          IODataToRegister( ps, ps->RegMotorDriverType,
                   (Byte)(ps->Scan.motorPower & (_MOTORR_MASK | _MOTORR_STRONG)));
    } else {
      IODataToRegister( ps, ps->RegMotorDriverType,
                          ps->AsicReg.RD_MotorDriverType );
    }

    IODataToRegister( ps, ps->RegXStepTime,     ps->AsicReg.RD_XStepTime );
    IODataToRegister( ps, ps->RegExtendedXStep, ps->AsicReg.RD_ExtXStepTime );
    IODataToRegister( ps, ps->RegScanControl1,
                    (Byte)(ps->AsicReg.RD_ScanControl1 & ~_MFRC_RUNSCANSTATE));

    if( ps->DataInf.dwVxdFlag & _VF_PREVIEW ) {

        TimerDef timer;

          motorP98003ModuleFreeRun( ps, dwBeginY );
        _DODELAY( 15 );

      MiscStartTimer( &timer, (_SECOND * 20));

          while(( IOGetExtendedStatus( ps ) & _STILL_FREE_RUNNING) &&
                                                      !MiscCheckTimer(&timer));
            IODataToRegister( ps, ps->RegModeControl, _ModeScan );
    } else {
      MotorP98003PositionYProc( ps, dwBeginY );
        IORegisterToScanner( ps, ps->RegRefreshScanState );
    }
}

/*.............................................................................
 * move the sensor to the appropriate shading position
 */
static Bool motorP98003GotoShadingPosition( pScanData ps )
{
    motorP98003ModuleToHome( ps );

    /* position to somewhere under the transparency adapter */
    if( ps->DataInf.dwScanFlag & SCANDEF_TPA ) {

      MotorP98003ForceToLeaveHomePos( ps );
        motorP98003DownloadNullScanStates( ps );

      IODataToRegister( ps, ps->RegStepControl, _MOTOR0_SCANSTATE );
          IODataToRegister( ps, ps->RegModeControl, _ModeScan);
      IODataToRegister( ps, ps->RegMotor0Control, _FORWARD_MOTOR );
          IODataToRegister( ps, ps->RegXStepTime, 6);
      IODataToRegister( ps, ps->RegExtendedXStep, 0);
          IODataToRegister( ps, ps->RegScanControl1, _MFRC_BY_XSTEP);

      MotorP98003PositionYProc( ps, _TPA_P98003_SHADINGORG );
    }

    return _TRUE;
}

/*.............................................................................
 * initialize this module and setup the correct function pointer according
 * to the ASIC
 */
static void motorP98003PositionModuleToHome( pScanData ps )
{
    Byte save, saveModel;

    saveModel = ps->AsicReg.RD_ModelControl;

    ps->Scan.fRefreshState = _FALSE;
      motorP98003DownloadNullScanStates( ps );

      _DODELAY( 1000UL / 8UL);

      save = ps->Shade.bIntermediate;

      ps->Shade.bIntermediate = _ScanMode_AverageOut;
      ps->ReInitAsic( ps, _FALSE );
    ps->Shade.bIntermediate = save;

      IODataToRegister( ps, ps->RegModeControl, _ModeScan );
      IORegisterToScanner( ps, ps->RegResetMTSC );
      IODataToRegister( ps, ps->RegScanControl1, 0 );

      IODataToRegister( ps, ps->RegModelControl,
                          ps->Device.ModelCtrl | _ModelDpi300 );

      IODataToRegister( ps, ps->RegLineControl, 80);
      IODataToRegister( ps, ps->RegXStepTime, ps->Device.XStepBack);
      IODataToRegister( ps, ps->RegMotorDriverType, ps->Scan.motorPower);

      _DODELAY( 12 );

      IODataToRegister( ps, ps->RegMotor0Control,
                              (_MotorHHomeStop | _MotorOn | _MotorHQuarterStep |
                                           _MotorPowerEnable));
      IODataToRegister( ps, ps->RegStepControl,
                                        (_MOTOR0_SCANSTATE | _MOTOR_FREERUN));

    memset( ps->a_nbNewAdrPointer, 0x88, _SCANSTATE_BYTES );
      IODownloadScanStates( ps );
      IORegisterToScanner( ps, ps->RegRefreshScanState );

    ps->AsicReg.RD_ModelControl = saveModel;
}

/************************ exported functions *********************************/

/*.............................................................................
 * initialize this module and setup the correct function pointer according
 * to the ASIC
 */
_LOC int MotorInitialize( pScanData ps )
{
      DBG( DBG_HIGH, "MotorInitialize()\n" );

      if( NULL == ps )
            return _E_NULLPTR;

      ps->a_wMoveStepTable  = a_wMoveStepTable;
      ps->a_bColorByteTable = a_bColorByteTable;
      wP96BaseDpi               = 0;

      ps->PauseColorMotorRunStates = motorPauseColorMotorRunStates;

      /*
       * depending on the asic, we set some functions
       */
      if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {

            ps->WaitForPositionY            = motorP98WaitForPositionY;
            ps->GotoShadingPosition         = motorP98GotoShadingPosition;
            ps->FillRunNewAdrPointer        = motorP98FillRunNewAdrPointer;
            ps->SetupMotorRunTable        = motorP98SetupRunTable;
            ps->UpdateDataCurrentReadLine = motorP98UpdateDataCurrentReadLine;
            ps->SetMotorSpeed                     = motorP98SetSpeed;

      } else if( _ASIC_IS_98003 == ps->sCaps.AsicID ) {

            ps->WaitForPositionY    = motorP98003WaitForPositionY;
            ps->GotoShadingPosition = motorP98003GotoShadingPosition;
            ps->SetMotorSpeed           = motorP98SetSpeed;

    } else if( _IS_ASIC96(ps->sCaps.AsicID)) {

            ps->WaitForPositionY            = motorP96WaitForPositionY;
            ps->GotoShadingPosition         = motorP96GotoShadingPosition;
            ps->FillRunNewAdrPointer        = motorP96FillRunNewAdrPointer;
            ps->SetupMotorRunTable        = motorP96SetupRunTable;
            ps->UpdateDataCurrentReadLine = motorP96UpdateDataCurrentReadLine;
            ps->SetMotorSpeed                     = motorP96SetSpeed;

      } else {

            DBG( DBG_HIGH , "NOT SUPPORTED ASIC !!!\n" );
            return _E_NOSUPP;
      }
      return _OK;
}

/*.............................................................................
 *
 */
_LOC void MotorSetConstantMove( pScanData ps, Byte bMovePerStep )
{
      DataPointer p;
    ULong       dw;

    p.pb = ps->a_nbNewAdrPointer;

    switch( bMovePerStep )
    {
      case 0:     /* doesn't move at all */
          for (dw = _NUMBER_OF_SCANSTEPS / 8; dw; dw--, p.pdw++) {

                  if( _ASIC_IS_98001 == ps->sCaps.AsicID )
                        *p.pdw &= 0x77777777;
                  else
                        *p.pdw &= 0xbbbbbbbb;
            }
          break;

      case 1:
          for (dw = _NUMBER_OF_SCANSTEPS / 8; dw; dw--, p.pdw++) {
                  if( _ASIC_IS_98001 == ps->sCaps.AsicID )
                        *p.pdw |= 0x88888888;
                  else
                        *p.pdw |= 0x44444444;
            }
          break;

      case 2:
          for (dw = _NUMBER_OF_SCANSTEPS / 8; dw; dw--, p.pdw++) {
                  if( _ASIC_IS_98001 == ps->sCaps.AsicID )
                        *p.pdw |= 0x80808080;
                  else
                        *p.pdw |= 0x40404040;
            }
          break;

      default:
          {
                  Byte bMoves = bMovePerStep;

                  for (dw = _SCANSTATE_BYTES; dw; dw--, p.pb++) {
                  if (!(--bMoves)) {
                              if( _ASIC_IS_98001 == ps->sCaps.AsicID )
                                    *p.pb |= 8;
                              else
                                    *p.pb |= 4;
                              bMoves = bMovePerStep;
                      }
                      if (!(--bMoves)) {
                              if( _ASIC_IS_98001 == ps->sCaps.AsicID )
                                    *p.pb |= 0x80;
                              else
                                    *p.pb |= 0x40;
                              bMoves = bMovePerStep;
                      }
                  }
          }
    }
      IOSetToMotorRegister( ps );
}

/*.............................................................................
 * function to bring the sensor back home
 */
_LOC void MotorToHomePosition( pScanData ps )
{
    TimerDef    timer;
    ScanState     StateStatus;

      DBG( DBG_HIGH, "Waiting for Sensor to be back in position\n" );
      _DODELAY( 250 );

      if( _ASIC_IS_98001 == ps->sCaps.AsicID ) {

      if (!(IODataRegisterFromScanner(ps,ps->RegStatus) & _FLAG_P98_PAPER)){
                  ps->GotoShadingPosition( ps );
            }
    } else if( _ASIC_IS_98003 == ps->sCaps.AsicID ) {

        ps->OpenScanPath( ps );

          if( !(IODataFromRegister( ps, ps->RegStatus ) & _FLAG_P98_PAPER)) {

            motorP98003PositionModuleToHome( ps );

            MiscStartTimer( &timer, _SECOND * 20);
            do {

                if( IODataFromRegister( ps, ps->RegStatus ) & _FLAG_P98_PAPER)
                    break;
            } while( !MiscCheckTimer( &timer));
        }
        ps->CloseScanPath( ps );

      } else {

            if( ps->sCaps.Model >= MODEL_OP_9630P ) {
                  if( ps->sCaps.Model == MODEL_OP_A3I )
                        IOCmdRegisterToScanner( ps, ps->RegLineControl, 0x34 );
                  else
                        IOCmdRegisterToScanner( ps, ps->RegLineControl, 0x30 );
            }

            ps->bExtraMotorCtrl     = 0;
      ps->Scan.fMotorBackward = _FALSE;
            MotorP96ConstantMoveProc( ps, 25 );

          ps->Scan.fMotorBackward = _TRUE;
            for(;;) {

                  motorP96GetScanStateAndStatus( ps, &StateStatus );

                  if ( StateStatus.bStatus & _FLAG_P96_PAPER ) {
                      break;
                  }

                  MotorP96ConstantMoveProc( ps, 50000 );
            }

      ps->Scan.fMotorBackward = _FALSE;
            ps->Asic96Reg.RD_MotorControl = 0;
            IOCmdRegisterToScanner( ps, ps->RegMotorControl, 0 );

          memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
            IOSetToMotorRegister( ps );
            _DODELAY(250);

          ps->Asic96Reg.RD_LedControl = 0;
      IOCmdRegisterToScanner(ps, ps->RegLedControl, ps->Asic96Reg.RD_LedControl);
      }

      DBG( DBG_HIGH, "- done !\n" );
}

/*.............................................................................
 *
 */
_LOC void MotorP98GoFullStep( pScanData ps, ULong dwStep )
{
      memset( ps->pColorRunTable, 1, dwStep );
    memset( ps->pColorRunTable + dwStep, 0xff, 0x40);

    ps->bOldStateCount = IOGetScanState( ps, _FALSE ) & _SCANSTATE_MASK;
    motorP98SetRunFullStep( ps );

    ps->pScanState = ps->pColorRunTable;
    ps->FillRunNewAdrPointer( ps );

    while(!motorCheckMotorPresetLength( ps ))
            motorP98FillRunNewAdrPointer1( ps );
}

/*.............................................................................
 *
 */
_LOC void MotorP96SetSpeedToStopProc( pScanData ps )
{
      Byte   bData;
    TimerDef timer;

      MiscStartTimer( &timer, _SECOND);
    while( !MiscCheckTimer( &timer )) {

            bData = IODataRegisterFromScanner( ps, ps->RegFifoOffset );

            if ((bData > ps->bMinReadFifo) && (bData != ps->bFifoCount))
                break;
    }
    bData = IOGetScanState( ps, _FALSE );

    if (!(bData & _SCANSTATE_STOP)) {

            MiscStartTimer( &timer, (_SECOND / 2));

            while (!MiscCheckTimer( &timer )) {

                if (IOGetScanState( ps, _FALSE) != bData )
                        break;
            }
    }

    ps->Scan.bModuleState = _MotorInStopState;
    ps->SetMotorSpeed( ps, ps->bCurrentSpeed, _FALSE );

      IOSetToMotorRegister( ps );
}

/*.............................................................................
 * Position Scan Module to specified line number (Forward or Backward & wait
 * for paper flag ON)
 */
_LOC void MotorP96ConstantMoveProc( pScanData ps, ULong dwLines )
{
      Byte        bRemainder, bLastState;
    UShort        wQuotient;
    ULong         dwDelayMaxTime;
    ScanState     StateStatus;
      TimerDef    timer;
      Bool        fTimeout = _FALSE;

    wQuotient  = (UShort)(dwLines / _NUMBER_OF_SCANSTEPS);  /* state cycles */
    bRemainder = (Byte)(dwLines % _NUMBER_OF_SCANSTEPS);

      /* 3.3 ms per line */
    dwDelayMaxTime = dwLines * _MOTOR_ONE_LINE_TIME + _SECOND * 2;

      MotorSetConstantMove( ps, 1 );      /* step every time */

    ps->OpenScanPath( ps );

    ps->AsicReg.RD_ModeControl = _ModeScan;
    IODataToRegister( ps, ps->RegModeControl, _ModeScan );

    if( ps->Scan.fMotorBackward ) {
            ps->Asic96Reg.RD_MotorControl = (ps->MotorFreeRun | ps->MotorOn |
                                                               ps->FullStep | ps->bExtraMotorCtrl);
    } else {
            ps->Asic96Reg.RD_MotorControl = (ps->MotorFreeRun | ps->MotorOn |
                                                        _MotorDirForward | ps->bExtraMotorCtrl);
      }

      IODataToRegister( ps, ps->RegMotorControl, ps->Asic96Reg.RD_MotorControl );
    ps->CloseScanPath( ps );

    bLastState = 0;

      MiscStartTimer( &timer, dwDelayMaxTime );

    do {

            motorP96GetScanStateAndStatus( ps, &StateStatus );

            if( ps->Scan.fMotorBackward && (StateStatus.bStatus&_FLAG_P96_PAPER)) {
                break;
            } else {

                /*
                   * 1) Forward will not reach the sensor.
                   * 2) Backwarding, doesn't reach the sensor
                   */
                if (wQuotient) {

                        /* stepped */
                        if (StateStatus.bStep != bLastState) {
                            bLastState = StateStatus.bStep;
                            if (!bLastState)    /* done a cycle! */
                                    wQuotient--;
                        }
                } else {
                        if (StateStatus.bStep >= bRemainder) {
                        break;
                        }
                  }
            }

            fTimeout = MiscCheckTimer( &timer );

      } while ( _OK == fTimeout );

    if ( _OK == fTimeout ) {
            memset( ps->a_nbNewAdrPointer, 0, _SCANSTATE_BYTES );
            IOSetToMotorRegister( ps );
    }
}

/*.............................................................................
 *
 */
_LOC Bool MotorP96AheadToDarkArea( pScanData ps )
{
      Byte   bDark;
      UShort   wTL;
    UShort   wTotalLastLine;
    TimerDef timer;

    ps->fColorMoreRedFlag  = _FALSE;
      ps->fColorMoreBlueFlag = _FALSE;
    ps->wOverBlue = 0;

    /* FillToDarkCounter () */
    memset( ps->a_nbNewAdrPointer, 0x30, _SCANSTATE_BYTES);
    MotorSetConstantMove( ps, 2 );

    /* SetToDarkRegister () */
    ps->AsicReg.RD_ModeControl    = _ModeScan;
    ps->AsicReg.RD_ScanControl    = ps->bLampOn | _SCAN_BYTEMODE;
    ps->Asic96Reg.RD_MotorControl = _MotorDirForward | ps->FullStep;
      ps->AsicReg.RD_ModelControl   = ps->Device.ModelCtrl | _ModelWhiteIs0;

    ps->AsicReg.RD_Dpi = 300;
      wTL = 296;
/*    if( MODEL_OP_A3I == ps->sCaps.Model ) { */
      if( ps->PhysicalDpi > 300 ) {
            wTL = 400;
          ps->AsicReg.RD_Origin = (UShort)(ps->Offset70 + 64 + 8 + 2048);
      } else {
          ps->AsicReg.RD_Origin = (UShort)(ps->Offset70 + 64 + 8 + 1024);
      }
    ps->AsicReg.RD_Pixels = 512;

      IOPutOnAllRegisters( ps );

    ps->Asic96Reg.RD_MotorControl = (ps->MotorFreeRun | ps->IgnorePF |
                                                                   ps->MotorOn | _MotorDirForward );

      IOCmdRegisterToScanner( ps, ps->RegMotorControl,
                                                                    ps->Asic96Reg.RD_MotorControl );

      MiscStartTimer( &timer, _SECOND * 2 );
    wTotalLastLine = 0;

#ifdef _A3I_EN
    while( !MiscCheckTimer( &timer )) {

            bDark = motorP96ReadDarkData( ps );

            wTotalLastLine++;
            if((bDark < 0x80) || (wTotalLastLine==wTL)) {

                IOCmdRegisterToScanner( ps, ps->RegMotorControl, 0 );
            return _TRUE;
            }
    }
#else
    while (!MiscCheckTimer( &timer )) {

            bDark = motorP96ReadDarkData( ps );

            wTotalLastLine++;
            if (((ps->sCaps.AsicID == _ASIC_IS_96001) && (bDark > 0x80)) ||
                  ((ps->sCaps.AsicID != _ASIC_IS_96001) && (bDark < 0x80)) ||
                                            (wTotalLastLine==wTL)) {

                IOCmdRegisterToScanner( ps, ps->RegModeControl, _ModeProgram );

                if (wTotalLastLine <= 24)
                        ps->fColorMoreRedFlag = _TRUE;
                else
                        if (wTotalLastLine >= 120) {
                            ps->wOverBlue = wTotalLastLine - 80;
                            ps->fColorMoreBlueFlag = _TRUE;
                        }

            return _TRUE;
            }
    }
#endif

    return _FALSE;      /* already timed out */
}

/*.............................................................................
 * limit the speed settings for 96001/3 based models
 */
_LOC void MotorP96AdjustCurrentSpeed( pScanData ps, Byte bSpeed )
{
    if (bSpeed != 1) {

            if (bSpeed > 34)
                ps->bCurrentSpeed = 34;
            else
                ps->bCurrentSpeed = (bSpeed + 1) & 0xfe;
    }
}

/*.............................................................................
 *
 */
_LOC void MotorP98003ForceToLeaveHomePos( pScanData ps )
{
    TimerDef timer;

    IODataToRegister( ps, ps->RegStepControl, _MOTOR0_ONESTEP );
    IODataToRegister( ps, ps->RegMotor0Control, _FORWARD_MOTOR );

      MiscStartTimer( &timer, _SECOND );

    do {
        if( !(IODataFromRegister( ps, ps->RegStatus ) & _FLAG_P98_PAPER))
          break;

        IORegisterToScanner( ps, ps->RegForceStep );
      _DODELAY( 10 );

    } while( _OK == MiscCheckTimer( &timer ));

    IODataToRegister( ps, ps->RegStepControl, _MOTOR0_SCANSTATE );
}

/*.............................................................................
 *
 */
_LOC void MotorP98003BackToHomeSensor( pScanData ps )
{
    TimerDef timer;

      DBG( DBG_HIGH, "MotorP98003BackToHomeSensor()\n" );

    IODataToRegister( ps, ps->RegStepControl, _MOTOR0_SCANSTATE );
    IODataToRegister( ps, ps->RegModeControl, _ModeScan );

    /* stepping every state */
    memset( ps->a_nbNewAdrPointer, 0x88, _SCANSTATE_BYTES );
    IODownloadScanStates( ps );

      MiscStartTimer( &timer, _SECOND * 2 );

    while(!(IOGetScanState( ps, _TRUE ) & _SCANSTATE_STOP) &&
                                                    !MiscCheckTimer( &timer ));

      _DODELAY( 1000UL );

    ps->AsicReg.RD_ModeControl = _ModeScan;

    if (!(ps->DataInf.dwScanFlag & SCANDEF_TPA)) {
        IODataToRegister( ps, ps->RegLineControl, _LOBYTE(ps->Shade.wExposure));
        IODataToRegister( ps, ps->RegXStepTime,   _LOBYTE(ps->Shade.wXStep));

    } else {
      IODataToRegister( ps, ps->RegLineControl, _DEFAULT_LINESCANTIME );
          IODataToRegister( ps, ps->RegXStepTime, 6);
    }

    IODataToRegister( ps, ps->RegStepControl,
                                         (_MOTOR_FREERUN | _MOTOR0_SCANSTATE));
    IODataToRegister( ps, ps->RegModeControl, ps->AsicReg.RD_ModeControl );
    IODataToRegister( ps, ps->RegMotor0Control,
                            (_MotorHQuarterStep | _MotorOn | _MotorDirBackward |
                                          _MotorPowerEnable | _MotorHHomeStop));
    IORegisterToScanner( ps, ps->RegRefreshScanState );

      MiscStartTimer( &timer, _SECOND * 5 );

    do {
        if( IODataFromRegister( ps, ps->RegStatus ) & _FLAG_P98_PAPER )
          break;

      _DODELAY( 55 );

    } while( !MiscCheckTimer( &timer ));

    IODataToRegister( ps, ps->RegLineControl, ps->AsicReg.RD_LineControl);
    IODataToRegister( ps, ps->RegXStepTime, ps->AsicReg.RD_XStepTime);

      DBG( DBG_HIGH, "LineCtrl=%u, XStepTime=%u\n",
                    ps->AsicReg.RD_LineControl, ps->AsicReg.RD_XStepTime );

    motorP98003DownloadNullScanStates( ps );
}

/*.............................................................................
 *
 */
_LOC void MotorP98003ModuleForwardBackward( pScanData ps )
{
    switch( ps->Scan.bModuleState ) {

      case _MotorInNormalState:
          ps->Scan.bModuleState = _MotorGoBackward;
          IODataToRegister( ps, ps->RegScanControl1,
                     (UChar)(ps->AsicReg.RD_ScanControl1 & ~_MFRC_RUNSCANSTATE));
          IODataToRegister( ps, ps->RegMotor0Control,
                      (UChar)(ps->AsicReg.RD_Motor0Control & ~_MotorDirForward));
          motorP98003ModuleFreeRun( ps, _P98003_BACKSTEPS );
          MiscStartTimer( &p98003MotorTimer, (15 * _MSECOND));
          break;

      case _MotorGoBackward:
          if( MiscCheckTimer(& p98003MotorTimer)) {
            if (!(IOGetExtendedStatus( ps ) & _STILL_FREE_RUNNING )) {
                ps->Scan.bModuleState = _MotorInStopState;
                MiscStartTimer( &p98003MotorTimer, (50 *_MSECOND));
                }
          }
          break;

      case _MotorInStopState:
          if( MiscCheckTimer(&p98003MotorTimer)) {

            if( IOReadFifoLength( ps ) < ps->Scan.dwMaxReadFifo ) {
                ps->Scan.bModuleState = _MotorAdvancing;
                IODataToRegister( ps, ps->RegScanControl1, ps->AsicReg.RD_ScanControl1);
                    IODataToRegister( ps, ps->RegMotor0Control, ps->AsicReg.RD_Motor0Control);
                motorP98003ModuleFreeRun( ps, _P98003_FORWARDSTEPS );
                MiscStartTimer( &p98003MotorTimer, (15 * _MSECOND));
                }
          }
          break;

      case _MotorAdvancing:
          if( MiscCheckTimer(&p98003MotorTimer)) {
            if( !(IOGetScanState( ps, _TRUE ) & _SCANSTATE_STOP))
                ps->Scan.bModuleState = _MotorInNormalState;
            else {
                if (!(IOGetExtendedStatus( ps ) & _STILL_FREE_RUNNING )) {
                        IORegisterToScanner( ps, ps->RegRefreshScanState );
                        ps->Scan.bModuleState = _MotorInNormalState;
                }
                }
            }
        break;
    }
}

/*.............................................................................
 *
 */
_LOC void MotorP98003PositionYProc( pScanData ps, ULong steps)
{
    TimerDef timer;

      DBG( DBG_HIGH, "MotorP98003PositionYProc()\n" );

      MiscStartTimer( &timer, _SECOND * 5 );

    while(!(IOGetScanState( ps, _TRUE ) & _SCANSTATE_STOP) &&
                                                (!MiscCheckTimer( &timer )));

    _DODELAY( 12 );

    motorP98003ModuleFreeRun( ps, steps );

    _DODELAY( 15 );

      MiscStartTimer( &timer, _SECOND * 30 );

    do  {
      if (!(IOGetExtendedStatus( ps ) & _STILL_FREE_RUNNING) ||
                                !(IOGetScanState( ps, _TRUE ) & _SCANSTATE_STOP))
          break;

    } while( !MiscCheckTimer( &timer ));

      DBG( DBG_HIGH, "MotorP98003PositionYProc() - done\n" );
}

/* END PLUSTEK-PP_MOTOR.C ...................................................*/

Generated by  Doxygen 1.6.0   Back to index