/* sane - Scanner Access Now Easy.

   Copyright (C) 2000-2005 Mustek.
   Originally maintained by Mustek

   Copyright (C) 2001-2005 by Henning Meier-Geinitz.

   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, see <https://www.gnu.org/licenses/>.

   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.

   This file implements a SANE backend for the Mustek BearPaw 2448 TA Pro
   and similar USB2 scanners. */

#define BUILD 10

#include "../include/sane/config.h"

#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>

#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "../include/sane/sane.h"
#include "../include/sane/sanei.h"
#include "../include/sane/saneopts.h"

#define BACKEND_NAME mustek_usb2

#include "../include/sane/sanei_backend.h"
#include "mustek_usb2_high.c"

#include "mustek_usb2.h"

static SANE_Int num_devices;
static const SANE_Device **devlist = 0;

static const SANE_Range u8_range = {
  0,				/* minimum */
  255,				/* maximum */
  0				/* quantization */
};
static SANE_Range x_range = {
  SANE_FIX (0.0),		/* minimum */
  SANE_FIX (8.3 * MM_PER_INCH),	/* maximum */
  SANE_FIX (0.0)		/* quantization */
};

static SANE_Range y_range = {
  SANE_FIX (0.0),		/* minimum */
  SANE_FIX (11.6 * MM_PER_INCH),	/* maximum */
  SANE_FIX (0.0)		/* quantization */
};

static SANE_Range gamma_range = {
  SANE_FIX (0.01),		/* minimum */
  SANE_FIX (5.0),		/* maximum */
  SANE_FIX (0.01)		/* quantization */
};
static SANE_String_Const mode_list[] = {
  SANE_I18N ("Color48"),
  SANE_I18N ("Color24"),
  SANE_I18N ("Gray16"),
  SANE_I18N ("Gray8"),
  SANE_VALUE_SCAN_MODE_LINEART,
  0
};

static SANE_String_Const negative_mode_list[] = {
  SANE_I18N ("Color24"),
  0
};

static SANE_String_Const source_list[] = {
  SANE_I18N ("Reflective"),
  SANE_I18N ("Positive"),
  SANE_I18N ("Negative"),
  0
};
static Scanner_Model mustek_A2nu2_model = {
  "mustek-A2nu2",		/* Name */
  "Mustek",			/* Device vendor string */

  "BearPaw 2448TA Pro",		/* Device model name */
  "",				/* Name of the firmware file */

  {1200, 600, 300, 150, 75, 0},	/* possible resolutions */

  SANE_FIX (0.0),		/* Start of scan area in mm  (x) */
  SANE_FIX (0.0),		/* Start of scan area in mm (y) */
  SANE_FIX (8.3 * MM_PER_INCH),	/* Size of scan area in mm (x) */
  SANE_FIX (11.6 * MM_PER_INCH),	/* Size of scan area in mm (y) */

  SANE_FIX (0.0),		/* Start of scan area in TA mode in mm (x) */
  SANE_FIX (0.0),		/* Start of scan area in TA mode in mm (y) */
  SANE_FIX (1.46 * MM_PER_INCH),	/* Size of scan area in TA mode in mm (x) */
  SANE_FIX (6.45 * MM_PER_INCH),	/* Size of scan area in TA mode in mm (y) */

  0,				/* Order of the CCD/CIS colors 0:RO_RGB 1:RO_BGR */
  SANE_FIX (2.0),		/* Default gamma value */

  SANE_FALSE,			/* Is this a CIS scanner? */
  0				/* Which flags are needed for this scanner? */
    /* Setup and tested */
};


/* Forward declarations */

static SANE_Bool GetDeviceStatus (void);
static SANE_Bool PowerControl (SANE_Bool isLampOn, SANE_Bool isTaLampOn);
static SANE_Bool CarriageHome (void);
static SANE_Bool SetParameters (LPSETPARAMETERS pSetParameters);
static SANE_Bool GetParameters (LPGETPARAMETERS pGetParameters);
static SANE_Bool StartScan (void);
static SANE_Bool ReadScannedData (LPIMAGEROWS pImageRows);
static SANE_Bool StopScan (void);
static SANE_Bool IsTAConnected (void);
static void AutoLevel (SANE_Byte *lpSource, SCANMODE scanMode, unsigned short ScanLines,
		unsigned int BytesPerLine);
static size_t max_string_size (const SANE_String_Const strings[]);
static SANE_Status calc_parameters (Mustek_Scanner * s);
#ifdef SANE_UNUSED
static SANE_Bool GetGammaInfo (LPGAMMAINFO pGamaInfo);
static SANE_Bool GetKeyStatus (SANE_Byte * pKey);
static void QBetChange (SANE_Byte *lpSource, SCANMODE scanMode, unsigned short ScanLines,
		 unsigned int BytesPerLine);
static void QBETDetectAutoLevel (void *pDIB, unsigned int ImageWidth, unsigned int ImageHeight);
#endif



static size_t
max_string_size (const SANE_String_Const strings[])
{
  size_t size, max_size = 0;
  SANE_Int i;

  for (i = 0; strings[i]; ++i)
    {
      size = strlen (strings[i]) + 1;
      if (size > max_size)
	max_size = size;
    }
  return max_size;
}

static SANE_Status
calc_parameters (Mustek_Scanner * s)
{
  SANE_String val, val_source;
  val = s->val[OPT_MODE].s;
  val_source = s->val[OPT_SOURCE].s;

  s->params.last_frame = SANE_TRUE;

  if (strcmp (val, "Color48") == 0)	/* Color48 */
    {
      s->params.format = SANE_FRAME_RGB;
      s->params.depth = 16;
      s->setpara.smScanMode = SM_RGB48;
      if (s->val[OPT_PREVIEW].w)
	{
	  DBG (DBG_DET, "calc_parameters : preview set ScanMode SM_RGB24\n");
	  s->params.depth = 8;
	  s->setpara.smScanMode = SM_RGB24;
	}
    }
  else if (strcmp (val, "Color24") == 0)	/* Color24 */
    {
      s->params.format = SANE_FRAME_RGB;
      s->params.depth = 8;
      s->setpara.smScanMode = SM_RGB24;
    }
  else if (strcmp (val, "Gray16") == 0)
    {
      s->params.format = SANE_FRAME_GRAY;
      s->params.depth = 16;
      s->setpara.smScanMode = SM_GRAY16;
      if (s->val[OPT_PREVIEW].w)
	{
	  s->params.depth = 8;
	  DBG (DBG_DET, "calc_parameters : preview set ScanMode SM_GRAY\n");
	  s->setpara.smScanMode = SM_GRAY;
	}
    }
  else if (strcmp (val, "Gray8") == 0)
    {
      s->params.format = SANE_FRAME_GRAY;
      s->params.depth = 8;
      s->setpara.smScanMode = SM_GRAY;
    }
  else if (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0)
    {
      s->params.format = SANE_FRAME_GRAY;
      s->params.depth = 1;
      s->setpara.smScanMode = SM_TEXT;
    }

  /*set Scan Source */
  DBG (DBG_DET, "calc_parameters :scan Source = %s\n", val_source);
  if (strcmp (val_source, "Reflective") == 0)
    {
      s->setpara.ssScanSource = SS_Reflective;
    }
  else if (strcmp (val_source, "Positive") == 0)
    {
      s->setpara.ssScanSource = SS_Positive;
    }
  else if (strcmp (val_source, "Negative") == 0)
    {
      s->setpara.ssScanSource = SS_Negative;
    }


  s->setpara.fmArea.x1 =
    (unsigned short) ((SANE_UNFIX (s->val[OPT_TL_X].w) * 300.0) / MM_PER_INCH + 0.5);
  s->setpara.fmArea.x2 =
    (unsigned short) ((SANE_UNFIX (s->val[OPT_BR_X].w) * 300.0) / MM_PER_INCH + 0.5);
  s->setpara.fmArea.y1 =
    (unsigned short) ((SANE_UNFIX (s->val[OPT_TL_Y].w) * 300.0) / MM_PER_INCH + 0.5);
  s->setpara.fmArea.y2 =
    (unsigned short) ((SANE_UNFIX (s->val[OPT_BR_Y].w) * 300.0) / MM_PER_INCH + 0.5);

  if (s->val[OPT_PREVIEW].w)
    {
      s->setpara.fmArea.y1 = s->setpara.fmArea.y1 + PER_ADD_START_LINES;
      s->setpara.fmArea.x1 += PRE_ADD_START_X;
    }				/*just for range bug. */

  s->setpara.pfPixelFlavor = PF_BlackIs0;
  s->setpara.wLinearThreshold = s->val[OPT_THRESHOLD].w;

  s->setpara.wTargetDPI = s->val[OPT_RESOLUTION].w;
  if (s->val[OPT_PREVIEW].w)
    {
      s->setpara.wTargetDPI = 75;
    }

  s->setpara.pGammaTable = NULL;

  s->params.pixels_per_line =
    (SANE_Int) ((s->setpara.fmArea.x2 -
		 s->setpara.fmArea.x1) * s->setpara.wTargetDPI / 300.0 + 0.5);

  switch (s->params.format)
    {
    case SANE_FRAME_RGB:
      if (s->params.depth == 8)
	s->params.bytes_per_line = s->params.pixels_per_line * 3;
      if (s->params.depth == 16)
	s->params.bytes_per_line = s->params.pixels_per_line * 6;
      break;
    case SANE_FRAME_GRAY:
      if (s->params.depth == 1)
	s->params.bytes_per_line = s->params.pixels_per_line / 8;
      if (s->params.depth == 8)
	s->params.bytes_per_line = s->params.pixels_per_line;
      if (s->params.depth == 16)
	s->params.bytes_per_line = s->params.pixels_per_line * 2;
      break;
    default:
      DBG (DBG_DET, "sane_star:sane params .format = %d\n", s->params.format);
    }

  s->params.lines =
    (SANE_Int) ((s->setpara.fmArea.y2 -
		 s->setpara.fmArea.y1) * s->setpara.wTargetDPI / 300 + 0.5);

  DBG (DBG_FUNC, "calc_parameters: end\n");

  return SANE_STATUS_GOOD;
}

