/* @file u12-motor.c
 * @brief all functions for motor control
 *
 * based on sources acquired from Plustek Inc.
 * Copyright (C) 2003-2004 Gerhard Jaeger <gerhard@gjaeger.de>
 *
 * History:
 * - 0.01 - initial version
 * - 0.02 - no changes
 * .
 * <hr>
 * This file is part of the SANE package.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 *
 * As a special exception, the authors of SANE give permission for
 * additional uses of the libraries contained in this release of SANE.
 *
 * The exception is that, if you link a SANE library with other files
 * to produce an executable, this does not by itself cause the
 * resulting executable to be covered by the GNU General Public
 * License.  Your use of that executable is in no way restricted on
 * account of linking the SANE library code into it.
 *
 * This exception does not, however, invalidate any other reasons why
 * the executable file might be covered by the GNU General Public
 * License.
 *
 * If you submit changes to SANE to the maintainers to be included in
 * a subsequent release, you agree by submitting the changes that
 * those changes may be distributed with this exception intact.
 *
 * If you write modifications of your own for SANE, it is your choice
 * whether to permit this exception to apply to your modifications.
 * If you do not wish that, delete this exception notice.
 * <hr>
 */

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

#define _BACKSTEPS     120
#define _FORWARDSTEPS  120

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

static TimerDef u12motor_Timer;


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

/**
 */
static void u12motor_DownloadNullScanStates( U12_Device *dev )
{
	memset( dev->scanStates, 0, _SCANSTATE_BYTES );
	u12io_DownloadScanStates( dev );
}

/**
 */
static void u12motor_Force16Steps( U12_Device *dev, int dir )
{
	u_long dw;

	if( dir == _DIR_FW )
		u12io_DataToRegister( dev, REG_MOTOR0CONTROL, _FORWARD_MOTOR );
	else if( dir == _DIR_BW )
		u12io_DataToRegister( dev, REG_MOTOR0CONTROL, _BACKWARD_MOTOR );

	for( dw = 16; dw; dw-- ) {
		u12io_RegisterToScanner( dev, REG_FORCESTEP );
		_DODELAY( 10 );
	}
}

/**
 */
static void u12motor_ModuleFreeRun( U12_Device *dev, u_long steps )
{
	SANE_Byte rb[6];

	rb[0] = REG_MOTORFREERUNCOUNT1;  rb[1] = _HIBYTE(steps);
	rb[2] = REG_MOTORFREERUNCOUNT0;  rb[3] = _LOBYTE(steps);
	rb[4] = REG_MOTORFREERUNTRIGGER; rb[5] = 0;

	u12io_DataToRegs( dev, rb, 3 );
}

/**
 */
static SANE_Status u12motor_PositionYProc( U12_Device *dev, u_long steps )
{
	TimerDef timer;

	DBG( _DBG_INFO, "u12motor_PositionYProc()\n" );
	u12io_StartTimer( &timer, _SECOND * 5 );

	u12io_ResetFifoLen();
	while(!(u12io_GetScanState( dev ) & _SCANSTATE_STOP) &&
                                                (!u12io_CheckTimer( &timer )));
	_DODELAY( 12 );
	u12motor_ModuleFreeRun( dev, steps );
	_DODELAY( 15 );

	u12io_StartTimer( &timer, _SECOND * 30 );
	do  {
		if( !(u12io_GetExtendedStatus( dev ) & _STILL_FREE_RUNNING)) {
			break;
		}

		if( u12io_IsEscPressed()) {
			DBG( _DBG_INFO, "* CANCEL detected!\n" );
			return SANE_STATUS_CANCELLED;
		}

	} while( !u12io_CheckTimer( &timer ));
	DBG( _DBG_INFO, "u12motor_PositionYProc() - done\n" );
	return SANE_STATUS_GOOD;
}

/** initialize this module and setup the correct function pointer according
 * to the ASIC
 */
