/* lexmark-low.c: scanner-interface file for low Lexmark scanners.

   (C) 2005 Fred Odendaal
   (C) 2006-2007 Stéphane Voltz	<stef.dev@free.fr>
   
   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.

   **************************************************************************/

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>

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

#include "../include/sane/sanei_usb.h"

#include "lexmark.h"
#include "../include/sane/sanei_backend.h"

#include "lexmark_sensors.c"
#include "lexmark_models.c"

/* numbre of ranges for offset */
#define OFFSET_RANGES 5

typedef enum
{
  black = 0,
  white
}
region_type;

/*
 * all these defines may be move in device specific constant in per model
 * struct if we need it
 */
#define HomeEdgePoint1 1235
#define HomeEdgePoint2 1258
#define HomeTolerance 30

#define LOBYTE(x)  ((u_int8_t)((x) & 0xFF))
#define HIBYTE(x)  ((u_int8_t)((x) >> 8))

/* Static low function proto-types */
static SANE_Status low_usb_bulk_write (SANE_Int devnum,
					 SANE_Byte * cmd, size_t * size);
static SANE_Status low_usb_bulk_read (SANE_Int devnum,
					SANE_Byte * buf, size_t * size);
static SANE_Status low_write_all_regs (SANE_Int devnum, SANE_Byte * regs);
static SANE_Bool low_is_home_line (unsigned char *buffer);
static SANE_Status low_get_start_loc (SANE_Int resolution,
					SANE_Int * vert_start,
					SANE_Int * hor_start,
					SANE_Int offset,
					Lexmark_Device * dev);
static void low_rewind (Lexmark_Device * dev, SANE_Byte * regs);
static SANE_Status low_start_mvmt (SANE_Int devnum);
static SANE_Status low_stop_mvmt (SANE_Int devnum);
static SANE_Status low_clr_c6 (SANE_Int devnum);
static SANE_Status low_simple_scan (Lexmark_Device * dev,
				      SANE_Byte * regs,
				      int xoffset,
				      int pixels,
				      int yoffset,
				      int lines, SANE_Byte ** data);
static void low_set_scan_area (SANE_Int res,
				 SANE_Int tlx,
				 SANE_Int tly,
				 SANE_Int brx,
				 SANE_Int bry,
				 SANE_Int offset,
				 SANE_Bool half_step,
				 SANE_Byte * regs, Lexmark_Device * dev);

/* Static Read Buffer Proto-types */
static SANE_Status read_buffer_init (Lexmark_Device * dev, int bytesperline);
static SANE_Status read_buffer_free (Read_Buffer * rb);
static size_t read_buffer_bytes_available (Read_Buffer * rb);
static SANE_Status read_buffer_add_byte (Read_Buffer * rb,
					 SANE_Byte * byte_pointer);
static SANE_Status read_buffer_add_byte_gray (Read_Buffer * rb,
					      SANE_Byte * byte_pointer);
static SANE_Status read_buffer_add_bit_lineart (Read_Buffer * rb,
						SANE_Byte * byte_pointer,
						SANE_Byte threshold);
static size_t read_buffer_get_bytes (Read_Buffer * rb, SANE_Byte * buffer,
				     size_t rqst_size);
static SANE_Bool read_buffer_is_empty (Read_Buffer * rb);


/*
 *         RTS88XX 	START
 *
 *         these rts88xx functions will be spin off in a separate lib
 *         so that they can be reused.
 */

/*
 * registers helpers to avoid direct access
 */
static SANE_Bool
rts88xx_is_color (SANE_Byte * regs)
{
  if ((regs[0x2f] & 0x11) == 0x11)
    return SANE_TRUE;
  return SANE_FALSE;
}

static void
rts88xx_set_gray_scan (SANE_Byte * regs)
{
  regs[0x2f] = (regs[0x2f] & 0x0f) | 0x20;
}

#if 0
static void
rts88xx_set_color_scan (SANE_Byte * regs)
{
  regs[0x2f] = (regs[0x2f] & 0x0f) | 0x10;
}
#endif

static void
rts88xx_set_offset (SANE_Byte * regs, SANE_Byte red, SANE_Byte green,
		    SANE_Byte blue)
{
  /* offset for odd pixels */
  regs[0x02] = red;
  regs[0x03] = green;
  regs[0x04] = blue;

  /* offset for even pixels */
  regs[0x05] = red;
  regs[0x06] = green;
  regs[0x07] = blue;
}

static void
rts88xx_set_gain (SANE_Byte * regs, SANE_Byte red, SANE_Byte green,
		  SANE_Byte blue)
{
  regs[0x08] = red;
  regs[0x09] = green;
  regs[0x0a] = blue;
}

/* set # of head moves per CIS read */
static int
rts88xx_set_scan_frequency (SANE_Byte * regs, int frequency)
{
  regs[0x64] = (regs[0x64] & 0xf0) | (frequency & 0x0f);
  return 0;
}

/*
 * read one register at given index 
 */
static SANE_Status
rts88xx_read_reg (SANE_Int devnum, SANE_Int index, SANE_Byte * reg)
{
  SANE_Status status = SANE_STATUS_GOOD;
  unsigned char cmd[] = { 0x80, 0x00, 0x00, 0x01 };
  size_t size;

  cmd[1] = index;

  size = 4;
#ifdef FAKE_USB
  status = SANE_STATUS_GOOD;
#else
  status = sanei_usb_write_bulk (devnum, cmd, &size);
#endif
  if (status != SANE_STATUS_GOOD)
    {
      DBG (5, "rts88xx_read_reg: bulk write failed\n");
      return status;
    }
  size = 1;
#ifdef FAKE_USB
  status = SANE_STATUS_GOOD;
#else
  status = sanei_usb_read_bulk (devnum, reg, &size);
#endif
  if (status != SANE_STATUS_GOOD)
    {
      DBG (5, "rts88xx_read_reg: bulk read failed\n");
      return status;
    }
  DBG (15, "rts88xx_read_reg: reg[0x%02x]=0x%02x\n", index, *reg);
  return status;
}

/*
 * write one register at given index 
 */
static SANE_Status
rts88xx_write_reg (SANE_Int devnum, SANE_Int index, SANE_Byte * reg)
{
  SANE_Status status = SANE_STATUS_GOOD;
  unsigned char cmd[] = { 0x88, 0x00, 0x00, 0x01 };
  size_t size;

  cmd[1] = index;

  size = 4;
#ifdef FAKE_USB
  status = SANE_STATUS_GOOD;
#else
  status = sanei_usb_write_bulk (devnum, cmd, &size);
#endif
  if (status != SANE_STATUS_GOOD)
    {
      DBG (5, "rts88xx_write_reg: bulk write failed\n");
      return status;
    }
  size = 1;
#ifdef FAKE_USB
  status = SANE_STATUS_GOOD;
#else
  status = sanei_usb_write_bulk (devnum, reg, &size);
#endif
  if (status != SANE_STATUS_GOOD)
    {
      DBG (5, "rts88xx_write_reg: bulk write failed\n");
      return status;
    }
  DBG (15, "rts88xx_write_reg: reg[0x%02x]=0x%02x\n", index, *reg);
  return status;
}

/*
 * write length consecutive registers, starting at index
 * register 0xb3 is never wrote in bulk register write, so we split
 * write if it belongs to the register set sent
 */
static SANE_Status
rts88xx_write_regs (SANE_Int devnum, SANE_Int start, SANE_Byte * source,
		    SANE_Int length)
{
  size_t size = 0;

  /* when writing several registers at a time, we avoid writing 0xb3
     register */
  if ((start + length > 0xb3) && (length > 1))
    {
      size = 0xb3 - start;
      if (low_usb_bulk_write (devnum, source, &size) != SANE_STATUS_GOOD)
	{
	  DBG (5, "rts88xx_write_regs : write registers part 1 failed ...\n");
	  return SANE_STATUS_IO_ERROR;
	}

      /* skip 0xB3 register */
      size++;
      start = 0xb4;
      source = source + size;
    }
  size = length - size;
  if (low_usb_bulk_write (devnum, source, &size) != SANE_STATUS_GOOD)
    {
      DBG (5, "rts88xx_write_regs : write registers part 2 failed ...\n");
      return SANE_STATUS_IO_ERROR;
    }

  return SANE_STATUS_GOOD;

}

/*
 * reads 'needed' bytes of scanned data into 'data'. Actual number of bytes get
 * is retruned in 'size'
 */
static SANE_Status
rts88xx_read_data (SANE_Int devnum, size_t needed, SANE_Byte * data,
		   size_t * size)
{
  SANE_Byte read_cmd[] = { 0x91, 0x00, 0x00, 0x00 };
  size_t cmd_size;
  SANE_Status status = SANE_STATUS_GOOD;

  /* this block would deserve to be a function */
  if (needed > MAX_XFER_SIZE)
    *size = MAX_XFER_SIZE;
  else
    *size = needed;
  read_cmd[3] = (*size) & 0xff;
  read_cmd[2] = (*size >> 8) & 0xff;
  read_cmd[1] = (*size >> 16) & 0xff;

  /* send header for 'get scanned data' */
  cmd_size = 4;
  status = low_usb_bulk_write (devnum, read_cmd, &cmd_size);
  if (status != SANE_STATUS_GOOD)
    {
      *size = 0;
      DBG (5, "rts88xx_read_data : header sending failed ...\n");
      return status;
    }
  /* get actual scanned data */
  status = low_usb_bulk_read (devnum, data, size);
  if (status != SANE_STATUS_GOOD)
    {
      *size = 0;
      DBG (5, "rts88xx_read_data : data reading failed ...\n");
    }
  return status;
}

/* starts scan by sending color depth, stopping head, the starting it */
static SANE_Status
rts88xx_commit (SANE_Int devnum, SANE_Byte depth)
{
  SANE_Status status;
  SANE_Byte reg;

  DBG (2, "rts88xx_commit: start\n");

  /* send color depth depth ??
   * X1100 -> 0x0f
   * X1100/B2 -> 0x0d
   * X1200 -> 0x01 */
  reg = depth;
  status = rts88xx_write_reg (devnum, 0x2c, &reg);

  /* stop before starting */
  status = low_stop_mvmt (devnum);

  /* effective start */
  status = low_start_mvmt (devnum);

  DBG (2, "rts88xx_commit: end\n");

  return status;
}

/*
 *         RTS88XX     END
 */



/*
 * sets the scanner idle
 */
static SANE_Status
lexmark_low_set_idle (SANE_Int devnum)
{
  SANE_Byte regs[14] =
    { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x60
  };
  if (rts88xx_write_regs (devnum, 16, regs, 14) != SANE_STATUS_GOOD)
    {
      DBG (5, "lexmark_low_set_idle : register write failed ...\n");
      return SANE_STATUS_IO_ERROR;
    }
  return SANE_STATUS_GOOD;
}


/* wake up scanner */
#if 0
static SANE_Status
lexmark_low_wake_up (Lexmark_Device * dev)
{
  SANE_Byte regs[5] = { 0x12, 0x14, 0x16, 0x18, 0x1a };
  SANE_Byte values[5] = { 0x0f, 0x00, 0x07, 0x00, 0x00 };
  int i;

  /* send the wake-up sequence, one reg at at time */
  for (i = 0; i < 10; i++)
    {
      if (rts88xx_write_reg (dev->devnum, regs[i], values + i) !=
	  SANE_STATUS_GOOD)
	{
	  DBG (5,
	       "lexmark_low_wake_up : register write pass %d failed ...\n",
	       i);
	  return SANE_STATUS_IO_ERROR;
	}
    }
  return SANE_STATUS_GOOD;
}
#endif


/**
 * 
 */
#ifdef DEEP_DEBUG
static void write_pnm_file(char *title, int pixels, int lines, int color, unsigned char *data)
{
FILE * fdbg;
int x,y;

 fdbg = fopen (title, "wb");
 if (fdbg == NULL)
	 return;

 if (color)
   {
      fprintf (fdbg, "P6\n%d %d\n255\n", pixels, lines);
     for (y = 0; y < lines; y++)
	{
	  for (x = 0; x < pixels; x += 2)
	    {
	      fputc (data[y * pixels * 3 + x + 1], fdbg);
	      fputc (data[y * pixels * 3 + x + 1 + pixels], fdbg);
	      fputc (data[y * pixels * 3 + x + 1 + pixels * 2], fdbg);
	      fputc (data[y * pixels * 3 + x], fdbg);
	      fputc (data[y * pixels * 3 + x + pixels], fdbg);
	      fputc (data[y * pixels * 3 + x + pixels * 2], fdbg);
	    }
	}
   }
 else
   {
     fprintf (fdbg, "P5\n%d %d\n255\n", pixels, lines);
     fwrite (data, pixels, lines, fdbg);
   }
 fclose (fdbg);
}
#endif

/*
 * mid level hardware functions
 */
/*
 * model init
 */
SANE_Status
sanei_lexmark_low_init (Lexmark_Device * dev)
{
  int i;
  SANE_Status status;

  DBG_INIT();

  status = SANE_STATUS_UNSUPPORTED;
  DBG (2, "low_init: start\n");

  /* clear all registers first */
  for (i = 0; i < 255; i++)
    {
      dev->shadow_regs[i] = 0;
    }

  /* set up per model constant values */
  switch (dev->model.sensor_type)
    {
    case X1100_B2_SENSOR:
      dev->shadow_regs[0x01] = 0x43;
      dev->shadow_regs[0x0b] = 0x70;
      dev->shadow_regs[0x11] = 0x01;
      dev->shadow_regs[0x12] = 0x0f;
      dev->shadow_regs[0x13] = 0x01;
      dev->shadow_regs[0x15] = 0x01;
      dev->shadow_regs[0x16] = 0x0f;
      dev->shadow_regs[0x1d] = 0x20;
      dev->shadow_regs[0x28] = 0xeb;
      dev->shadow_regs[0x29] = 0xee;
      dev->shadow_regs[0x2a] = 0xf7;
      dev->shadow_regs[0x2b] = 0x01;
      dev->shadow_regs[0x2e] = 0x86;
      dev->shadow_regs[0x30] = 0x48;
      dev->shadow_regs[0x33] = 0x01;
      dev->shadow_regs[0x3a] = 0x20;
      dev->shadow_regs[0x3b] = 0x37;
      dev->shadow_regs[0x3c] = 0x88;
      dev->shadow_regs[0x3d] = 0x08;
      dev->shadow_regs[0x40] = 0x80;
      dev->shadow_regs[0x72] = 0x05;
      dev->shadow_regs[0x74] = 0x0e;
      dev->shadow_regs[0x8b] = 0xff;
      dev->shadow_regs[0x8c] = 0x02;
      dev->shadow_regs[0x8d] = 0x01;
      dev->shadow_regs[0x8e] = 0x60;
      dev->shadow_regs[0x8f] = 0x80;
      dev->shadow_regs[0x94] = 0x0e;
      dev->shadow_regs[0xa3] = 0xcc;
      dev->shadow_regs[0xa4] = 0x27;
      dev->shadow_regs[0xa5] = 0x24;
      dev->shadow_regs[0xb0] = 0xb2;
      dev->shadow_regs[0xb2] = 0x04;
      dev->shadow_regs[0xc2] = 0x80;
      dev->shadow_regs[0xc4] = 0x20;
      dev->shadow_regs[0xc8] = 0x04;
      dev->shadow_regs[0xc9] = 0x3b;
      dev->shadow_regs[0xed] = 0xc2;
      dev->shadow_regs[0xee] = 0x02;
      dev->shadow_regs[0xf3] = 0xf8;
      dev->shadow_regs[0xf4] = 0x7f;
      status = SANE_STATUS_GOOD;
      break;
    case X1100_2C_SENSOR:
      dev->shadow_regs[0x00] = 0x00;
      dev->shadow_regs[0x01] = 0x43;
      dev->shadow_regs[0x0b] = 0x70;
      dev->shadow_regs[0x0c] = 0x28;
      dev->shadow_regs[0x0d] = 0xa4;
      dev->shadow_regs[0x11] = 0x01;
      dev->shadow_regs[0x12] = 0x0f;
      dev->shadow_regs[0x13] = 0x01;
      dev->shadow_regs[0x15] = 0x01;
      dev->shadow_regs[0x16] = 0x0f;
      dev->shadow_regs[0x17] = 0x00;
      dev->shadow_regs[0x1d] = 0x20;
      dev->shadow_regs[0x28] = 0xf5;
      dev->shadow_regs[0x29] = 0xf7;
      dev->shadow_regs[0x2a] = 0xf5;
      dev->shadow_regs[0x2b] = 0x17;
      dev->shadow_regs[0x2d] = 0x41;
      dev->shadow_regs[0x2e] = 0x86;
      dev->shadow_regs[0x2f] = 0x11;
      dev->shadow_regs[0x30] = 0x48;
      dev->shadow_regs[0x31] = 0x01;
      dev->shadow_regs[0x33] = 0x01;
      dev->shadow_regs[0x34] = 0x50;
      dev->shadow_regs[0x35] = 0x01;
      dev->shadow_regs[0x36] = 0x50;
      dev->shadow_regs[0x37] = 0x01;
      dev->shadow_regs[0x38] = 0x50;
      dev->shadow_regs[0x3a] = 0x20;
      dev->shadow_regs[0x3b] = 0x37;
      dev->shadow_regs[0x3c] = 0x88;
      dev->shadow_regs[0x3d] = 0x08;
      dev->shadow_regs[0x40] = 0x80;
      dev->shadow_regs[0x47] = 0x01;
      dev->shadow_regs[0x48] = 0x1a;
      dev->shadow_regs[0x49] = 0x5b;
      dev->shadow_regs[0x4a] = 0x1b;
      dev->shadow_regs[0x4b] = 0x5b;
      dev->shadow_regs[0x4c] = 0x05;
      dev->shadow_regs[0x4d] = 0x3f;
      dev->shadow_regs[0x60] = 0x2f;
      dev->shadow_regs[0x61] = 0x36;
      dev->shadow_regs[0x62] = 0x30;
      dev->shadow_regs[0x63] = 0x36;
      dev->shadow_regs[0x65] = 0x80;
      dev->shadow_regs[0x66] = 0x64;
      dev->shadow_regs[0x6c] = 0xc8;
      dev->shadow_regs[0x6d] = 0x00;
      dev->shadow_regs[0x72] = 0x35;
      dev->shadow_regs[0x74] = 0x4e;
      dev->shadow_regs[0x75] = 0x03;
      dev->shadow_regs[0x79] = 0x40;
      dev->shadow_regs[0x7a] = 0x01;
      dev->shadow_regs[0x85] = 0x02;
      dev->shadow_regs[0x86] = 0x33;
      dev->shadow_regs[0x87] = 0x0f;
      dev->shadow_regs[0x88] = 0x24;
      dev->shadow_regs[0x8b] = 0xff;
      dev->shadow_regs[0x8c] = 0x02;
      dev->shadow_regs[0x8d] = 0x01;
      dev->shadow_regs[0x8e] = 0x60;
      dev->shadow_regs[0x8f] = 0x80;
      dev->shadow_regs[0x91] = 0x19;
      dev->shadow_regs[0x92] = 0x20;
      dev->shadow_regs[0x93] = 0x02;
      dev->shadow_regs[0x94] = 0x0e;
      dev->shadow_regs[0xa3] = 0x0d;
      dev->shadow_regs[0xa4] = 0x5e;
      dev->shadow_regs[0xa5] = 0x23;
      dev->shadow_regs[0xb0] = 0x2c;
      dev->shadow_regs[0xb1] = 0x07;
      dev->shadow_regs[0xb2] = 0x04;
      dev->shadow_regs[0xc2] = 0x80;
      dev->shadow_regs[0xc3] = 0x01;
      dev->shadow_regs[0xc4] = 0x20;
      dev->shadow_regs[0xc5] = 0x0a;
      dev->shadow_regs[0xc8] = 0x04;
      dev->shadow_regs[0xc9] = 0x3b;
      dev->shadow_regs[0xca] = 0x0a;
      dev->shadow_regs[0xe2] = 0xf8;
      dev->shadow_regs[0xe3] = 0x2a;
      dev->shadow_regs[0xf3] = 0xf8;
      dev->shadow_regs[0xf4] = 0x7f;
      status = SANE_STATUS_GOOD;
      break;
    case X1200_USB2_SENSOR:
      dev->shadow_regs[0x01] = 0x43;
      dev->shadow_regs[0x11] = 0x01;
      dev->shadow_regs[0x12] = 0x0f;
      dev->shadow_regs[0x13] = 0x01;
      dev->shadow_regs[0x15] = 0x01;
      dev->shadow_regs[0x16] = 0x0f;
      dev->shadow_regs[0x17] = 0x00;
      dev->shadow_regs[0x1d] = 0x20;
      dev->shadow_regs[0x28] = 0xf5;
      dev->shadow_regs[0x29] = 0xf7;
      dev->shadow_regs[0x2a] = 0xf5;
      dev->shadow_regs[0x2b] = 0x17;
      dev->shadow_regs[0x2d] = 0x41;
      dev->shadow_regs[0x2e] = 0x86;
      dev->shadow_regs[0x30] = 0x48;
      dev->shadow_regs[0x31] = 0x01;
      dev->shadow_regs[0x33] = 0x01;
      dev->shadow_regs[0x34] = 0x50;
      dev->shadow_regs[0x35] = 0x01;
      dev->shadow_regs[0x36] = 0x50;
      dev->shadow_regs[0x37] = 0x01;
      dev->shadow_regs[0x38] = 0x50;
      dev->shadow_regs[0x3c] = 0x88;
      dev->shadow_regs[0x3d] = 0x08;
      dev->shadow_regs[0x66] = 0x64;
      dev->shadow_regs[0x67] = 0x00;
      dev->shadow_regs[0x6c] = 0xc8;
      dev->shadow_regs[0x6d] = 0x00;
      dev->shadow_regs[0x72] = 0x35;
      dev->shadow_regs[0x74] = 0x4e;
      dev->shadow_regs[0x75] = 0x03;
      dev->shadow_regs[0x7a] = 0x01;
      dev->shadow_regs[0x93] = 0x0a;
      dev->shadow_regs[0x94] = 0x0e;

      dev->shadow_regs[0xc3] = 0x01;
      dev->shadow_regs[0xc4] = 0x20;
      dev->shadow_regs[0xc5] = 0x0a;
      dev->shadow_regs[0xc8] = 0x04;
      dev->shadow_regs[0xc9] = 0x3b;
      dev->shadow_regs[0xca] = 0x0a;
      dev->shadow_regs[0xe2] = 0xf8;
      dev->shadow_regs[0xe3] = 0x2a;
      dev->shadow_regs[0xf3] = 0xf8;
      dev->shadow_regs[0xf4] = 0x7f;
      status = SANE_STATUS_GOOD;
      break;
    case A920_SENSOR:
      dev->shadow_regs[0x01] = 0x43;
      dev->shadow_regs[0x0b] = 0x70;
      dev->shadow_regs[0x0c] = 0x28;
      dev->shadow_regs[0x0d] = 0xa4;
      dev->shadow_regs[0x11] = 0x01;
      dev->shadow_regs[0x12] = 0x0f;
      dev->shadow_regs[0x13] = 0x01;
      dev->shadow_regs[0x15] = 0x01;
      dev->shadow_regs[0x16] = 0x07;
      dev->shadow_regs[0x1d] = 0x20;
      dev->shadow_regs[0x28] = 0xf5;
      dev->shadow_regs[0x29] = 0xf7;
      dev->shadow_regs[0x2a] = 0xf5;
      dev->shadow_regs[0x2b] = 0x17;
      dev->shadow_regs[0x2e] = 0x86;
      dev->shadow_regs[0x30] = 0x48;
      dev->shadow_regs[0x31] = 0x01;
      dev->shadow_regs[0x33] = 0x01;
      dev->shadow_regs[0x3a] = 0x20;
      dev->shadow_regs[0x3b] = 0x37;
      dev->shadow_regs[0x3c] = 0x88;
      dev->shadow_regs[0x3d] = 0x08;
      dev->shadow_regs[0x47] = 0x21;
      dev->shadow_regs[0x48] = 0x1a;
      dev->shadow_regs[0x49] = 0x5b;
      dev->shadow_regs[0x4a] = 0x1b;
      dev->shadow_regs[0x4b] = 0x5b;
      dev->shadow_regs[0x4c] = 0x05;
      dev->shadow_regs[0x4d] = 0x3f;
      dev->shadow_regs[0x65] = 0x80;
      dev->shadow_regs[0x86] = 0x14;
      dev->shadow_regs[0x87] = 0x06;
      dev->shadow_regs[0x89] = 0xf5;
      dev->shadow_regs[0x8d] = 0x01;
      dev->shadow_regs[0x8e] = 0x60;
      dev->shadow_regs[0x8f] = 0x80;
      dev->shadow_regs[0x94] = 0x0e;
      dev->shadow_regs[0xa3] = 0x0d;
      dev->shadow_regs[0xa4] = 0x5e;
      dev->shadow_regs[0xa5] = 0x23;
      dev->shadow_regs[0xb0] = 0x2c;
      dev->shadow_regs[0xb1] = 0x0f;
      dev->shadow_regs[0xc2] = 0x80;
      dev->shadow_regs[0xc4] = 0x20;
      dev->shadow_regs[0xc8] = 0x04;
      dev->shadow_regs[0xf3] = 0xf8;
      dev->shadow_regs[0xf4] = 0x7f;
      status = SANE_STATUS_GOOD;
      break;
    case X1200_SENSOR:
      dev->shadow_regs[0x01] = 0x43;
      dev->shadow_regs[0x0b] = 0x70;
      dev->shadow_regs[0x0c] = 0x28;
      dev->shadow_regs[0x0d] = 0xa4;
      dev->shadow_regs[0x11] = 0x01;
      dev->shadow_regs[0x12] = 0x0f;
      dev->shadow_regs[0x13] = 0x01;
      dev->shadow_regs[0x15] = 0x01;
      dev->shadow_regs[0x16] = 0x0f;
      dev->shadow_regs[0x1d] = 0x20;
      dev->shadow_regs[0x28] = 0xe9;
      dev->shadow_regs[0x29] = 0xeb;
      dev->shadow_regs[0x2a] = 0xe9;
      dev->shadow_regs[0x2b] = 0x0b;
      dev->shadow_regs[0x2d] = 0x01;
      dev->shadow_regs[0x2e] = 0x86;
      dev->shadow_regs[0x2f] = 0x11;
      dev->shadow_regs[0x30] = 0x48;
      dev->shadow_regs[0x33] = 0x01;
      dev->shadow_regs[0x34] = 0x50;
      dev->shadow_regs[0x35] = 0x01;
      dev->shadow_regs[0x36] = 0x50;
      dev->shadow_regs[0x37] = 0x01;
      dev->shadow_regs[0x38] = 0x50;
      dev->shadow_regs[0x3a] = 0x20;
      dev->shadow_regs[0x3b] = 0x37;
      dev->shadow_regs[0x3c] = 0x88;
      dev->shadow_regs[0x3d] = 0x08;
      dev->shadow_regs[0x40] = 0x80;
      dev->shadow_regs[0x47] = 0x01;
      dev->shadow_regs[0x48] = 0x1a;
      dev->shadow_regs[0x49] = 0x5b;
      dev->shadow_regs[0x4a] = 0x1b;
      dev->shadow_regs[0x4b] = 0x5b;
      dev->shadow_regs[0x4c] = 0x05;
      dev->shadow_regs[0x4d] = 0x3f;
      dev->shadow_regs[0x60] = 0x12;
      dev->shadow_regs[0x62] = 0x81;
      dev->shadow_regs[0x63] = 0x03;
      dev->shadow_regs[0x65] = 0x80;
      dev->shadow_regs[0x66] = 0x64;
      dev->shadow_regs[0x6c] = 0xc8;
      dev->shadow_regs[0x72] = 0x1e;
      dev->shadow_regs[0x74] = 0x3c;
      dev->shadow_regs[0x75] = 0x03;
      dev->shadow_regs[0x79] = 0x40;
      dev->shadow_regs[0x7a] = 0x01;
      dev->shadow_regs[0x85] = 0x20;
      dev->shadow_regs[0x86] = 0x1e;
      dev->shadow_regs[0x87] = 0x39;
      dev->shadow_regs[0x8b] = 0xff;
      dev->shadow_regs[0x8c] = 0x02;
      dev->shadow_regs[0x8d] = 0x01;
      dev->shadow_regs[0x8e] = 0x60;
      dev->shadow_regs[0x8f] = 0x80;
      dev->shadow_regs[0x92] = 0x92;
      dev->shadow_regs[0x93] = 0x02;
      dev->shadow_regs[0x94] = 0x0e;
      dev->shadow_regs[0xa3] = 0x0d;
      dev->shadow_regs[0xa4] = 0x5e;
      dev->shadow_regs[0xa5] = 0x23;
      dev->shadow_regs[0xb0] = 0x2c;
      dev->shadow_regs[0xb1] = 0x07;
      dev->shadow_regs[0xb2] = 0x04;
      dev->shadow_regs[0xc2] = 0x80;
      dev->shadow_regs[0xc3] = 0x01;
      dev->shadow_regs[0xc4] = 0x20;
      dev->shadow_regs[0xc5] = 0x0a;
      dev->shadow_regs[0xc8] = 0x04;
      dev->shadow_regs[0xc9] = 0x3b;
      dev->shadow_regs[0xca] = 0x0a;
      dev->shadow_regs[0xe2] = 0xf8;
      dev->shadow_regs[0xe3] = 0x2a;
      dev->shadow_regs[0xf3] = 0xff;
      dev->shadow_regs[0xf4] = 0x0f;
      break;
    }
  DBG (5, "sanei_lexmark_low_init: init done for model %s/%s\n",
       dev->model.model, dev->model.name);
  DBG (2, "low_init: done\n");
  return status;
}