static SANE_Status
init_options (Mustek_Scanner * s)
{
  SANE_Int option, count;
  SANE_Word *dpi_list;		/*Resolution Support */

  DBG (DBG_FUNC, "init_options: start\n");

  memset (s->opt, 0, sizeof (s->opt));
  memset (s->val, 0, sizeof (s->val));

  for (option = 0; option < NUM_OPTIONS; ++option)
    {
      s->opt[option].size = sizeof (SANE_Word);
      s->opt[option].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
    }
  /*  Option num */
  s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
  s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
  s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
  s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
  s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
  s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;

  /* "Mode" group: */
  s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
  s->opt[OPT_MODE_GROUP].desc = "";
  s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
  s->opt[OPT_MODE_GROUP].size = 0;
  s->opt[OPT_MODE_GROUP].cap = 0;
  s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;

  /* scan mode */
  s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
  s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
  s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
  s->opt[OPT_MODE].type = SANE_TYPE_STRING;
  s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
  s->opt[OPT_MODE].size = max_string_size (mode_list);
  s->opt[OPT_MODE].constraint.string_list = mode_list;
  s->val[OPT_MODE].s = strdup ("Color24");

  /* Scan Source */
  s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
  s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
  s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
  s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
  s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
  s->opt[OPT_SOURCE].size = max_string_size (source_list);
  s->opt[OPT_SOURCE].constraint.string_list = source_list;
  s->val[OPT_SOURCE].s = strdup ("Reflective");

  if (!IsTAConnected ())
    {
      DISABLE (OPT_SOURCE);
    }


  /* resolution */

  for (count = 0; s->model.dpi_values[count] != 0; count++)
    {
    }
  dpi_list = malloc ((count + 1) * sizeof (SANE_Word));
  if (!dpi_list)
    return SANE_STATUS_NO_MEM;
  dpi_list[0] = count;

  for (count = 0; s->model.dpi_values[count] != 0; count++)
    dpi_list[count + 1] = s->model.dpi_values[count];

  s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
  s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;


  s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
  s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
  s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
  s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
  s->opt[OPT_RESOLUTION].constraint.word_list = dpi_list;
  s->val[OPT_RESOLUTION].w = 300;

  /* preview */
  s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
  s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
  s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
  s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
  s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
  s->val[OPT_PREVIEW].w = SANE_FALSE;

  /* "Debug" group: */
  s->opt[OPT_DEBUG_GROUP].title = SANE_I18N ("Debugging Options");
  s->opt[OPT_DEBUG_GROUP].desc = "";
  s->opt[OPT_DEBUG_GROUP].type = SANE_TYPE_GROUP;
  s->opt[OPT_DEBUG_GROUP].size = 0;
  s->opt[OPT_DEBUG_GROUP].cap = 0;
  s->opt[OPT_DEBUG_GROUP].constraint_type = SANE_CONSTRAINT_NONE;

  /* auto warmup */
  s->opt[OPT_AUTO_WARMUP].name = "auto-warmup";
  s->opt[OPT_AUTO_WARMUP].title = SANE_I18N ("Automatic warmup");
  s->opt[OPT_AUTO_WARMUP].desc =
    SANE_I18N ("Warm-up until the lamp's brightness is constant "
	       "instead of insisting on 40 seconds warm-up time.");
  s->opt[OPT_AUTO_WARMUP].type = SANE_TYPE_BOOL;
  s->opt[OPT_AUTO_WARMUP].unit = SANE_UNIT_NONE;
  s->opt[OPT_AUTO_WARMUP].constraint_type = SANE_CONSTRAINT_NONE;
  s->val[OPT_AUTO_WARMUP].w = SANE_FALSE;
  if (s->model.is_cis)
    DISABLE (OPT_AUTO_WARMUP);

  /* "Enhancement" group: */
  s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
  s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
  s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;

  s->opt[OPT_ENHANCEMENT_GROUP].size = 0;
  s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
  s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;

  /* threshold */
  s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
  s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
  s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
  s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
  s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
  s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
  s->opt[OPT_THRESHOLD].constraint.range = &u8_range;
  s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
  s->val[OPT_THRESHOLD].w = DEF_LINEARTTHRESHOLD;

  /* internal gamma value */
  s->opt[OPT_GAMMA_VALUE].name = "gamma-value";
  s->opt[OPT_GAMMA_VALUE].title = SANE_I18N ("Gamma value");
  s->opt[OPT_GAMMA_VALUE].desc =
    SANE_I18N ("Sets the gamma value of all channels.");
  s->opt[OPT_GAMMA_VALUE].type = SANE_TYPE_FIXED;
  s->opt[OPT_GAMMA_VALUE].unit = SANE_UNIT_NONE;
  s->opt[OPT_GAMMA_VALUE].constraint_type = SANE_CONSTRAINT_RANGE;
  s->opt[OPT_GAMMA_VALUE].constraint.range = &gamma_range;
  s->opt[OPT_GAMMA_VALUE].cap |= SANE_CAP_EMULATED;
  s->val[OPT_GAMMA_VALUE].w = s->model.default_gamma_value;

  DISABLE (OPT_GAMMA_VALUE);

  /* "Geometry" group: */
  s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
  s->opt[OPT_GEOMETRY_GROUP].desc = "";
  s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
  s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
  s->opt[OPT_GEOMETRY_GROUP].size = 0;
  s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;

  x_range.max = s->model.x_size;
  y_range.max = s->model.y_size;

  /* top-left x */
  s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
  s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
  s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
  s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
  s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
  s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
  s->opt[OPT_TL_X].constraint.range = &x_range;

  s->val[OPT_TL_X].w = 0;

  /* top-left y */
  s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
  s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
  s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
  s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
  s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
  s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
  s->opt[OPT_TL_Y].constraint.range = &y_range;
  s->val[OPT_TL_Y].w = 0;

  /* bottom-right x */
  s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
  s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
  s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
  s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
  s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
  s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
  s->opt[OPT_BR_X].constraint.range = &x_range;
  s->val[OPT_BR_X].w = x_range.max;

  /* bottom-right y */
  s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
  s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
  s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
  s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
  s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
  s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
  s->opt[OPT_BR_Y].constraint.range = &y_range;
  s->val[OPT_BR_Y].w = y_range.max;

  calc_parameters (s);

  DBG (DBG_FUNC, "init_options: exit\n");
  return SANE_STATUS_GOOD;
}

/***************************** Code from spicall.c *****************************/

static SANE_Byte * g_lpNegImageData = NULL;
static SANE_Bool g_bIsFirstGetNegData = TRUE;
static SANE_Bool g_bIsMallocNegData = FALSE;
static unsigned int g_dwAlreadyGetNegLines = 0;