static void u12motor_PositionModuleToHome( U12_Device *dev )
{
	SANE_Byte rb[50];
	SANE_Byte save, saveModel;
	int       c = 0;

	DBG( _DBG_INFO, "u12motor_PositionModuleToHome()\n" );
	saveModel = dev->regs.RD_ModelControl;

	dev->scan.refreshState = SANE_FALSE;
	u12motor_DownloadNullScanStates( dev );

	_DODELAY( 125 );
	save = dev->shade.intermediate;

	dev->shade.intermediate = _ScanMode_AverageOut;
	u12hw_InitAsic( dev, SANE_FALSE );
	dev->shade.intermediate = save;

	_SET_REG( rb, c, REG_MODECONTROL, _ModeScan );
	_SET_REG( rb, c, REG_RESETMTSC, 0 );
	_SET_REG( rb, c, REG_SCANCONTROL1, 0 );
	_SET_REG( rb, c, REG_MODELCONTROL, (dev->ModelCtrl | _MODEL_DPI300));
	_SET_REG( rb, c, REG_LINECONTROL, 80 );
	_SET_REG( rb, c, REG_XSTEPTIME, dev->XStepBack );
	_SET_REG( rb, c, REG_MOTORDRVTYPE, dev->MotorPower );
	_SET_REG( rb, c, REG_MOTOR0CONTROL, (_MotorHHomeStop | _MotorOn |
	                                  _MotorHQuarterStep | _MotorPowerEnable));
	_SET_REG( rb, c, REG_STEPCONTROL, (_MOTOR0_SCANSTATE | _MOTOR_FREERUN));
	u12io_DataToRegs( dev, rb, c );

	memset( dev->scanStates, 0x88, _SCANSTATE_BYTES );
	u12io_DownloadScanStates( dev );

	u12io_RegisterToScanner( dev, REG_REFRESHSCANSTATE );
	dev->regs.RD_ModelControl = saveModel;
}

/** function to bring the sensor back home
 */
static void u12motor_ToHomePosition( U12_Device *dev, SANE_Bool wait )
{
	TimerDef  timer;

	DBG( _DBG_INFO, "Waiting for Sensor to be back in position\n" );
	if( !(u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER)) {

		u12motor_PositionModuleToHome( dev );

		if( wait ) {
			u12io_StartTimer( &timer, _SECOND * 20);
			do {
				if( u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER)
					break;
			} while( !u12io_CheckTimer( &timer ));
		}
	}
	DBG( _DBG_INFO, "- done !\n" );
}

/**
 */