void
sanei_lexmark_low_destroy (Lexmark_Device * dev)
{
  /* free the read buffer */
  if (dev->read_buffer != NULL)
    read_buffer_free (dev->read_buffer);
}


SANE_Status
low_usb_bulk_write (SANE_Int devnum, SANE_Byte * cmd, size_t * size)
{
  SANE_Status status;
  size_t cmd_size;

  cmd_size = *size;
#ifdef FAKE_USB
  status = SANE_STATUS_GOOD;
#else
  status = sanei_usb_write_bulk (devnum, cmd, size);
#endif
  if (status != SANE_STATUS_GOOD)
    {
      DBG (5,
	   "low_usb_bulk_write: returned %s (size = %lu, expected %lu)\n",
	   sane_strstatus (status), (u_long) *size, (u_long) cmd_size);
      /* F.O. should reset the pipe here... */
    }
  return status;
}

SANE_Status
low_usb_bulk_read (SANE_Int devnum, SANE_Byte * buf, size_t * size)
{
  SANE_Status status;
  size_t exp_size;

  exp_size = *size;
#ifdef FAKE_USB
  status = SANE_STATUS_GOOD;
#else
  status = sanei_usb_read_bulk (devnum, buf, size);
#endif
  if (status != SANE_STATUS_GOOD)
    {
      DBG (5,
	   "low_usb_bulk_read: returned %s (size = %lu, expected %lu)\n",
	   sane_strstatus (status), (u_long) *size, (u_long) exp_size);
      /* F.O. should reset the pipe here... */
    }
  DBG (7, "low_usb_bulk_read: returned size = %lu (required %lu)\n",
       (u_long) *size, (u_long) exp_size);
  return status;
}


SANE_Status
low_start_mvmt (SANE_Int devnum)
{
  SANE_Status status;
  SANE_Byte reg;

  reg = 0x68;
  status = rts88xx_write_reg (devnum, 0xb3, &reg);
  status = rts88xx_write_reg (devnum, 0xb3, &reg);
  return status;
}

SANE_Status
low_stop_mvmt (SANE_Int devnum)
{
  SANE_Status status;
  SANE_Byte reg;

  /* Stop scanner - clear reg 0xb3: */
  reg = 0x02;
  status = rts88xx_write_reg (devnum, 0xb3, &reg);
  status = rts88xx_write_reg (devnum, 0xb3, &reg);
  reg = 0x00;
  status = rts88xx_write_reg (devnum, 0xb3, &reg);
  status = rts88xx_write_reg (devnum, 0xb3, &reg);
  return status;
}

SANE_Status
low_clr_c6 (SANE_Int devnum)
{
  SANE_Status status;
  SANE_Byte reg;

  /* Clear register 0xC6 */
  /* cmd_size = 0x05;
  return low_usb_bulk_write (devnum, clearC6_command_block, &cmd_size);*/
  
  reg = 0x00;
  status = rts88xx_write_reg (devnum, 0xc6, &reg);
  return status;
}

/* stops current scan */
static SANE_Status
low_cancel (SANE_Int devnum)
{
  SANE_Status status;

  DBG (2, "low_cancel: start\n");
  status = low_stop_mvmt (devnum);
  if (status != SANE_STATUS_GOOD)
    return status;
  status = low_clr_c6 (devnum);
  if (status != SANE_STATUS_GOOD)
    return status;
  DBG (2, "low_cancel: end.\n");
  return status;
}

static SANE_Status
low_start_scan (SANE_Int devnum, SANE_Byte * regs)
{
  SANE_Status status;

  DBG (2, "low_start_scan: start\n");

  /* writes registers to scanner */
  regs[0x32] = 0x00;
  status = low_write_all_regs (devnum, regs);
  if (status != SANE_STATUS_GOOD)
    return status;
  regs[0x32] = 0x40;
  status = low_write_all_regs (devnum, regs);
  if (status != SANE_STATUS_GOOD)
    return status;

  /* Stop scanner - clear reg 0xb3: */
  /* status = low_stop_mvmt (devnum);
  if (status != SANE_STATUS_GOOD)
    return status; */

  /* then start */
  status = rts88xx_commit (devnum, regs[0x2c]);
  DBG (2, "low_start_scan: end.\n");
  return status;
}

/* wait for scan data being available */
static SANE_Status
low_poll_data (SANE_Int devnum)
{
  SANE_Status status;
  int loops = 0;
  size_t size;
  static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 };
  SANE_Byte result[3];
  SANE_Word count;

  /* Poll the available byte count until not 0 */
  while (loops < 1000)
    {
      /* 10 ms sleep */
      usleep (10000);

      /* as stated in sanei_lexmark_low_search_home_bwd, we read
       * available data count twice */
      size = 4;
      status = low_usb_bulk_write (devnum, command4_block, &size);
      if (status != SANE_STATUS_GOOD)
	return status;
      size = 0x3;
      status = low_usb_bulk_read (devnum, result, &size);
      if (status != SANE_STATUS_GOOD)
	return status;
      size = 4;
      /* read availbale data size again */
      status = low_usb_bulk_write (devnum, command4_block, &size);
      if (status != SANE_STATUS_GOOD)
	return status;
      size = 0x3;
      status = low_usb_bulk_read (devnum, result, &size);
      if (status != SANE_STATUS_GOOD)
	return status;
      count = result[0] + (result[1] << 8) + (result[2] << 16);
      if (count != 0)
	{
	  DBG (15, "low_poll_data: %d bytes available\n", count);
	  return SANE_STATUS_GOOD;
	}
      loops++;
    }
  return SANE_STATUS_IO_ERROR;
}

/**
 * do a simple scan with the given registers. data buffer is allocated within
 * the function
 */
static SANE_Status
low_simple_scan (Lexmark_Device * dev, SANE_Byte * regs, int xoffset,
		   int pixels, int yoffset, int lines, SANE_Byte ** data)
{
  SANE_Status status = SANE_STATUS_GOOD;
  static SANE_Byte reg;
  size_t size, read, needed;
  int i, bpl, yend;

  DBG (2, "low_simple_scan: start\n");
  DBG (15, "low_simple_scan: x=%d, pixels=%d (ex=%d), y=%d, lines=%d\n",
       xoffset, pixels, xoffset + pixels * regs[0x7a], yoffset, lines);

  /* set up registers */
  regs[0x60] = LOBYTE (yoffset);
  regs[0x61] = HIBYTE (yoffset);
  yend = yoffset + lines;
  if (dev->model.motor_type == A920_MOTOR && rts88xx_is_color (regs)
      && dev->val[OPT_RESOLUTION].w == 600)
    yend *= 2;
  regs[0x62] = LOBYTE (yend);
  regs[0x63] = HIBYTE (yend);

  regs[0x66] = LOBYTE (xoffset);
  regs[0x67] = HIBYTE (xoffset);

  regs[0x6c] = LOBYTE (xoffset + pixels * regs[0x7a]);
  regs[0x6d] = HIBYTE (xoffset + pixels * regs[0x7a]);

  /* allocate memory */
  if (rts88xx_is_color (regs))
    bpl = 3 * pixels;
  else
    bpl = pixels;
  *data = (SANE_Byte *) malloc (bpl * lines);
  if (*data == NULL)
    {
      DBG (2,
	   "low_simple_scan: failed to allocate %d bytes !\n", bpl * lines);
      return SANE_STATUS_NO_MEM;
    }

  /* start scan */
  status = low_cancel (dev->devnum);
  if (status != SANE_STATUS_GOOD)
    return status;


  status = low_start_scan (dev->devnum, regs);
  if (status != SANE_STATUS_GOOD)
    return status;

  /* wait for data */
  status = low_poll_data (dev->devnum);
  if (status != SANE_STATUS_GOOD)
    {
      DBG (1, "low_simple_scan: time-out while waiting for data.\n");
      return status;
    }

  /* data reading loop */
  needed = bpl * lines;
  DBG (1, "low_simple_scan: bpl=%d, lines=%d, needed=%lu.\n", bpl, lines,
       (u_long) needed);
  read = 0;
  do
    {
      /* this block would deserve to be a function */
      status =
	rts88xx_read_data (dev->devnum, needed - read, (*data) + read, &size);
      if (status != SANE_STATUS_GOOD)
	return status;
      read += size;
    }
  while (read < needed);

  /* if needed, wait for motor to stop */
  if (regs[0xc3] & 0x80)
    {
      i = 0;
      do
	{
	  if (rts88xx_read_reg (dev->devnum, 0xb3, &reg) != SANE_STATUS_GOOD)
	    {
	      DBG (5, "low_simple_scan: register read failed ...\n");
	      return SANE_STATUS_IO_ERROR;
	    }
	  usleep (100000);
	  i++;
	}
      while ((reg & 0x08) && (i < 100));
      if (reg & 0x08)
	{
	  DBG (5,
	       "low_simple_scan : timeout waiting for motor to stop ...\n");
	  return SANE_STATUS_IO_ERROR;
	}
    }

  /* stop scan */
  status = low_cancel (dev->devnum);
  if (status != SANE_STATUS_GOOD)
    {
      DBG (1, "low_simple_scan: cancel failed.\n");
      return status;
    }

  DBG (2, "low_simple_scan: end.\n");
  return status;
}

/*
 * open USB device ,read initial registers values and probe sensor
 */
SANE_Status
sanei_lexmark_low_open_device (Lexmark_Device * dev)
{
  /* This function calls the Sane Interface to open this usb device.
     It also needlessly does what the Windows driver does and reads
     the entire register set - this may be removed. */

  SANE_Status result;
  static SANE_Byte command_block[] = { 0x80, 0, 0x00, 0xFF };
  size_t size;
  SANE_Byte variant = 0;
  SANE_Byte shadow_regs[255];
#ifdef DEEP_DEBUG
  int i;
#endif
  int sx, ex;
  int sy, ey;


#ifdef FAKE_USB
  result = SANE_STATUS_GOOD;
  shadow_regs[0x00] = 0x91;
  shadow_regs[0xb0] = 0x2c;
  shadow_regs[0x10] = 0x97;
  shadow_regs[0x10] = 0x87;
  shadow_regs[0xf3] = 0xf8;
  shadow_regs[0xf4] = 0x7f;
#else
  result = sanei_usb_open (dev->sane.name, &(dev->devnum));
#endif
  DBG (2, "sanei_lexmark_low_open_device: devnum=%d\n", dev->devnum);

  size = 4;
  low_usb_bulk_write (dev->devnum, command_block, &size);
  size = 0xFF;
  low_usb_bulk_read (dev->devnum, shadow_regs, &size);
#ifdef DEEP_DEBUG
  if (DBG_LEVEL > 2)
    {
      fprintf (stderr,
	       "sanei_lexmark_low_open_device: initial registers values\n");
      fprintf (stderr, "read_all(0x00,255)=");
      for (i = 0; i < 255; i++)
	{
	  fprintf (stderr, "0x%02x ", shadow_regs[i]);
	}
      fprintf (stderr, "\n");
    }
#endif

  /* it seems that at first read after reset, registers hold information
   * about the scanner. Register 0x00 is overwritten with 0, so only first read
   * after USB plug-in gives this value */
  if (shadow_regs[0] == 0x91)
    {
      sx = shadow_regs[0x67] * 256 + shadow_regs[0x66];
      ex = shadow_regs[0x6d] * 256 + shadow_regs[0x6c];
      DBG (7, "startx=%d, endx=%d, pixels=%d, coef=%d, r2f=0x%02x\n", sx, ex,
	   ex - sx, dev->shadow_regs[0x7a], shadow_regs[0x2f]);
      sy = shadow_regs[0x61] * 256 + shadow_regs[0x60];
      ey = shadow_regs[0x63] * 256 + shadow_regs[0x62];
      DBG (7, "starty=%d, endy=%d, lines=%d\n", sy, ey, ey - sy);
    }

  /* we use register 0xb0 to identify details about models   */
  /* this register isn't overwritten during normal operation */
  if (shadow_regs[0xb0] == 0x2c && dev->model.sensor_type == X1100_B2_SENSOR)
    {
      variant = shadow_regs[0xb0];
    }
  /* now the same with register 0x10 */
  /* which most likely signals USB2.0/USB1.1 */
  if ((dev->model.sensor_type == X1200_SENSOR) && (shadow_regs[0x10] == 0x97))
    {
      variant = shadow_regs[0x10];
    }

  /* if find a case where default model given is inappropriate, reassign it
   * since we have now the informations to get the real one. Such
   * We could avoid this if attach() did open and read registers, not init */
  if (variant != 0)
    {
      DBG (3,
	   "sanei_lexmark_low_open_device: reassign model/sensor for varaint 0x%02x\n",
	   variant);
      sanei_lexmark_low_assign_model (dev, dev->sane.name,
					dev->model.vendor_id,
					dev->model.product_id, variant);
      /* since model has changed, run init again */
      sanei_lexmark_low_init (dev);
    }
  DBG (2, "sanei_lexmark_low_open_device: end\n");
  return result;
}

void
sanei_lexmark_low_close_device (Lexmark_Device * dev)
{
  /* put scanner in idle state */
  lexmark_low_set_idle (dev->devnum);

  /* This function calls the Sane USB library to close this usb device */
#ifndef FAKE_USB
  sanei_usb_close (dev->devnum);
#endif
  return;
}


/* This function writes the contents of the given registers to the 
     scanner. */
SANE_Status
low_write_all_regs (SANE_Int devnum, SANE_Byte * regs)
{
  int i;
  SANE_Status status;
  size_t size;
  static SANE_Byte command_block1[0xb7];
  static SANE_Byte command_block2[0x4f];
  command_block1[0] = 0x88;
  command_block1[1] = 0x00;
  command_block1[2] = 0x00;
  command_block1[3] = 0xb3;
  for (i = 0; i < 0xb3; i++)
    {
      command_block1[i + 4] = regs[i];
    }
  command_block2[0] = 0x88;
  command_block2[1] = 0xb4;
  command_block2[2] = 0x00;
  command_block2[3] = 0x4b;
  for (i = 0; i < 0x4b; i++)
    {
      command_block2[i + 4] = regs[i + 0xb4];
    }
  size = 0xb7;

#ifdef DEEP_DEBUG
  fprintf (stderr, "write_all(0x00,255)=");
  for (i = 0; i < 255; i++)
    {
      fprintf (stderr, "0x%02x ", regs[i]);
    }
  fprintf (stderr, "\n");
#endif

  status = low_usb_bulk_write (devnum, command_block1, &size);
  if (status != SANE_STATUS_GOOD)
    return status;
  size = 0x4f;
  status = low_usb_bulk_write (devnum, command_block2, &size);
  if (status != SANE_STATUS_GOOD)
    return status;
  return SANE_STATUS_GOOD;
}


SANE_Bool
low_is_home_line (unsigned char *buffer)
{
  /*
     This function assumes the buffer has a size of 2500 bytes.It is 
     destructive to the buffer.

     Here is what it does:

     Go through the buffer finding low and high values, which are computed by 
     comparing to the average: 
     average = (lowest value + highest value)/2
     High bytes are changed to 0xFF (white), lower or equal bytes are changed 
     to 0x00 (black),so that the buffer only contains white (0xFF) or black 
     (0x00) values.

     Next, we go through the buffer. We use a tolerance of 5 bytes on each end
     of the buffer and check a region from bytes 5 to 2495. We start assuming
     we are in a white region and look for the start of a black region. We save
     this index as the transition from white to black. We also save where we 
     change from black back to white. We continue checking for transitions 
     until the end of the check region. If we don't have exactly two 
     transitions when we reach the end we return SANE_FALSE.

     The final check compares the transition indices to the nominal values
     plus or minus the tolerence. For the first transition (white to black
     index) the value must lie in the range 1235-30 (1205) to 1235+30 (1265).
     For the second transition (black to white) the value must lie in the range
     1258-30 (1228) to 1258+30 (1288). If the indices are out of range we 
     return SANE_FALSE. Otherwise, we return SANE_TRUE.
   */


  unsigned char max_byte = 0;
  unsigned char min_byte = 0xFF;
  unsigned char average;
  int i;

  region_type region;
  int transition_counter;
  int index1 = 0;
  int index2 = 0;
  int low_range, high_range;

#ifdef DEEP_DEBUG
  static int numero = 0;
  char titre[80];
  FILE *trace = NULL;
  sprintf (titre, "lgn%03d.pnm", numero);
  trace = fopen (titre, "wb");
  if (trace)
    {
      fprintf (trace, "P5\n2500 1\n255\n");
      fwrite (buffer, 2500, 1, trace);
      fclose (trace);
    }
  numero++;
#endif

  /* Find the max and the min */
  for (i = 0; i < 2500; i++)
    {
      if (*(buffer + i) > max_byte)
	max_byte = *(buffer + i);
      if (*(buffer + i) < min_byte)
	min_byte = *(buffer + i);
    }

  /* The average */
  average = ((max_byte + min_byte) / 2);

  /* Set bytes as white (0xFF) or black (0x00) */
  for (i = 0; i < 2500; i++)
    {
      if (*(buffer + i) > average)
	*(buffer + i) = 0xFF;
      else
	*(buffer + i) = 0x00;
    }

  region = white;
  transition_counter = 0;

  /* Go through the check region - bytes 5 to 2495 */
  for (i = 5; i <= 2495; i++)
    {
      /* Check for transition to black */
      if ((region == white) && (*(buffer + i) == 0))
	{
	  if (transition_counter < 2)
	    {
	      region = black;
	      index1 = i;
	      transition_counter++;
	    }
	  else
	    {
	      return SANE_FALSE;
	    }
	}
      /* Check for transition to white */
      else if ((region == black) && (*(buffer + i) == 0xFF))
	{
	  if (transition_counter < 2)
	    {
	      region = white;
	      index2 = i;
	      transition_counter++;
	    }
	  else
	    {
	      return SANE_FALSE;
	    }
	}
    }

  /* Check that the number of transitions is 2 */
  if (transition_counter != 2)
    {
      return SANE_FALSE;
    }

  /* Check that the 1st index is in range */
  low_range = HomeEdgePoint1 - HomeTolerance;
  high_range = HomeEdgePoint1 + HomeTolerance;

  if ((index1 < low_range) || (index1 > high_range))
    {
      return SANE_FALSE;
    }

  /* Check that the 2nd index is in range */
  low_range = HomeEdgePoint2 - HomeTolerance;
  high_range = HomeEdgePoint2 + HomeTolerance;

  if ((index2 < low_range) || (index2 > high_range))
    {
      return SANE_FALSE;
    }

  /* We made it this far, so its a good home line. Return True */
  return SANE_TRUE;

}