/**********************************************************************
Author: Jack            Date: 2005/05/13
Routine Description:
	Check the device connect status
Parameters:
	none
Return value:
	if the device is connected
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
GetDeviceStatus (void)
{
  DBG (DBG_FUNC, "GetDeviceStatus: start\n");
  return MustScanner_GetScannerState ();
}

/**********************************************************************
Author: Jack            Date: 2005/05/13
Routine Description:
	Turn the lamp on or off
Parameters:
	isLampOn: turn the lamp on or off
	isTALampOn: turn the TA lamp on or off
Return value:
	if operation success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
PowerControl (SANE_Bool isLampOn, SANE_Bool isTALampOn)
{
  DBG (DBG_FUNC, "PowerControl: start\n");
  return MustScanner_PowerControl (isLampOn, isTALampOn);
}

/**********************************************************************
Author: Jack            Date: 2005/05/13
Routine Description:
	Turn the carriage home
Parameters:
	none
Return value:
	if the operation success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
CarriageHome (void)
{
  DBG (DBG_FUNC, "CarriageHome: start\n");
  return MustScanner_BackHome ();
}

#ifdef SANE_UNUSED
/**********************************************************************
Author: Jack            Date: 2005/05/13
Routine Description:
	Get gamma input/output bit count
Parameters:
	pGammaInfo: the gamma information
Return value:
	if the operation success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
GetGammaInfo (LPGAMMAINFO pGammaInfo)
{
  DBG (DBG_FUNC, "GetGammaInfo: start\n");

  switch (pGammaInfo->smScanMode)
    {
    case SM_GRAY:
      pGammaInfo->wInputGammaBits = 12;
      pGammaInfo->wOutputGammaBits = 8;
      break;
    case SM_RGB24:
      pGammaInfo->wInputGammaBits = 12;
      pGammaInfo->wOutputGammaBits = 8;
      break;
    case SM_GRAY16:
      pGammaInfo->wInputGammaBits = 16;
      pGammaInfo->wOutputGammaBits = 16;
      break;
    case SM_RGB48:
      pGammaInfo->wInputGammaBits = 16;
      pGammaInfo->wOutputGammaBits = 16;
      break;
    default:
      pGammaInfo->wInputGammaBits = 0;
      pGammaInfo->wOutputGammaBits = 0;
      return FALSE;
    }

  DBG (DBG_FUNC, "GetGammaInfo: exit\n");
  return TRUE;
}
#endif
/**********************************************************************
Author: Jack            Date: 2005/05/13
Routine Description:
	set scan parameters
Parameters:
	pSetParameters: the information of scanning
Return value:
	if the operation success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
SetParameters (LPSETPARAMETERS pSetParameters)
{
  unsigned short X1inTargetDpi;
  unsigned short Y1inTargetDpi;
  unsigned short X2inTargetDpi;
  unsigned short Y2inTargetDpi;

  DBG (DBG_FUNC, "SetParameters: start\n");

  /*0. Reset */
  if (ST_Reflective == g_ScanType)
    {
      Reflective_Reset ();
    }
  else
    {
      Transparent_Reset ();
    }

  /*1. Scan mode */
  switch (pSetParameters->smScanMode)
    {
    case SM_TEXT:
      g_tiTarget.cmColorMode = CM_TEXT;
      break;
    case SM_GRAY:
      g_tiTarget.cmColorMode = CM_GRAY8;
      break;
    case SM_GRAY16:
      g_tiTarget.cmColorMode = CM_GRAY16;
      break;
    case SM_RGB24:
      g_tiTarget.cmColorMode = CM_RGB24;
      break;
    case SM_RGB48:
      g_tiTarget.cmColorMode = CM_RGB48;
      break;
    default:
      return FALSE;
    }

  /*2. Scan source */
  g_ssScanSource = pSetParameters->ssScanSource;
  g_tiTarget.bScanSource = pSetParameters->ssScanSource;


  if (SS_Reflective == pSetParameters->ssScanSource)
    {
      g_ScanType = ST_Reflective;
    }
  else if (SS_Positive == pSetParameters->ssScanSource
	   || SS_Negative == pSetParameters->ssScanSource
	   || SS_ADF == pSetParameters->ssScanSource)
    {
      g_ScanType = ST_Transparent;
    }
  else
    {
      DBG (DBG_ERR, "SetParameters: ScanSource error\n");
      return FALSE;
    }

  /*3. pixel flavor */
  if (PF_BlackIs0 == pSetParameters->pfPixelFlavor
      || PF_WhiteIs0 == pSetParameters->pfPixelFlavor)
    {
      g_PixelFlavor = pSetParameters->pfPixelFlavor;
    }
  else
    {
      DBG (DBG_ERR, "SetParameters: PixelFlavor error\n");
      return FALSE;
    }

  /*4. Scan area */
  if (pSetParameters->fmArea.x1 >= pSetParameters->fmArea.x2)
    {
      DBG (DBG_ERR, "SetParameters: x1 > x2, error\n");
      return FALSE;
    }
  if (pSetParameters->fmArea.y1 >= pSetParameters->fmArea.y2)
    {
      DBG (DBG_ERR, "SetParameters: y1 >= y2, error\n");
      return FALSE;
    }
  if (pSetParameters->fmArea.x2 > MAX_SCANNING_WIDTH)	/* Just for A4 size */
    {
      DBG (DBG_ERR, "SetParameters: x2 > MAX_SCANNING_WIDTH, error\n");
      return FALSE;
    }
  if (pSetParameters->fmArea.y2 > MAX_SCANNING_HEIGHT)	/* Just for A4 size */
    {
      DBG (DBG_ERR, "SetParameters: y2 > MAX_SCANNING_HEIGHT, error\n");
      return FALSE;
    }

  X1inTargetDpi =
    (unsigned short) ((unsigned int) (pSetParameters->fmArea.x1) *
	    (unsigned int) (pSetParameters->wTargetDPI) / 300L);
  Y1inTargetDpi =
    (unsigned short) ((unsigned int) (pSetParameters->fmArea.y1) *
	    (unsigned int) (pSetParameters->wTargetDPI) / 300L);
  X2inTargetDpi =
    (unsigned short) ((unsigned int) (pSetParameters->fmArea.x2) *
	    (unsigned int) (pSetParameters->wTargetDPI) / 300L);
  Y2inTargetDpi =
    (unsigned short) ((unsigned int) (pSetParameters->fmArea.y2) *
	    (unsigned int) (pSetParameters->wTargetDPI) / 300L);

  g_tiTarget.isOptimalSpeed = TRUE;
  g_tiTarget.wDpi = pSetParameters->wTargetDPI;
  g_tiTarget.wX = X1inTargetDpi;
  g_tiTarget.wY = Y1inTargetDpi;
  g_tiTarget.wWidth = X2inTargetDpi - X1inTargetDpi;
  g_tiTarget.wHeight = Y2inTargetDpi - Y1inTargetDpi;

  DBG (DBG_INFO, "SetParameters: g_tiTarget.wDpi=%d\n", g_tiTarget.wDpi);
  DBG (DBG_INFO, "SetParameters: g_tiTarget.wX=%d\n", g_tiTarget.wX);
  DBG (DBG_INFO, "SetParameters: g_tiTarget.wY=%d\n", g_tiTarget.wY);
  DBG (DBG_INFO, "SetParameters: g_tiTarget.wWidth=%d\n", g_tiTarget.wWidth);
  DBG (DBG_INFO, "SetParameters: g_tiTarget.wHeight=%d\n",
       g_tiTarget.wHeight);

  /*5.Prepare */
  if (FALSE == MustScanner_Prepare (g_tiTarget.bScanSource))
    {
      DBG (DBG_ERR, "SetParameters: MustScanner_Prepare fail\n");
      return FALSE;
    }

  /*6. Linear threshold */
  if (pSetParameters->wLinearThreshold > 256
      && pSetParameters->smScanMode == SM_TEXT)
    {
      DBG (DBG_ERR, "SetParameters: LinearThreshold error\n");
      return FALSE;
    }
  else
    {
      g_wLineartThreshold = pSetParameters->wLinearThreshold;
    }

  /*7. Gamma table */
  if (NULL != pSetParameters->pGammaTable)
    {
      DBG (DBG_INFO, "SetParameters: IN gamma table not NULL\n");
      g_pGammaTable = pSetParameters->pGammaTable;
      g_isSelfGamma = FALSE;
    }
  else if (pSetParameters->smScanMode == SM_GRAY
	   || pSetParameters->smScanMode == SM_RGB24)
    {
      unsigned short i;
      SANE_Byte byGammaData;
      double pow_d;
      double pow_z = (double) 10 / 16.0;

      g_pGammaTable = (unsigned short *) malloc (sizeof (unsigned short) * 4096 * 3);

      DBG (DBG_INFO, "SetParameters: gamma table malloc %ld Bytes\n",
	   (long int) sizeof (unsigned short) * 4096 * 3);
      DBG (DBG_INFO, "SetParameters: address of g_pGammaTable=%p\n",
	   (void *) g_pGammaTable);

      if (NULL == g_pGammaTable)
	{
	  DBG (DBG_ERR, "SetParameters: gamma table malloc fail\n");
	  return FALSE;
	}
      g_isSelfGamma = TRUE;

      for (i = 0; i < 4096; i++)
	{
	  pow_d = (double) i / (double) 4096;

	  byGammaData = (SANE_Byte) (pow (pow_d, pow_z) * 255);

	  *(g_pGammaTable + i) = byGammaData;
	  *(g_pGammaTable + i + 4096) = byGammaData;
	  *(g_pGammaTable + i + 8192) = byGammaData;
	}
    }
  else if (pSetParameters->smScanMode == SM_GRAY16
	   || pSetParameters->smScanMode == SM_RGB48)
    {
      unsigned int i, wGammaData;
      g_pGammaTable = (unsigned short *) malloc (sizeof (unsigned short) * 65536 * 3);

      if (g_pGammaTable == NULL)
	{
	  DBG (DBG_ERR, "SetParameters: gamma table malloc fail\n");
	  return FALSE;
	}
      g_isSelfGamma = TRUE;

      for (i = 0; i < 65536; i++)
	{
	  wGammaData =
	    (unsigned short) (pow ((((float) i) / 65536.0), (((float) 10) / 16.0)) *
		    65535);

	  *(g_pGammaTable + i) = wGammaData;
	  *(g_pGammaTable + i + 65536) = wGammaData;
	  *(g_pGammaTable + i + 65536 * 2) = wGammaData;
	}
    }
  else
    {
      DBG (DBG_INFO, "SetParameters: set g_pGammaTable to NULL\n");
      g_pGammaTable = NULL;
    }

  DBG (DBG_FUNC, "SetParameters: exit\n");
  return TRUE;
}