static SANE_Status u12motor_BackToHomeSensor( U12_Device *dev )
{
	SANE_Byte rb[20];
	int       c;
	TimerDef  timer;

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

	c = 0;
	_SET_REG( rb, c, REG_STEPCONTROL, _MOTOR0_SCANSTATE );
	_SET_REG( rb, c, REG_MODECONTROL, _ModeScan );

	u12io_DataToRegs( dev, rb, c );

	u12motor_Force16Steps( dev, _DIR_NONE );

	/* stepping every state */
	memset( dev->scanStates, 0x88, _SCANSTATE_BYTES );
	u12io_DownloadScanStates( dev );
	_DODELAY(50);

	u12io_StartTimer( &timer, _SECOND * 2 );

	u12io_ResetFifoLen();
	while(!(u12io_GetScanState( dev ) & _SCANSTATE_STOP) &&
                                                  !u12io_CheckTimer( &timer )) {
		if( u12io_IsEscPressed()) {
			DBG( _DBG_INFO, "* CANCEL detected!\n" );
			return SANE_STATUS_CANCELLED;
		}
	}

	u12motor_Force16Steps( dev, _DIR_BW );
	dev->regs.RD_ModeControl = _ModeScan;

	c = 0;

	if(!(dev->DataInf.dwScanFlag & _SCANDEF_TPA)) {
		_SET_REG( rb, c, REG_LINECONTROL, _LOBYTE(dev->shade.wExposure));
		_SET_REG( rb, c, REG_XSTEPTIME,   _LOBYTE(dev->shade.wXStep));
	} else {
		_SET_REG( rb, c, REG_LINECONTROL, _DEFAULT_LINESCANTIME );
		_SET_REG( rb, c, REG_XSTEPTIME,   6 );
	}

	_SET_REG( rb, c, REG_STEPCONTROL, (_MOTOR_FREERUN | _MOTOR0_SCANSTATE));
	_SET_REG( rb, c, REG_MOTOR0CONTROL,
	                  (_MotorHQuarterStep | _MotorOn | _MotorDirBackward |
                                    	_MotorPowerEnable | _MotorHHomeStop));
	_SET_REG( rb, c, REG_REFRESHSCANSTATE, 0);
	u12io_DataToRegs( dev, rb, c );

	u12io_StartTimer( &timer, _SECOND * 5 );
	do {
		if( u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER )
			break;

		if( u12io_IsEscPressed()) {
			DBG( _DBG_INFO, "* CANCEL detected!\n" );
			return SANE_STATUS_CANCELLED;
		}

		_DODELAY( 55 );

	} while( !u12io_CheckTimer( &timer ));

	c = 0;
	_SET_REG( rb, c, REG_LINECONTROL, dev->regs.RD_LineControl);
	_SET_REG( rb, c, REG_XSTEPTIME,   dev->regs.RD_XStepTime);
	u12io_DataToRegs( dev, rb, c );

	DBG( _DBG_INFO, "* LineCtrl=0x%02x, XStepTime=0x%02x\n",
	                dev->regs.RD_LineControl, dev->regs.RD_XStepTime );

	u12motor_DownloadNullScanStates( dev );
	return SANE_STATUS_GOOD;
}

/**
 */
static SANE_Status u12motor_ModuleToHome( U12_Device *dev )
{
	SANE_Status res;

	DBG( _DBG_INFO, "u12motor_ModuleToHome()\n" );
	if(!(u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER)) {

		u12io_DataToRegister( dev, REG_MOTOR0CONTROL,
		             (SANE_Byte)(dev->regs.RD_Motor0Control|_MotorDirForward));

		res = u12motor_PositionYProc( dev, 40 );
		if( SANE_STATUS_GOOD != res )
			return res;

		res = u12motor_BackToHomeSensor( dev );
		if( SANE_STATUS_GOOD != res )
			return res;

		_DODELAY( 250 );
	}
	DBG( _DBG_INFO, "* done.\n" );
	return SANE_STATUS_GOOD;
}

/**
 */