void
sanei_lexmark_low_move_fwd (SANE_Int distance, Lexmark_Device * dev,
			      SANE_Byte * regs)
{
  /*
     This function moves the scan head forward with the highest vertical 
     resolution of 1200dpi. The distance moved is given by the distance
     parameter. 

     As an example, given a distance parameter of 600, the scan head will
     move 600/1200", or 1/2" forward.
   */

  static SANE_Byte pollstopmoving_command_block[] =
    { 0x80, 0xb3, 0x00, 0x01 };


  size_t cmd_size;
  SANE_Int devnum;
  SANE_Bool scan_head_moving;
  SANE_Byte read_result;

  DBG (2, "sanei_lexmark_low_move_fwd: \n");
  devnum = dev->devnum;


  /* registers set-up */
  regs[0x2c] = 0x00;
  regs[0x2d] = 0x41;
  regs[0x65] = 0x80;
  switch (dev->model.sensor_type)
    {
    case X1100_B2_SENSOR:
      regs[0x8b] = 0x00;
      regs[0x8c] = 0x00;
      regs[0x93] = 0x06;
      break;
    case X1100_2C_SENSOR:
      rts88xx_set_scan_frequency (regs, 0);
      regs[0x93] = 0x06;
      break;
    case A920_SENSOR:
      rts88xx_set_scan_frequency (regs, 0);
      regs[0x8b] = 0xff;
      regs[0x8c] = 0x02;
      regs[0x93] = 0x0e;
      break;
    case X1200_SENSOR:
      dev->shadow_regs[0x2d] = 0x01;
      rts88xx_set_scan_frequency (regs, 0);
      break;
    case X1200_USB2_SENSOR:
      dev->shadow_regs[0x2d] = 0x4f;
      rts88xx_set_scan_frequency (regs, 0);
      break;
    }

  /* set grayscale scan + nodata/nochannel?  */
  regs[0x2f] = 0xa1;

  /* set ? */
  regs[0x34] = 0x50;
  regs[0x35] = 0x01;
  regs[0x36] = 0x50;
  regs[0x37] = 0x01;
  regs[0x38] = 0x50;
  /* set motor resolution divisor */
  regs[0x39] = 0x00;
  /* set vertical start/end positions */
  regs[0x60] = LOBYTE (distance - 1);
  regs[0x61] = HIBYTE (distance - 1);
  regs[0x62] = LOBYTE (distance);
  regs[0x63] = HIBYTE (distance);
  /* set horizontal start position */
  regs[0x66] = 0x64;
  regs[0x67] = 0x00;
  /* set horizontal end position */
  regs[0x6c] = 0xc8;
  regs[0x6d] = 0x00;
  /* set horizontal resolution */
  regs[0x79] = 0x40;
  regs[0x7a] = 0x01;
  /* don't buffer data for this scan */
  regs[0xb2] = 0x04;
  /* Motor enable & Coordinate space denominator */
  regs[0xc3] = 0x81;
  /* ? */
  regs[0x80] = 0x00;
  regs[0x81] = 0x00;
  regs[0x82] = 0x00;
  regs[0xc5] = 0x0a;
  /* Movement direction & step size */
  regs[0xc6] = 0x09;
  /* step size range2 */
  regs[0xc9] = 0x3b;
  /* ? */
  regs[0xca] = 0x0a;
  /* motor curve stuff */
  regs[0xe0] = 0x00;
  regs[0xe1] = 0x00;
  regs[0xe4] = 0x00;
  regs[0xe5] = 0x00;
  regs[0xe7] = 0x00;
  regs[0xe8] = 0x00;

  regs[0xe2] = 0x09;
  regs[0xe3] = 0x1a;
  regs[0xe6] = 0xdc;
  regs[0xe9] = 0x1b;
  regs[0xec] = 0x07;
  regs[0xef] = 0x03;

  /* prepare for register write */
  low_clr_c6 (devnum);
  low_stop_mvmt (devnum);

/* Move Forward without scanning: */
  regs[0x32] = 0x00;
  low_write_all_regs (devnum, regs);
  regs[0x32] = 0x40;
  low_write_all_regs (devnum, regs);

  /* Stop scanner - clear reg 0xb3: */
  /* low_stop_mvmt (devnum); */

  rts88xx_commit (devnum, regs[0x2c]);

  /* Poll for scanner stopped - return value(3:0) = 0: */
  scan_head_moving = SANE_TRUE;
  while (scan_head_moving)
    {
#ifdef FAKE_USB
      scan_head_moving = SANE_FALSE;
#else
      cmd_size = 0x04;
      low_usb_bulk_write (devnum, pollstopmoving_command_block, &cmd_size);
      cmd_size = 0x1;
      low_usb_bulk_read (devnum, &read_result, &cmd_size);
      if ((read_result & 0xF) == 0x0)
	{
	  scan_head_moving = SANE_FALSE;
	}
#endif
    }
  DBG (2, "sanei_lexmark_low_move_fwd: end.\n");
}

SANE_Bool
sanei_lexmark_low_search_home_fwd (Lexmark_Device * dev)
{
  /* This function actually searches backwards one line looking for home */

  SANE_Int devnum;
  int i;
  SANE_Byte poll_result[3];
  SANE_Byte *buffer;
  SANE_Byte temp_byte;

  static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 };

  static SANE_Byte command5_block[] = { 0x91, 0x00, 0x09, 0xc4 };

  size_t cmd_size;
  SANE_Bool got_line;
  SANE_Bool ret_val;

  devnum = dev->devnum;

  DBG (2, "sanei_lexmark_low_search_home_fwd:\n");

  /* set up registers according to the sensor type */
  switch (dev->model.sensor_type)
    {
    case X1100_B2_SENSOR:
      dev->shadow_regs[0x2c] = 0x0f;
      dev->shadow_regs[0x2d] = 0x51;
      dev->shadow_regs[0x34] = 0x04;
      dev->shadow_regs[0x35] = 0x04;
      dev->shadow_regs[0x36] = 0x08;
      dev->shadow_regs[0x37] = 0x08;
      dev->shadow_regs[0x38] = 0x0b;
      dev->shadow_regs[0x93] = 0x06;

      break;
    case X1100_2C_SENSOR:
      dev->shadow_regs[0x2c] = 0x0d;
      dev->shadow_regs[0x2d] = 0x4f;
      dev->shadow_regs[0x34] = 0x05;
      dev->shadow_regs[0x35] = 0x05;
      dev->shadow_regs[0x36] = 0x09;
      dev->shadow_regs[0x37] = 0x09;
      dev->shadow_regs[0x38] = 0x0d;
      dev->shadow_regs[0x40] = 0x80;
      dev->shadow_regs[0x72] = 0x35;
      dev->shadow_regs[0x74] = 0x4e;

      dev->shadow_regs[0x85] = 0x20;	/* 05 */
      dev->shadow_regs[0x86] = 0x00;	/* 05 */
      dev->shadow_regs[0x87] = 0x00;	/* 05 */
      dev->shadow_regs[0x88] = 0x00;	/* 45 */
      dev->shadow_regs[0x89] = 0x00;
      dev->shadow_regs[0x8b] = 0xff;

      dev->shadow_regs[0x93] = 0x06;	/* 0e */

      dev->shadow_regs[0x75] = 0x00;	/*    */
      dev->shadow_regs[0x91] = 0x00;	/* 60 */
      dev->shadow_regs[0x92] = 0x00;	/* 8d */
      dev->shadow_regs[0xb1] = 0x00;	/*    */
      dev->shadow_regs[0xc5] = 0x00;	/*    */
      dev->shadow_regs[0xca] = 0x00;	/*    */
      dev->shadow_regs[0xc3] = 0x01;	/*    */
      break;
    case A920_SENSOR:
      dev->shadow_regs[0x2c] = 0x0d;
      dev->shadow_regs[0x2d] = 0x4f;
      dev->shadow_regs[0x34] = 0x05;
      dev->shadow_regs[0x35] = 0x05;
      dev->shadow_regs[0x36] = 0x09;
      dev->shadow_regs[0x37] = 0x09;
      dev->shadow_regs[0x38] = 0x0d;
      dev->shadow_regs[0x40] = 0x80;
      dev->shadow_regs[0x72] = 0x35;
      dev->shadow_regs[0x74] = 0x4e;
      dev->shadow_regs[0x85] = 0x05;
      dev->shadow_regs[0x88] = 0x45;
      dev->shadow_regs[0x89] = 0x00;
      dev->shadow_regs[0x8b] = 0xff;
      dev->shadow_regs[0x91] = 0x60;
      dev->shadow_regs[0x92] = 0x8d;
      dev->shadow_regs[0x93] = 0x0e;
      break;
    case X1200_SENSOR:
      dev->shadow_regs[0x2c] = 0x01;
      dev->shadow_regs[0x2d] = 0x03;
      dev->shadow_regs[0x34] = 0x04;
      dev->shadow_regs[0x35] = 0x04;
      dev->shadow_regs[0x36] = 0x08;
      dev->shadow_regs[0x37] = 0x08;
      dev->shadow_regs[0x38] = 0x0b;
      dev->shadow_regs[0x66] = 0x88;
      dev->shadow_regs[0x6c] = 0x10;
      dev->shadow_regs[0x6d] = 0x14;
      dev->shadow_regs[0x75] = 0x00;
      dev->shadow_regs[0x93] = 0x06;
      dev->shadow_regs[0xc5] = 0x00;
      dev->shadow_regs[0xca] = 0x00;
      break;
    case X1200_USB2_SENSOR:
      dev->shadow_regs[0x0b] = 0x70;
      dev->shadow_regs[0x0c] = 0x28;
      dev->shadow_regs[0x0d] = 0xa4;

      dev->shadow_regs[0x2c] = 0x0d;
      dev->shadow_regs[0x2d] = 0x4f;
      dev->shadow_regs[0x2f] = 0x21;
      dev->shadow_regs[0x32] = 0x40;
      dev->shadow_regs[0x34] = 0x05;
      dev->shadow_regs[0x35] = 0x05;
      dev->shadow_regs[0x36] = 0x09;
      dev->shadow_regs[0x37] = 0x09;
      dev->shadow_regs[0x38] = 0x0d;
      dev->shadow_regs[0x3a] = 0x20;
      dev->shadow_regs[0x3b] = 0x37;
      dev->shadow_regs[0x40] = 0x80;
      dev->shadow_regs[0x47] = 0x01;
      dev->shadow_regs[0x48] = 0x1a;
      dev->shadow_regs[0x49] = 0x5b;
      dev->shadow_regs[0x4a] = 0x1b;
      dev->shadow_regs[0x4b] = 0x5b;
      dev->shadow_regs[0x4c] = 0x05;
      dev->shadow_regs[0x4d] = 0x3f;
      dev->shadow_regs[0x75] = 0x00;

      dev->shadow_regs[0x85] = 0x03;
      dev->shadow_regs[0x86] = 0x33;
      dev->shadow_regs[0x87] = 0x8f;
      dev->shadow_regs[0x88] = 0x34;

      dev->shadow_regs[0x8b] = 0xff;
      dev->shadow_regs[0x8e] = 0x60;
      dev->shadow_regs[0x8f] = 0x80;

      dev->shadow_regs[0x91] = 0x59;
      dev->shadow_regs[0x92] = 0x10;
      dev->shadow_regs[0x93] = 0x06;

      dev->shadow_regs[0xa3] = 0x0d;
      dev->shadow_regs[0xa4] = 0x5e;
      dev->shadow_regs[0xa5] = 0x23;
      dev->shadow_regs[0xb1] = 0x07;

      dev->shadow_regs[0xc2] = 0x80;
      dev->shadow_regs[0xc5] = 0x00;
      dev->shadow_regs[0xca] = 0x00;
      break;
    }
  dev->shadow_regs[0x65] = 0x80;
  dev->shadow_regs[0x8c] = 0x02;
  dev->shadow_regs[0x8d] = 0x01;
  dev->shadow_regs[0xb2] = 0x00;
  dev->shadow_regs[0xed] = 0x00;
  dev->shadow_regs[0xee] = 0x00;

  rts88xx_set_gain (dev->shadow_regs, dev->sensor->default_gain,
		    dev->sensor->default_gain, dev->sensor->default_gain);
  rts88xx_set_offset (dev->shadow_regs, 0x80, 0x80, 0x80);

  /* set grayscale scan */
  rts88xx_set_gray_scan (dev->shadow_regs);

  /* set motor resolution divisor */
  dev->shadow_regs[0x39] = 0x07;

  /* set vertical start/end positions */
  dev->shadow_regs[0x60] = 0x01;
  dev->shadow_regs[0x61] = 0x00;
  dev->shadow_regs[0x62] = 0x02;
  dev->shadow_regs[0x63] = 0x00;

  /* set # of head moves per CIS read */
  rts88xx_set_scan_frequency (dev->shadow_regs, 1);

  /* set horizontal start position */
  dev->shadow_regs[0x66] = 0x6a;	/* 0x88 for X1200 */
  dev->shadow_regs[0x67] = 0x00;
  /* set horizontal end position */
  dev->shadow_regs[0x6c] = 0xf2;	/* 0x1410 for X1200 */
  dev->shadow_regs[0x6d] = 0x13;
  /* set horizontal resolution */
  dev->shadow_regs[0x79] = 0x40;
  dev->shadow_regs[0x7a] = 0x02;
  /* Movement direction & step size */
  dev->shadow_regs[0xc6] = 0x01;
  /* step size range2 */
  dev->shadow_regs[0xc9] = 0x3b;
  /* step size range0 */
  dev->shadow_regs[0xe2] = 0x01;
  /* ? */
  dev->shadow_regs[0xe3] = 0x03;

  /* Motor disable & Coordinate space denominator */
  dev->shadow_regs[0xc3] = 0x01;


  /* Stop the scanner */
  low_stop_mvmt (devnum);

  /* write regs out twice */
  dev->shadow_regs[0x32] = 0x00;
  low_write_all_regs (devnum, dev->shadow_regs);
  dev->shadow_regs[0x32] = 0x40;
  low_write_all_regs (devnum, dev->shadow_regs);

  /* Start Scan */
  rts88xx_commit (devnum, dev->shadow_regs[0x2c]);

  /* Poll the available byte count until not 0 */
  got_line = SANE_FALSE;
  while (!got_line)
    {
      cmd_size = 4;
      low_usb_bulk_write (devnum, command4_block, &cmd_size);
      cmd_size = 0x3;
      low_usb_bulk_read (devnum, poll_result, &cmd_size);
      if (!
	  (poll_result[0] == 0 && poll_result[1] == 0 && poll_result[2] == 0))
	{
	  /* if result != 00 00 00 we got data */
	  got_line = SANE_TRUE;
	}
    }

  /* create buffer for scan data */
  buffer = calloc (2500, sizeof (char));

  /* Tell the scanner to send the data */
  /* Write: 91 00 09 c4 */
  cmd_size = 4;
  low_usb_bulk_write (devnum, command5_block, &cmd_size);
  /* Read it */
  cmd_size = 0x09c4;
  low_usb_bulk_read (devnum, buffer, &cmd_size);

  /* Reverse order of bytes in words of buffer */
  for (i = 0; i < 2500; i = i + 2)
    {
      temp_byte = *(buffer + i);
      *(buffer + i) = *(buffer + i + 1);
      *(buffer + i + 1) = temp_byte;
    }

  /* check for home position */
  ret_val = low_is_home_line (buffer);

  if (ret_val)
    DBG (2, "sanei_lexmark_low_search_home_fwd: !!!HOME POSITION!!!\n");

  /* free the buffer */
  free (buffer);
  DBG (2, "sanei_lexmark_low_search_home_fwd: end.\n");

  return ret_val;
}



SANE_Bool
sanei_lexmark_low_search_home_bwd (Lexmark_Device * dev)
{
/* This function must only be called if the scan head is past the home dot.
   It could damage the scanner if not.

   This function tells the scanner to do a grayscale scan backwards with a 
   300dpi resolution. It reads 2500 bytes of data between horizontal
   co-ordinates 0x6a and 0x13f2.

   The scan is set to read between vertical co-ordinates from 0x0a to 0x0f46,
   or 3900 lines. This equates to 13" at 300dpi, so we must stop the scan
   before it bangs against the end. A line limit is set so that a maximum of
   0x0F3C (13"*300dpi) lines can be read.

   To read the scan data we create a buffer space large enough to hold 10 
   lines of data. For each read we poll twice, ignoring the first poll. This 
   is required for timing. We repeat the double poll until there is data
   available. The number of lines (or number of buffers in our buffer space)
   is calculated from the size of the data available from the scanner. The
   number of buffers is calculated as the space required to hold 1.5 times
   the the size of the data available from the scanner.

   After data is read from the scanner each line is checked if it is on the
   home dot. Lines are continued to be read until we are no longer on the home
   dot. */


  SANE_Int devnum;
  SANE_Status status;
  int i, j;
  SANE_Byte poll_result[3];
  SANE_Byte *buffer;
  SANE_Byte *buffer_start;
  SANE_Byte temp_byte;

  SANE_Int buffer_count = 0;
  SANE_Int size_requested;
  SANE_Int size_returned;
  SANE_Int no_of_buffers;
  SANE_Int buffer_limit = 0xF3C;
  SANE_Int high_byte, mid_byte, low_byte;
  SANE_Int home_line_count;
  SANE_Bool in_home_region;

  static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 };

  static SANE_Byte command5_block[] = { 0x91, 0x00, 0xff, 0xc0 };
#ifdef DEEP_DEBUG
  FILE *img = NULL;
#endif

  size_t cmd_size;
  SANE_Bool got_line;

  devnum = dev->devnum;

  DBG (2, "sanei_lexmark_low_search_home_bwd:\n");

  /* set up registers */
  switch (dev->model.sensor_type)
    {
    case X1100_B2_SENSOR:
      dev->shadow_regs[0x2c] = 0x0f;
      dev->shadow_regs[0x2d] = 0x51;
      dev->shadow_regs[0x34] = 0x07;
      dev->shadow_regs[0x35] = 0x07;
      dev->shadow_regs[0x36] = 0x0f;
      dev->shadow_regs[0x37] = 0x0f;
      dev->shadow_regs[0x38] = 0x15;
      dev->shadow_regs[0x85] = 0x20;
      dev->shadow_regs[0x93] = 0x06;
      break;
    case X1100_2C_SENSOR:
      dev->shadow_regs[0x2c] = 0x0d;
      dev->shadow_regs[0x2d] = 0x4f;
      dev->shadow_regs[0x34] = 0x09;
      dev->shadow_regs[0x35] = 0x09;
      dev->shadow_regs[0x36] = 0x11;
      dev->shadow_regs[0x37] = 0x11;
      dev->shadow_regs[0x38] = 0x19;
      dev->shadow_regs[0x85] = 0x20;
      dev->shadow_regs[0x93] = 0x06;
      break;
    case A920_SENSOR:
      dev->shadow_regs[0x2c] = 0x0d;
      dev->shadow_regs[0x2d] = 0x4f;
      dev->shadow_regs[0x34] = 0x09;
      dev->shadow_regs[0x35] = 0x09;
      dev->shadow_regs[0x36] = 0x11;
      dev->shadow_regs[0x37] = 0x11;
      dev->shadow_regs[0x38] = 0x19;
      dev->shadow_regs[0x85] = 0x05;
      dev->shadow_regs[0x93] = 0x0e;
      break;
    case X1200_SENSOR:
      dev->shadow_regs[0x2c] = 0x01;
      dev->shadow_regs[0x2d] = 0x03;
      dev->shadow_regs[0x34] = 0x07;
      dev->shadow_regs[0x35] = 0x07;
      dev->shadow_regs[0x36] = 0x0f;
      dev->shadow_regs[0x37] = 0x0f;
      dev->shadow_regs[0x38] = 0x15;
      break;
    case X1200_USB2_SENSOR:
      dev->shadow_regs[0x2c] = 0x0d;
      dev->shadow_regs[0x2d] = 0x4f;
      dev->shadow_regs[0x34] = 0x09;
      dev->shadow_regs[0x35] = 0x09;
      dev->shadow_regs[0x36] = 0x11;
      dev->shadow_regs[0x37] = 0x11;
      dev->shadow_regs[0x38] = 0x19;
      dev->shadow_regs[0x85] = 0x03;
      dev->shadow_regs[0x93] = 0x06;
      break;
    }
  rts88xx_set_gain (dev->shadow_regs, dev->sensor->default_gain,
		    dev->sensor->default_gain, dev->sensor->default_gain);
  dev->shadow_regs[0x65] = 0x80;
  dev->shadow_regs[0x8b] = 0xff;
  dev->shadow_regs[0x8c] = 0x02;
  dev->shadow_regs[0xb2] = 0x00;

  /* set calibration */
  rts88xx_set_offset (dev->shadow_regs, 0x80, 0x80, 0x80);

  /* set grayscale  scan  */
  dev->shadow_regs[0x2f] = 0x21;
  /* set motor resolution divisor */
  dev->shadow_regs[0x39] = 0x03;
  /* set vertical start/end positions */
  dev->shadow_regs[0x60] = 0x0a;
  dev->shadow_regs[0x61] = 0x00;
  dev->shadow_regs[0x62] = 0x46;
  dev->shadow_regs[0x63] = 0x0f;
  /* set # of head moves per CIS read */
  rts88xx_set_scan_frequency (dev->shadow_regs, 2);
  /* set horizontal start position */
  dev->shadow_regs[0x66] = 0x6a;	/* 0x88 for X1200 */
  dev->shadow_regs[0x67] = 0x00;
  /* set horizontal end position */
  dev->shadow_regs[0x6c] = 0xf2;	/* 0x1410 for X1200, 13f2 for X1200/rev. 97 */
  dev->shadow_regs[0x6d] = 0x13;
  /* set horizontal resolution */
  dev->shadow_regs[0x79] = 0x40;
  dev->shadow_regs[0x7a] = 0x02;
  /* Motor enable & Coordinate space denominator */
  dev->shadow_regs[0xc3] = 0x81;
  /* ? */
  dev->shadow_regs[0xc5] = 0x19;
  /* Movement direction & step size */
  dev->shadow_regs[0xc6] = 0x01;
  /* step size range2 */
  dev->shadow_regs[0xc9] = 0x3a;
  /* ? */
  dev->shadow_regs[0xca] = 0x08;
  /* motor curve stuff */
  dev->shadow_regs[0xe0] = 0xe3;
  dev->shadow_regs[0xe1] = 0x18;
  dev->shadow_regs[0xe2] = 0x03;
  dev->shadow_regs[0xe3] = 0x06;
  dev->shadow_regs[0xe4] = 0x2b;
  dev->shadow_regs[0xe5] = 0x17;
  dev->shadow_regs[0xe6] = 0xdc;
  dev->shadow_regs[0xe7] = 0xb3;
  dev->shadow_regs[0xe8] = 0x07;
  dev->shadow_regs[0xe9] = 0x1b;
  dev->shadow_regs[0xec] = 0x07;
  dev->shadow_regs[0xef] = 0x03;

  /* Stop the scanner */
  low_stop_mvmt (devnum);

  /* write regs out twice */
  dev->shadow_regs[0x32] = 0x00;
  low_write_all_regs (devnum, dev->shadow_regs);
  dev->shadow_regs[0x32] = 0x40;
  low_write_all_regs (devnum, dev->shadow_regs);

  /* Start Scan */
  status = rts88xx_commit (devnum, dev->shadow_regs[0x2c]);

  /* create buffer to hold up to 10 lines of  scan data */
  buffer = calloc (10 * 2500, sizeof (char));

  home_line_count = 0;
  in_home_region = SANE_FALSE;