/**********************************************************************
Author: Jack            Date: 2005/05/13
Routine Description:
	get the optical dpi and scan area
Parameters:
	pGetParameters: the information of scan
Return value:
	if the operation is success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
GetParameters (LPGETPARAMETERS pGetParameters)
{
  DBG (DBG_FUNC, "GetParameters: start\n");
  if (ST_Reflective == g_ScanType)
    {
      if (FALSE == Reflective_ScanSuggest (&g_tiTarget, &g_ssSuggest))
	{
	  DBG (DBG_ERR, "GetParameters: Reflective_ScanSuggest error\n");
	  return FALSE;
	}
    }
  else
    {
      if (FALSE == Transparent_ScanSuggest (&g_tiTarget, &g_ssSuggest))
	{
	  DBG (DBG_ERR, "GetParameters: Transparent_ScanSuggest error\n");
	  return FALSE;
	}
    }

  pGetParameters->wSourceXDPI = g_ssSuggest.wXDpi;
  pGetParameters->wSourceYDPI = g_ssSuggest.wYDpi;
  pGetParameters->dwLength = (unsigned int) g_ssSuggest.wHeight;
  pGetParameters->dwLineByteWidth = g_ssSuggest.dwBytesPerRow;

  DBG (DBG_FUNC, "GetParameters: exit\n");

  return TRUE;
}

/**********************************************************************
Author: Jack            Date: 2005/05/13

Routine Description:
	start scan image
Parameters:
	none
Return value:
	if operation is success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
StartScan (void)
{
  DBG (DBG_FUNC, "StartScan: start\n");
  if (ST_Reflective == g_ScanType)
    {
      DBG (DBG_INFO, "StartScan: g_ScanType==ST_Reflective\n");

      return Reflective_SetupScan (g_ssSuggest.cmScanMode,
				   g_ssSuggest.wXDpi,
				   g_ssSuggest.wYDpi,
				   PF_BlackIs0,
				   g_ssSuggest.wX,
				   g_ssSuggest.wY,
				   g_ssSuggest.wWidth, g_ssSuggest.wHeight);
    }
  else

    {

      DBG (DBG_INFO, "StartScan: g_ScanType==ST_Transparent\n");

      return Transparent_SetupScan (g_ssSuggest.cmScanMode,
				    g_ssSuggest.wXDpi,
				    g_ssSuggest.wYDpi,
				    PF_BlackIs0,
				    g_ssSuggest.wX,
				    g_ssSuggest.wY,
				    g_ssSuggest.wWidth, g_ssSuggest.wHeight);
    }
}

/**********************************************************************
Author: Jack             Date: 2005/05/14
Routine Description:
	Read the scanner data
Parameters:

	pImageRows: the information of the data
Return value:
	if the operation is seccuss
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
ReadScannedData (LPIMAGEROWS pImageRows)
{
  SANE_Bool isRGBInvert;
  unsigned short Rows = 0;
  SANE_Byte *lpBlock = (SANE_Byte *) pImageRows->pBuffer;
  SANE_Byte *lpReturnData = (SANE_Byte *) pImageRows->pBuffer;
  int i = 0;

  DBG (DBG_FUNC, "ReadScannedData: start\n");

  if (pImageRows->roRgbOrder == RO_RGB)
    isRGBInvert = FALSE;
  else
    isRGBInvert = TRUE;

  Rows = pImageRows->wWantedLineNum;

  DBG (DBG_INFO, "ReadScannedData: wanted Rows = %d\n", Rows);

  if (ST_Reflective == g_ScanType)
    {
      if (FALSE == Reflective_GetRows (lpBlock, &Rows, isRGBInvert))
	return FALSE;
    }
  else if (SS_Positive == g_ssScanSource)
    {
      if (FALSE == Transparent_GetRows (lpBlock, &Rows, isRGBInvert))
	return FALSE;
    }

  pImageRows->wXferedLineNum = Rows;

  if (g_PixelFlavor == PF_WhiteIs0 || g_ScanMode == CM_TEXT)
    {
      int TotalSize = Rows * g_ssSuggest.dwBytesPerRow;
      for (i = 0; i < TotalSize; i++)
	{
	  *(lpBlock++) ^= 0xff;
	}
    }

  if (SS_Negative == g_ssScanSource)
    {
      DBG (DBG_INFO, "ReadScannedData: deal with the Negative\n");

      if (g_bIsFirstGetNegData)
	{
	  unsigned int TotalImgeSize = g_SWHeight * g_ssSuggest.dwBytesPerRow;
	  g_lpNegImageData = (SANE_Byte *) malloc (TotalImgeSize);
	  if (NULL != g_lpNegImageData)
	    {
	      SANE_Byte * lpTempData = g_lpNegImageData;
	      DBG (DBG_INFO,
		   "ReadScannedData: malloc the negative data is success!\n");
	      g_bIsMallocNegData = TRUE;
	      if (!Transparent_GetRows
		  (g_lpNegImageData, &g_SWHeight, isRGBInvert))
		{
		  return FALSE;
		}

	      DBG (DBG_INFO, "ReadScannedData: get image data is over!\n");

	      for (i = 0; i < (int) TotalImgeSize; i++)
		{
		  *(g_lpNegImageData++) ^= 0xff;
		}
	      g_lpNegImageData = lpTempData;
	      AutoLevel (g_lpNegImageData, g_ScanMode, g_SWHeight,
			 g_ssSuggest.dwBytesPerRow);
	      DBG (DBG_INFO, "ReadScannedData: autolevel is ok\n");
	    }
	  g_bIsFirstGetNegData = FALSE;
	}

      if (g_bIsMallocNegData)
	{
	  memcpy (pImageRows->pBuffer,
		  g_lpNegImageData +
		  g_ssSuggest.dwBytesPerRow * g_dwAlreadyGetNegLines,
		  g_ssSuggest.dwBytesPerRow * Rows);

	  DBG (DBG_INFO, "ReadScannedData: copy the data over!\n");

	  g_dwAlreadyGetNegLines += Rows;
	  if (g_dwAlreadyGetNegLines >= g_SWHeight)
	    {
	      DBG (DBG_INFO, "ReadScannedData: free the image data!\n");
	      free (g_lpNegImageData);
	      g_lpNegImageData = NULL;
	      g_bIsFirstGetNegData = TRUE;
	      g_dwAlreadyGetNegLines = 0;
	      g_bIsMallocNegData = FALSE;
	    }
	}
      else
	{
	  int TotalSize = Rows * g_ssSuggest.dwBytesPerRow;
	  DBG (DBG_INFO,
	       "ReadScannedData: malloc the negative data is fail!\n");
	  if (!Transparent_GetRows (lpReturnData, &Rows, isRGBInvert))
	    {
	      return FALSE;
	    }

	  for (i = 0; i < TotalSize; i++)
	    {
	      *(lpReturnData++) ^= 0xff;
	    }
	  pImageRows->wXferedLineNum = Rows;

	  g_dwAlreadyGetNegLines += Rows;
	  if (g_dwAlreadyGetNegLines >= g_SWHeight)
	    {
	      g_bIsFirstGetNegData = TRUE;
	      g_dwAlreadyGetNegLines = 0;
	      g_bIsMallocNegData = FALSE;
	    }
	}

    }

  DBG (DBG_FUNC, "ReadScannedData: leave ReadScannedData\n");

  return TRUE;
}

/**********************************************************************
Author: Jack             Date: 2005/05/14
Routine Description:
	Stop scan
Parameters:
	none
Return value:
	if operation is success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
StopScan (void)
{
  SANE_Bool rt;
  int i;

  DBG (DBG_FUNC, "StopScan: start\n");

  /*stop read data and kill thread */
  if (ST_Reflective == g_ScanType)
    {
      rt = Reflective_StopScan ();
    }
  else
    {
      rt = Transparent_StopScan ();
    }

  /*free gamma table */
  if (g_isSelfGamma && g_pGammaTable != NULL)
    {
      for (i = 0; i < 20; i++)
	{
	  if (!g_isScanning)
	    {
	      free (g_pGammaTable);
	      g_pGammaTable = NULL;
	      break;
	    }
	  else
	    {
	      sleep (1);	/*waiting ReadScannedData return. */
	    }
	}
    }

  /*free image buffer */
  if (g_lpReadImageHead != NULL)

    {
      free (g_lpReadImageHead);
      g_lpReadImageHead = NULL;
    }

  DBG (DBG_FUNC, "StopScan: exit\n");
  return rt;
}