static SANE_Status u12motor_WaitForPositionY( U12_Device *dev )
{
	SANE_Byte   rb[20];
	SANE_Status res;
	SANE_Byte   bXStep;
	int         c;
	u_long      dwBeginY;

	c        = 0;
	dwBeginY = (u_long)dev->DataInf.crImage.y * 4 + dev->scan.dwScanOrigin;

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

	bXStep = (SANE_Byte)((dev->DataInf.wPhyDataType <= COLOR_256GRAY) ?
	                      dev->XStepMono : dev->XStepColor);

	if( dev->shade.intermediate & _ScanMode_AverageOut )
		bXStep = 8;

	u12motor_Force16Steps( dev, _DIR_NONE );
	dwBeginY -= 16;

	if( dwBeginY > (_RFT_SCANNING_ORG + _YOFFSET) &&
	                                        bXStep < dev->regs.RD_XStepTime ) {

		u12io_DataToRegister( dev, REG_MOTORDRVTYPE, dev->MotorPower );
		_DODELAY( 12 );
		u12io_DataToRegister( dev, REG_XSTEPTIME, bXStep);
		u12io_DataToRegister( dev, REG_EXTENDEDXSTEP, 0 );
		u12io_DataToRegister( dev, REG_SCANCONTROL1,
		         (SANE_Byte)(dev->regs.RD_ScanControl1 & ~_MFRC_RUNSCANSTATE));
		res = u12motor_PositionYProc( dev, dwBeginY - 64 );
		if( SANE_STATUS_GOOD != res )
			return res;
		dwBeginY = 64;
	} else {
		_SET_REG(rb, c, REG_SCANCONTROL1, dev->regs.RD_ScanControl1 );
	}

	_SET_REG( rb, c, REG_FIFOFULLEN0, _LOBYTE(dev->regs.RD_BufFullSize));
	_SET_REG( rb, c, REG_FIFOFULLEN1, _HIBYTE(dev->regs.RD_BufFullSize));
	_SET_REG( rb, c, REG_FIFOFULLEN2, _LOBYTE(_HIWORD(dev->regs.RD_BufFullSize)));
	u12io_DataToRegs( dev, rb, c );

	u12io_DataToRegister( dev, REG_MOTORDRVTYPE, dev->regs.RD_MotorDriverType);
	_DODELAY( 12 );

	if(!dev->f2003 || (dev->shade.intermediate & _ScanMode_AverageOut) ||
		    (  dev->DataInf.xyAppDpi.y <= 75 &&
	                              dev->DataInf.wPhyDataType <= COLOR_256GRAY)) {
		u12io_DataToRegister( dev, REG_MOTORDRVTYPE,
	                (SANE_Byte)(dev->MotorPower & (_MOTORR_MASK | _MOTORR_STRONG)));
	} else {
		u12io_DataToRegister( dev, REG_MOTORDRVTYPE,
	                          dev->regs.RD_MotorDriverType );
	}

	c = 0;
	_SET_REG( rb, c, REG_XSTEPTIME,     dev->regs.RD_XStepTime );
	_SET_REG( rb, c, REG_EXTENDEDXSTEP, dev->regs.RD_ExtXStepTime );
	_SET_REG( rb, c, REG_SCANCONTROL1,
                    (SANE_Byte)(dev->regs.RD_ScanControl1 & ~_MFRC_RUNSCANSTATE));
	u12io_DataToRegs( dev, rb, c );

	if( dev->DataInf.dwScanFlag & _SCANDEF_PREVIEW ) {

		TimerDef timer;

		u12motor_ModuleFreeRun( dev, dwBeginY );
		_DODELAY( 15 );

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

		while(( u12io_GetExtendedStatus( dev ) & _STILL_FREE_RUNNING) &&
		                                             !u12io_CheckTimer(&timer));
		u12io_DataToRegister( dev, REG_MODECONTROL, _ModeScan );
	} else {
		u12motor_PositionYProc( dev, dwBeginY );
		u12io_RegisterToScanner( dev, REG_REFRESHSCANSTATE );
	}
	return SANE_STATUS_GOOD;
}

/**
 */
static void u12motor_ForceToLeaveHomePos( U12_Device *dev )
{
	SANE_Byte rb[4];
	TimerDef  timer;

	DBG( _DBG_INFO, "u12motor_ForceToLeaveHomePos()\n" );
	rb[0] = REG_STEPCONTROL;
	rb[1] = _MOTOR0_ONESTEP;
	rb[2] = REG_MOTOR0CONTROL;
	rb[3] = _FORWARD_MOTOR;
	u12io_DataToRegs( dev, rb, 2 );

	u12io_StartTimer( &timer, _SECOND );

	do {
		if( !(u12io_DataFromRegister( dev, REG_STATUS ) & _FLAG_PAPER))
			break;

		u12io_RegisterToScanner( dev, REG_FORCESTEP );
		_DODELAY( 10 );

	} while( !u12io_CheckTimer( &timer ));

	u12io_DataToRegister( dev, REG_STEPCONTROL, _MOTOR0_SCANSTATE );
}

/** move the sensor to the appropriate shading position
 */