#ifdef DEEP_DEBUG
  img = fopen ("find_bwd.pnm", "wb");
  fprintf (img, "P5\n2500 100\n255\n");
#endif
  while (buffer_count < buffer_limit)
    {
      size_returned = 0;
      got_line = SANE_FALSE;
      while (!got_line)
	{
	  /* always poll twice (needed for timing) - disregard 1st poll */
	  cmd_size = 4;
	  status = low_usb_bulk_write (devnum, command4_block, &cmd_size);
	  if (status != SANE_STATUS_GOOD)
	    return SANE_FALSE;
	  cmd_size = 0x3;
	  status = low_usb_bulk_read (devnum, poll_result, &cmd_size);
	  if (status != SANE_STATUS_GOOD)
	    return SANE_FALSE;
	  cmd_size = 4;
	  status = low_usb_bulk_write (devnum, command4_block, &cmd_size);
	  if (status != SANE_STATUS_GOOD)
	    return SANE_FALSE;
	  cmd_size = 0x3;
	  status = low_usb_bulk_read (devnum, poll_result, &cmd_size);
	  if (status != SANE_STATUS_GOOD)
	    return SANE_FALSE;
	  if (!
	      (poll_result[0] == 0 && poll_result[1] == 0
	       && poll_result[2] == 0))
	    {
	      /* if result != 00 00 00 we got data */
	      got_line = SANE_TRUE;
	      high_byte = poll_result[2] << 16;
	      mid_byte = poll_result[1] << 8;
	      low_byte = poll_result[0];
	      size_returned = high_byte + mid_byte + low_byte;
	    }
	}

      size_requested = size_returned;
      size_requested = 2500;
      no_of_buffers = size_returned * 3;
      no_of_buffers = no_of_buffers / 2500;
      no_of_buffers = no_of_buffers >> 1;
      /* force 1 buffer at a time to improve accuray, which slow downs search */
      no_of_buffers = 1;

      if (no_of_buffers < 1)
	no_of_buffers = 1;
      else if (no_of_buffers > 10)
	no_of_buffers = 10;
      buffer_count = buffer_count + no_of_buffers;
      size_requested = no_of_buffers * 2500;

      /* Tell the scanner to send the data */
      /* Write: 91 <size_requested> */
      command5_block[1] = (SANE_Byte) (size_requested >> 16);
      command5_block[2] = (SANE_Byte) (size_requested >> 8);
      command5_block[3] = (SANE_Byte) (size_requested & 0xFF);

      cmd_size = 4;
      status = low_usb_bulk_write (devnum, command5_block, &cmd_size);
      if (status != SANE_STATUS_GOOD)
	return SANE_FALSE;
      /* Read it */
      cmd_size = size_requested;
      status = low_usb_bulk_read (devnum, buffer, &cmd_size);
      if (status != SANE_STATUS_GOOD)
	return SANE_FALSE;
      for (i = 0; i < no_of_buffers; i++)
	{
	  buffer_start = buffer + (i * 2500);
	  /* Reverse order of bytes in words of buffer */
	  for (j = 0; j < 2500; j = j + 2)
	    {
	      temp_byte = *(buffer_start + j);
	      *(buffer_start + j) = *(buffer_start + j + 1);
	      *(buffer_start + j + 1) = temp_byte;
	    }
#ifdef DEEP_DEBUG
	  fwrite (buffer + (i * 2500), 2500, 1, img);
#endif
	  if (low_is_home_line (buffer_start))
	    {
	      home_line_count++;
	      if (home_line_count > 7)
		in_home_region = SANE_TRUE;
	    }
	  if (in_home_region)
	    {
	      /* slow down scanning : on purpose backtracking */
	      if (home_line_count)
		sleep (1);
	      free (buffer);
#ifdef DEEP_DEBUG
	      fflush (img);
	      i = ftell (img) / 2500;
	      rewind (img);
	      DBG (2, "sanei_lexmark_low_search_home_bwd: offset=%d\n", i);
	      fprintf (img, "P5\n2500 %03d\n", i);
	      fclose (img);
#endif
	      low_stop_mvmt (devnum);
	      DBG (2,
		   "sanei_lexmark_low_search_home_bwd: in home region, end.\n");
	      return SANE_TRUE;
	    }
	}
    }				/*   end while (buffer_count > buffer_limit); */
  free (buffer);
#ifdef DEEP_DEBUG
  fflush (img);
  i = ftell (img) / 2500;
  rewind (img);
  fprintf (img, "P5\n2500 %03d\n", i);
  fclose (img);
#endif
  low_stop_mvmt (devnum);

  DBG (2, "sanei_lexmark_low_search_home_bwd: end.\n");

  return SANE_FALSE;
}

SANE_Status
low_get_start_loc (SANE_Int resolution, SANE_Int * vert_start,
		     SANE_Int * hor_start, SANE_Int offset,
		     Lexmark_Device * dev)
{
  SANE_Int start_600;

  /* Calculate vertical start distance at 600dpi */
  start_600 = 195 - offset;

  switch (resolution)
    {
    case 75:
      *vert_start = start_600 / 8;
      *hor_start = 0x68;
      break;
    case 150:
      *vert_start = start_600 / 4;
      *hor_start = 0x68;
      break;
    case 300:
      *vert_start = start_600 / 2;
      *hor_start = 0x6a;
      break;
    case 600:
      *vert_start = start_600;
      *hor_start = 0x6b;
      break;
    case 1200:
      *vert_start = start_600 * 2;
      *hor_start = 0x6b;
      break;
    default:
      /* If we're here we have an invalid resolution */
      return SANE_STATUS_INVAL;
    }

  /* maybe left margin could be autodetected */
  if (dev->model.sensor_type != X1100_B2_SENSOR)
    *hor_start = 0x68;

  return SANE_STATUS_GOOD;
}

void
low_set_scan_area (SANE_Int res,
		     SANE_Int tlx,
		     SANE_Int tly,
		     SANE_Int brx,
		     SANE_Int bry,
		     SANE_Int offset,
		     SANE_Bool half_step,
		     SANE_Byte * regs, Lexmark_Device * dev)
{

  SANE_Status status;
  SANE_Int vert_start = 0;
  SANE_Int hor_start = 0;
  SANE_Int vert_end;
  SANE_Int hor_end;

  status = low_get_start_loc (res, &vert_start, &hor_start, offset, dev);

  /* convert pixel height to vertical location coordinates */
  vert_end = vert_start + (bry * res) / 600;
  vert_start += (tly * res) / 600;

  /* scan area size : for A920, 600 color scans are done at 1200 y dpi */
  /* this follow what was found in usb logs                            */
  if (half_step)
    {
      vert_end = vert_end * 2;
      vert_start = vert_start * 2;
    }

  /* set vertical start position registers */
  regs[0x60] = LOBYTE (vert_start);
  regs[0x61] = HIBYTE (vert_start);
  /* set vertical end position registers */
  regs[0x62] = LOBYTE (vert_end);
  regs[0x63] = HIBYTE (vert_end);

  /* convert pixel width to horizontal location coordinates */
  hor_end = hor_start + brx;

  /* set horizontal start position registers */
  hor_start += tlx;
  regs[0x66] = LOBYTE (hor_start);
  regs[0x67] = HIBYTE (hor_start);
  /* set horizontal end position registers */
  regs[0x6c] = LOBYTE (hor_end);
  regs[0x6d] = HIBYTE (hor_end);

  /* Debug */
  DBG (2, "low_set_scan_area: vert_start: %d (tly=%d)\n", vert_start, tly);
  DBG (2, "low_set_scan_area: vert_end: %d\n", vert_end);
  DBG (2, "low_set_scan_area: hor_start: %d\n", hor_start);
  DBG (2, "low_set_scan_area: hor_end: %d\n", hor_end);

}

SANE_Int
sanei_lexmark_low_find_start_line (Lexmark_Device * dev)
{
  /*
     This function scans forward 59 lines, reading 88 bytes per line from the
     middle of the horizontal line: pixel 0xa84 to pixel 0x9d4. It scans with
     the following parameters:
     dir=fwd 
     mode=grayscale
     h.res=300 dpi
     v.res=600 dpi   
     hor. pixels = (0xa84 - 0x9d4)/2 = 0x58 = 88
     vert. pixels = 0x3e - 0x03 = 0x3b = 59
     data = 88x59=5192=0x1448

     It assumes we are in the start dot, or just before it. We are reading
     enough lines at 600dpi to read past the dot. We return the number of
     entirely white lines read consecutively, so we know how far past the
     dot we are.

     To find the number of consecutive white lines we do the following:

     Byte swap the order of the bytes in the buffer.

     Go through the buffer finding low and high values, which are computed by 
     comparing to the weighted average: 
     weighted_average = (lowest value + (highest value - lowest value)/4)
     Low bytes are changed to 0xFF (white), higher of equal bytes are changed 
     to 0x00 (black),so that the buffer only contains white (0xFF) or black 
     (0x00) values.

     Next, we go through the buffer a line (88 bytes) at a time for 59 lines
     to read the entire buffer. For each byte in a line we check if the
     byte is black. If it is we increment the black byte counter.

     After each line we check the black byte counter. If it is greater than 0 
     we increment the black line count and set the white line count to 0. If
     there were no black bytes in the line we set the black line count to 0
     and increment the white line count.

     When all lines have been processed we return the white line count.
   */


  int blackLineCount = 0;
  int whiteLineCount = 0;
  int blackByteCounter = 0;
  unsigned char max_byte = 0;
  unsigned char min_byte = 0xFF;
  unsigned char weighted_average;
  int i, j;
#ifdef DEEP_DEBUG
  FILE *fdbg = NULL;
#endif

  SANE_Byte poll_result[3];
  SANE_Byte *buffer;
  SANE_Byte temp_byte;

  static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 };

  static SANE_Byte command5_block[] = { 0x91, 0x00, 0x14, 0x48 };

  size_t cmd_size;
  SANE_Bool got_line;

  DBG (2, "sanei_lexmark_low_find_start_line:\n");

  /* set up registers */
  switch (dev->model.sensor_type)
    {
    case X1100_B2_SENSOR:
      dev->shadow_regs[0x2c] = 0x0f;
      dev->shadow_regs[0x2d] = 0x51;
      dev->shadow_regs[0x34] = 0x0d;
      dev->shadow_regs[0x35] = 0x0d;
      dev->shadow_regs[0x36] = 0x1d;
      dev->shadow_regs[0x37] = 0x1d;
      dev->shadow_regs[0x38] = 0x29;
      dev->shadow_regs[0x65] = 0x80;
      dev->shadow_regs[0x85] = 0x00;
      dev->shadow_regs[0x93] = 0x06;
      rts88xx_set_gain (dev->shadow_regs, 6, 6, 6);
      break;
    case X1100_2C_SENSOR:
      rts88xx_set_gain (dev->shadow_regs, 10, 10, 10);
      dev->shadow_regs[0x28] = 0xf5;
      dev->shadow_regs[0x29] = 0xf7;
      dev->shadow_regs[0x2a] = 0xf5;
      dev->shadow_regs[0x2b] = 0x17;

      dev->shadow_regs[0x2c] = 0x0d;
      dev->shadow_regs[0x2d] = 0x4f;
      dev->shadow_regs[0x31] = 0x01;
      dev->shadow_regs[0x34] = 0x11;
      dev->shadow_regs[0x35] = 0x11;
      dev->shadow_regs[0x36] = 0x21;
      dev->shadow_regs[0x37] = 0x21;
      dev->shadow_regs[0x38] = 0x31;
      dev->shadow_regs[0x72] = 0x35;
      dev->shadow_regs[0x74] = 0x4e;
      dev->shadow_regs[0x85] = 0x02;
      dev->shadow_regs[0x86] = 0x33;
      dev->shadow_regs[0x87] = 0x0f;
      dev->shadow_regs[0x88] = 0x24;
      dev->shadow_regs[0x91] = 0x19;
      dev->shadow_regs[0x92] = 0x20;
      dev->shadow_regs[0xea] = 0x00;
      dev->shadow_regs[0xeb] = 0x00;
      dev->shadow_regs[0xf3] = 0xf8;
      dev->shadow_regs[0xf4] = 0x7f;
      break;
    case A920_SENSOR:
      dev->shadow_regs[0x2c] = 0x0d;
      dev->shadow_regs[0x2d] = 0x4f;
      dev->shadow_regs[0x34] = 0x11;
      dev->shadow_regs[0x35] = 0x11;
      dev->shadow_regs[0x36] = 0x21;
      dev->shadow_regs[0x37] = 0x21;
      dev->shadow_regs[0x38] = 0x31;
      dev->shadow_regs[0x85] = 0x05;
      dev->shadow_regs[0x88] = 0x44;
      dev->shadow_regs[0x92] = 0x85;
      dev->shadow_regs[0x93] = 0x0e;
      rts88xx_set_gain (dev->shadow_regs, 6, 6, 6);
      break;
    case X1200_SENSOR:
      dev->shadow_regs[0x2c] = 0x01;
      dev->shadow_regs[0x2d] = 0x03;
      dev->shadow_regs[0x34] = 0x0d;
      dev->shadow_regs[0x35] = 0x0d;
      dev->shadow_regs[0x36] = 0x1d;
      dev->shadow_regs[0x37] = 0x1d;
      dev->shadow_regs[0x38] = 0x29;
      dev->shadow_regs[0xea] = 0x00;
      dev->shadow_regs[0xeb] = 0x00;
      dev->shadow_regs[0x40] = 0x80;
      dev->shadow_regs[0x50] = 0x00;
      dev->shadow_regs[0x81] = 0x00;
      dev->shadow_regs[0x82] = 0x00;
      dev->shadow_regs[0x85] = 0x00;
      dev->shadow_regs[0x86] = 0x00;
      dev->shadow_regs[0x87] = 0xff;
      dev->shadow_regs[0x88] = 0x02;
      dev->shadow_regs[0x92] = 0x00;
      rts88xx_set_gain (dev->shadow_regs, 10, 10, 10);
      break;
    case X1200_USB2_SENSOR:
      dev->shadow_regs[0x2c] = 0x01;
      dev->shadow_regs[0x2d] = 0x03;
      dev->shadow_regs[0x34] = 0x0d;
      dev->shadow_regs[0x35] = 0x0d;
      dev->shadow_regs[0x36] = 0x1d;
      dev->shadow_regs[0x37] = 0x1d;
      dev->shadow_regs[0x38] = 0x29;
      dev->shadow_regs[0xea] = 0x00;
      dev->shadow_regs[0xeb] = 0x00;
      rts88xx_set_gain (dev->shadow_regs, 10, 10, 10);
      break;
    }

  /* set offset to a safe value */
  rts88xx_set_offset (dev->shadow_regs, 0x80, 0x80, 0x80);
  /* set grayscale  scan  */
  dev->shadow_regs[0x2f] = 0x21;
  /* set motor resolution divisor */
  dev->shadow_regs[0x39] = 0x01;
  /* set vertical start/end positions */
  dev->shadow_regs[0x60] = 0x03;
  dev->shadow_regs[0x61] = 0x00;
  dev->shadow_regs[0x62] = 0x3e;
  dev->shadow_regs[0x63] = 0x00;
  /* set # of head moves per CIS read */
  rts88xx_set_scan_frequency (dev->shadow_regs, 1);
  /* set horizontal start position */
  dev->shadow_regs[0x66] = 0xd4;	/* 0xf2 for X1200 */
  dev->shadow_regs[0x67] = 0x09;
  /* set horizontal end position */
  dev->shadow_regs[0x6c] = 0x84;	/* 0xa2 for X1200 */
  dev->shadow_regs[0x6d] = 0x0a;
  /* set horizontal resolution */
  dev->shadow_regs[0x79] = 0x40;
  dev->shadow_regs[0x7a] = 0x02;
  /* set for ? */
  /* Motor enable & Coordinate space denominator */
  dev->shadow_regs[0xc3] = 0x81;
  /* set for ? */
  dev->shadow_regs[0xc5] = 0x22;
  /* Movement direction & step size */
  dev->shadow_regs[0xc6] = 0x09;
  /* step size range2 */
  dev->shadow_regs[0xc9] = 0x3b;
  /* set for ? */
  dev->shadow_regs[0xca] = 0x1f;
  dev->shadow_regs[0xe0] = 0xf7;
  dev->shadow_regs[0xe1] = 0x16;
  /* step size range0 */
  dev->shadow_regs[0xe2] = 0x87;
  /* ? */
  dev->shadow_regs[0xe3] = 0x13;
  dev->shadow_regs[0xe4] = 0x1b;
  dev->shadow_regs[0xe5] = 0x16;
  dev->shadow_regs[0xe6] = 0xdc;
  dev->shadow_regs[0xe7] = 0x00;
  dev->shadow_regs[0xe8] = 0x00;
  dev->shadow_regs[0xe9] = 0x1b;
  dev->shadow_regs[0xec] = 0x07;
  dev->shadow_regs[0xef] = 0x03;

  /* Stop the scanner */
  low_stop_mvmt (dev->devnum);

  /* write regs out twice */
  dev->shadow_regs[0x32] = 0x00;
  low_write_all_regs (dev->devnum, dev->shadow_regs);
  dev->shadow_regs[0x32] = 0x40;
  low_write_all_regs (dev->devnum, dev->shadow_regs);

  /* Start Scan */
  rts88xx_commit (dev->devnum, dev->shadow_regs[0x2c]);

  /* Poll the available byte count until not 0 */
  got_line = SANE_FALSE;
  while (!got_line)
    {
      cmd_size = 4;
      low_usb_bulk_write (dev->devnum, command4_block, &cmd_size);
      cmd_size = 0x3;
      low_usb_bulk_read (dev->devnum, poll_result, &cmd_size);
      if (!
	  (poll_result[0] == 0 && poll_result[1] == 0 && poll_result[2] == 0))
	{
	  /* if result != 00 00 00 we got data */
	  got_line = SANE_TRUE;
	}
#ifdef FAKE_USB
      got_line = SANE_TRUE;
#endif
    }


  /* create buffer for scan data */
  buffer = calloc (5192, sizeof (char));

  /* Tell the scanner to send the data */
  /* Write: 91 00 14 48 */
  cmd_size = 4;
  low_usb_bulk_write (dev->devnum, command5_block, &cmd_size);
  /* Read it */
  cmd_size = 0x1448;
  low_usb_bulk_read (dev->devnum, buffer, &cmd_size);

  /* Stop the scanner */
  low_stop_mvmt (dev->devnum);


  /* Reverse order of bytes in words of buffer */
  for (i = 0; i < 5192; i = i + 2)
    {
      temp_byte = *(buffer + i);
      *(buffer + i) = *(buffer + i + 1);
      *(buffer + i + 1) = temp_byte;
    }

#ifdef DEEP_DEBUG
  fdbg = fopen ("find_start.pnm", "wb");
  if (fdbg != NULL)
    {
      fprintf (fdbg, "P5\n%d %d\n255\n", 88, 59);
      fwrite (buffer, 5192, 1, fdbg);
      fclose (fdbg);
    }
#endif

  /* Find the max and the min */
  for (i = 0; i < 5192; i++)
    {
      if (*(buffer + i) > max_byte)
	max_byte = *(buffer + i);
      if (*(buffer + i) < min_byte)
	min_byte = *(buffer + i);
    }

  weighted_average = min_byte + ((max_byte - min_byte) / 4);

  /* Set bytes as black (0x00) or white (0xFF) */
  for (i = 0; i < 5192; i++)
    {
      if (*(buffer + i) > weighted_average)
	*(buffer + i) = 0xFF;
      else
	*(buffer + i) = 0x00;
    }

  /* Go through 59 lines */
  for (j = 0; j < 59; j++)
    {
      blackByteCounter = 0;
      /* Go through 88 bytes per line */
      for (i = 0; i < 88; i++)
	{
	  /* Is byte black? */
	  if (*(buffer + (j * 88) + i) == 0)
	    {
	      blackByteCounter++;
	    }
	}			/* end for line */
      if (blackByteCounter > 0)
	{
	  /* This was a black line */
	  blackLineCount++;
	  whiteLineCount = 0;
	}
      else
	{
	  /* This is a white line */
	  whiteLineCount++;
	  blackLineCount = 0;
	}
    }				/* end for buffer */

  DBG (2, "sanei_lexmark_low_find_start_line: end.\n");
  return whiteLineCount;
}