/**********************************************************************
Author: Jack             Date: 2005/05/14
Routine Description:
	Check the status of TA
Parameters:
	none
Return value:
	if operation is success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
IsTAConnected (void)
{
  SANE_Bool hasTA;

  DBG (DBG_FUNC, "StopScan: start\n");

  if (Asic_Open (&g_chip, g_pDeviceFile) != SANE_STATUS_GOOD)
    {
      return FALSE;
    }

  if (Asic_IsTAConnected (&g_chip, &hasTA) != SANE_STATUS_GOOD)
    {
      Asic_Close (&g_chip);
      return FALSE;
    }

  Asic_Close (&g_chip);

  DBG (DBG_FUNC, "StopScan: exit\n");
  return hasTA;
}

#ifdef SANE_UNUSED
/**********************************************************************
Author: Jack             Date: 2005/05/14
Routine Description:
	Get the status of the HK
Parameters:
	pKey: the status of key
Return value:
	if the operation is success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
GetKeyStatus (SANE_Byte * pKey)
{
  SANE_Byte pKeyTemp = 0x00;
  SANE_Status status = Asic_CheckFunctionKey (&g_chip, &pKeyTemp);
  DBG (DBG_FUNC, "GetKeyStatus: start\n");

  if (SANE_STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile))
    {
      DBG (DBG_ERR, "GetKeyStatus: Asic_Open is fail\n");
      return FALSE;
    }

  if (SANE_STATUS_GOOD != status)
    {
      DBG (DBG_ERR, "GetKeyStatus: Asic_CheckFunctionKey is fail\n");
      return FALSE;
    }

  if (0x01 == pKeyTemp)
    {
      *pKey = 0x01;		/*Scan key pressed */
    }

  if (0x02 == pKeyTemp)
    {
      *pKey = 0x02;		/*Copy key pressed */
    }
  if (0x04 == pKeyTemp)
    {
      *pKey = 0x03;		/*Fax key pressed */
    }
  if (0x08 == pKeyTemp)
    {
      *pKey = 0x04;		/*Email key pressed */
    }
  if (0x10 == pKeyTemp)
    {
      *pKey = 0x05;		/*Panel key pressed */
    }

  if (SANE_STATUS_GOOD != Asic_Close (&g_chip))
    {
      DBG (DBG_ERR, "GetKeyStatus: Asic_Close is fail\n");
      return FALSE;
    }

  DBG (DBG_FUNC, "GetKeyStatus: exit\n");
  return TRUE;
}
#endif
/**********************************************************************
Author: Jack             Date: 2005/05/14
Routine Description:
	Deal with the image with auto level
Parameters:
	lpSource: the data of image
	scanMode: the scan mode
	ScanLines: the rows of image
	BytesPerLine: the bytes of per line
Return value:
	none
***********************************************************************/
static void
AutoLevel (SANE_Byte *lpSource, SCANMODE scanMode, unsigned short ScanLines,
	   unsigned int BytesPerLine)
{
  int ii;
  unsigned int i, j;
  unsigned int tLines, CountPixels, TotalImgSize;
  unsigned short R, G, B, max_R, max_G, max_B, min_R, min_G, min_B;
  float fmax_R, fmax_G, fmax_B;
  unsigned int sum_R = 0, sum_G = 0, sum_B = 0;
  unsigned int hisgram_R[256], hisgram_G[256], hisgram_B[256];

  unsigned int iWidth = BytesPerLine / 3;
  unsigned int iHeight = ScanLines;
  SANE_Byte *pbmpdata = (SANE_Byte *) lpSource;

  unsigned short imin_threshold[3];
  unsigned short imax_threshold[3];

  DBG (DBG_FUNC, "AutoLevel: start\n");

  if (scanMode != CM_RGB24ext)
    {
      return;
    }

  i = j = 0;
  tLines = CountPixels = TotalImgSize = 0;

  TotalImgSize = iWidth * iHeight;



  for (i = 0; i < 256; i++)
    {

      hisgram_R[i] = 0;
      hisgram_G[i] = 0;
      hisgram_B[i] = 0;
    }


  DBG (DBG_INFO, "AutoLevel: init data is over\n");

  /* Find min , max, mean */
  max_R = max_G = max_B = 0;
  min_R = min_G = min_B = 255;
  tLines = 0;
  DBG (DBG_INFO, "AutoLevel: iHeight = %d, iWidth = %d\n", iHeight, iWidth);

  for (j = 0; j < iHeight; j++)
    {
      tLines = j * iWidth * 3;


      for (i = 0; i < iWidth; i++)
	{
	  R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2));
	  G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1));
	  B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3));

	  max_R = _MAX (R, max_R);
	  max_G = _MAX (G, max_G);
	  max_B = _MAX (B, max_B);

	  min_R = _MIN (R, min_R);
	  min_G = _MIN (G, min_G);
	  min_B = _MIN (B, min_B);

	  hisgram_R[(SANE_Byte) R]++;
	  hisgram_G[(SANE_Byte) G]++;
	  hisgram_B[(SANE_Byte) B]++;

	  sum_R += R;
	  sum_G += G;
	  sum_B += B;

	  *(pbmpdata + (tLines + i * 3 + 2)) = (SANE_Byte) R;
	  *(pbmpdata + (tLines + i * 3 + 1)) = (SANE_Byte) G;
	  *(pbmpdata + (tLines + i * 3)) = (SANE_Byte) B;

	  CountPixels++;
	}

    }

  DBG (DBG_INFO, "AutoLevel: Find min , max is over!\n");


  imin_threshold[0] = 0;
  imin_threshold[1] = 0;
  imin_threshold[2] = 0;
  imax_threshold[0] = 0;
  imax_threshold[1] = 0;
  imax_threshold[2] = 0;

  for (ii = 0; ii < 256; ii++)
    {
      if (hisgram_R[ii] > 0)
	if (hisgram_R[ii] >= imin_threshold[0])
	  {
	    min_R = ii;
	    break;
	  }
    }

  for (ii = 255; ii >= 0; ii--)
    {
      if (hisgram_R[ii] > 0)
	if (hisgram_R[ii] >= imax_threshold[0])
	  {
	    max_R = ii;
	    break;
	  }
    }

  for (ii = 0; ii < 256; ii++)
    {
      if (hisgram_G[ii] > 0)
	if (hisgram_G[ii] >= imin_threshold[1])
	  {
	    min_G = ii;
	    break;
	  }
    }

  for (ii = 255; ii >= 0; ii--)
    {
      if (hisgram_G[ii] > 0)
	if (hisgram_G[ii] >= imax_threshold[1])
	  {
	    max_G = ii;
	    break;
	  }
    }

  for (ii = 0; ii < 256; ii++)
    {
      if (hisgram_B[ii] > 0)
	if (hisgram_B[ii] >= imin_threshold[2])
	  {
	    min_B = ii;
	    break;
	  }
    }

  for (ii = 255; ii >= 0; ii--)
    {
      if (hisgram_B[ii] > 0)
	if (hisgram_B[ii] >= imax_threshold[2])
	  {
	    max_B = ii;
	    break;
	  }
    }

  DBG (DBG_INFO, "AutoLevel: Set min , max is over!\n");

  /*Autolevel: */
  sum_R = max_R - min_R;
  sum_G = max_G - min_G;
  sum_B = max_B - min_B;

  for (j = 0; j < iHeight; j++)
    {
      tLines = j * iWidth * 3;
      for (i = 0; i < iWidth; i++)
	{
	  R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2));
	  G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1));
	  B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3));

	   /*R*/ if (sum_R == 0)
	    R = max_R;
	  else if (R < min_R)
	    R = 0;
	  else if (R <= 255)
	    {
	      fmax_R = ((float) ((R - min_R) * 255) / (float) sum_R);
	      R = (unsigned short) fmax_R;
	      fmax_R = (fmax_R - R) * 10;

	      if (fmax_R >= 5)
		R++;
	    }
	  if (R > 255)
	    R = 255;

	   /*G*/ if (sum_G == 0)
	    G = max_G;
	  else if (G < min_G)
	    G = 0;
	  else if (G <= 255)
	    {
	      fmax_G = ((float) ((G - min_G) * 255) / (float) sum_G);
	      G = (unsigned short) fmax_G;
	      fmax_G = (fmax_G - G) * 10;
	      if (fmax_G >= 5)
		G++;

	    }
	  if (G > 255)
	    G = 255;

	   /*B*/ if (sum_B == 0)
	    B = max_B;
	  else if (B < min_B)
	    B = 0;
	  else if (B <= 255)
	    {
	      fmax_B = ((float) (B - min_B) * 255 / (float) sum_B);
	      B = (unsigned short) fmax_B;
	      fmax_B = (fmax_B - B) * 10;
	      if (fmax_B >= 5)
		B++;
	    }
	  if (B > 255)
	    B = 255;

	  hisgram_R[(SANE_Byte) R]++;
	  hisgram_G[(SANE_Byte) G]++;
	  hisgram_B[(SANE_Byte) B]++;

	  *(pbmpdata + (tLines + i * 3 + 2)) = (SANE_Byte) R;
	  *(pbmpdata + (tLines + i * 3 + 1)) = (SANE_Byte) G;
	  *(pbmpdata + (tLines + i * 3)) = (SANE_Byte) B;

	}
    }

  DBG (DBG_FUNC, "AutoLevel: exit\n");
  return;
}

#ifdef SANE_UNUSED
/**********************************************************************
Author: Jack             Date: 2005/05/14
Routine Description:
	Deal with image with auto level
Parameters:
	pDIB: the data of image
	ImageWidth: the width of image
	ImageHeight: the height of image
Return value:
	none
***********************************************************************/
static void
QBETDetectAutoLevel (void *pDIB, unsigned int ImageWidth, unsigned int ImageHeight)
{
  unsigned short *pbmpdata;
  float fRPercent = 0.0;
  float fGPercent = 0.0;
  float fBPercent = 0.0;
  float fRSum, fGSum, fBSum;

  int i, j;
  unsigned int tLines, CountPixels, TotalImgSize;
  unsigned short R, G, B, max_R, max_G, max_B, min_R, min_G, min_B;
  unsigned short wIndexR, wIndexG, wIndexB;
  float fmax_R, fmax_G, fmax_B;
  unsigned int sum_R = 0, sum_G = 0, sum_B = 0;
  unsigned int hisgram_R[1024], hisgram_G[1024], hisgram_B[1024];

  if (!pDIB)
    {
      return;
    }

  pbmpdata = (unsigned short *) pDIB;

  CountPixels = 0;
  TotalImgSize = ImageWidth * ImageHeight;


  for (i = 0; i < 1024; i++)
    {

      hisgram_R[i] = 0;
      hisgram_G[i] = 0;
      hisgram_B[i] = 0;
    }


  /*Find min , max, mean */
  max_R = max_G = max_B = 0;
  min_R = min_G = min_B = 1023;
  tLines = 0;

  for (j = 0; j < (int) ImageHeight; j++)
    {
      tLines = j * ImageWidth * 3;
      for (i = 0; i < (int) ImageWidth; i++)
	{
	  R = *(pbmpdata + (tLines + i * 3 + 2));
	  G = *(pbmpdata + (tLines + i * 3 + 1));
	  B = *(pbmpdata + (tLines + i * 3));

	  max_R = _MAX (R, max_R);
	  max_G = _MAX (G, max_G);
	  max_B = _MAX (B, max_B);

	  min_R = _MIN (R, min_R);
	  min_G = _MIN (G, min_G);
	  min_B = _MIN (B, min_B);

	  hisgram_R[R]++;
	  hisgram_G[G]++;
	  hisgram_B[B]++;

	  sum_R += R;
	  sum_G += G;
	  sum_B += B;

	  *(pbmpdata + (tLines + i * 3 + 2)) = R;
	  *(pbmpdata + (tLines + i * 3 + 1)) = G;
	  *(pbmpdata + (tLines + i * 3)) = B;

	  CountPixels++;
	}

    }


  fRSum = 0.0;
  fGSum = 0.0;
  fBSum = 0.0;

  wIndexR = 511;
  wIndexG = 511;
  wIndexB = 511;

  for (i = 0; i < 1024; i++)
    {
      fRSum += (float) hisgram_R[i];
      fRPercent = (fRSum / CountPixels) * 100;
      if (fRPercent > 50)
	{
	  wIndexR = i;
	  break;
	}

    }

  for (i = 0; i < 1024; i++)
    {
      fGSum += (float) hisgram_G[i];
      fGPercent = (fGSum / CountPixels) * 100;
      if (fGPercent > 50)
	{
	  wIndexG = i;
	  break;
	}
    }

  for (i = 0; i < 1024; i++)
    {
      fBSum += (float) hisgram_B[i];
      fBPercent = (fBSum / CountPixels) * 100;
      if (fBPercent > 50)
	{
	  wIndexB = i;
	  break;
	}

    }


  fRSum = 0.0;

  for (i = wIndexR; i >= 0; i--)
    {
      fRSum += (float) hisgram_R[i];
      fRPercent = (fRSum / CountPixels) * 100;
      if (fRPercent >= 48)
	{
	  min_R = i;
	  break;
	}

    }

  fRSum = 0.0;
  for (i = wIndexR; i < 1024; i++)
    {
      fRSum += (float) hisgram_R[i];
      fRPercent = (fRSum / CountPixels) * 100;
      if (fRPercent >= 47)
	{
	  max_R = i;
	  break;
	}

    }


  fGSum = 0.0;
  for (i = wIndexG; i >= 0; i--)
    {
      fGSum += (float) hisgram_G[i];
      fGPercent = (fGSum / CountPixels) * 100;
      if (fGPercent >= 48)
	{
	  min_G = i;
	  break;
	}

    }

  fGSum = 0.0;
  for (i = wIndexG; i < 1024; i++)
    {
      fGSum += (float) hisgram_G[i];
      fGPercent = (fGSum / CountPixels) * 100;
      if (fGPercent >= 47)
	{
	  max_G = i;
	  break;
	}

    }

  fBSum = 0.0;
  for (i = wIndexB; i >= 0; i--)
    {
      fBSum += (float) hisgram_B[i];
      fBPercent = (fBSum / CountPixels) * 100;
      if (fBPercent >= 46)
	{
	  min_B = i;
	  break;
	}

    }

  fBSum = 0.0;
  for (i = wIndexB; i < 1024; i++)
    {
      fBSum += (float) hisgram_B[i];
      fBPercent = (fBSum / CountPixels) * 100;
      if (fBPercent >= 47)
	{
	  max_B = i;
	  break;
	}

    }


  /*Autolevel: */
  sum_R = max_R - min_R;
  sum_G = max_G - min_G;
  sum_B = max_B - min_B;

  for (j = 0; j < (int) ImageHeight; j++)
    {
      tLines = j * ImageWidth * 3;
      for (i = 0; i < (int) ImageWidth; i++)
	{
	  R = *(pbmpdata + (tLines + i * 3 + 2));
	  G = *(pbmpdata + (tLines + i * 3 + 1));
	  B = *(pbmpdata + (tLines + i * 3));


	   /*R*/ if (sum_R == 0)
	    R = max_R;
	  else if (R < min_R)
	    {

	      R = 0;
	    }
	  else if ((R >= min_R) && (R <= 1023))
	    {
	      fmax_R = ((float) ((R - min_R) * 923) / (float) sum_R) + 100;
	      R = (unsigned short) fmax_R;
	      fmax_R = (fmax_R - R) * 10;
	      if (fmax_R >= 5)
		R++;
	    }
	  if (R > 1023)
	    R = 1023;

	   /*G*/ if (sum_G == 0)
	    G = max_G;
	  else if (G < min_G)
	    {

	      G = 0;
	    }
	  else if ((G >= min_G) && (G <= 1023))
	    {
	      fmax_G = ((float) ((G - min_G) * 923) / (float) sum_G) + 100;
	      G = (unsigned short) fmax_G;
	      fmax_G = (fmax_G - G) * 10;
	      if (fmax_G >= 5)
		G++;
	    }
	  if (G > 1023)
	    G = 1023;

	   /*B*/ if (sum_B == 0)
	    B = max_B;
	  else if (B < min_R)
	    {

	      B = 0;
	    }
	  else if ((B >= min_B) && (R <= 1023))
	    {
	      fmax_B = ((float) (B - min_B) * 923 / (float) sum_B) + 100;

	      B = (unsigned short) fmax_B;
	      fmax_B = (fmax_B - B) * 10;
	      if (fmax_B >= 5)
		B++;
	    }
	  if (B > 1023)
	    B = 1023;

	  *(pbmpdata + (tLines + i * 3 + 2)) = R;
	  *(pbmpdata + (tLines + i * 3 + 1)) = G;
	  *(pbmpdata + (tLines + i * 3)) = B;

	}
    }

  return;
}
#endif

