/* @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-2013 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
 * - 0.44 - fix format string issues, as Long types default to int32_t
 *          now
 * .
 * <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 > %u !!\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 > %u !!\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 ...................................................*/