SANE_Status
sanei_lexmark_low_set_scan_regs (Lexmark_Device * dev, SANE_Int offset,
				   SANE_Bool calibrated)
{
  SANE_Int yres;
  SANE_Bool isColourScan;

  DBG (2, "sanei_lexmark_low_set_scan_regs:\n");
  /* resolution */
  yres = dev->val[OPT_RESOLUTION].w;

  DBG (7, "sanei_lexmark_low_set_scan_regs: yres=%d DPI\n", yres);

  /* colour mode */
  if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
    isColourScan = SANE_TRUE;
  else
    isColourScan = SANE_FALSE;

  /* set up registers */
  switch (dev->model.sensor_type)
    {
    case X1100_B2_SENSOR:
      dev->shadow_regs[0x2c] = 0x0f;
      dev->shadow_regs[0x2d] = 0x51;
      break;
    case X1100_2C_SENSOR:
      dev->shadow_regs[0x2c] = 0x0d;
      dev->shadow_regs[0x2d] = 0x4f;
      break;
    case A920_SENSOR:
      dev->shadow_regs[0x2c] = 0x0d;
      dev->shadow_regs[0x2d] = 0x4f;
      break;
    case X1200_SENSOR:
      dev->shadow_regs[0x2c] = 0x01;
      dev->shadow_regs[0x2d] = 0x03;
      break;
    case X1200_USB2_SENSOR:
      dev->shadow_regs[0x2c] = 0x01;
      dev->shadow_regs[0x2d] = 0x03;
      break;
    }

  low_set_scan_area (yres,
		       dev->val[OPT_TL_X].w,
		       dev->val[OPT_TL_Y].w,
		       dev->val[OPT_BR_X].w,
		       dev->val[OPT_BR_Y].w,
		       offset,
		       dev->model.motor_type == A920_MOTOR && isColourScan
		       && (yres == 600), dev->shadow_regs, dev);

  /* may be we could use a sensor descriptor that would held the max horiz dpi */
  if (dev->val[OPT_RESOLUTION].w < 600)
    dev->shadow_regs[0x7a] = 600 / dev->val[OPT_RESOLUTION].w;
  else
    dev->shadow_regs[0x7a] = 1;

  /* 75dpi x 75dpi */
  if (yres == 75)
    {
      DBG (5, "sanei_lexmark_low_set_scan_regs(): 75 DPI resolution\n");

      if (isColourScan)
	{
	  /* set colour scan */
	  dev->shadow_regs[0x2f] = 0x11;

	  switch (dev->model.sensor_type)
	    {
	    case X1100_B2_SENSOR:
	      dev->shadow_regs[0x34] = 0x05;
	      dev->shadow_regs[0x36] = 0x05;
	      dev->shadow_regs[0x38] = 0x05;

	      dev->shadow_regs[0x80] = 0x0c;
	      dev->shadow_regs[0x81] = 0x0c;
	      dev->shadow_regs[0x82] = 0x09;

	      dev->shadow_regs[0x85] = 0x00;
	      dev->shadow_regs[0x86] = 0x00;
	      dev->shadow_regs[0x87] = 0x00;
	      dev->shadow_regs[0x88] = 0x00;

	      dev->shadow_regs[0x91] = 0x8c;
	      dev->shadow_regs[0x92] = 0x40;
	      dev->shadow_regs[0x93] = 0x06;
	      break;

	    case X1100_2C_SENSOR:
	      dev->shadow_regs[0x34] = 0x03;
	      dev->shadow_regs[0x36] = 0x04;
	      dev->shadow_regs[0x38] = 0x03;

	      dev->shadow_regs[0x80] = 0x00;
	      dev->shadow_regs[0x81] = 0x02;
	      dev->shadow_regs[0x82] = 0x03;

	      dev->shadow_regs[0x85] = 0x20;
	      dev->shadow_regs[0x86] = 0x00;
	      dev->shadow_regs[0x87] = 0x00;
	      dev->shadow_regs[0x88] = 0x00;

	      dev->shadow_regs[0x91] = 0x00;
	      dev->shadow_regs[0x92] = 0x00;
	      dev->shadow_regs[0x93] = 0x06;
	      break;

	    case A920_SENSOR:
	      dev->shadow_regs[0x34] = 0x02;
	      dev->shadow_regs[0x36] = 0x04;
	      dev->shadow_regs[0x38] = 0x03;

	      dev->shadow_regs[0x80] = 0x07;
	      dev->shadow_regs[0x81] = 0x0f;
	      dev->shadow_regs[0x82] = 0x03;

	      dev->shadow_regs[0x85] = 0x05;
	      dev->shadow_regs[0x86] = 0x14;
	      dev->shadow_regs[0x87] = 0x06;
	      dev->shadow_regs[0x88] = 0x44;

	      dev->shadow_regs[0x91] = 0x60;
	      dev->shadow_regs[0x92] = 0x85;
	      dev->shadow_regs[0x93] = 0x0e;
	      break;

	    case X1200_SENSOR:
	      dev->shadow_regs[0x34] = 0x02;
	      dev->shadow_regs[0x36] = 0x03;
	      dev->shadow_regs[0x38] = 0x01;

	      dev->shadow_regs[0x79] = 0x20;

	      dev->shadow_regs[0x80] = 0x08;
	      dev->shadow_regs[0x81] = 0x02;
	      dev->shadow_regs[0x82] = 0x0d;

	      dev->shadow_regs[0x85] = 0x20;
	      dev->shadow_regs[0x86] = 0x1e;
	      dev->shadow_regs[0x87] = 0x39;
	      dev->shadow_regs[0x88] = 0x00;

	      dev->shadow_regs[0x91] = 0x00;
	      /* dev->shadow_regs[0x92] = 0x92; */
	      dev->shadow_regs[0x93] = 0x06;
	      break;

	    case X1200_USB2_SENSOR:
	      dev->shadow_regs[0x34] = 0x04;
	      dev->shadow_regs[0x36] = 0x05;
	      dev->shadow_regs[0x38] = 0x04;

	      dev->shadow_regs[0x80] = 0x01;
	      dev->shadow_regs[0x81] = 0x0a;
	      dev->shadow_regs[0x82] = 0x0b;
	      break;
	    }

	  dev->shadow_regs[0x35] = 0x01;
	  dev->shadow_regs[0x37] = 0x01;

	  /* Motor enable & Coordinate space denominator */
	  dev->shadow_regs[0xc3] = 0x83;
	  /*  ? */
	  dev->shadow_regs[0xc5] = 0x0a;
	  /* bounds of movement range0 */
	  dev->shadow_regs[0xe0] = 0x2b;
	  dev->shadow_regs[0xe1] = 0x0a;
	  /* step size range0 */
	  dev->shadow_regs[0xe2] = 0x7f;
	  /* ? */
	  dev->shadow_regs[0xe3] = 0x01;
	  /* bounds of movement range1 */
	  dev->shadow_regs[0xe4] = 0xbb;
	  dev->shadow_regs[0xe5] = 0x09;
	  /* step size range1 */
	  dev->shadow_regs[0xe6] = 0x0e;
	  /* bounds of movement range2 */
	  dev->shadow_regs[0xe7] = 0x2b;
	  dev->shadow_regs[0xe8] = 0x03;
	  /* step size range2 */
	  dev->shadow_regs[0xe9] = 0x05;
	  /* bounds of movement range3 */
	  dev->shadow_regs[0xea] = 0xa0;
	  dev->shadow_regs[0xeb] = 0x01;
	  /* step size range3 */
	  dev->shadow_regs[0xec] = 0x01;
	  /* step size range4 */
	  dev->shadow_regs[0xef] = 0x01;
	}
      else
	{
	  /* set grayscale  scan  */
	  dev->shadow_regs[0x2f] = 0x21;
	  switch (dev->model.sensor_type)
	    {
	    case X1100_B2_SENSOR:
	      dev->shadow_regs[0x34] = 0x02;
	      dev->shadow_regs[0x35] = 0x02;
	      dev->shadow_regs[0x36] = 0x04;
	      dev->shadow_regs[0x37] = 0x04;
	      dev->shadow_regs[0x38] = 0x06;

	      dev->shadow_regs[0x85] = 0x20;
	      dev->shadow_regs[0x86] = 0x00;
	      dev->shadow_regs[0x87] = 0x00;
	      dev->shadow_regs[0x88] = 0x00;

	      dev->shadow_regs[0x91] = 0x00;
	      dev->shadow_regs[0x92] = 0x00;
	      dev->shadow_regs[0x93] = 0x06;
	      break;
	    case X1100_2C_SENSOR:
	      dev->shadow_regs[0x34] = 0x01;
	      dev->shadow_regs[0x35] = 0x01;
	      dev->shadow_regs[0x36] = 0x02;
	      dev->shadow_regs[0x37] = 0x02;
	      dev->shadow_regs[0x38] = 0x03;	/* these are half of B2 sensor */

	      dev->shadow_regs[0x85] = 0x20;
	      dev->shadow_regs[0x86] = 0x00;
	      dev->shadow_regs[0x87] = 0x00;
	      dev->shadow_regs[0x88] = 0x00;

	      dev->shadow_regs[0x91] = 0x00;
	      dev->shadow_regs[0x92] = 0x00;
	      dev->shadow_regs[0x92] = 0x00;
	      dev->shadow_regs[0x93] = 0x06;
	      break;
	    case A920_SENSOR:
	      dev->shadow_regs[0x34] = 0x01;
	      dev->shadow_regs[0x35] = 0x01;
	      dev->shadow_regs[0x36] = 0x02;
	      dev->shadow_regs[0x37] = 0x02;
	      dev->shadow_regs[0x38] = 0x03;

	      dev->shadow_regs[0x85] = 0x0d;
	      dev->shadow_regs[0x86] = 0x14;
	      dev->shadow_regs[0x87] = 0x06;
	      dev->shadow_regs[0x88] = 0x45;

	      dev->shadow_regs[0x91] = 0x60;
	      dev->shadow_regs[0x92] = 0x8d;
	      dev->shadow_regs[0x93] = 0x0e;
	      break;
	    case X1200_SENSOR:
	      dev->shadow_regs[0x34] = 0x01;
	      dev->shadow_regs[0x35] = 0x01;
	      dev->shadow_regs[0x36] = 0x02;
	      dev->shadow_regs[0x37] = 0x02;
	      dev->shadow_regs[0x38] = 0x02;

	      dev->shadow_regs[0x79] = 0x20;

	      dev->shadow_regs[0x85] = 0x00;
	      dev->shadow_regs[0x86] = 0x00;
	      dev->shadow_regs[0x87] = 0xff;
	      dev->shadow_regs[0x88] = 0x02;

	      dev->shadow_regs[0x92] = 0x00;
	      break;
	    case X1200_USB2_SENSOR:
	      dev->shadow_regs[0x34] = 0x01;
	      dev->shadow_regs[0x35] = 0x01;
	      dev->shadow_regs[0x36] = 0x02;
	      dev->shadow_regs[0x37] = 0x02;
	      dev->shadow_regs[0x38] = 0x02;

	      dev->shadow_regs[0x79] = 0x20;

	      dev->shadow_regs[0x85] = 0x00;
	      dev->shadow_regs[0x86] = 0x00;
	      dev->shadow_regs[0x87] = 0xff;
	      dev->shadow_regs[0x88] = 0x02;

	      dev->shadow_regs[0x92] = 0x00;
	      break;
	    }

	  /* set ? only for colour? */
	  dev->shadow_regs[0x80] = 0x00;
	  dev->shadow_regs[0x81] = 0x00;
	  dev->shadow_regs[0x82] = 0x00;
	  /* Motor enable & Coordinate space denominator */
	  dev->shadow_regs[0xc3] = 0x81;
	  /*  ? */
	  dev->shadow_regs[0xc5] = 0x10;
	  /* bounds of movement range0 */
	  dev->shadow_regs[0xe0] = 0x4d;
	  dev->shadow_regs[0xe1] = 0x1c;
	  /* step size range0 */
	  dev->shadow_regs[0xe2] = 0x71;
	  /* ? */
	  dev->shadow_regs[0xe3] = 0x02;
	  /* bounds of movement range1 */
	  dev->shadow_regs[0xe4] = 0x6d;
	  dev->shadow_regs[0xe5] = 0x15;
	  /* step size range1 */
	  dev->shadow_regs[0xe6] = 0xdc;
	  /* bounds of movement range2 */
	  dev->shadow_regs[0xe7] = 0xad;
	  dev->shadow_regs[0xe8] = 0x07;
	  /* step size range2 */
	  dev->shadow_regs[0xe9] = 0x1b;
	  /* bounds of movement range3 */
	  dev->shadow_regs[0xea] = 0xe1;
	  dev->shadow_regs[0xeb] = 0x03;
	  /* step size range3 */
	  dev->shadow_regs[0xec] = 0x07;
	  /* bounds of movement range4 -only for 75dpi grayscale */
	  dev->shadow_regs[0xed] = 0xc2;
	  dev->shadow_regs[0xee] = 0x02;
	  /* step size range4 */
	  dev->shadow_regs[0xef] = 0x03;
	}

      /* set motor resolution divisor */
      dev->shadow_regs[0x39] = 0x0f;
      /* Movement direction & step size */
      dev->shadow_regs[0xc6] = 0x09;

      dev->shadow_regs[0xc9] = 0x3b;
      dev->shadow_regs[0xca] = 0x01;

      /* set # of head moves per CIS read */
      rts88xx_set_scan_frequency (dev->shadow_regs, 1);
      /* set horizontal resolution */
      if (dev->model.sensor_type != X1200_SENSOR)
	dev->shadow_regs[0x79] = 0x08;
    }

  /* 150dpi x 150dpi */
  if (yres == 150)
    {
      DBG (5, "sanei_lexmark_low_set_scan_regs(): 150 DPI resolution\n");

      if (isColourScan)
	{
	  /* set colour scan */
	  dev->shadow_regs[0x2f] = 0x11;
	  switch (dev->model.sensor_type)
	    {
	    case X1100_B2_SENSOR:
	      dev->shadow_regs[0x34] = 0x0b;
	      dev->shadow_regs[0x36] = 0x0b;
	      dev->shadow_regs[0x38] = 0x0a;

	      dev->shadow_regs[0x80] = 0x05;
	      dev->shadow_regs[0x81] = 0x05;
	      dev->shadow_regs[0x82] = 0x0a;

	      dev->shadow_regs[0x85] = 0x83;
	      dev->shadow_regs[0x86] = 0x7e;
	      dev->shadow_regs[0x87] = 0xad;
	      dev->shadow_regs[0x88] = 0x35;

	      dev->shadow_regs[0x91] = 0xfe;
	      dev->shadow_regs[0x92] = 0xdf;
	      dev->shadow_regs[0x93] = 0x0e;
	      break;
	    case X1100_2C_SENSOR:
	      dev->shadow_regs[0x34] = 0x05;
	      dev->shadow_regs[0x36] = 0x07;
	      dev->shadow_regs[0x38] = 0x05;

	      dev->shadow_regs[0x80] = 0x00;
	      dev->shadow_regs[0x81] = 0x02;
	      dev->shadow_regs[0x82] = 0x06;

	      dev->shadow_regs[0x85] = 0x20;
	      dev->shadow_regs[0x86] = 0x00;
	      dev->shadow_regs[0x87] = 0x00;
	      dev->shadow_regs[0x88] = 0x00;

	      dev->shadow_regs[0x91] = 0x00;
	      dev->shadow_regs[0x92] = 0x00;
	      dev->shadow_regs[0x93] = 0x06;
	      break;
	    case A920_SENSOR:
	      dev->shadow_regs[0x34] = 0x03;
	      dev->shadow_regs[0x36] = 0x08;
	      dev->shadow_regs[0x38] = 0x05;

	      dev->shadow_regs[0x80] = 0x0e;
	      dev->shadow_regs[0x81] = 0x07;
	      dev->shadow_regs[0x82] = 0x02;

	      dev->shadow_regs[0x85] = 0x05;
	      dev->shadow_regs[0x86] = 0x14;
	      dev->shadow_regs[0x87] = 0x06;
	      dev->shadow_regs[0x88] = 0x04;

	      dev->shadow_regs[0x91] = 0xe0;
	      dev->shadow_regs[0x92] = 0x85;
	      dev->shadow_regs[0x93] = 0x0e;
	      break;
	    case X1200_SENSOR:
	      dev->shadow_regs[0x34] = 0x04;
	      dev->shadow_regs[0x36] = 0x05;
	      dev->shadow_regs[0x38] = 0x02;
	      /* data compression
	      dev->shadow_regs[0x40] = 0x90;
	      dev->shadow_regs[0x50] = 0x20; */
	      /* no data compression */
	      dev->shadow_regs[0x40] = 0x80;
	      dev->shadow_regs[0x50] = 0x00;

	      dev->shadow_regs[0x79] = 0x20;

	      dev->shadow_regs[0x80] = 0x00;
	      dev->shadow_regs[0x81] = 0x07;
	      dev->shadow_regs[0x82] = 0x0b;

	      dev->shadow_regs[0x85] = 0x20;
	      dev->shadow_regs[0x86] = 0x1e;
	      dev->shadow_regs[0x87] = 0x39;
	      dev->shadow_regs[0x88] = 0x00;

	      dev->shadow_regs[0x92] = 0x92;

	      break;
	    case X1200_USB2_SENSOR:
	      dev->shadow_regs[0x34] = 0x04;
	      dev->shadow_regs[0x36] = 0x05;
	      dev->shadow_regs[0x38] = 0x02;
	      
	      dev->shadow_regs[0x40] = 0x80;
	      dev->shadow_regs[0x50] = 0x00;

	      dev->shadow_regs[0x79] = 0x20;

	      dev->shadow_regs[0x80] = 0x00;
	      dev->shadow_regs[0x81] = 0x07;
	      dev->shadow_regs[0x82] = 0x0b;

	      dev->shadow_regs[0x85] = 0x20;
	      dev->shadow_regs[0x86] = 0x1e;
	      dev->shadow_regs[0x87] = 0x39;
	      dev->shadow_regs[0x88] = 0x00;

	      dev->shadow_regs[0x92] = 0x92;
	      break;
	    }
	  /* set ? */
	  dev->shadow_regs[0x35] = 0x01;
	  dev->shadow_regs[0x37] = 0x01;
	  /* Motor enable & Coordinate space denominator */
	  dev->shadow_regs[0xc3] = 0x83;
	  /*  ? */
	  dev->shadow_regs[0xc5] = 0x0e;
	  /*  ? */
	  dev->shadow_regs[0xc9] = 0x3a;
	  dev->shadow_regs[0xca] = 0x03;
	  /* bounds of movement range0 */
	  dev->shadow_regs[0xe0] = 0x61;
	  dev->shadow_regs[0xe1] = 0x0a;
	  /* step size range0 */
	  dev->shadow_regs[0xe2] = 0xed;
	  /* ? */
	  dev->shadow_regs[0xe3] = 0x02;
	  /* bounds of movement range1 */
	  dev->shadow_regs[0xe4] = 0x29;
	  dev->shadow_regs[0xe5] = 0x0a;
	  /* step size range1 */
	  dev->shadow_regs[0xe6] = 0x0e;
	  /* bounds of movement range2 */
	  dev->shadow_regs[0xe7] = 0x29;
	  dev->shadow_regs[0xe8] = 0x03;
	  /* step size range2 */
	  dev->shadow_regs[0xe9] = 0x05;
	  /* bounds of movement range3 */
	  dev->shadow_regs[0xea] = 0x00;
	  dev->shadow_regs[0xeb] = 0x00;
	  /* step size range3 */
	  dev->shadow_regs[0xec] = 0x01;
	  /* step size range4 */
	  dev->shadow_regs[0xef] = 0x01;

	}
      else
	{
	  /* set grayscale  scan */
	  dev->shadow_regs[0x2f] = 0x21;
	  switch (dev->model.sensor_type)
	    {
	    case X1100_B2_SENSOR:
	      dev->shadow_regs[0x34] = 0x04;
	      dev->shadow_regs[0x35] = 0x04;
	      dev->shadow_regs[0x36] = 0x07;
	      dev->shadow_regs[0x37] = 0x07;
	      dev->shadow_regs[0x38] = 0x0a;

	      dev->shadow_regs[0x85] = 0x00;
	      dev->shadow_regs[0x86] = 0x00;
	      dev->shadow_regs[0x87] = 0x00;
	      dev->shadow_regs[0x88] = 0x00;

	      dev->shadow_regs[0x91] = 0x00;
	      dev->shadow_regs[0x92] = 0x00;
	      dev->shadow_regs[0x93] = 0x06;
	      break;
	    case X1100_2C_SENSOR:
	      dev->shadow_regs[0x34] = 0x02;
	      dev->shadow_regs[0x35] = 0x02;
	      dev->shadow_regs[0x36] = 0x04;
	      dev->shadow_regs[0x37] = 0x04;
	      dev->shadow_regs[0x38] = 0x05;

	      dev->shadow_regs[0x85] = 0x20;
	      dev->shadow_regs[0x86] = 0x00;
	      dev->shadow_regs[0x87] = 0x00;
	      dev->shadow_regs[0x88] = 0x00;

	      dev->shadow_regs[0x91] = 0x00;
	      dev->shadow_regs[0x92] = 0x00;
	      dev->shadow_regs[0x93] = 0x06;
	      break;
	    case A920_SENSOR:
	      dev->shadow_regs[0x34] = 0x02;
	      dev->shadow_regs[0x35] = 0x02;
	      dev->shadow_regs[0x36] = 0x04;
	      dev->shadow_regs[0x37] = 0x04;
	      dev->shadow_regs[0x38] = 0x05;

	      dev->shadow_regs[0x85] = 0x0d;
	      dev->shadow_regs[0x86] = 0x14;
	      dev->shadow_regs[0x87] = 0x06;
	      dev->shadow_regs[0x88] = 0x45;

	      dev->shadow_regs[0x91] = 0x60;
	      dev->shadow_regs[0x92] = 0x8d;
	      dev->shadow_regs[0x93] = 0x0e;
	      break;
	    case X1200_SENSOR:
	      dev->shadow_regs[0x34] = 0x01;
	      dev->shadow_regs[0x35] = 0x01;
	      dev->shadow_regs[0x36] = 0x02;
	      dev->shadow_regs[0x37] = 0x02;
	      dev->shadow_regs[0x38] = 0x03;

	      /* dev->shadow_regs[0x40] = 0x90;
	      dev->shadow_regs[0x50] = 0x20;*/
	      /* no data compression */
	      dev->shadow_regs[0x40] = 0x80;
	      dev->shadow_regs[0x50] = 0x00;

	      dev->shadow_regs[0x79] = 0x20;

	      dev->shadow_regs[0x85] = 0x00;
	      dev->shadow_regs[0x86] = 0x00;
	      dev->shadow_regs[0x87] = 0xff;
	      dev->shadow_regs[0x88] = 0x02;

	      dev->shadow_regs[0x92] = 0x92;
	      break;
	    case X1200_USB2_SENSOR:
	      dev->shadow_regs[0x34] = 0x01;
	      dev->shadow_regs[0x35] = 0x01;
	      dev->shadow_regs[0x36] = 0x02;
	      dev->shadow_regs[0x37] = 0x02;
	      dev->shadow_regs[0x38] = 0x03;

	      dev->shadow_regs[0x40] = 0x80;
	      dev->shadow_regs[0x50] = 0x00;

	      dev->shadow_regs[0x79] = 0x20;

	      dev->shadow_regs[0x85] = 0x00;
	      dev->shadow_regs[0x86] = 0x00;
	      dev->shadow_regs[0x87] = 0xff;
	      dev->shadow_regs[0x88] = 0x02;

	      dev->shadow_regs[0x92] = 0x92;
	      break;
	    }

	  /* set ? only for colour? */
	  dev->shadow_regs[0x80] = 0x00;
	  dev->shadow_regs[0x81] = 0x00;
	  dev->shadow_regs[0x82] = 0x00;
	  /* Motor enable & Coordinate space denominator */
	  dev->shadow_regs[0xc3] = 0x81;
	  /*  ? */
	  dev->shadow_regs[0xc5] = 0x16;
	  /*  ? */
	  dev->shadow_regs[0xc9] = 0x3b;
	  dev->shadow_regs[0xca] = 0x01;
	  /* bounds of movement range0 */
	  dev->shadow_regs[0xe0] = 0xdd;
	  dev->shadow_regs[0xe1] = 0x18;
	  /* step size range0 */
	  dev->shadow_regs[0xe2] = 0x01;
	  /* ? */
	  dev->shadow_regs[0xe3] = 0x03;
	  /* bounds of movement range1 */
	  dev->shadow_regs[0xe4] = 0x6d;
	  dev->shadow_regs[0xe5] = 0x15;
	  /* step size range1 */
	  dev->shadow_regs[0xe6] = 0xdc;
	  /* bounds of movement range2 */
	  dev->shadow_regs[0xe7] = 0xad;
	  dev->shadow_regs[0xe8] = 0x07;
	  /* step size range2 */
	  dev->shadow_regs[0xe9] = 0x1b;
	  /* bounds of movement range3 */
	  dev->shadow_regs[0xea] = 0xe1;
	  dev->shadow_regs[0xeb] = 0x03;
	  /* step size range3 */
	  dev->shadow_regs[0xec] = 0x07;
	  /* step size range4 */
	  dev->shadow_regs[0xef] = 0x03;
	}

      /* set motor resolution divisor */
      dev->shadow_regs[0x39] = 0x07;
      /* Movement direction & step size */
      dev->shadow_regs[0xc6] = 0x09;

      /* set # of head moves per CIS read */
      rts88xx_set_scan_frequency (dev->shadow_regs, 1);

      /* hum, horizontal resolution different for X1200 ? */
      if (dev->model.sensor_type != X1200_SENSOR)
	dev->shadow_regs[0x79] = 0x20;
    }

  /*300dpi x 300dpi */
  if (yres == 300)
    {
      DBG (5, "sanei_lexmark_low_set_scan_regs(): 300 DPI resolution\n");

      if (isColourScan)
	{
	  /* set colour scan */
	  dev->shadow_regs[0x2f] = 0x11;

	  switch (dev->model.sensor_type)
	    {
	    case X1100_B2_SENSOR:
	      dev->shadow_regs[0x34] = 0x15;
	      dev->shadow_regs[0x36] = 0x15;
	      dev->shadow_regs[0x38] = 0x14;

	      dev->shadow_regs[0x80] = 0x0a;
	      dev->shadow_regs[0x81] = 0x0a;
	      dev->shadow_regs[0x82] = 0x06;

	      dev->shadow_regs[0x85] = 0x83;
	      dev->shadow_regs[0x86] = 0x7e;
	      dev->shadow_regs[0x87] = 0xad;
	      dev->shadow_regs[0x88] = 0x35;

	      dev->shadow_regs[0x91] = 0xfe;
	      dev->shadow_regs[0x92] = 0xdf;
	      dev->shadow_regs[0x93] = 0x0e;
	      break;
	    case X1100_2C_SENSOR:
	      dev->shadow_regs[0x34] = 0x08;
	      dev->shadow_regs[0x36] = 0x0d;
	      dev->shadow_regs[0x38] = 0x09;

	      dev->shadow_regs[0x80] = 0x0e;
	      dev->shadow_regs[0x81] = 0x04;
	      dev->shadow_regs[0x82] = 0x0a;

	      dev->shadow_regs[0x85] = 0x20;
	      dev->shadow_regs[0x86] = 0x00;
	      dev->shadow_regs[0x87] = 0x00;
	      dev->shadow_regs[0x88] = 0x00;

	      dev->shadow_regs[0x91] = 0x00;
	      dev->shadow_regs[0x92] = 0x00;
	      dev->shadow_regs[0x93] = 0x06;
	      break;
	    case A920_SENSOR:
	      dev->shadow_regs[0x34] = 0x06;
	      dev->shadow_regs[0x36] = 0x10;
	      dev->shadow_regs[0x38] = 0x09;

	      dev->shadow_regs[0x80] = 0x0c;
	      dev->shadow_regs[0x81] = 0x02;
	      dev->shadow_regs[0x82] = 0x04;

	      dev->shadow_regs[0x85] = 0x05;
	      dev->shadow_regs[0x86] = 0x14;
	      dev->shadow_regs[0x87] = 0x06;
	      dev->shadow_regs[0x88] = 0x04;

	      dev->shadow_regs[0x91] = 0xe0;
	      dev->shadow_regs[0x92] = 0x85;
	      dev->shadow_regs[0x93] = 0x0e;
	      break;
	    case X1200_SENSOR:
	      dev->shadow_regs[0x34] = 0x07;
	      dev->shadow_regs[0x36] = 0x09;
	      dev->shadow_regs[0x38] = 0x04;

	      /* data compression 
	      dev->shadow_regs[0x40] = 0x90;
	      dev->shadow_regs[0x50] = 0x20; */
	      /* no data compression */
	      dev->shadow_regs[0x40] = 0x80;
	      dev->shadow_regs[0x50] = 0x00;

	      dev->shadow_regs[0x80] = 0x00;
	      dev->shadow_regs[0x81] = 0x0e;
	      dev->shadow_regs[0x82] = 0x06;
	      break;
	    case X1200_USB2_SENSOR:
	      dev->shadow_regs[0x34] = 0x07;
	      dev->shadow_regs[0x36] = 0x09;
	      dev->shadow_regs[0x38] = 0x04;

	      dev->shadow_regs[0x40] = 0x80;
	      dev->shadow_regs[0x50] = 0x00;

	      dev->shadow_regs[0x80] = 0x00;
	      dev->shadow_regs[0x81] = 0x0e;
	      dev->shadow_regs[0x82] = 0x06;
	      break;
	    }

	  /* set ? */
	  dev->shadow_regs[0x35] = 0x01;
	  dev->shadow_regs[0x37] = 0x01;
	  /* set motor resolution divisor */
	  dev->shadow_regs[0x39] = 0x03;
	  /* set ? */
	  /* Motor enable & Coordinate space denominator */
	  dev->shadow_regs[0xc3] = 0x83;
	  /*  ? */
	  dev->shadow_regs[0xc5] = 0x17;
	  /* Movement direction & step size */
	  dev->shadow_regs[0xc6] = 0x09;
	  /*  ? */
	  dev->shadow_regs[0xc9] = 0x3a;
	  dev->shadow_regs[0xca] = 0x0a;
	  /* bounds of movement range0 */
	  dev->shadow_regs[0xe0] = 0x75;
	  dev->shadow_regs[0xe1] = 0x0a;
	  /* step size range0 */
	  dev->shadow_regs[0xe2] = 0xdd;
	  /* ? */
	  dev->shadow_regs[0xe3] = 0x05;
	  /* bounds of movement range1 */
	  dev->shadow_regs[0xe4] = 0x59;
	  dev->shadow_regs[0xe5] = 0x0a;
	  /* step size range1 */
	  dev->shadow_regs[0xe6] = 0x0e;
	  /* bounds of movement range2 */
	  dev->shadow_regs[0xe7] = 0x00;
	  dev->shadow_regs[0xe8] = 0x00;
	  /* step size range2 */
	  dev->shadow_regs[0xe9] = 0x05;
	  /* bounds of movement range3 */
	  dev->shadow_regs[0xea] = 0x00;
	  dev->shadow_regs[0xeb] = 0x00;
	  /* step size range3 */
	  dev->shadow_regs[0xec] = 0x01;
	  /* step size range4 */
	  dev->shadow_regs[0xef] = 0x01;
	}
      else
	{
	  /* set grayscale  scan */
	  dev->shadow_regs[0x2f] = 0x21;
	  /* set ? */
	  switch (dev->model.sensor_type)
	    {
	    case X1100_B2_SENSOR:
	      dev->shadow_regs[0x34] = 0x08;
	      dev->shadow_regs[0x35] = 0x08;
	      dev->shadow_regs[0x36] = 0x0f;
	      dev->shadow_regs[0x37] = 0x0f;
	      dev->shadow_regs[0x38] = 0x16;

	      dev->shadow_regs[0x85] = 0x00;
	      dev->shadow_regs[0x86] = 0x00;
	      dev->shadow_regs[0x87] = 0x00;
	      dev->shadow_regs[0x88] = 0x00;

	      dev->shadow_regs[0x91] = 0x00;
	      dev->shadow_regs[0x92] = 0x00;
	      dev->shadow_regs[0x93] = 0x06;
	      break;
	    case X1100_2C_SENSOR:
	      dev->shadow_regs[0x34] = 0x04;
	      dev->shadow_regs[0x35] = 0x04;
	      dev->shadow_regs[0x36] = 0x07;
	      dev->shadow_regs[0x37] = 0x07;
	      dev->shadow_regs[0x38] = 0x0a;

	      dev->shadow_regs[0x85] = 0x20;
	      dev->shadow_regs[0x86] = 0x00;
	      dev->shadow_regs[0x87] = 0x00;
	      dev->shadow_regs[0x88] = 0x00;

	      dev->shadow_regs[0x91] = 0x00;
	      dev->shadow_regs[0x92] = 0x00;
	      dev->shadow_regs[0x93] = 0x06;
	      break;
	    case A920_SENSOR:
	      dev->shadow_regs[0x34] = 0x03;
	      dev->shadow_regs[0x35] = 0x03;
	      dev->shadow_regs[0x36] = 0x06;
	      dev->shadow_regs[0x37] = 0x06;
	      dev->shadow_regs[0x38] = 0x09;

	      dev->shadow_regs[0x85] = 0x05;
	      dev->shadow_regs[0x86] = 0x14;
	      dev->shadow_regs[0x87] = 0x06;
	      dev->shadow_regs[0x88] = 0x04;

	      dev->shadow_regs[0x91] = 0xe0;
	      dev->shadow_regs[0x92] = 0x85;
	      dev->shadow_regs[0x93] = 0x0e;
	      break;
	    case X1200_SENSOR:
	      dev->shadow_regs[0x34] = 0x02;
	      dev->shadow_regs[0x35] = 0x02;
	      dev->shadow_regs[0x36] = 0x04;
	      dev->shadow_regs[0x37] = 0x04;
	      dev->shadow_regs[0x38] = 0x06;
	      break;
	    case X1200_USB2_SENSOR:
	      dev->shadow_regs[0x34] = 0x02;
	      dev->shadow_regs[0x35] = 0x02;
	      dev->shadow_regs[0x36] = 0x04;
	      dev->shadow_regs[0x37] = 0x04;
	      dev->shadow_regs[0x38] = 0x06;
	      break;
	    }

	  /* set motor resolution divisor */
	  dev->shadow_regs[0x39] = 0x03;
	  /* set ? only for colour? */
	  dev->shadow_regs[0x80] = 0x00;
	  dev->shadow_regs[0x81] = 0x00;
	  dev->shadow_regs[0x82] = 0x00;
	  /* Motor enable & Coordinate space denominator */
	  dev->shadow_regs[0xc3] = 0x81;
	  /*  ? */
	  dev->shadow_regs[0xc5] = 0x19;
	  /* Movement direction & step size */
	  dev->shadow_regs[0xc6] = 0x09;
	  /*  ? */
	  dev->shadow_regs[0xc9] = 0x3a;
	  dev->shadow_regs[0xca] = 0x08;
	  /* bounds of movement range0 */
	  dev->shadow_regs[0xe0] = 0xe3;
	  dev->shadow_regs[0xe1] = 0x18;
	  /* step size range0 */
	  dev->shadow_regs[0xe2] = 0x03;
	  /* ? */
	  dev->shadow_regs[0xe3] = 0x06;
	  /* bounds of movement range1 */
	  dev->shadow_regs[0xe4] = 0x2b;
	  dev->shadow_regs[0xe5] = 0x17;
	  /* step size range1 */
	  dev->shadow_regs[0xe6] = 0xdc;
	  /* bounds of movement range2 */
	  dev->shadow_regs[0xe7] = 0xb3;
	  dev->shadow_regs[0xe8] = 0x07;
	  /* step size range2 */
	  dev->shadow_regs[0xe9] = 0x1b;
	  /* bounds of movement range3 */
	  dev->shadow_regs[0xea] = 0x00;
	  dev->shadow_regs[0xeb] = 0x00;
	  /* step size range3 */
	  dev->shadow_regs[0xec] = 0x07;
	  /* step size range4 */
	  dev->shadow_regs[0xef] = 0x03;
	}

      /* set # of head moves per CIS read */
      rts88xx_set_scan_frequency (dev->shadow_regs, 1);
      /* set horizontal resolution */
      dev->shadow_regs[0x79] = 0x20;
    }

  /* 600dpi x 600dpi */
  if (yres == 600)
    {
      DBG (5, "sanei_lexmark_low_set_scan_regs(): 600 DPI resolution\n");

      if (isColourScan)
	{
	  /* set colour scan */
	  dev->shadow_regs[0x2f] = 0x11;
	  /* set ? */
	  switch (dev->model.sensor_type)
	    {
	    case X1100_B2_SENSOR:
	      dev->shadow_regs[0x34] = 0x15;
	      dev->shadow_regs[0x36] = 0x15;
	      dev->shadow_regs[0x38] = 0x14;

	      dev->shadow_regs[0x80] = 0x02;
	      dev->shadow_regs[0x81] = 0x02;
	      dev->shadow_regs[0x82] = 0x08;

	      dev->shadow_regs[0x85] = 0x83;
	      dev->shadow_regs[0x86] = 0x7e;
	      dev->shadow_regs[0x87] = 0xad;
	      dev->shadow_regs[0x88] = 0x35;

	      dev->shadow_regs[0x91] = 0xfe;
	      dev->shadow_regs[0x92] = 0xdf;
	      dev->shadow_regs[0x93] = 0x0e;
	      break;
	    case X1100_2C_SENSOR:
	      dev->shadow_regs[0x34] = 0x08;
	      dev->shadow_regs[0x36] = 0x0d;
	      dev->shadow_regs[0x38] = 0x09;

	      dev->shadow_regs[0x80] = 0x0e;
	      dev->shadow_regs[0x81] = 0x02;
	      dev->shadow_regs[0x82] = 0x0a;

	      dev->shadow_regs[0x85] = 0x20;
	      dev->shadow_regs[0x86] = 0x00;
	      dev->shadow_regs[0x87] = 0x00;
	      dev->shadow_regs[0x88] = 0x00;

	      dev->shadow_regs[0x91] = 0x00;
	      dev->shadow_regs[0x92] = 0x00;
	      dev->shadow_regs[0x93] = 0x06;
	      break;
	    case A920_SENSOR:
	      dev->shadow_regs[0x34] = 0x06;
	      dev->shadow_regs[0x36] = 0x0f;
	      dev->shadow_regs[0x38] = 0x09;

	      dev->shadow_regs[0x79] = 0x40;

	      dev->shadow_regs[0x80] = 0x0e;
	      dev->shadow_regs[0x81] = 0x0e;
	      dev->shadow_regs[0x82] = 0x00;

	      dev->shadow_regs[0x85] = 0x05;
	      dev->shadow_regs[0x86] = 0x14;
	      dev->shadow_regs[0x87] = 0x06;
	      dev->shadow_regs[0x88] = 0x04;

	      dev->shadow_regs[0x91] = 0x60;
	      dev->shadow_regs[0x92] = 0x85;
	      dev->shadow_regs[0x93] = 0x0e;
	      break;
	    case X1200_SENSOR:
	      dev->shadow_regs[0x34] = 0x07;
	      dev->shadow_regs[0x36] = 0x0a;
	      dev->shadow_regs[0x38] = 0x04;

	      /* data compression 
	      dev->shadow_regs[0x40] = 0x90;
	      dev->shadow_regs[0x50] = 0x20; */

	      /* no data compression */
	      dev->shadow_regs[0x40] = 0x80;
	      dev->shadow_regs[0x50] = 0x00;

	      dev->shadow_regs[0x80] = 0x02;
	      dev->shadow_regs[0x81] = 0x00;
	      dev->shadow_regs[0x82] = 0x06;
	      break;
	    case X1200_USB2_SENSOR:
	      dev->shadow_regs[0x34] = 0x0d;
	      dev->shadow_regs[0x36] = 0x13;
	      dev->shadow_regs[0x38] = 0x10;

	      dev->shadow_regs[0x80] = 0x04;
	      dev->shadow_regs[0x81] = 0x0e;
	      dev->shadow_regs[0x82] = 0x08;

	      dev->shadow_regs[0x85] = 0x02;
	      dev->shadow_regs[0x86] = 0x3b;
	      dev->shadow_regs[0x87] = 0x0f;
	      dev->shadow_regs[0x88] = 0x24;

	      dev->shadow_regs[0x91] = 0x19;
	      dev->shadow_regs[0x92] = 0x30;
	      dev->shadow_regs[0x93] = 0x0e;
	      dev->shadow_regs[0xc5] = 0x17;
	      dev->shadow_regs[0xc6] = 0x09;
	      dev->shadow_regs[0xca] = 0x0a;
	      break;
	    }
	  dev->shadow_regs[0x35] = 0x01;
	  dev->shadow_regs[0x37] = 0x01;

	  /* set motor resolution divisor */
	  dev->shadow_regs[0x39] = 0x03;
	  /* set # of head moves per CIS read */
	  rts88xx_set_scan_frequency (dev->shadow_regs, 2);
	  /* Motor enable & Coordinate space denominator */
	  dev->shadow_regs[0xc3] = 0x86;
	  /*  ? */
	  dev->shadow_regs[0xc5] = 0x27;
	  /* Movement direction & step size */
	  dev->shadow_regs[0xc6] = 0x0c;
	  /*  ? */
	  dev->shadow_regs[0xc9] = 0x3a;
	  dev->shadow_regs[0xca] = 0x1a;
	  /* bounds of movement range0 */
	  dev->shadow_regs[0xe0] = 0x57;
	  dev->shadow_regs[0xe1] = 0x0a;
	  /* step size range0 */
	  dev->shadow_regs[0xe2] = 0xbf;
	  /* ? */
	  dev->shadow_regs[0xe3] = 0x05;
	  /* bounds of movement range1 */
	  dev->shadow_regs[0xe4] = 0x3b;
	  dev->shadow_regs[0xe5] = 0x0a;
	  /* step size range1 */
	  dev->shadow_regs[0xe6] = 0x0e;
	  /* bounds of movement range2 */
	  dev->shadow_regs[0xe7] = 0x00;
	  dev->shadow_regs[0xe8] = 0x00;
	  /* step size range2 */
	  dev->shadow_regs[0xe9] = 0x05;
	  /* bounds of movement range3 */
	  dev->shadow_regs[0xea] = 0x00;
	  dev->shadow_regs[0xeb] = 0x00;
	  /* step size range3 */
	  dev->shadow_regs[0xec] = 0x01;
	  /* step size range4 */
	  dev->shadow_regs[0xef] = 0x01;
	}
      else
	{
	  /* set grayscale  scan */
	  dev->shadow_regs[0x2f] = 0x21;
	  switch (dev->model.sensor_type)
	    {
	    case X1100_B2_SENSOR:
	      dev->shadow_regs[0x34] = 0x11;
	      dev->shadow_regs[0x35] = 0x11;
	      dev->shadow_regs[0x36] = 0x21;
	      dev->shadow_regs[0x37] = 0x21;
	      dev->shadow_regs[0x38] = 0x31;

	      dev->shadow_regs[0x85] = 0x00;
	      dev->shadow_regs[0x86] = 0x00;
	      dev->shadow_regs[0x87] = 0x00;
	      dev->shadow_regs[0x88] = 0x00;

	      dev->shadow_regs[0x91] = 0x00;
	      dev->shadow_regs[0x92] = 0x00;
	      dev->shadow_regs[0x93] = 0x06;
	      break;
	    case X1100_2C_SENSOR:
	      dev->shadow_regs[0x34] = 0x07;
	      dev->shadow_regs[0x35] = 0x07;
	      dev->shadow_regs[0x36] = 0x0d;
	      dev->shadow_regs[0x37] = 0x0d;
	      dev->shadow_regs[0x38] = 0x13;

	      dev->shadow_regs[0x85] = 0x20;
	      dev->shadow_regs[0x86] = 0x00;
	      dev->shadow_regs[0x87] = 0x00;
	      dev->shadow_regs[0x88] = 0x00;

	      dev->shadow_regs[0x91] = 0x00;
	      dev->shadow_regs[0x92] = 0x00;
	      dev->shadow_regs[0x93] = 0x06;
	      break;
	    case A920_SENSOR:
	      dev->shadow_regs[0x34] = 0x05;
	      dev->shadow_regs[0x35] = 0x05;
	      dev->shadow_regs[0x36] = 0x0b;
	      dev->shadow_regs[0x37] = 0x0b;
	      dev->shadow_regs[0x38] = 0x11;

	      dev->shadow_regs[0x85] = 0x05;
	      dev->shadow_regs[0x86] = 0x14;
	      dev->shadow_regs[0x87] = 0x06;
	      dev->shadow_regs[0x88] = 0x04;

	      dev->shadow_regs[0x91] = 0xe0;
	      dev->shadow_regs[0x92] = 0x85;
	      dev->shadow_regs[0x93] = 0x0e;
	      break;
	    case X1200_SENSOR:
	      dev->shadow_regs[0x34] = 0x03;
	      dev->shadow_regs[0x35] = 0x03;
	      dev->shadow_regs[0x36] = 0x07;
	      dev->shadow_regs[0x37] = 0x07;
	      dev->shadow_regs[0x38] = 0x0b;

	      /* data compression 
	      dev->shadow_regs[0x40] = 0x90; 
	      dev->shadow_regs[0x50] = 0x20; */
	      /* no data compression */
	      dev->shadow_regs[0x40] = 0x80;
	      dev->shadow_regs[0x50] = 0x00;

	      dev->shadow_regs[0x85] = 0x00;
	      dev->shadow_regs[0x86] = 0x00;
	      dev->shadow_regs[0x87] = 0xff;
	      dev->shadow_regs[0x88] = 0x02;

	      dev->shadow_regs[0x92] = 0x00;

	      break;
	    case X1200_USB2_SENSOR:
	      dev->shadow_regs[0x34] = 0x03;
	      dev->shadow_regs[0x35] = 0x03;
	      dev->shadow_regs[0x36] = 0x07;
	      dev->shadow_regs[0x37] = 0x07;
	      dev->shadow_regs[0x38] = 0x0b;

	      dev->shadow_regs[0x40] = 0x80;
	      dev->shadow_regs[0x50] = 0x00;

	      dev->shadow_regs[0x85] = 0x00;
	      dev->shadow_regs[0x86] = 0x00;
	      dev->shadow_regs[0x87] = 0xff;
	      dev->shadow_regs[0x88] = 0x02;

	      dev->shadow_regs[0x92] = 0x00;
	      break;
	    }
	  /* set motor resolution divisor */
	  dev->shadow_regs[0x39] = 0x01;
	  /* set # of head moves per CIS read */
	  rts88xx_set_scan_frequency (dev->shadow_regs, 1);

	  /* set ? only for colour? */
	  dev->shadow_regs[0x80] = 0x00;
	  dev->shadow_regs[0x81] = 0x00;
	  dev->shadow_regs[0x82] = 0x00;
	  /* Motor enable & Coordinate space denominator */
	  dev->shadow_regs[0xc3] = 0x81;
	  /*  ? */
	  dev->shadow_regs[0xc5] = 0x22;
	  /* Movement direction & step size */
	  dev->shadow_regs[0xc6] = 0x09;
	  /*  ? */
	  dev->shadow_regs[0xc9] = 0x3b;
	  dev->shadow_regs[0xca] = 0x1f;
	  /* bounds of movement range0 */
	  dev->shadow_regs[0xe0] = 0xf7;
	  dev->shadow_regs[0xe1] = 0x16;
	  /* step size range0 */
	  dev->shadow_regs[0xe2] = 0x87;
	  /* ? */
	  dev->shadow_regs[0xe3] = 0x13;
	  /* bounds of movement range1 */
	  dev->shadow_regs[0xe4] = 0x1b;
	  dev->shadow_regs[0xe5] = 0x16;
	  /* step size range1 */
	  dev->shadow_regs[0xe6] = 0xdc;
	  /* bounds of movement range2 */
	  dev->shadow_regs[0xe7] = 0x00;
	  dev->shadow_regs[0xe8] = 0x00;
	  /* step size range2 */
	  dev->shadow_regs[0xe9] = 0x1b;
	  /* bounds of movement range3 */
	  dev->shadow_regs[0xea] = 0x00;
	  dev->shadow_regs[0xeb] = 0x00;
	  /* step size range3 */
	  dev->shadow_regs[0xec] = 0x07;
	  /* step size range4 */
	  dev->shadow_regs[0xef] = 0x03;
	}

      /* set horizontal resolution */
      dev->shadow_regs[0x79] = 0x40;
    }
  /*600dpi x 1200dpi */
  if (yres == 1200)
    {
      DBG (5, "sanei_lexmark_low_set_scan_regs(): 1200 DPI resolution\n");

      if (isColourScan)
	{
	  /* set colour scan */
	  dev->shadow_regs[0x2f] = 0x11;
	  /* set motor resolution divisor */
	  dev->shadow_regs[0x39] = 0x01;
	  /* set # of head moves per CIS read */
	  rts88xx_set_scan_frequency (dev->shadow_regs, 2);

	  if (dev->model.sensor_type == X1100_B2_SENSOR)
	    {
	      /* set ? */
	      dev->shadow_regs[0x34] = 0x29;
	      dev->shadow_regs[0x36] = 0x29;
	      dev->shadow_regs[0x38] = 0x28;
	      /* set ? */
	      dev->shadow_regs[0x80] = 0x04;
	      dev->shadow_regs[0x81] = 0x04;
	      dev->shadow_regs[0x82] = 0x08;
	      dev->shadow_regs[0x85] = 0x83;
	      dev->shadow_regs[0x86] = 0x7e;
	      dev->shadow_regs[0x87] = 0xad;
	      dev->shadow_regs[0x88] = 0x35;
	      dev->shadow_regs[0x91] = 0xfe;
	      dev->shadow_regs[0x92] = 0xdf;
	    }
	  else
	    {			/* A920 case */
	      dev->shadow_regs[0x34] = 0x0c;
	      dev->shadow_regs[0x36] = 0x1e;
	      dev->shadow_regs[0x38] = 0x10;

	      dev->shadow_regs[0x80] = 0x0c;
	      dev->shadow_regs[0x81] = 0x08;
	      dev->shadow_regs[0x82] = 0x0c;

	      dev->shadow_regs[0x85] = 0x05;
	      dev->shadow_regs[0x86] = 0x14;
	      dev->shadow_regs[0x87] = 0x06;
	      dev->shadow_regs[0x88] = 0x04;

	      dev->shadow_regs[0x91] = 0x60;
	      dev->shadow_regs[0x92] = 0x85;
	    }

	  dev->shadow_regs[0x35] = 0x01;
	  dev->shadow_regs[0x37] = 0x01;
	  /* set motor resolution divisor */
	  dev->shadow_regs[0x39] = 0x01;
	  dev->shadow_regs[0x93] = 0x0e;

	  /* Motor enable & Coordinate space denominator */
	  dev->shadow_regs[0xc3] = 0x86;
	  /*  ? */
	  dev->shadow_regs[0xc5] = 0x41;
	  /* Movement direction & step size */
	  dev->shadow_regs[0xc6] = 0x0c;
	  /*  ? */
	  dev->shadow_regs[0xc9] = 0x3a;
	  dev->shadow_regs[0xca] = 0x40;
	  /* bounds of movement range0 */
	  dev->shadow_regs[0xe0] = 0x00;
	  dev->shadow_regs[0xe1] = 0x00;
	  /* step size range0 */
	  dev->shadow_regs[0xe2] = 0x85;
	  /* ? */
	  dev->shadow_regs[0xe3] = 0x0b;
	  /* bounds of movement range1 */
	  dev->shadow_regs[0xe4] = 0x00;
	  dev->shadow_regs[0xe5] = 0x00;
	  /* step size range1 */
	  dev->shadow_regs[0xe6] = 0x0e;
	  /* bounds of movement range2 */
	  dev->shadow_regs[0xe7] = 0x00;
	  dev->shadow_regs[0xe8] = 0x00;
	  /* step size range2 */
	  dev->shadow_regs[0xe9] = 0x05;
	  /* bounds of movement range3 */
	  dev->shadow_regs[0xea] = 0x00;
	  dev->shadow_regs[0xeb] = 0x00;
	  /* step size range3 */
	  dev->shadow_regs[0xec] = 0x01;
	  /* step size range4 */
	  dev->shadow_regs[0xef] = 0x01;
	}
      else
	{
	  /* set grayscale  scan */
	  dev->shadow_regs[0x2f] = 0x21;
	  /* set ? */
	  dev->shadow_regs[0x34] = 0x22;
	  dev->shadow_regs[0x35] = 0x22;
	  dev->shadow_regs[0x36] = 0x42;
	  dev->shadow_regs[0x37] = 0x42;
	  dev->shadow_regs[0x38] = 0x62;
	  /* set motor resolution divisor */
	  dev->shadow_regs[0x39] = 0x01;
	  /* set # of head moves per CIS read */
	  rts88xx_set_scan_frequency (dev->shadow_regs, 0);

	  /* set ? only for colour? */
	  dev->shadow_regs[0x80] = 0x00;
	  dev->shadow_regs[0x81] = 0x00;
	  dev->shadow_regs[0x82] = 0x00;
	  dev->shadow_regs[0x85] = 0x00;
	  dev->shadow_regs[0x86] = 0x00;
	  dev->shadow_regs[0x87] = 0x00;
	  dev->shadow_regs[0x88] = 0x00;
	  dev->shadow_regs[0x91] = 0x00;
	  dev->shadow_regs[0x92] = 0x00;
	  dev->shadow_regs[0x93] = 0x06;
	  /* Motor enable & Coordinate space denominator */
	  dev->shadow_regs[0xc3] = 0x81;
	  /*  ? */
	  dev->shadow_regs[0xc5] = 0x41;
	  /* Movement direction & step size */
	  dev->shadow_regs[0xc6] = 0x09;
	  /*  ? */
	  dev->shadow_regs[0xc9] = 0x3a;
	  dev->shadow_regs[0xca] = 0x40;
	  /* bounds of movement range0 */
	  dev->shadow_regs[0xe0] = 0x00;
	  dev->shadow_regs[0xe1] = 0x00;
	  /* step size range0 */
	  dev->shadow_regs[0xe2] = 0xc7;
	  /* ? */
	  dev->shadow_regs[0xe3] = 0x29;
	  /* bounds of movement range1 */
	  dev->shadow_regs[0xe4] = 0x00;
	  dev->shadow_regs[0xe5] = 0x00;
	  /* step size range1 */
	  dev->shadow_regs[0xe6] = 0xdc;
	  /* bounds of movement range2 */
	  dev->shadow_regs[0xe7] = 0x00;
	  dev->shadow_regs[0xe8] = 0x00;
	  /* step size range2 */
	  dev->shadow_regs[0xe9] = 0x1b;
	  /* bounds of movement range3 */
	  dev->shadow_regs[0xea] = 0x00;
	  dev->shadow_regs[0xeb] = 0x00;
	  /* step size range3 */
	  dev->shadow_regs[0xec] = 0x07;
	  /* step size range4 */
	  dev->shadow_regs[0xef] = 0x03;
	}

      /* set horizontal resolution */
      dev->shadow_regs[0x79] = 0x40;
    }

  /* is calibration has been done, we override fixed settings with detected ones */
  if (calibrated)
    {
      /* override fixed values with ones from calibration */
      if (rts88xx_is_color (dev->shadow_regs))
	{
	  rts88xx_set_offset (dev->shadow_regs,
			      dev->offset.red,
			      dev->offset.green, dev->offset.blue);
	  rts88xx_set_gain (dev->shadow_regs,
			    dev->gain.red, dev->gain.green, dev->gain.blue);
	}
      else
	{
	  rts88xx_set_offset (dev->shadow_regs,
			      dev->offset.gray,
			      dev->offset.gray, dev->offset.gray);
	  rts88xx_set_gain (dev->shadow_regs,
			    dev->gain.gray, dev->gain.gray, dev->gain.gray);
	}
    }
  DBG (2, "sanei_lexmark_low_set_scan_regs: end.\n");
  return SANE_STATUS_GOOD;
}