#ifdef SANE_UNUSED
/**********************************************************************
Author: Jack             Date: 2005/05/14
Routine Description:
	Change the image data and deal with auto level
Parameters:
	lpSource: the data of image
	scanMode: the scan mode
	ScanLines: the rows of image
	BytesPerLine: the bytes of per line
Return value:
	none
***********************************************************************/
static void
QBetChange (SANE_Byte *lpSource, SCANMODE scanMode, unsigned short ScanLines,
	    unsigned int BytesPerLine)
{
  unsigned short i, j;
  unsigned int tLines, TotalImgSize;
  unsigned short R1, G1, B1, R, G, B, R2, G2, B2, QBET_RGB = 0, PointF, PointB;
  unsigned short *pwRGB;

  int k;

  unsigned int ImageWidth = BytesPerLine / 3;
  unsigned int ImageHeight = ScanLines;
  SANE_Byte *pbmpdata = (SANE_Byte *) lpSource;

  if (scanMode != CM_RGB24ext)
    {
      return;
    }


  TotalImgSize = ImageWidth * ImageHeight * 3 * 2;
  if ((pwRGB = (unsigned short *) malloc (TotalImgSize)) == NULL)
    {
      return;
    }


  for (j = 0; j < ImageHeight; j++)
    {
      tLines = j * ImageWidth * 3;
      for (i = 0; i < ImageWidth; i++)
	{
	  if (i == 0)
	    {
	      R1 = R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2));
	      G1 = G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1));
	      B1 = B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3));
	      R2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3 + 2));
	      G2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3 + 1));
	      B2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3));
	    }
	  else if (i == (ImageWidth - 1))
	    {
	      R1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3 + 2));
	      G1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3 + 1));
	      B1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3));
	      R2 = R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2));
	      G2 = G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1));
	      B2 = B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3));
	    }
	  else
	    {
	      R1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3 + 2));
	      G1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3 + 1));
	      B1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3));

	      R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2));
	      G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1));
	      B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3));

	      R2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3 + 2));
	      G2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3 + 1));
	      B2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3));
	    }

	  R1 = R1 & 0x0003;
	  G1 = G1 & 0x0003;
	  B1 = B1 & 0x0003;

	  R2 = R2 & 0x0003;
	  G2 = G2 & 0x0003;
	  B2 = B2 & 0x0003;
	  for (k = 0; k < 3; k++)
	    {
	      if (k == 0)
		{
		  PointF = R1;
		  PointB = R2;
		}
	      else if (k == 1)
		{
		  PointF = G1;
		  PointB = G2;
		}
	      else if (k == 2)
		{
		  PointF = B1;
		  PointB = B2;
		}

	      switch (PointF)
		{
		case 0:
		case 1:
		  if (PointB == 0)
		    QBET_RGB = 0xFFFC;
		  else if (PointB == 1)
		    QBET_RGB = 0xFFFC;
		  else if (PointB == 2)
		    QBET_RGB = 0xFFFD;
		  else if (PointB == 3)
		    QBET_RGB = 0xFFFE;
		  break;
		case 2:
		  if (PointB == 0)
		    QBET_RGB = 0xFFFD;
		  else if (PointB == 1)
		    QBET_RGB = 0xFFFD;
		  else if (PointB == 2)
		    QBET_RGB = 0xFFFF;
		  else if (PointB == 3)
		    QBET_RGB = 0xFFFF;
		  break;
		case 3:
		  if (PointB == 0)
		    QBET_RGB = 0xFFFE;
		  else if (PointB == 1)
		    QBET_RGB = 0xFFFE;
		  else if (PointB == 2)
		    QBET_RGB = 0xFFFF;
		  else if (PointB == 3)
		    QBET_RGB = 0xFFFF;
		  break;
		default:
		  break;
		}

	      if (k == 0)
		{
		  R = R << 2;
		  R = R + 0x0003;
		  R = R & QBET_RGB;
		}
	      else if (k == 1)
		{
		  G = G << 2;
		  G = G + 0x0003;
		  G = G & QBET_RGB;
		}
	      else if (k == 2)
		{
		  B = B << 2;
		  B = B + 0x0003;
		  B = B & QBET_RGB;
		}

	    }

	  *(pwRGB + (tLines + i * 3 + 2)) = R;
	  *(pwRGB + (tLines + i * 3 + 1)) = G;
	  *(pwRGB + (tLines + i * 3)) = B;

	}

    }


  QBETDetectAutoLevel (pwRGB, ImageWidth, ImageHeight);


  for (j = 0; j < ImageHeight; j++)
    {
      tLines = j * ImageWidth * 3;

      for (i = 0; i < ImageWidth; i++)
	{
	  R = *(pwRGB + (tLines + i * 3 + 2));
	  G = *(pwRGB + (tLines + i * 3 + 1));
	  B = *(pwRGB + (tLines + i * 3));

	  R = R >> 2;
	  G = G >> 2;

	  B = B >> 2;
	  if (R > 255)
	    R = 255;
	  if (G > 255)
	    G = 255;
	  if (B > 255)
	    B = 255;

	  *(pbmpdata + (tLines + i * 3 + 2)) = (SANE_Byte) R;
	  *(pbmpdata + (tLines + i * 3 + 1)) = (SANE_Byte) G;
	  *(pbmpdata + (tLines + i * 3)) = (SANE_Byte) B;

	}

    }


  if (pwRGB != NULL)
    {
      free (pwRGB);
    }

  return;
}
#endif

/****************************** SANE API functions *****************************/

SANE_Status
sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
{
  DBG_INIT ();
  DBG (DBG_FUNC, "sane_init: start\n");
  DBG (DBG_ERR, "SANE Mustek USB2 backend version %d.%d build %d from %s\n",
       SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, PACKAGE_STRING);

  num_devices = 1;		/* HOLD: only one device in this backend */

  if (version_code != NULL)
    *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD);

  DBG (DBG_INFO, "sane_init: authorize %s null\n", authorize ? "!=" : "==");

  DBG (DBG_FUNC, "sane_init: exit\n");
  return SANE_STATUS_GOOD;
}

void
sane_exit (void)
{
  DBG (DBG_FUNC, "sane_exit: start\n");

  if (devlist != NULL)
    {
      free (devlist);
      devlist = NULL;
    }

  devlist = NULL;
  DBG (DBG_FUNC, "sane_exit: exit\n");
}

SANE_Status
sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
{
  SANE_Int dev_num;
  DBG (DBG_FUNC, "sane_get_devices: start: local_only = %s\n",
       local_only == SANE_TRUE ? "true" : "false");

  if (devlist != NULL)
    {
      free (devlist);
      devlist = NULL;
    }

  devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
  if (devlist == NULL)
    return SANE_STATUS_NO_MEM;

  dev_num = 0;
  /* HOLD: This is ugly (only one scanner!) and should go to sane_init */
  if (GetDeviceStatus ())
    {
      SANE_Device *sane_device;

      sane_device = malloc (sizeof (*sane_device));
      if (sane_device == NULL)
	return SANE_STATUS_NO_MEM;
      sane_device->name = strdup (device_name);
      sane_device->vendor = strdup ("Mustek");
      sane_device->model = strdup ("BearPaw 2448 TA Pro");
      sane_device->type = strdup ("flatbed scanner");
      devlist[dev_num++] = sane_device;
    }
  devlist[dev_num] = 0;
  *device_list = devlist;
  DBG (DBG_FUNC, "sane_get_devices: exit\n");
  return SANE_STATUS_GOOD;
}