static SANE_Status u12motor_GotoShadingPosition( U12_Device *dev )
{
	SANE_Byte   rb[20];
	SANE_Status res;
	int         c;

	DBG( _DBG_INFO, "u12motor_GotoShadingPosition()\n" );
	res = u12motor_ModuleToHome( dev );
	if( SANE_STATUS_GOOD == res )
		return res;

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

		u12motor_ForceToLeaveHomePos( dev );
		u12motor_DownloadNullScanStates( dev );

		c = 0;
		_SET_REG( rb, c, REG_STEPCONTROL, _MOTOR0_SCANSTATE );
		_SET_REG( rb, c, REG_MODECONTROL, _ModeScan);
		_SET_REG( rb, c, REG_MOTOR0CONTROL, _FORWARD_MOTOR );
		_SET_REG( rb, c, REG_XSTEPTIME, 6);
		_SET_REG( rb, c, REG_EXTENDEDXSTEP, 0);
		_SET_REG( rb, c, REG_SCANCONTROL1, _MFRC_BY_XSTEP);
		u12io_DataToRegs( dev, rb, c );

		res = u12motor_PositionYProc( dev, _TPA_SHADINGORG );
		if( SANE_STATUS_GOOD != res )
			return res;
	}
	DBG( _DBG_INFO, "* Position reached\n" );
	return SANE_STATUS_GOOD;
}

/**
 */
static void u12motor_ModuleForwardBackward( U12_Device *dev )
{
	DBG( _DBG_INFO, "u12motor_ModuleForwardBackward()\n" );

	switch( dev->scan.bModuleState ) {

	case _MotorInNormalState:
		DBG( _DBG_INFO, "* _MotorInNormalState\n" );
		dev->scan.bModuleState = _MotorGoBackward;
		u12io_DataToRegister( dev, REG_SCANCONTROL1,
			     (SANE_Byte)(dev->regs.RD_ScanControl1 & ~_MFRC_RUNSCANSTATE));
		u12io_DataToRegister( dev, REG_MOTOR0CONTROL,
			      (SANE_Byte)(dev->regs.RD_Motor0Control & ~_MotorDirForward));

		u12motor_ModuleFreeRun( dev, _BACKSTEPS );
		u12io_StartTimer( &u12motor_Timer, (15 * _MSECOND));
		break;

	case _MotorGoBackward:
		DBG( _DBG_INFO, "* _MotorGoBackward\n" );
		if( u12io_CheckTimer( &u12motor_Timer)) {
			if(!(u12io_GetExtendedStatus( dev ) & _STILL_FREE_RUNNING )) {
				dev->scan.bModuleState = _MotorInStopState;
				u12io_StartTimer( &u12motor_Timer, (50 *_MSECOND));
			}
		}
		break;

	case _MotorInStopState:
		DBG( _DBG_INFO, "* _MotorInStopState\n" );
		if( u12io_CheckTimer( &u12motor_Timer )) {

			if( u12io_GetFifoLength( dev ) < dev->scan.dwMaxReadFifo ) {
				dev->scan.bModuleState = _MotorAdvancing;
				u12io_DataToRegister( dev, REG_SCANCONTROL1,
				                                    dev->regs.RD_ScanControl1);
				u12io_DataToRegister( dev, REG_MOTOR0CONTROL,
				                                    dev->regs.RD_Motor0Control);
				u12motor_ModuleFreeRun( dev, _FORWARDSTEPS );
				u12io_StartTimer( &u12motor_Timer, (15 * _MSECOND));
			}
		}
		break;

	case _MotorAdvancing:
		DBG( _DBG_INFO, "* _MotorAdvancing\n" );
		if( u12io_CheckTimer( &u12motor_Timer)) {
			if( !(u12io_GetScanState( dev ) & _SCANSTATE_STOP))
				dev->scan.bModuleState = _MotorInNormalState;
			else {
				if (!(u12io_GetExtendedStatus( dev ) & _STILL_FREE_RUNNING )) {
					u12io_RegisterToScanner( dev, REG_REFRESHSCANSTATE );
					dev->scan.bModuleState = _MotorInNormalState;
				}
			}
		}
		break;
	}
}

/* END U12-MOTOR.C ..........................................................*/