SANE_Status
sanei_lexmark_low_start_scan (Lexmark_Device * dev)
{
  SANE_Int devnum;

  static SANE_Byte command4_block[] = { 0x90, 0x00, 0x00, 0x03 };

  static SANE_Byte command5_block[] = { 0x80, 0xb3, 0x00, 0x01 };

  SANE_Byte poll_result[3];
  SANE_Byte read_result;
  SANE_Bool scan_head_moving;
  size_t size;

  devnum = dev->devnum;

  dev->transfer_buffer = NULL;	/* No data xferred yet */
  DBG (2, "sanei_lexmark_low_start_scan:\n");


  /* 80 b3 00 01  - poll for scanner not moving */
  scan_head_moving = SANE_TRUE;
  while (scan_head_moving)
    {
      size = 4;
      low_usb_bulk_write (devnum, command5_block, &size);
      size = 0x1;
      low_usb_bulk_read (devnum, &read_result, &size);
      if ((read_result & 0xF) == 0x0)
	{
	  scan_head_moving = SANE_FALSE;
	}
      /* F.O. Should be a timeout here so we don't hang if something breaks */
#ifdef FAKE_USB
      scan_head_moving = SANE_FALSE;
#endif
    }

  /* Clear C6 */
  low_clr_c6 (devnum);
  /* Stop the scanner */
  low_stop_mvmt (devnum);

  /*Set regs x2 */
  dev->shadow_regs[0x32] = 0x00;
  low_write_all_regs (devnum, dev->shadow_regs);
  dev->shadow_regs[0x32] = 0x40;
  low_write_all_regs (devnum, dev->shadow_regs);

  /* Start Scan */
  rts88xx_commit (devnum, dev->shadow_regs[0x2c]);

  /* We start with 0 bytes remaining to be read */
  dev->bytes_remaining = 0;
  /* and 0 bytes in the transfer buffer */
  dev->bytes_in_buffer = 0;
  dev->bytes_read = 0;

  /* Poll the available byte count until not 0 */
  while (1)
    {
      size = 4;
      low_usb_bulk_write (devnum, command4_block, &size);
      size = 0x3;
      low_usb_bulk_read (devnum, poll_result, &size);
      if (!
	  (poll_result[0] == 0 && poll_result[1] == 0 && poll_result[2] == 0))
	{
	  /* if result != 00 00 00 we got data */

	  /* data_size should be used to set bytes_remaining */
	  /* data_size is set from sane_get_parameters () */
	  dev->bytes_remaining = dev->data_size;
	  /* Initialize the read buffer */
	  read_buffer_init (dev, dev->params.bytes_per_line);
	  return SANE_STATUS_GOOD;

	}
      size = 4;
      /* I'm not sure why the Windows driver does this - probably a timeout? */
      low_usb_bulk_write (devnum, command5_block, &size);
      size = 0x1;
      low_usb_bulk_read (devnum, &read_result, &size);
      if (read_result != 0x68)
	{
	  dev->bytes_remaining = 0;
	  return SANE_STATUS_IO_ERROR;
	}
    }

  DBG (2, "sanei_lexmark_low_start_scan: end.\n");
  return SANE_STATUS_GOOD;
}