SANE_Status
sane_open (SANE_String_Const devicename, SANE_Handle * handle)
{
  Mustek_Scanner *s;

  DBG (DBG_FUNC, "sane_open: start :devicename = %s\n", devicename);

  if (!MustScanner_Init ())
    {
      return SANE_STATUS_INVAL;
    }
  if (!PowerControl (SANE_FALSE, SANE_FALSE))
    {
      return SANE_STATUS_INVAL;
    }
  if (!CarriageHome ())
    {
      return SANE_STATUS_INVAL;
    }

  s = malloc (sizeof (*s));
  if (s == NULL)
    return SANE_STATUS_NO_MEM;
  memset (s, 0, sizeof (*s));

  s->gamma_table = NULL;
  memcpy (&s->model, &mustek_A2nu2_model, sizeof (Scanner_Model));
  s->next = NULL;
  s->bIsScanning = SANE_FALSE;
  s->bIsReading = SANE_FALSE;

  init_options (s);
  *handle = s;

  s->read_rows = 0;
  s->scan_buffer_len = 0;

  DBG (DBG_FUNC, "sane_open: exit\n");
  return SANE_STATUS_GOOD;
}


void
sane_close (SANE_Handle handle)
{
  Mustek_Scanner *s = handle;
  DBG (DBG_FUNC, "sane_close: start\n");

  PowerControl (SANE_FALSE, SANE_FALSE);

  CarriageHome ();

  if (NULL != g_pDeviceFile)
    {
      free (g_pDeviceFile);
      g_pDeviceFile = NULL;
    }

  if (s->Scan_data_buf != NULL)
    free (s->Scan_data_buf);

  s->Scan_data_buf = NULL;

  free (handle);

  DBG (DBG_FUNC, "sane_close: exit\n");
}


const SANE_Option_Descriptor *
sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
{
  Mustek_Scanner *s = handle;

  if ((unsigned) option >= NUM_OPTIONS)
    return 0;
  DBG (DBG_FUNC, "sane_get_option_descriptor: option = %s (%d)\n",
       s->opt[option].name, option);
  return s->opt + option;
}


SANE_Status
sane_control_option (SANE_Handle handle, SANE_Int option,
		     SANE_Action action, void *val, SANE_Int * info)
{
  Mustek_Scanner *s = handle;
  SANE_Status status;
  SANE_Word cap;
  SANE_Int myinfo = 0;

  DBG (DBG_FUNC,
       "sane_control_option: start: action = %s, option = %s (%d)\n",
       (action == SANE_ACTION_GET_VALUE) ? "get" : (action ==
						    SANE_ACTION_SET_VALUE) ?
       "set" : (action == SANE_ACTION_SET_AUTO) ? "set_auto" : "unknown",
       s->opt[option].name, option);


  if (info)
    *info = 0;

  if (s->bIsScanning)
    {
      DBG (DBG_ERR, "sane_control_option: don't call this function while "
	   "scanning\n");
      return SANE_STATUS_DEVICE_BUSY;
    }
  if (option >= NUM_OPTIONS || option < 0)
    {
      DBG (DBG_ERR,
	   "sane_control_option: option %d >= NUM_OPTIONS || option < 0\n",
	   option);
      return SANE_STATUS_INVAL;
    }

  cap = s->opt[option].cap;
  if (!SANE_OPTION_IS_ACTIVE (cap))
    {
      DBG (DBG_ERR, "sane_control_option: option %d is inactive\n", option);
      return SANE_STATUS_INVAL;
    }
  if (action == SANE_ACTION_GET_VALUE)
    {
      switch (option)
	{
	  /* word options: */
	case OPT_NUM_OPTS:
	case OPT_RESOLUTION:
	case OPT_PREVIEW:
	case OPT_AUTO_WARMUP:
	case OPT_GAMMA_VALUE:
	case OPT_THRESHOLD:
	case OPT_TL_X:
	case OPT_TL_Y:
	case OPT_BR_X:
	case OPT_BR_Y:
	  *(SANE_Word *) val = s->val[option].w;
	  break;
	  /* string options: */
	case OPT_MODE:
	  strcpy (val, s->val[option].s);
	  break;

	case OPT_SOURCE:
	  strcpy (val, s->val[option].s);
	  break;
	default:
	  DBG (DBG_ERR, "sane_control_option: can't get unknown option %d\n",
	       option);
	  ;
	}
    }
  else if (action == SANE_ACTION_SET_VALUE)
    {
      if (!SANE_OPTION_IS_SETTABLE (cap))
	{
	  DBG (DBG_ERR, "sane_control_option: option %d is not settable\n",
	       option);
	  return SANE_STATUS_INVAL;
	}

      status = sanei_constrain_value (s->opt + option, val, &myinfo);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (2, "sane_control_option: sanei_constrain_value returned %s\n",
	       sane_strstatus (status));
	  return status;
	}

      switch (option)
	{
	  /* (mostly) side-effect-free word options: */
	case OPT_RESOLUTION:
	case OPT_PREVIEW:
	case OPT_TL_X:
	case OPT_TL_Y:
	case OPT_BR_X:
	case OPT_BR_Y:
	  s->val[option].w = *(SANE_Word *) val;
	  RIE (calc_parameters (s));
	  myinfo |= SANE_INFO_RELOAD_PARAMS;
	  break;
	case OPT_THRESHOLD:
	case OPT_AUTO_WARMUP:
	case OPT_GAMMA_VALUE:
	  s->val[option].w = *(SANE_Word *) val;
	  break;
	  /* side-effect-free word-array options: */
	case OPT_MODE:
	  if (s->val[option].s)
	    free (s->val[option].s);
	  s->val[option].s = strdup (val);
	  if (strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_LINEART) == 0)
	    {
	      ENABLE (OPT_THRESHOLD);
	    }
	  else
	    {
	      DISABLE (OPT_THRESHOLD);
	    }
	  RIE (calc_parameters (s));
	  myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
	  break;
	case OPT_SOURCE:
	  if (strcmp (s->val[option].s, val) != 0)
	    {			/* something changed */
	      if (s->val[option].s)
		free (s->val[option].s);
	      s->val[option].s = strdup (val);
	      if (strcmp (s->val[option].s, "Reflective") == 0)
		{
		  PowerControl (SANE_TRUE, SANE_FALSE);
		  s->opt[OPT_MODE].size = max_string_size (mode_list);
		  s->opt[OPT_MODE].constraint.string_list = mode_list;
		  s->val[OPT_MODE].s = strdup ("Color24");
		  x_range.max = s->model.x_size;
		  y_range.max = s->model.y_size;
		}
	      else if (0 == strcmp (s->val[option].s, "Negative"))
		{
		  PowerControl (SANE_FALSE, SANE_TRUE);
		  s->opt[OPT_MODE].size =
		    max_string_size (negative_mode_list);
		  s->opt[OPT_MODE].constraint.string_list =
		    negative_mode_list;
		  s->val[OPT_MODE].s = strdup ("Color24");
		  x_range.max = s->model.x_size_ta;
		  y_range.max = s->model.y_size_ta;
		}
	      else if (0 == strcmp (s->val[option].s, "Positive"))
		{
		  PowerControl (SANE_FALSE, SANE_TRUE);
		  s->opt[OPT_MODE].size = max_string_size (mode_list);
		  s->opt[OPT_MODE].constraint.string_list = mode_list;
		  s->val[OPT_MODE].s = strdup ("Color24");
		  x_range.max = s->model.x_size_ta;
		  y_range.max = s->model.y_size_ta;
		}
	    }
	  myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
	  break;
	default:
	  DBG (DBG_ERR, "sane_control_option: can't set unknown option %d\n",
	       option);
	}
    }
  else
    {
      DBG (DBG_ERR, "sane_control_option: unknown action %d for option %d\n",
	   action, option);
      return SANE_STATUS_INVAL;
    }
  if (info)
    *info = myinfo;

  DBG (DBG_FUNC, "sane_control_option: exit\n");
  return SANE_STATUS_GOOD;
}

SANE_Status
sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
{
  Mustek_Scanner *s = handle;

  DBG (DBG_FUNC, "sane_get_parameters: start\n");

  DBG (DBG_INFO, "sane_get_parameters :params.format = %d\n",
       s->params.format);

  DBG (DBG_INFO, "sane_get_parameters :params.depth = %d\n", s->params.depth);
  DBG (DBG_INFO, "sane_get_parameters :params.pixels_per_line = %d\n",
       s->params.pixels_per_line);
  DBG (DBG_INFO, "sane_get_parameters :params.bytes_per_line = %d\n",
       s->params.bytes_per_line);
  DBG (DBG_INFO, "sane_get_parameters :params.lines = %d\n", s->params.lines);
  if (params != NULL)
    *params = s->params;

  DBG (DBG_FUNC, "sane_get_parameters: exit\n");

  return SANE_STATUS_GOOD;

}