long
sanei_lexmark_low_read_scan_data (SANE_Byte * data, SANE_Int size,
				    Lexmark_Device * dev)
{
  SANE_Bool isColourScan, isGrayScan;
  static SANE_Byte command1_block[] = { 0x91, 0x00, 0xff, 0xc0 };
  size_t cmd_size, xfer_request;
  long bytes_read;
  SANE_Bool even_byte;
  SANE_Status status;
  int i, k, val;

  DBG (2, "sanei_lexmark_low_read_scan_data:\n");

  /* colour mode */
  isGrayScan = SANE_FALSE;
  if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
    isColourScan = SANE_TRUE;
  else
    {
      isColourScan = SANE_FALSE;
      /* grayscale  mode */
      if (strcmp (dev->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) == 0)
	isGrayScan = SANE_TRUE;
    }

  /* Check if we have a transfer buffer. Create one and fill it if we don't */
  if (dev->transfer_buffer == NULL)
    {
      if (dev->bytes_remaining > 0)
	{
	  if (dev->bytes_remaining > MAX_XFER_SIZE)
	    xfer_request = MAX_XFER_SIZE;
	  else
	    xfer_request = dev->bytes_remaining;

	  command1_block[2] = (SANE_Byte) (xfer_request >> 8);
	  command1_block[3] = (SANE_Byte) (xfer_request & 0xFF);

          /* wait for data */
          status = low_poll_data (dev->devnum);
          if (status != SANE_STATUS_GOOD)
            {
              DBG (1, "sanei_lexmark_low_read_scan_data: time-out while waiting for data.\n");
              return status;
            }

	  /* Create buffer to hold the amount we will request */
	  dev->transfer_buffer = (SANE_Byte *) malloc (MAX_XFER_SIZE);
	  if (dev->transfer_buffer == NULL)
	    return SANE_STATUS_NO_MEM;

	  /* Fill it */
	  /* Write: 91 00 (xfer_size) */
	  cmd_size = 4;
	  low_usb_bulk_write (dev->devnum, command1_block, &cmd_size);

	  /* Read: xfer_size  bytes */
	  cmd_size = xfer_request;
	  low_usb_bulk_read (dev->devnum, dev->transfer_buffer, &cmd_size);

	  /* apply shading coefficients */
	  k = dev->bytes_read % dev->read_buffer->linesize;
	  for (i = 0; i < (int) cmd_size; i++)
	    {
	      val = dev->transfer_buffer[i];
	      val = (int) ((float) val * dev->shading_coeff[k] + 0.5);
	      if (val > 255)
		val = 255;
	      dev->transfer_buffer[i] = val;
	      k++;
	      if ((size_t) k == dev->read_buffer->linesize)
		k = 0;
	    }

	  /* advance by the amount actually read from device */
	  dev->bytes_read += cmd_size;
	  dev->bytes_remaining -= cmd_size;
	  dev->bytes_in_buffer = cmd_size;
	  dev->read_pointer = dev->transfer_buffer;
	  DBG (2, "sanei_lexmark_low_read_scan_data:\n");
	  DBG (2, "   Filled a buffer from the scanner\n");
	  DBG (2, "   bytes_remaining: %lu\n", (u_long) dev->bytes_remaining);
	  DBG (2, "   bytes_in_buffer: %lu\n", (u_long) dev->bytes_in_buffer);
	  DBG (2, "   read_pointer: %p\n", dev->read_pointer);
	}
    }

  DBG (5, "READ BUFFER INFO: \n");
  DBG (5, "   write ptr:     %p\n", dev->read_buffer->writeptr);
  DBG (5, "   read ptr:      %p\n", dev->read_buffer->readptr);
  DBG (5, "   max write ptr: %p\n", dev->read_buffer->max_writeptr);
  DBG (5, "   buffer size:   %lu\n", (u_long) dev->read_buffer->size);
  DBG (5, "   line size:     %lu\n", (u_long) dev->read_buffer->linesize);
  DBG (5, "   empty:         %d\n", dev->read_buffer->empty);
  DBG (5, "   line no:       %d\n", dev->read_buffer->image_line_no);


  /* If there is space in the read buffer, copy the transfer buffer over */
  if (read_buffer_bytes_available (dev->read_buffer) >= dev->bytes_in_buffer)
    {
      even_byte = SANE_TRUE;
      while (dev->bytes_in_buffer)
	{

	  /* Colour Scan */
	  if (isColourScan)
	    {
	      if (even_byte)
		read_buffer_add_byte (dev->read_buffer,
				      dev->read_pointer + 1);
	      else
		read_buffer_add_byte (dev->read_buffer,
				      dev->read_pointer - 1);
	      even_byte = !even_byte;
	    }
	  /* Gray Scan */
	  else if (isGrayScan)
	    {
	      if (even_byte)
		read_buffer_add_byte_gray (dev->read_buffer,
					   dev->read_pointer + 1);
	      else
		read_buffer_add_byte_gray (dev->read_buffer,
					   dev->read_pointer - 1);
	      even_byte = !even_byte;
	    }
	  /* Lineart Scan */
	  else
	    {
	      if (even_byte)
		read_buffer_add_bit_lineart (dev->read_buffer,
					     dev->read_pointer + 1,
					     dev->threshold);
	      else
		read_buffer_add_bit_lineart (dev->read_buffer,
					     dev->read_pointer - 1,
					     dev->threshold);
	      even_byte = !even_byte;
	    }
	  dev->read_pointer = dev->read_pointer + sizeof (SANE_Byte);
	  dev->bytes_in_buffer--;
	}
      /* free the transfer buffer */
      free (dev->transfer_buffer);
      dev->transfer_buffer = NULL;
    }

  DBG (5, "READ BUFFER INFO: \n");
  DBG (5, "   write ptr:     %p\n", dev->read_buffer->writeptr);
  DBG (5, "   read ptr:      %p\n", dev->read_buffer->readptr);
  DBG (5, "   max write ptr: %p\n", dev->read_buffer->max_writeptr);
  DBG (5, "   buffer size:   %lu\n", (u_long) dev->read_buffer->size);
  DBG (5, "   line size:     %lu\n", (u_long) dev->read_buffer->linesize);
  DBG (5, "   empty:         %d\n", dev->read_buffer->empty);
  DBG (5, "   line no:       %d\n", dev->read_buffer->image_line_no);

  /* Read blocks out of read buffer */
  bytes_read = read_buffer_get_bytes (dev->read_buffer, data, size);

  DBG (2, "sanei_lexmark_low_read_scan_data:\n");
  DBG (2, "    Copying lines from buffer to data\n");
  DBG (2, "    bytes_remaining: %lu\n", (u_long) dev->bytes_remaining);
  DBG (2, "    bytes_in_buffer: %lu\n", (u_long) dev->bytes_in_buffer);
  DBG (2, "    read_pointer: %p\n", dev->read_buffer->readptr);
  DBG (2, "    bytes_read %lu\n", (u_long) bytes_read);

  /* if no more bytes to xfer and read buffer empty we're at the end */
  if ((dev->bytes_remaining == 0) && read_buffer_is_empty (dev->read_buffer))
    {
      if (!dev->eof)
	{
	  DBG (2,
	       "sanei_lexmark_low_read_scan_data: EOF- parking the scanner\n");
	  dev->eof = SANE_TRUE;
	  low_rewind (dev, dev->shadow_regs);
	}
      else
	{
	  DBG (2, "ERROR: Why are we trying to set eof more than once?\n");
	}
    }

  DBG (2, "sanei_lexmark_low_read_scan_data: end.\n");
  return bytes_read;
}

void
low_rewind (Lexmark_Device * dev, SANE_Byte * regs)
{
  SANE_Int new_location;
  SANE_Int location;
  SANE_Int scale;

  DBG (2, "low_rewind: \n");

  /* We rewind at 1200dpi resolution. We rely on content of shadow registers
     to compute the number of lines at 1200 dpi to go back */

  /* first move to start of scanning area */
  scale = 600 / dev->val[OPT_RESOLUTION].w;
  new_location = ((dev->val[OPT_BR_Y].w / scale) * scale) * 2;

  /* then add distance to go to the "origin dot" */
  if (rts88xx_is_color (regs))
    new_location += 400;
  else
    new_location += 420;

  location = new_location - 1;
  DBG (2, "low_rewind: %d=>new_location=%d\n", dev->val[OPT_BR_Y].w,
       new_location);

  /* stops any pending scan */
  low_clr_c6 (dev->devnum);
  low_cancel (dev->devnum);

  /* set regs for rewind */
  regs[0x2f] = 0xa1;
  regs[0x32] = 0x00;
  regs[0x39] = 0x00;

  /* all other regs are always the same. these ones change with parameters */
  /* the following 4 regs are the location 61,60 and the location+1 63,62 */

  regs[0x60] = LOBYTE (location);
  regs[0x61] = HIBYTE (location);
  regs[0x62] = LOBYTE (new_location);
  regs[0x63] = HIBYTE (new_location);

/* set regs for rewind */
  regs[0x79] = 0x40;
  regs[0xb2] = 0x04;
  regs[0xc3] = 0x81;
  regs[0xc6] = 0x01;
  regs[0xc9] = 0x3b;
  regs[0xe0] = 0x2b;
  regs[0xe1] = 0x17;
  regs[0xe2] = 0xe7;
  regs[0xe3] = 0x03;
  regs[0xe6] = 0xdc;
  regs[0xe7] = 0xb3;
  regs[0xe8] = 0x07;
  regs[0xe9] = 0x1b;
  regs[0xea] = 0x00;
  regs[0xeb] = 0x00;
  regs[0xec] = 0x07;
  regs[0xef] = 0x03;

  /* starts scan */
  low_start_scan (dev->devnum, regs);
  DBG (2, "low_rewind: end.\n");
}


SANE_Status
read_buffer_init (Lexmark_Device * dev, int bytesperline)
{
  size_t no_lines_in_buffer;

  DBG (2, "read_buffer_init: Start\n");

  dev->read_buffer = (Read_Buffer *) malloc (sizeof (Read_Buffer));
  if (dev->read_buffer == NULL)
    return SANE_STATUS_NO_MEM;
  dev->read_buffer->linesize = bytesperline;
  dev->read_buffer->gray_offset = 0;
  dev->read_buffer->max_gray_offset = bytesperline - 1;
  dev->read_buffer->region = RED;
  dev->read_buffer->red_offset = 0;
  dev->read_buffer->green_offset = 1;
  dev->read_buffer->blue_offset = 2;
  dev->read_buffer->max_red_offset = bytesperline - 3;
  dev->read_buffer->max_green_offset = bytesperline - 2;
  dev->read_buffer->max_blue_offset = bytesperline - 1;
  no_lines_in_buffer = 3 * MAX_XFER_SIZE / bytesperline;
  dev->read_buffer->size = bytesperline * no_lines_in_buffer;
  dev->read_buffer->data = (SANE_Byte *) malloc (dev->read_buffer->size);
  if (dev->read_buffer->data == NULL)
    return SANE_STATUS_NO_MEM;
  dev->read_buffer->readptr = dev->read_buffer->data;
  dev->read_buffer->writeptr = dev->read_buffer->data;
  dev->read_buffer->max_writeptr = dev->read_buffer->data +
    (no_lines_in_buffer - 1) * bytesperline;
  dev->read_buffer->empty = SANE_TRUE;
  dev->read_buffer->image_line_no = 0;
  dev->read_buffer->bit_counter = 0;
  dev->read_buffer->max_lineart_offset = dev->params.pixels_per_line - 1;
  return SANE_STATUS_GOOD;
}

SANE_Status
read_buffer_free (Read_Buffer * read_buffer)
{
  DBG (2, "read_buffer_free:\n");
  if (read_buffer)
    {
      free (read_buffer->data);
      free (read_buffer);
      read_buffer = NULL;
    }
  return SANE_STATUS_GOOD;
}

size_t
read_buffer_bytes_available (Read_Buffer * rb)
{

  DBG (2, "read_buffer_bytes_available:\n");

  if (rb->empty)
    return rb->size;
  else if ((size_t) abs (rb->writeptr - rb->readptr) < rb->linesize)
    return 0;			/* ptrs are less than one line apart */
  else if (rb->writeptr < rb->readptr)
    return (rb->readptr - rb->writeptr - rb->linesize);
  else
    return (rb->size + rb->readptr - rb->writeptr - rb->linesize);
}

SANE_Status
read_buffer_add_byte (Read_Buffer * rb, SANE_Byte * byte_pointer)
{

  /* DBG(2, "read_buffer_add_byte:\n"); */
  /* F.O. Need to fix the endian byte ordering here */

  switch (rb->region)
    {
    case RED:
      *(rb->writeptr + rb->red_offset) = *byte_pointer;
      if (rb->red_offset == rb->max_red_offset)
	{
	  rb->red_offset = 0;
	  rb->region = GREEN;
	}
      else
	rb->red_offset = rb->red_offset + (3 * sizeof (SANE_Byte));
      return SANE_STATUS_GOOD;
    case GREEN:
      *(rb->writeptr + rb->green_offset) = *byte_pointer;
      if (rb->green_offset == rb->max_green_offset)
	{
	  rb->green_offset = 1;
	  rb->region = BLUE;
	}
      else
	rb->green_offset = rb->green_offset + (3 * sizeof (SANE_Byte));
      return SANE_STATUS_GOOD;
    case BLUE:
      *(rb->writeptr + rb->blue_offset) = *byte_pointer;
      if (rb->blue_offset == rb->max_blue_offset)
	{
	  rb->image_line_no++;
	  /* finished a line. read_buffer no longer empty */
	  rb->empty = SANE_FALSE;
	  rb->blue_offset = 2;
	  rb->region = RED;
	  if (rb->writeptr == rb->max_writeptr)
	    rb->writeptr = rb->data;	/* back to beginning of buffer */
	  else
	    rb->writeptr = rb->writeptr + rb->linesize;	/* next line */
	}
      else
	rb->blue_offset = rb->blue_offset + (3 * sizeof (SANE_Byte));
      return SANE_STATUS_GOOD;
    }
  return SANE_STATUS_GOOD;
}

SANE_Status
read_buffer_add_byte_gray (Read_Buffer * rb, SANE_Byte * byte_pointer)
{

  /*  DBG(2, "read_buffer_add_byte_gray:\n"); */

  *(rb->writeptr + rb->gray_offset) = *byte_pointer;

  if (rb->gray_offset == rb->max_gray_offset)
    {
      rb->image_line_no++;
      /* finished a line. read_buffer no longer empty */
      rb->empty = SANE_FALSE;
      rb->gray_offset = 0;

      if (rb->writeptr == rb->max_writeptr)
	rb->writeptr = rb->data;	/* back to beginning of buffer */
      else
	rb->writeptr = rb->writeptr + rb->linesize;	/* next line */
    }
  else
    rb->gray_offset = rb->gray_offset + (1 * sizeof (SANE_Byte));
  return SANE_STATUS_GOOD;
}