SANE_Status
sane_start (SANE_Handle handle)
{
  int i;
  Mustek_Scanner *s = handle;

  DBG (DBG_FUNC, "sane_start: start\n");

  s->scan_buffer_len = 0;

  calc_parameters (s);

  if (s->val[OPT_TL_X].w >= s->val[OPT_BR_X].w)
    {
      DBG (DBG_CRIT,
	   "sane_start: top left x >= bottom right x --- exiting\n");
      return SANE_STATUS_INVAL;
    }
  if (s->val[OPT_TL_Y].w >= s->val[OPT_BR_Y].w)
    {
      DBG (DBG_CRIT,
	   "sane_start: top left y >= bottom right y --- exiting\n");
      return SANE_STATUS_INVAL;
    }

  s->setpara.pGammaTable = NULL;

  DBG (DBG_INFO, "Sane_start:setpara ,setpara.fmArea.x1=%d\n",
       s->setpara.fmArea.x1);
  DBG (DBG_INFO, "Sane_start:setpara ,setpara.fmArea.x2=%d\n",
       s->setpara.fmArea.x2);
  DBG (DBG_INFO, "Sane_start:setpara ,setpara.fmArea.y1=%d\n",
       s->setpara.fmArea.y1);
  DBG (DBG_INFO, "Sane_start:setpara ,setpara.fmArea.y2=%d\n",
       s->setpara.fmArea.y2);
  DBG (DBG_INFO, "Sane_start:setpara ,setpara.pfPixelFlavor=%d\n",
       s->setpara.pfPixelFlavor);
  DBG (DBG_INFO, "Sane_start:setpara ,setpara.wLinearThreshold=%d\n",
       s->setpara.wLinearThreshold);
  DBG (DBG_INFO, "Sane_start:setpara ,setpara.wTargetDPI=%d\n",
       s->setpara.wTargetDPI);
  DBG (DBG_INFO, "Sane_start:setpara ,setpara.smScanMode=%d\n",
       s->setpara.smScanMode);
  DBG (DBG_INFO, "Sane_start:setpara ,setpara.ssScanSource =%d\n",
       s->setpara.ssScanSource);
  DBG (DBG_INFO, "Sane_start:setpara ,setpara.pGammaTable =%p\n",
       (void *) s->setpara.pGammaTable);

  SetParameters (&s->setpara);

  GetParameters (&s->getpara);

  switch (s->params.format)
    {
    case SANE_FRAME_RGB:
      if (s->params.depth == 8)

	s->params.pixels_per_line = s->getpara.dwLineByteWidth / 3;
      if (s->params.depth == 16)
	s->params.pixels_per_line = s->getpara.dwLineByteWidth / 6;


      break;
    case SANE_FRAME_GRAY:
      if (s->params.depth == 1)
	s->params.pixels_per_line = s->getpara.dwLineByteWidth * 8;
      if (s->params.depth == 8)
	s->params.pixels_per_line = s->getpara.dwLineByteWidth;
      if (s->params.depth == 16)
	s->params.pixels_per_line = s->getpara.dwLineByteWidth / 2;
      break;
    default:
      DBG (DBG_INFO, "sane_start: sane_params.format = %d\n",
	   s->params.format);
    }

  s->params.bytes_per_line = s->getpara.dwLineByteWidth;
  s->params.lines = s->getpara.dwLength;

  s->params.last_frame = TRUE;


  s->read_rows = s->getpara.dwLength;
  DBG (DBG_INFO, "sane_start : read_rows = %d\n", s->read_rows);

  /*warmming up */
  if (s->val[OPT_AUTO_WARMUP].w)
    {
      for (i = 30; i > 0; i--)
	{
	  sleep (1);
	  DBG (DBG_ERR, "warming up: %d\n", i);
	}
    }
  DBG (DBG_INFO, "SCANNING ... \n");

  s->bIsScanning = SANE_TRUE;
  if (s->Scan_data_buf != NULL)
    free (s->Scan_data_buf);
  s->Scan_data_buf = NULL;

  s->Scan_data_buf = malloc (SCAN_BUFFER_SIZE * sizeof (SANE_Byte));
  if (s->Scan_data_buf == NULL)
    return SANE_STATUS_NO_MEM;

  StartScan ();

  DBG (DBG_FUNC, "sane_start: exit\n");

  return SANE_STATUS_GOOD;
}

SANE_Status
sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
	   SANE_Int * len)
{

  Mustek_Scanner *s = handle;
  static SANE_Byte *tempbuf;
  SANE_Int lines_to_read, lines_read;
  IMAGEROWS image_row;

  int maxbuffersize = max_len;

  DBG (DBG_FUNC, "sane_read: start: max_len=%d\n", max_len);

  if (s == NULL)
    {
      DBG (DBG_ERR, "sane_read: handle is null!\n");
      return SANE_STATUS_INVAL;
    }

  if (buf == NULL)
    {
      DBG (DBG_ERR, "sane_read: buf is null!\n");
      return SANE_STATUS_INVAL;
    }

  if (len == NULL)
    {
      DBG (DBG_ERR, "sane_read: len is null!\n");
      return SANE_STATUS_INVAL;
    }
  *len = 0;
  if (!s->bIsScanning)
    {
      DBG (DBG_WARN, "sane_read: scan was cancelled, is over or has not been "
	   "initiated yet\n");
      return SANE_STATUS_CANCELLED;
    }
  DBG (DBG_DBG, "sane_read: before read data read_row=%d\n", s->read_rows);
  if (s->scan_buffer_len == 0)
    {
      if (s->read_rows > 0)
	{
	  lines_to_read = SCAN_BUFFER_SIZE / s->getpara.dwLineByteWidth;

	  if (lines_to_read > s->read_rows)
	    lines_to_read = s->read_rows;

	  tempbuf =
	    (SANE_Byte *) malloc (sizeof (SANE_Byte) * lines_to_read *
			     s->getpara.dwLineByteWidth + 3 * 1024 + 1);
	  memset (tempbuf, 0,
		  sizeof (SANE_Byte) * lines_to_read * s->getpara.dwLineByteWidth +
		  3 * 1024 + 1);

	  DBG (DBG_INFO, "sane_read: buffer size is %ld\n",
	       (long int) sizeof (SANE_Byte) * lines_to_read * s->getpara.dwLineByteWidth +
	       3 * 1024 + 1);

	  image_row.roRgbOrder = mustek_A2nu2_model.line_mode_color_order;
	  image_row.wWantedLineNum = lines_to_read;
	  image_row.pBuffer = (SANE_Byte *) tempbuf;
	  s->bIsReading = SANE_TRUE;

	  if (!ReadScannedData (&image_row))
	    {
	      DBG (DBG_ERR, "sane_read: ReadScannedData error\n");
	      s->bIsReading = SANE_FALSE;
	      return SANE_STATUS_INVAL;
	    }

	  DBG (DBG_DBG, "sane_read: Finish ReadScanedData\n");
	  s->bIsReading = SANE_FALSE;
	  memset (s->Scan_data_buf, 0, SCAN_BUFFER_SIZE);
	  s->scan_buffer_len =
	    image_row.wXferedLineNum * s->getpara.dwLineByteWidth;
	  DBG (DBG_INFO, "sane_read : s->scan_buffer_len = %ld\n",
	       (long int) s->scan_buffer_len);

	  memcpy (s->Scan_data_buf, tempbuf, s->scan_buffer_len);

	  DBG (DBG_DBG, "sane_read :after memcpy\n");
	  free (tempbuf);
	  s->Scan_data_buf_start = s->Scan_data_buf;
	  s->read_rows -= image_row.wXferedLineNum;

	}
      else
	{
	  DBG (DBG_FUNC, "sane_read: scan finished -- exit\n");
	  sane_cancel (handle);
	  return SANE_STATUS_EOF;
	}
    }
  if (s->scan_buffer_len == 0)
    {
      DBG (DBG_FUNC, "sane_read: scan finished -- exit\n");
      sane_cancel (handle);
      return SANE_STATUS_EOF;
    }




  lines_read =
    (maxbuffersize <
     (SANE_Int) s->scan_buffer_len) ? maxbuffersize : (SANE_Int) s->scan_buffer_len;
  DBG (DBG_DBG, "sane_read: after %d\n", lines_read);

  *len = (SANE_Int) lines_read;

  DBG (DBG_INFO, "sane_read : get lines_read = %d\n", lines_read);
  DBG (DBG_INFO, "sane_read : get *len = %d\n", *len);
  memcpy (buf, s->Scan_data_buf_start, lines_read);

  s->scan_buffer_len -= lines_read;
  s->Scan_data_buf_start += lines_read;
  DBG (DBG_FUNC, "sane_read: exit\n");
  return SANE_STATUS_GOOD;

}

void
sane_cancel (SANE_Handle handle)
{
  Mustek_Scanner *s = handle;
  int i;
  DBG (DBG_FUNC, "sane_cancel: start\n");
  if (s->bIsScanning)
    {
      s->bIsScanning = SANE_FALSE;
      if (s->read_rows > 0)
	{
	  DBG (DBG_INFO, "sane_cancel: warning: is scanning\n");

	}
      else
	{
	  DBG (DBG_INFO, "sane_cancel: Scan finished\n");
	}

      StopScan ();

      CarriageHome ();
      for (i = 0; i < 20; i++)
	{
	  if (s->bIsReading == SANE_FALSE)
	    {
	      if (s->gamma_table != NULL)
		{
		  free (s->gamma_table);
		  s->gamma_table = NULL;
		  break;
		}
	    }
	  else
	    sleep (1);
	}
      if (s->Scan_data_buf != NULL)
	{
	  free (s->Scan_data_buf);
	  s->Scan_data_buf = NULL;
	  s->Scan_data_buf_start = NULL;
	}

      s->read_rows = 0;
      s->scan_buffer_len = 0;
      memset (&s->setpara, 0, sizeof (s->setpara));
      memset (&s->getpara, 0, sizeof (s->getpara));

    }
  else
    {
      DBG (DBG_INFO, "sane_cancel: do nothing\n");
    }


  DBG (DBG_FUNC, "sane_cancel: exit\n");

}

SANE_Status
sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
{
  Mustek_Scanner *s = handle;
  DBG (DBG_FUNC, "sane_set_io_mode: handle = %p, non_blocking = %s\n",
       handle, non_blocking == SANE_TRUE ? "true" : "false");
  if (!s->bIsScanning)
    {
      DBG (DBG_WARN, "sane_set_io_mode: not scanning\n");
      return SANE_STATUS_INVAL;
    }
  if (non_blocking)
    return SANE_STATUS_UNSUPPORTED;
  return SANE_STATUS_GOOD;
}

SANE_Status
sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
{
  Mustek_Scanner *s = handle;
  DBG (DBG_FUNC, "sane_get_select_fd: handle = %p, fd = %p\n", handle,
       (void *) fd);
  if (!s->bIsScanning)
    {
      DBG (DBG_WARN, "%s", "sane_get_select_fd: not scanning\n");
      return SANE_STATUS_INVAL;
    }
  return SANE_STATUS_UNSUPPORTED;
}