SANE_Status
read_buffer_add_bit_lineart (Read_Buffer * rb, SANE_Byte * byte_pointer,
			     SANE_Byte threshold)
{
  SANE_Byte tmpByte;
  SANE_Byte *currentBytePtr;
  SANE_Int bitIndex;

  /*  DBG(2, "read_buffer_add_bit_lineart:\n"); */

  /* threshold = 0x80;  */
  tmpByte = 0;
  /* Create a bit by comparing incoming byte to threshold */
  if (*byte_pointer <= threshold)
    {
      tmpByte = 128;
    }

  /* Calculate the bit index in the current byte */
  bitIndex = rb->bit_counter % 8;
  /* Move the bit to its correct position in the temporary byte */
  tmpByte = tmpByte >> bitIndex;
  /* Get the pointer to the current byte */
  currentBytePtr = rb->writeptr + rb->gray_offset;

  /* If this is the first write to this byte, clear the byte */
  if (bitIndex == 0)
    *currentBytePtr = 0;
  /* Set the value of the bit in the current byte */
  *currentBytePtr = *currentBytePtr | tmpByte;

  /* last bit in the line? */
  if (rb->bit_counter == rb->max_lineart_offset)
    {
      /* Check if we're at the last byte of the line - error if not */
      if (rb->gray_offset != rb->max_gray_offset)
	{
	  DBG (5, "read_buffer_add_bit_lineart:\n");
	  DBG (5, "  Last bit of line is not last byte.\n");
	  DBG (5, "  Bit Index: %d, Byte Index: %d. \n", rb->bit_counter,
	       rb->max_gray_offset);
	  return SANE_STATUS_INVAL;
	}
      rb->image_line_no++;
      /* line finished read_buffer no longer empty */
      rb->empty = SANE_FALSE;
      rb->gray_offset = 0;
      /* are we at the last line in the read buffer ? */
      if (rb->writeptr == rb->max_writeptr)
	rb->writeptr = rb->data;	/* back to beginning of buffer */
      else
	rb->writeptr = rb->writeptr + rb->linesize;	/* next line */
      /* clear the bit counter */
      rb->bit_counter = 0;
    }
  /* last bit in the byte? */
  else if (bitIndex == 7)
    {
      /* Not at the end of the line, but byte done. Increment byte offset */
      rb->gray_offset = rb->gray_offset + (1 * sizeof (SANE_Byte));
      /* increment bit counter */
      rb->bit_counter++;
    }
  else
    {
      /* else increment bit counter */
      rb->bit_counter++;
    }

  return SANE_STATUS_GOOD;
}


size_t
read_buffer_get_bytes (Read_Buffer * rb, SANE_Byte * buffer, size_t rqst_size)
{
  /* Read_Buffer *rb; */
  size_t available_bytes;

  /* rb = read_buffer; */
  if (rb->empty)
    return 0;
  else if (rb->writeptr > rb->readptr)
    {
      available_bytes = rb->writeptr - rb->readptr;
      if (available_bytes <= rqst_size)
	{
	  /* We can read from the read pointer up to the write pointer */
	  memcpy (buffer, rb->readptr, available_bytes);
	  rb->readptr = rb->writeptr;
	  rb->empty = SANE_TRUE;
	  return available_bytes;
	}
      else
	{
	  /* We can read from the full request size */
	  memcpy (buffer, rb->readptr, rqst_size);
	  rb->readptr = rb->readptr + rqst_size;
	  return rqst_size;
	}
    }
  else
    {
      /* The read pointer is ahead of the write pointer. Its wrapped around. */
      /* We can read to the end of the buffer and make a recursive call to */
      /* read any available lines at the beginning of the buffer */
      available_bytes = rb->data + rb->size - rb->readptr;
      if (available_bytes <= rqst_size)
	{
	  /* We can read from the read pointer up to the end of the buffer */
	  memcpy (buffer, rb->readptr, available_bytes);
	  rb->readptr = rb->data;
	  if (rb->writeptr == rb->readptr)
	    rb->empty = SANE_TRUE;
	  return available_bytes +
	    read_buffer_get_bytes (rb, buffer + available_bytes,
				   rqst_size - available_bytes);
	}
      else
	{
	  /* We can read from the full request size */
	  memcpy (buffer, rb->readptr, rqst_size);
	  rb->readptr = rb->readptr + rqst_size;
	  return rqst_size;
	}
    }
}

SANE_Bool
read_buffer_is_empty (Read_Buffer * read_buffer)
{
  return read_buffer->empty;
}

/* 
 * average a width*height rgb/monochrome area
 * return values in given pointers
 */
static int
average_area (SANE_Byte * regs, SANE_Byte * data, int width, int height,
	      int *ra, int *ga, int *ba)
{
  int x, y;
  int global = 0;
  int rc, gc, bc;

  *ra = 0;
  *ga = 0;
  *ba = 0;
  rc = 0;
  gc = 0;
  bc = 0;
  if (rts88xx_is_color (regs))
    {
      for (x = 0; x < width; x++)
	for (y = 0; y < height; y++)
	  {
	    rc += data[3 * width * y + x];
	    gc += data[3 * width * y + width + x];
	    bc += data[3 * width * y + 2 * width + x];
	  }
      global = (rc + gc + bc) / (3 * width * height);
      *ra = rc / (width * height);
      *ga = gc / (width * height);
      *ba = bc / (width * height);
    }
  else
    {
      for (x = 0; x < width; x++)
	for (y = 0; y < height; y++)
	  {
	    gc += data[width * y + x];
	  }
      global = gc / (width * height);
      *ga = gc / (width * height);
    }
  DBG (7, "average_area: global=%d, red=%d, green=%d, blue=%d\n", global, *ra,
       *ga, *ba);
  return global;
}

/**
 * we scan a dark area with gain minimum to detect offset
 */
SANE_Status
sanei_lexmark_low_offset_calibration (Lexmark_Device * dev)
{
  SANE_Byte regs[255];		/* we have our own copy of shadow registers */
  SANE_Status status = SANE_STATUS_GOOD;
  int i, lines = 8, yoffset = 2;
  int pixels;
  int failed=0;
  /* offsets */
  int ro = 0, go = 0, bo = 0;
  /* averages */
  int ra, ga, ba, average;
  SANE_Byte *data = NULL;
  SANE_Byte top[OFFSET_RANGES] = { 0, 0x7f, 0x9f, 0xbf, 0xff };
#ifdef DEEP_DEBUG
  char title[20];
#endif

  DBG (2, "sanei_lexmark_low_offset_calibration: start\n");
  /* copy registers */
  for (i = 0; i < 255; i++)
    regs[i] = dev->shadow_regs[i];

  /* we clear movement bit */
  regs[0xc3] = regs[0xc3] & 0x7f;

  pixels =
    (dev->sensor->offset_endx - dev->sensor->offset_startx) / regs[0x7a];

  /* there are 4 ranges of offset:
     00-7F : almost no offset
     80-9F : high noise
     A0-BF : high noise
     C0-FF : high noise
     we start with the highest one and decrease until
     overall offset is ok
     First loop may have such an high offset that scanned data overflow
     and gives a low average. So we allways skip its results
   */

  /* minimal gains */
  DBG (3,
       "sanei_lexmark_low_offset_calibration: setting gains to (1,1,1).\n");
  rts88xx_set_gain (regs, 1, 1, 1);

  i = OFFSET_RANGES;
  average = 255;

  /* loop on ranges until one fits. Then adjust offset, first loop is
   * always done. TODO detect overflow by 'noise looking' data pattern */
  while (((i > 0) && (average > dev->sensor->offset_threshold))
	 || (i == OFFSET_RANGES))
    {
      /* next range  */
      i--;

      /* sets to top of range */
      ro = top[i];
      go = top[i];
      bo = top[i];
      rts88xx_set_offset (regs, ro, ro, ro);
      DBG (3,
	   "sanei_lexmark_low_offset_calibration: setting offsets to (%d,%d,%d).\n",
	   ro, ro, ro);

      status =
	low_simple_scan (dev, regs, dev->sensor->offset_startx, pixels,
			   yoffset, lines, &data);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (1,
	       "sanei_lexmark_low_offset_calibration: low_simple_scan failed!\n");
	  if (data != NULL)
	    free (data);
	  return status;
	}
#ifdef DEEP_DEBUG
      sprintf (title, "offset%02x.pnm", ro);
      write_pnm_file(title,pixels,lines,rts88xx_is_color (regs),data);
#endif
      average = average_area (regs, data, pixels, lines, &ra, &ga, &ba);
    }
  if (i == 0)
    {
      DBG (2, "sanei_lexmark_low_offset_calibration: failed !\n");
      failed=1;
    }

  /* increase gain and scan again */
  /* increase gain for fine offset tuning */
  rts88xx_set_gain (regs, 6, 6, 6);
  status =
    low_simple_scan (dev, regs, dev->sensor->offset_startx, pixels, yoffset,
		       lines, &data);
  if (status != SANE_STATUS_GOOD)
    {
      DBG (1,
	   "sanei_lexmark_low_offset_calibration: low_simple_scan failed!\n");
      if (data != NULL)
	free (data);
      return status;
    }
  average = average_area (regs, data, pixels, lines, &ra, &ga, &ba);
#ifdef DEEP_DEBUG
  write_pnm_file("offset-final.pnm",pixels,lines,rts88xx_is_color (regs),data);
#endif

  /* this "law" is a guess, may (should?) be changed ... */
  if(!failed)
  {
	  if (ro > ra)
		  dev->offset.red = ro - ra;
	  if (go > ga)
	  {
      	dev->offset.green = go - ga;
      	dev->offset.gray = go - ga;
	  }
	  if (bo > ba)
		  dev->offset.blue = bo - ba;
  }
  else
  {
	  dev->offset.red=dev->sensor->offset_fallback;
	  dev->offset.green=dev->sensor->offset_fallback;
	  dev->offset.blue=dev->sensor->offset_fallback;
  }
  DBG (7,
       "sanei_lexmark_low_offset_calibration: offset=(0x%02x,0x%02x,0x%02x).\n",
       dev->offset.red, dev->offset.green, dev->offset.blue);

  DBG (2, "sanei_lexmark_low_offset_calibration: end.\n");
  free (data);
  return status;
}

/*
 * we scan a white area until average is good enough
 * level is good enough when it maximize the range value of output:
 * ie max-min is maximum
 */
SANE_Status
sanei_lexmark_low_gain_calibration (Lexmark_Device * dev)
{
  SANE_Byte regs[255];		/* we have our own copy of shadow registers */
  SANE_Status status = SANE_STATUS_GOOD;
  int i, lines = 4, yoffset = 1;
  int sx, ex;
  int pixels;
  /* averages */
  int ra, ga, ba, average;
  SANE_Byte *data = NULL;
  int red, green, blue;
#ifdef DEEP_DEBUG
  char title[20];
#endif

  DBG (2, "sanei_lexmark_low_gain_calibration: start\n");
  /* copy registers */
  for (i = 0; i < 255; i++)
    regs[i] = dev->shadow_regs[i];

  /* we clear movement bit */
  regs[0xc3] = regs[0xc3] & 0x7f;
  sx = regs[0x67] * 256 + regs[0x66];
  ex = regs[0x6d] * 256 + regs[0x6c];
  pixels = (ex - sx) / regs[0x7a];

  /* set up inital gains */
  red = 6;
  green = 6;
  blue = 6;
  rts88xx_set_gain (regs, red, green, blue);

  /* init loop */
  i = 0;
  ra = 0;
  ba = 0;
  ga = 0;

  status = low_cancel (dev->devnum);
  if (status != SANE_STATUS_GOOD)
    return status;

  /* we do a simple scan all 3 averages give the choosen level */
  while (((rts88xx_is_color (regs)
	   && ((ra < dev->sensor->red_gain_target)
	       || (ga < dev->sensor->green_gain_target)
	       || (ba < dev->sensor->blue_gain_target)))
	  || (!rts88xx_is_color (regs)
	      && (ga < dev->sensor->gray_gain_target))) && (i < 25))
    {
      status =
	low_simple_scan (dev, regs, sx, pixels, yoffset, lines, &data);
      if (status != SANE_STATUS_GOOD)
	{
	  DBG (1,
	       "sanei_lexmark_low_gain_calibration: low_simple_scan failed!\n");
	  if (data != NULL)
	    free (data);
	  return status;
	}
#ifdef DEEP_DEBUG
      sprintf (title, "gain%02d.pnm", i);
      write_pnm_file(title,pixels,lines,rts88xx_is_color (regs),data);
#endif
      average = average_area (regs, data, pixels, lines, &ra, &ga, &ba);
      free (data);
      if (ra < dev->sensor->red_gain_target)
	red++;
      if (ga < dev->sensor->green_gain_target
	  || (dev->sensor->gray_gain_target && !rts88xx_is_color (regs)))
	green++;
      if (ba < dev->sensor->blue_gain_target)
	blue++;
      rts88xx_set_gain (regs, red, green, blue);
      i++;
    }
  dev->gain.red = red;
  dev->gain.green = green;
  dev->gain.blue = blue;
  dev->gain.gray = green;
  DBG (7,
       "sanei_lexmark_low_gain_calibration: gain=(0x%02x,0x%02x,0x%02x).\n",
       dev->gain.red, dev->gain.green, dev->gain.blue);
  DBG (2, "sanei_lexmark_low_gain_calibration: end.\n");
  return status;
}

/**
 * there is no hardware shading correction. So we have to do it in software.
 * We do it by scanning a pure white area which is before scanning area. Then
 * we compute per pixel coefficient to move the scanned value to the target
 * value. These coefficients are used later to correct scanned data.
 * The scan is done with all the final scan settings but the heigth and vertical
 * start position.
 */
SANE_Status
sanei_lexmark_low_shading_calibration (Lexmark_Device * dev)
{
  SANE_Byte regs[255];		/* we have our own copy of shadow registers */
  int i, j, pixels, bpl;
  int sx, ex;
  SANE_Byte *data = NULL;
  SANE_Status status;
  /* enough 75 dpi lines to "go off" home position dot,
     and include shading area */
  int lines = 4 + 4;
  int yoffset = 1;
  int x, y;
  float rtarget, btarget, gtarget;

  DBG (2, "sanei_lexmark_low_shading_calibration: start\n");
  /* copy registers */
  for (i = 0; i < 255; i++)
    regs[i] = dev->shadow_regs[i];

  /* allocate memory for scan */
  sx = regs[0x67] * 256 + regs[0x66];
  ex = regs[0x6d] * 256 + regs[0x6c];
  DBG (7, "startx=%d, endx=%d, coef=%d, r2f=0x%02x\n",
       sx, ex, regs[0x7a], regs[0x2f]);
  pixels = (ex - sx) / regs[0x7a];
  if (rts88xx_is_color (regs))
    bpl = 3 * pixels;
  else
    bpl = pixels;

  /* adjust the target area to the scanning resolution */
  lines = (8 * lines) / regs[0x7a];

  data = (SANE_Byte *) malloc (bpl * lines);
  DBG (7, "pixels=%d, lines=%d, size=%d\n", pixels, lines, bpl * lines);
  if (data == NULL)
    {
      DBG (2,
	   "sanei_lexmark_low_shading_calibration: failed to allocate %d bytes !\n",
	   bpl * lines);
      return SANE_STATUS_NO_MEM;
    }
  if (dev->shading_coeff != NULL)
    free (dev->shading_coeff);
  dev->shading_coeff = (float *) malloc (bpl * sizeof (float));
  if (dev->shading_coeff == NULL)
    {
      DBG (2,
	   "sanei_lexmark_low_shading_calibration: failed to allocate %d floats !\n",
	   bpl);
      free (data);
      return SANE_STATUS_NO_MEM;
    }

  /* we set movement bit this time */
  regs[0xc3] = regs[0xc3] | 0x80;

  /* execute scan */
  status = low_simple_scan (dev, regs, sx, pixels, yoffset, lines, &data);
  if (status != SANE_STATUS_GOOD)
    {
      DBG (1,
	   "sanei_lexmark_low_shading_calibration: low_simple_scan failed!\n");
      if (data != NULL)
	free (data);
      return status;
    }

  yoffset = -1;
  /* the very first lines of the scan may include the dark dot used
   * locate park position. We find the first line free of it in the scan.
   * We can't use is_home_line since it modifies data.
   */
  for (y = 0; (y < lines) && (yoffset == y - 1); y++)
    {
      if (rts88xx_is_color (regs))
	{
	  for (x = 0; x < 3 * pixels; x++)
	    {
	      if (data[x + y * 3 * pixels] < 30)
		yoffset = y;
	    }
	}
      else
	{
	  for (x = 0; x < pixels; x++)
	    {
	      if (data[x + y * pixels] < 30)
		yoffset = y;
	    }
	}
    }
  /* make sure we are really out of the dot */
  yoffset++;

  /* yoffset is index of last dot line, go to first white line */
  if (yoffset >= lines - 1)
    {
      DBG (7,
	   "sanei_lexmark_low_shading_calibration: failed to detect yoffset.\n");
      /* fail safe fallback, picture will be altered at dot position,
         but scanner is safe */
      yoffset = lines - 2;
    }
  else
    yoffset++;
  DBG (7, "sanei_lexmark_low_shading_calibration: yoffset=%d.\n", yoffset);

#ifdef DEEP_DEBUG
  write_pnm_file("shading.pnm",pixels,lines,rts88xx_is_color (regs),data);
#endif

  /* computes coefficients */
  /* there are 8 lines usable for shading calibration at 150 dpi, between
     bottom of "home position" dot and the start of the scanner's window 
     assembly, we only use 7 of them */
  if (yoffset + (8 * 4) / regs[0x7a] < lines)
    lines = yoffset + (8 * 4) / regs[0x7a];
  rtarget = dev->sensor->red_shading_target;
  gtarget = dev->sensor->green_shading_target;
  btarget = dev->sensor->blue_shading_target;
  for (i = 0; i < pixels; i++)
    {
      /* we computes the coefficient needed to move the scanned value to
         the target value */
      if (rts88xx_is_color (dev->shadow_regs))
	{
	  /* RED */
	  dev->shading_coeff[i] = 0;
	  for (j = yoffset; j < lines; j++)
	    dev->shading_coeff[i] += data[i + j * bpl];
	  dev->shading_coeff[i] =
	    (rtarget / (dev->shading_coeff[i] / (lines - yoffset)));

	  /* GREEN */
	  dev->shading_coeff[i + pixels] = 0;
	  for (j = yoffset; j < lines; j++)
	    dev->shading_coeff[i + pixels] += data[i + j * bpl + pixels];
	  dev->shading_coeff[i + pixels] =
	    ((gtarget / dev->shading_coeff[i + pixels]) * (lines - yoffset));

	  /* BLUE */
	  dev->shading_coeff[i + 2 * pixels] = 0;
	  for (j = yoffset; j < lines; j++)
	    dev->shading_coeff[i + 2 * pixels] +=
	      data[i + j * bpl + 2 * pixels];
	  dev->shading_coeff[i + 2 * pixels] =
	    ((btarget / dev->shading_coeff[i + 2 * pixels]) *
	     (lines - yoffset));
	}
      else
	{
	  dev->shading_coeff[i] = 0;
	  for (j = yoffset; j < lines; j++)
	    {
	      dev->shading_coeff[i] += data[i + j * bpl];
	    }
	  dev->shading_coeff[i] =
	    (rtarget / dev->shading_coeff[i]) * (lines - yoffset);
	}
    }

  /* do the scan backward to go back to start position */
  regs[0xc6] &= 0xF7;
  lines = (8 * 8) / regs[0x7a];

  /* execute scan */
  status = low_simple_scan (dev, regs, sx, pixels, 1, lines, &data);
  if (status != SANE_STATUS_GOOD)
    {
      DBG (1,
	   "sanei_lexmark_low_shading_calibration: low_simple_scan failed!\n");
      return status;
    }

#ifdef DEEP_DEBUG
  write_pnm_file("shading_bwd.pnm",pixels,lines,rts88xx_is_color (regs),data);
#endif
  free (data);

  DBG (2, "sanei_lexmark_low_shading_calibration: end.\n");
  return status;
}


SANE_Status
sanei_lexmark_low_calibration (Lexmark_Device * dev)
{
  SANE_Status status;

  status = sanei_lexmark_low_offset_calibration (dev);
  if (status != SANE_STATUS_GOOD)
    return status;

  /* we put the offset just computed in scanning regs */
  if (rts88xx_is_color (dev->shadow_regs))
    {
      rts88xx_set_offset (dev->shadow_regs,
			  dev->offset.red,
			  dev->offset.green, dev->offset.blue);
    }
  else
    {
      rts88xx_set_offset (dev->shadow_regs,
			  dev->offset.gray,
			  dev->offset.gray, dev->offset.gray);
    }

  /* if manual gain settings, no gain calibration */
  if (dev->val[OPT_MANUAL_GAIN].w == SANE_TRUE)
    {
      if (rts88xx_is_color (dev->shadow_regs))
	{
	  dev->gain.red = dev->val[OPT_RED_GAIN].w;
	  dev->gain.green = dev->val[OPT_GREEN_GAIN].w;
	  dev->gain.blue = dev->val[OPT_BLUE_GAIN].w;
	}
      else
	dev->gain.gray = dev->val[OPT_GRAY_GAIN].w;
    }
  else
    {
      status = sanei_lexmark_low_gain_calibration (dev);
      if (status != SANE_STATUS_GOOD)
	return status;
    }

  /* put the calibrated or manual settings before shading calibration 
     which must be done with final setting values */
  if (rts88xx_is_color (dev->shadow_regs))
    {
      rts88xx_set_gain (dev->shadow_regs, dev->gain.red, dev->gain.green,
			dev->gain.blue);
    }
  else
    {
      rts88xx_set_gain (dev->shadow_regs, dev->gain.gray, dev->gain.gray,
			dev->gain.gray);
    }

  status = sanei_lexmark_low_shading_calibration (dev);
  if (status != SANE_STATUS_GOOD)
    return status;

  DBG (2, "sanei_lexmark_low_calibration: end.\n");
  return SANE_STATUS_GOOD;
}

/* assign sensor data */
static SANE_Status
sanei_lexmark_low_assign_sensor (Lexmark_Device * dev)
{
  int dn;

  /* init sensor data */
  dn = 0;
  while (sensor_list[dn].id != 0
	 && sensor_list[dn].id != dev->model.sensor_type)
    dn++;
  if (sensor_list[dn].id == 0)
    {
      DBG (1,
	   "sanei_lexmark_low_assign_sensor: unknown sensor %d\n",
	   dev->model.sensor_type);
      return SANE_STATUS_UNSUPPORTED;
    }
  dev->sensor = &(sensor_list[dn]);
  DBG (1, "sanei_lexmark_low_assign_sensor: assigned sensor number %d\n",
       dev->model.sensor_type);
  return SANE_STATUS_GOOD;
}

/* assign model description, based on USB id, and register B0 when
 * available */
SANE_Status
sanei_lexmark_low_assign_model (Lexmark_Device * dev, char *devname,
				  SANE_Int vendor, SANE_Int product,
				  SANE_Byte mainboard)
{
  int dn;
  SANE_Bool found = SANE_FALSE;

  DBG (2, "sanei_lexmark_low_assign_model: start\n");
  DBG (3,
       "sanei_lexmark_low_assign_model: assigning %04x:%04x, variant %d\n",
       vendor, product, mainboard);

  dn = 0;
  /* walk the list of known devices */
  while (!found && model_list[dn].vendor_id != 0)
    {
      /* no mainboard id given (at attach time) */
      if (mainboard == 0
	  && vendor == model_list[dn].vendor_id
	  && product == model_list[dn].product_id)
	found = SANE_TRUE;
      /* mainboard given (init time) */
      if (mainboard != 0
	  && mainboard == model_list[dn].mainboard_id
	  && vendor == model_list[dn].vendor_id
	  && product == model_list[dn].product_id)
	found = SANE_TRUE;

      if (!found)
	dn++;
    }

  /* we hit the end of list, so we don't know about the current model */
  if (!found)
    {
      DBG (1,
	   "sanei_lexmark_low_assign_model: unknown device 0x%04x:0x%04x\n",
	   vendor, product);
      return SANE_STATUS_UNSUPPORTED;
    }

  dev->sane.name = strdup (devname);
  dev->sane.vendor = model_list[dn].vendor;
  dev->sane.model = model_list[dn].model;
  dev->model = model_list[dn];
  dev->sane.type = "flatbed scanner";

  DBG (3, "sanei_lexmark_low_assign_model: assigned %s\n",
       dev->model.model);

  /* init sensor data */
  return sanei_lexmark_low_assign_sensor (dev);
  DBG (2, "sanei_lexmark_low_assign_model: end.\n");
}
