/* sane - Scanner Access Now Easy.

   Copyright (C) 2001-2003 Eddy De Greef <eddy_de_greef at tiscali dot be>
   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.

   This file implements a SANE backend for Mustek PP flatbed _CIS_ scanners.
*/

/*
   Global picture
   
      Mustek_PP_handle -> Mustek_PP_dev
                       -> priv = Mustek_PP_CIS_dev -> CIS
*/
                       
/*
 * This flag determines whether the scanner uses fast skipping at high
 * resolutions. It is possible that this fast skipping introduces 
 * inaccuracies. It if turns out to be a problem, fast skipping can
 * be disabled by setting this flag to 0.
 */
#define MUSTEK_PP_CIS_FAST_SKIP 1
#define MUSTEK_PP_CIS_WAIT_BANK 200

/*
 * These parameters determine where the scanable area starts at the top.
 * If there is a consistent offset error, you can tune it through these
 * parameters. Note that an inaccuracy in the order of 1 mm seems to be
 * normal for the Mustek 600/1200 CP series.
 */
#define MUSTEK_PP_CIS_600CP_DEFAULT_SKIP   	250
#define MUSTEK_PP_CIS_1200CP_DEFAULT_SKIP 	330

/*
 * Number of scan lines on which the average is taken to determine the 
 * maximum number of color levels.
 */
#define MUSTEK_PP_CIS_AVERAGE_COUNT 32

#define MUSTEK_PP_CIS600		1
#define MUSTEK_PP_CIS1200		2
#define MUSTEK_PP_CIS1200PLUS 		3

#define MUSTEK_PP_CIS_CHANNEL_RED	0
#define MUSTEK_PP_CIS_CHANNEL_GREEN	1
#define MUSTEK_PP_CIS_CHANNEL_BLUE	2
#define MUSTEK_PP_CIS_CHANNEL_GRAY	1

#define MUSTEK_PP_CIS_MAX_H_PIXEL 	5120
#define MUSTEK_PP_CIS_MAX_V_PIXEL 	7000

#define MUSTEK_PP_CIS_MOTOR_REVERSE 	0

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

#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <math.h>
#include "../include/sane/sane.h"
#include "../include/sane/sanei_pa4s2.h"
#define DEBUG_DECLARE_ONLY
#include "mustek_pp.h"
#include "mustek_pp_decl.h"
#include "mustek_pp_cis.h"

/******************************************************************************
 ******************************************************************************
 ***                 MA1015 chipset related functionality                   ***
 ******************************************************************************
 *****************************************************************************/
 
/*
   These defines control some debugging functionality 

   #define M1015_TRACE_REGS   -> trace the status of the internal registers
   #define M1015_LOG_HL       -> create a high-level log file (register-level)
   #define M1015_LOG_LL       -> create a low-level log file (byte-level)
   
   By default, all logging/tracing is turned off.
*/

/******************************************************************************
 * Low level logging: logs read and writes at the byte level, similar to
 *                    the sequences produced by tool of Jochen Eisinger
 *                    for analysing the TWAIN driver communication.
 *                    This simplifies comparison of the sequences.
 *****************************************************************************/
#ifdef M1015_LOG_LL

   static FILE* M1015_LOG_1;
   
   #define M1015_START_LL\
      M1015_LOG_1 = fopen("cis_ll.log", "w");
        
   #define M1015_STOP_LL\
      fclose(M1015_LOG_1);
   
   #define SANEI_PA4S2_WRITEBYTE(fd, reg, val)\
      do\
      {\
         sanei_pa4s2_writebyte (fd, reg, val);\
         fprintf(M1015_LOG_1, "\tsanei_pa4s2_writebyte(fd, %d, 0x%02X);\n", \
                 reg, val);\
      } while (0)
   
   static const char* cis_last_rreg_name;
   static int cis_read_count;       
   
   #define SANEI_PA4S2_READBEGIN(fd, reg)\
      do\
      {\
         cis_last_rreg_name = Mustek_PP_1015_reg_r_name(reg);\
         cis_read_count = 0;\
         sanei_pa4s2_readbegin(fd, reg);\
      } while (0)
   
   #define SANEI_PA4S2_READBYTE(fd, val)\
      do\
      {\
         sanei_pa4s2_readbyte(fd, val);\
         ++cis_read_count;\
      } while (0)
   
   #define SANEI_PA4S2_READEND(fd)\
      do\
      {\
         sanei_pa4s2_readend(fd);\
         fprintf(M1015_LOG_1, "\tread_reg(%s, %d);\n", \
                 cis_last_rreg_name, cis_read_count);\
      } while (0)
      
   #define M1015_MARK_LL(info)\
      fprintf(M1015_LOG_1, "* %s\n", info);
      
#else /* M1015_LOG_LL */

   #define M1015_START_LL
   #define M1015_STOP_LL
   
   #define SANEI_PA4S2_WRITEBYTE(fd, reg, val)\
      sanei_pa4s2_writebyte (fd, reg, val)
      
   #define SANEI_PA4S2_READBEGIN(fd, reg)\
      sanei_pa4s2_readbegin(fd, reg)
   
   #define SANEI_PA4S2_READBYTE(fd, val)\
      sanei_pa4s2_readbyte(fd, val)
   
   #define SANEI_PA4S2_READEND(fd)\
      sanei_pa4s2_readend(fd)
      
   #define M1015_MARK_LL(info)
      
#endif /* M1015_LOG_LL */

 
/******************************************************************************
 * High-level logging: traces the flow of the driver in a hierarchical way
 *                     up to the level of register acccesses.
 *****************************************************************************/ 
#ifdef M1015_LOG_HL

   static FILE* M1015_LOG_2;
   static char hl_prev_line[4096], hl_next_line[4096], hl_repeat_count;
   
   /*
    * A few variables for hierarchical log message indentation.
    */

   static const char* cis_indent_start =
      "                                                                      ";
   static const char* cis_indent;
   static const char* cis_indent_end;
   
   #define M1015_START_HL\
       M1015_LOG_2 = fopen("cis_hl.log", "w");\
       cis_indent = cis_indent_start + strlen(cis_indent_start);\
       cis_indent_end = cis_indent;\
       hl_prev_line[0] = 0;\
       hl_next_line[0] = 0;\
       hl_repeat_count = 0;
       
   #define M1015_FLUSH_HL\
      if (strcmp(hl_prev_line, hl_next_line))\
      {\
         fprintf(M1015_LOG_2, &hl_prev_line[0]);\
         strcpy(&hl_prev_line[0], &hl_next_line[0]);\
         if (hl_repeat_count != 0)\
         {\
            fprintf(M1015_LOG_2, "%s [last message repeated %d times]\n",\
                    cis_indent, hl_repeat_count+1);  \
         }\
         hl_repeat_count = 0;\
      }\
      else\
      {\
         hl_repeat_count += 1;\
      }
       
   #define M1015_MARK(info)\
      sprintf(&hl_next_line[0], "%s+ %s\n", cis_indent, info);\
      M1015_FLUSH_HL

   #define M1015_STOP_HL\
       hl_next_line[0] = 0;\
       M1015_FLUSH_HL\
       fclose(M1015_LOG_2); 
   
#else  /* M1015_LOG_HL */ 

   #define M1015_START_HL
   #define M1015_STOP_HL
   #define M1015_MARK(info)
   #define M1015_FLUSH_HL
      
#endif /* M1015_LOG_HL */

#ifdef M1015_TRACE_REGS
   #define M1015_DISPLAY_REGS(dev, msg) Mustek_PP_1015_display_regs(dev, msg)
   #define M1015_DISPLAY_REG(msg, val)  Mustek_PP_1015_display_reg(msg, val)
#else
   #define M1015_DISPLAY_REGS(dev, msg) 
   #define M1015_DISPLAY_REG(msg, val)  
#endif

 
#if defined (M1015_LOG_HL) || defined (M1015_LOG_LL)
static const char* 
Mustek_PP_1015_reg_r_name(Mustek_PP_1015R_reg id)
{
   static const char* names[4] = { "ASIC", "SCAN_VAL", "MOTOR", "BANK_COUNT" };
   return names[id & 0x03];
}

static const char* 
Mustek_PP_1015_bit_name(Mustek_PP_1015R_bit id)
{
   static const char* names[4] = { "????", "MOTOR_HOME", "????", "MOTOR_BUSY" };
   return names[id & 0x03];
}

static const char* 
Mustek_PP_1015_reg_w_name(Mustek_PP_1015R_reg id)
{
   static const char* names[4][4] = 
   {
      { "RED_REF",        "GREEN_REF",     "BLUE_REF",       "DPI_CONTROL" },
      { "BYTE_COUNT_HB",  "BYTE_COUNT_LB", "SKIP_COUNT",     "EXPOSE_TIME" },
      { "SRAM_SOURCE_PC", "MOTOR_CONTROL", "UNKNOWN_42",     "UNKNOWN_82"  },
      { "POWER_ON_DELAY", "CCD_TIMING",    "CCD_TIMING_ADJ", "RIGHT_BOUND" }
   };
   return names[(id & 0x30) >> 4][id & 0x03];
}
#endif

/******************************************************************************
 * Converts a register value to a hex/dec/bin representation.
 *****************************************************************************/
static const char*
Mustek_PP_1015_show_val(int val)
{
   /*
      Since we use a static temporary buffer, we must make sure that the
      buffer isn't altered while it is still in use (typically because
      more than one value is converted in a printf statement). 
      Therefore the buffer is organized as a ring buffer. If should contain
      at least 21 elements in order to be able to display all registers 
      with one printf statement.
   */
   #define Mustek_PP_1015_RING_BUFFER_SIZE 50
   static char buf[Mustek_PP_1015_RING_BUFFER_SIZE][64];
   static int index = 0;
   int i;
   char* current = (char*)buf[index++];
   
   if (index >= Mustek_PP_1015_RING_BUFFER_SIZE) index = 0;
   
   if (val < 0)
   {
      /* The register has not been initialized yet. */
      sprintf(current, "---- (---) --------");
   }
   else
   {
      sprintf(current, "0x%02X (%3d) ", val & 0xFF, val & 0xFF);
      for (i=0; i<8; ++i)
      {
         sprintf(current+11+i, "%d", (val >> (7-i)) & 1);
      }
   }
   return current;
}

#ifdef M1015_TRACE_REGS
/******************************************************************************
 * Displays the contents of all registers of the scanner on stderr.
 *****************************************************************************/
static void
Mustek_PP_1015_display_regs(Mustek_PP_CIS_dev * dev, const char* info)
{
   /*
    * Register naming convention:
    *   Rx   : read-only register no. x
    *   ByWx : write-only register no. x of bank no. y
    */
   
   fprintf(stderr, 
           "\n"
           "Register status: %s\n"
           "\n"
           "    R0: %s  : ASIC info\n"
           "    R1: %s  : scan value\n"
           "    R2: %s  : CCD/motor info\n"
           "    R3: %s  : bank count\n"
           "\n"
           "  B0W0: %s  : red reference\n"
           "  B0W1: %s  : green reference\n"
           "  B0W2: %s  : blue reference\n"
           "  B0W3: %s  : DPI control\n"
           "\n"
           "  B1W0: %s  : byte count, high byte\n"
           "  B1W1: %s  : byte count, low byte\n"
           "  B1W2: %s  : skip x32 pixels\n"
           "  B1W3: %s  : expose time (CCDWIDTH)\n"
           "\n"
           "  B2W0: %s  : SRAM source PC\n"
           "  B2W1: %s  : motor control\n"
           "  B2W2: %s  : -\n"
           "  B2W3: %s  : -\n"
           "\n"
           "  B3W0: %s  : power on delay\n"
           "  B3W1: %s  : CCD timing - always 0x05\n"
           "  B3W2: %s  : CCD timing adjust - always 0x00\n"
           "  B3W3: %s  : right bound (not used)\n"
           "\n"
           "  CHAN: %s  : channel [%s]\n"
           "\n",
           info,
           Mustek_PP_1015_show_val (dev->CIS.regs.in_regs[0]),
           Mustek_PP_1015_show_val (dev->CIS.regs.in_regs[1]),
           Mustek_PP_1015_show_val (dev->CIS.regs.in_regs[2]),
           Mustek_PP_1015_show_val (dev->CIS.regs.in_regs[3]),
           Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[0][0]),
           Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[0][1]),
           Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[0][2]),
           Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[0][3]),
           Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[1][0]),
           Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[1][1]),
           Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[1][2]),
           Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[1][3]),
           Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[2][0]),
           Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[2][1]),
           Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[2][2]),
           Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[2][3]),
           Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[3][0]),
           Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[3][1]),
           Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[3][2]),
           Mustek_PP_1015_show_val (dev->CIS.regs.out_regs[3][3]),
           Mustek_PP_1015_show_val (dev->CIS.regs.channel),
           (dev->CIS.regs.channel == 0x80 ? "RED"   :
           (dev->CIS.regs.channel == 0x40 ? "GREEN" :
           (dev->CIS.regs.channel == 0xC0 ? "BLUE"  : "unknown")))
           );
}   

/******************************************************************************
 * Displays a single register value
 *****************************************************************************/
static void 
Mustek_PP_1015_display_reg(const char* info, int val)
{
   fprintf (stderr, "%s: %s\n", info, Mustek_PP_1015_show_val(val));
}

#endif /* M1015_TRACE_REGS */


/******************************************************************************
 *
 * Reads one of the 4 internal registers of the scanner
 *
 *   0: ASIC identification
 *   1: scan values
 *   2: CCD info / motor info
 *   3: bank count info
 *
 *****************************************************************************/
static SANE_Byte
Mustek_PP_1015_read_reg(Mustek_PP_CIS_dev * dev, Mustek_PP_1015R_reg reg)
{
   SANE_Byte tmp;
   assert(reg <= 3);
   
   SANEI_PA4S2_READBEGIN (dev->desc->fd, reg & 0x03); 
   SANEI_PA4S2_READBYTE (dev->desc->fd, &tmp); 
   SANEI_PA4S2_READEND (dev->desc->fd); 
   
#ifdef M1015_LOG_HL
   sprintf(&hl_next_line[0], "%s read_reg(%s); [%s]\n", cis_indent, 
           Mustek_PP_1015_reg_r_name(reg), Mustek_PP_1015_show_val(tmp));
   M1015_FLUSH_HL;
#endif
   
#ifdef M1015_TRACE_REGS   
   dev->CIS.regs.in_regs[reg & 0x03] = tmp;
#endif
   
   return tmp;
}

/******************************************************************************
 *
 * Waits for a bit of register to become 1 or 0. The period of checking can be
 * controlled through the sleep parameter (microseconds).
 *
 *****************************************************************************/
static SANE_Bool
Mustek_PP_1015_wait_bit(Mustek_PP_CIS_dev * dev, Mustek_PP_1015R_reg reg,
                        Mustek_PP_1015R_bit bit, SANE_Bool on, unsigned period)
{
   SANE_Byte tmp;
   SANE_Byte mask, val;
   int tries = 0;
   
   assert(reg <= 3);
   assert(bit <= 3);
   
   mask = 1 << bit;
   
   /* We don't want to wait forever */
   while (dev->desc->state != STATE_CANCELLED)
   {
#if defined (M1015_LOG_LL) || defined (M1015_LOG_HL)
      ++tries;
#endif

      sanei_pa4s2_readbegin (dev->desc->fd, reg & 0x03); 
      sanei_pa4s2_readbyte (dev->desc->fd, &tmp); 
      sanei_pa4s2_readend (dev->desc->fd); 
      
#ifdef M1015_LOG_HL
   sprintf(&hl_next_line[0], "%s wait_bit(%s, %s, %d): %s %s;\n", cis_indent, 
           Mustek_PP_1015_reg_r_name(reg), Mustek_PP_1015_bit_name(bit),
           on?1:0, Mustek_PP_1015_show_val(mask), Mustek_PP_1015_show_val(tmp));
   M1015_FLUSH_HL;
#endif
      val = ((on == SANE_TRUE) ? tmp : ~tmp ) & mask;
      
      if (val != 0) break;
            
      if (period) usleep(period);
      
      if (tries > 50000) 
      {
#ifdef M1015_LOG_HL
         sprintf(&hl_next_line[0], "%s wait_bit(%s, %s, %d): failed;\n", cis_indent, 
           Mustek_PP_1015_reg_r_name(reg), Mustek_PP_1015_bit_name(bit), on?1:0);
         M1015_FLUSH_HL;
#endif
         DBG(2, "Mustek_PP_1015_wait_bit: failed (reg %d, bit %d, on: %d)\n",
             reg, bit, on?1:0);
         return SANE_FALSE;
      }
   }
   
#ifdef M1015_LOG_HL
   sprintf(&hl_next_line[0], "%s wait_bit(%s, %s, %d);\n", cis_indent, 
           Mustek_PP_1015_reg_r_name(reg), Mustek_PP_1015_bit_name(bit), on?1:0);
   M1015_FLUSH_HL;
#endif
#ifdef M1015_LOG_LL
   fprintf(M1015_LOG_1, "\tread_reg(%s, %d);\n", Mustek_PP_1015_reg_r_name(reg),
           tries);
#endif
   
#ifdef M1015_TRACE_REGS   
   dev->CIS.regs.in_regs[reg & 0x03] = tmp;
#endif
   return dev->desc->state != STATE_CANCELLED ? SANE_TRUE : SANE_FALSE;
}   

/******************************************************************************
 *
 * Writes one out of 4 registers of one of the 4 register banks (I guess)
 *
 * Bank 0
 *    0: voltage red   --+
 *    1: voltage green   +-> always set to 0x96
 *    2: voltage blue  --+
 *    3: DPI control
 *
 * Bank 1
 *    0: line adjust (?) - high byte
 *    1: line adjust (?) - low  byte
 *    2: unknown                       (values seen: 0x00, 0x02, 0x03, 0x1D)
 *    3: expose time (?)               (values seen: 0xAA, 0xFD, 0xFE, 0xFF)
 *
 * Bank 2
 *    0: unknown, used to start linear sequence during calibration
 *    1: motor control code (forward, return home, ...)
 *    2: never used 
 *    3: never used
 * 
 * Bank 3
 *    0: reduction factor (16bit internal -> 8bit) -> target for calibration
 *    1: unknown -> always set to 0x05
 *    2: unknown -> always set to 0x00
 *    3: never used
 *
 *****************************************************************************/
  
static void
Mustek_PP_1015_write_reg(Mustek_PP_CIS_dev * dev, Mustek_PP_1015W_reg reg, SANE_Byte val)
{
   
   SANE_Byte regBank = (reg & 0xF0) >> 4;
   SANE_Byte regNo   = (reg & 0x0F);
   
   assert (regNo   <= 3);
   assert (regBank <= 3);
   
   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (4+regNo))+ regBank);
   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val);
   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, regBank);
   
#ifdef M1015_TRACE_REGS   
   dev->CIS.regs.out_regs[regBank][regNo] = val;
#endif

#ifdef M1015_LOG_HL
   sprintf(&hl_next_line[0], "%s write_reg(%s, 0x%02X);\n", cis_indent, 
           Mustek_PP_1015_reg_w_name(reg), val);
   M1015_FLUSH_HL;
#endif
}

/******************************************************************************
 *
 * Writes 2 values to 2 adjecent registers.
 * It is probably equivalent to 2 simple write operations (but I'm not sure).
 *
 *   val1 is written to register[regNo]
 *   val2 is written to register[regNo+1]
 *
 *****************************************************************************/
static void
Mustek_PP_1015_write_reg2(Mustek_PP_CIS_dev * dev, Mustek_PP_1015W_reg reg, 
               SANE_Byte val1, SANE_Byte val2)
{
   SANE_Byte regBank = (reg & 0xF0) >> 4;
   SANE_Byte regNo   = (reg & 0x0F);
   
   assert (regNo   <= 2);
   assert (regBank <= 3);
   
   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (4+regNo))+ regBank);
   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val1);
   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (5+regNo))+ regBank);
   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val2);
   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, regBank);
   
#ifdef M1015_TRACE_REGS   
   dev->CIS.regs.out_regs[regBank][regNo]   = val1;
   dev->CIS.regs.out_regs[regBank][regNo+1] = val2;
#endif
   
#ifdef M1015_LOG_HL
   sprintf(&hl_next_line[0], "%s write_reg2(%s, 0x%02X, 0x%02X);\n", 
           cis_indent, Mustek_PP_1015_reg_w_name(reg), val1, val2);
   M1015_FLUSH_HL;
#endif
}

/******************************************************************************
 *
 * Writes 3 values to 3 adjecent registers.
 * It is probably equivalent to 3 simple write operations (but I'm not sure).
 *
 *   val1 is written to register[regNo]
 *   val2 is written to register[regNo+1]
 *   val3 is written to register[regNo+2]
 *
 *****************************************************************************/
static void
Mustek_PP_1015_write_reg3(Mustek_PP_CIS_dev * dev, Mustek_PP_1015W_reg reg, 
               SANE_Byte val1, SANE_Byte val2, SANE_Byte val3)
{
   SANE_Byte regBank = (reg & 0xF0) >> 4;
   SANE_Byte regNo   = (reg & 0x0F);
   
   assert (regNo   <= 1);
   assert (regBank <= 3);
   
   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (4+regNo))+ regBank);
   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val1);
   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (5+regNo))+ regBank);
   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val2);
   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (6+regNo))+ regBank);
   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val3);
   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, regBank);
   
#ifdef M1015_TRACE_REGS   
   dev->CIS.regs.out_regs[regBank][regNo  ] = val1;
   dev->CIS.regs.out_regs[regBank][regNo+1] = val2;
   dev->CIS.regs.out_regs[regBank][regNo+2] = val3;
#endif
   
#ifdef M1015_LOG_HL
   sprintf(&hl_next_line[0], "%s write_reg3(%s, 0x%02X, 0x%02X, 0x%02X);\n", 
           cis_indent, Mustek_PP_1015_reg_w_name(reg), val1, val2, val3);
   M1015_FLUSH_HL;
#endif
}

/******************************************************************************
 * Opens a register for a (series of) write operation(s).
 *****************************************************************************/ 
static void
Mustek_PP_1015_write_reg_start(Mustek_PP_CIS_dev * dev, Mustek_PP_1015W_reg reg)
{
   SANE_Byte regBank = (reg & 0xF0) >> 4;
   SANE_Byte regNo   = (reg & 0x0F);
   
   assert (regNo   <= 3);
   assert (regBank <= 3);
   
   dev->CIS.regs.current_write_reg = reg;
   
#ifdef M1015_LOG_HL
   dev->CIS.regs.write_count = 0;
#endif
      
   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, (1 << (4+regNo))+ regBank);
}

/******************************************************************************
 * Writes a value to the currently open register.
 *****************************************************************************/ 
static void
Mustek_PP_1015_write_reg_val(Mustek_PP_CIS_dev * dev, SANE_Byte val)
{
#ifdef M1015_TRACE_REGS   
   SANE_Byte regBank = (dev->CIS.regs.current_write_reg & 0xF0) >> 4;
   SANE_Byte regNo   = (dev->CIS.regs.current_write_reg & 0x0F);
   
   assert (regNo   <= 3);
   assert (regBank <= 3);
   
   dev->CIS.regs.out_regs[regBank][regNo] = val;
#endif
   
   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 5, val);
   
#ifdef M1015_LOG_HL
   ++dev->CIS.regs.write_count;
#endif
}

/******************************************************************************
 * Closes a register after a (series of) write operation(s).
 *****************************************************************************/ 
static void
Mustek_PP_1015_write_reg_stop(Mustek_PP_CIS_dev * dev)
{
   SANE_Byte regBank = (dev->CIS.regs.current_write_reg & 0xF0) >> 4;
#ifdef M1015_LOG_HL
   SANE_Byte regNo   = (dev->CIS.regs.current_write_reg & 0x0F);
   assert (regNo   <= 3);
   
   sprintf(&hl_next_line[0], "%s write_reg_multi(%s, *%d);\n",  cis_indent,
           Mustek_PP_1015_reg_w_name(dev->CIS.regs.current_write_reg), 
           dev->CIS.regs.write_count);
   M1015_FLUSH_HL;
#endif
   assert (regBank <= 3);
   
   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, regBank);
}   

/******************************************************************************
 *
 * Sends a command to the scanner. The command should not access one of the
 * internal registers, ie., the 3rd bit should not be zero.
 *
 *****************************************************************************/
static void 
Mustek_PP_1015_send_command(Mustek_PP_CIS_dev * dev, SANE_Byte command)
{
   assert (command & 0x04);
   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, command);
   
#ifdef M1015_LOG_HL
   sprintf(&hl_next_line[0], "%s send_command(0x%02X);\n", cis_indent, command);
   M1015_FLUSH_HL;
#endif
}

/******************************************************************************
 ##############################################################################
 ##                              CIS driver                                  ##
 ##############################################################################
 *****************************************************************************/
 
/******************************************************************************
 * Resolution conversion functions
 *****************************************************************************/
static int
max2hw_hres(Mustek_PP_CIS_dev *dev, int dist)
{
   return (int)((dist * dev->CIS.hw_hres) / dev->desc->dev->maxres + 0.5);
}

#ifdef NOT_USED
static int
max2hw_vres(Mustek_PP_CIS_dev *dev, int dist)
{
   return (int)((dist * dev->CIS.hw_vres) / dev->desc->dev->maxres + 0.5);
}
#endif

static int
max2cis_hres(Mustek_PP_CIS_dev *dev, int dist)
{
   return (int)((dist * dev->CIS.cisRes) / dev->desc->dev->maxres + 0.5);
}

static int
cis2max_res(Mustek_PP_CIS_dev *dev, int dist)
{
   return (int)((dist * dev->desc->dev->maxres) / dev->CIS.cisRes + 0.5);
}

#ifdef NOT_USED
static int
hw2max_vres(Mustek_PP_CIS_dev *dev, int dist)
{
   return (int)((dist * dev->desc->dev->maxres) / dev->CIS.hw_vres + 0.5);
}
#endif

/******************************************************************************
 * Attempts to extract the current bank no.
 *****************************************************************************/
static void
cis_get_bank_count(Mustek_PP_CIS_dev *dev)
{
   dev->bank_count = (Mustek_PP_1015_read_reg(dev, MA1015R_BANK_COUNT) & 0x7);
   if (dev->CIS.use8KBank) dev->bank_count >>= 1;
}

/******************************************************************************
 * Triggers a bank switch (I assume).
 *****************************************************************************/
static void
cis_set_sti(Mustek_PP_CIS_dev *dev)
{
   SANEI_PA4S2_WRITEBYTE(dev->desc->fd, 3, 0xFF);
   dev->bank_count++;
   dev->bank_count &= (dev->CIS.use8KBank == SANE_TRUE) ? 3 : 7;
}

/******************************************************************************
 * Wait till the bank with a given number becomes available.
 *****************************************************************************/
static SANE_Bool
cis_wait_bank_change (Mustek_PP_CIS_dev * dev, int bankcount)
{
  struct timeval start, end;
  unsigned long diff;
  int firsttime = 1;
  
  gettimeofday (&start, NULL);

  do
    {
      if (1 /*niceload*/)
	{
	  if (firsttime)
	    firsttime = 0;
	  else
	    usleep (10);	/* for a little nicer load */
	}
      cis_get_bank_count (dev);

      gettimeofday (&end, NULL);
      diff = (end.tv_sec * 1000 + end.tv_usec / 1000) -
	(start.tv_sec * 1000 + start.tv_usec / 1000);

    }
  while ((dev->bank_count != bankcount) && (diff < MUSTEK_PP_CIS_WAIT_BANK));
  
  if (dev->bank_count != bankcount && dev->desc->state != STATE_CANCELLED)
  {
     u_char tmp;
     tmp = Mustek_PP_1015_read_reg(dev, 3);
     DBG(2, "cis_wait_bank_change: Missed a bank: got %d [%s], "
            "wanted %d, waited %d msec\n", 
             dev->bank_count, Mustek_PP_1015_show_val(tmp), bankcount, 
             MUSTEK_PP_CIS_WAIT_BANK);
  }

   return dev->bank_count == bankcount ? SANE_TRUE : SANE_FALSE;
}

/******************************************************************************
 * Configure the CIS for a given resolution.
 * 
 * CIS scanners seem to have 2 modes: 
 *
 *   low resolution (50-300 DPI) and 
 *   high resolution (300-600 DPI).
 *
 * Depending on the resolution requested by the user, the scanner is used
 * in high or low resolution mode. In high resolution mode, the motor step
 * sizes are also reduced by a factor of two.
 *
 *****************************************************************************/
static void
cis_set_dpi_value (Mustek_PP_CIS_dev * dev)
{
   u_char val = 0;
  
   if (dev->model == MUSTEK_PP_CIS1200PLUS)
   {
      /* Toshiba CIS: only 600 DPI + decimation */
      switch (dev->CIS.hw_hres)
      {
         case 75:
            val = 0x48; /* 1/8 */
            break;
         case 100:
            val = 0x08; /* 1/6 */
            break;
         case 200:
            val = 0x00; /* 1/3 */
            break;
         case 300:
            val = 0x50; /* 2/4 */
            break;
         case 400:
            val = 0x10; /* 2/3 */
            break;
         case 600:
            val = 0x20; /* 3/3 */
            break;
         default:
            assert (0);
      }      
   }
   else
   {
      /* Canon CIS: sensor can use 300 or 600 DPI */
      switch (dev->CIS.hw_hres)
      {
         case 50:
            val = 0x08; /* 1/6 */
            break;
         case 100:
            val = 0x00; /* 1/3 */
            break;
         case 200:
            val = 0x10; /* 2/3 */
            break;
         case 300:
            val = 0x20; /* 3/3 */
            break;
         case 400:
            val = 0x10; /* 2/3 */
            break;
         case 600:
            val = 0x20; /* 3/3 */
            break;
         default:
            assert (0);
      }
   }
   
   Mustek_PP_1015_write_reg(dev, MA1015W_DPI_CONTROL, val | 0x04);

   DBG (4, "cis_set_dpi_value: dpi: %d -> value 0x%02x\n", dev->CIS.hw_hres, val);
}

static void
cis_set_ccd_channel (Mustek_PP_CIS_dev * dev)
{

   SANE_Byte codes[] = { 0x84, 0x44, 0xC4 };
   SANE_Byte chancode;
   
   assert (dev->CIS.channel < 3);
   
   chancode = codes[dev->CIS.channel];
   
   /* 
      The TWAIN driver sets an extra bit in lineart mode. 
      When I do this too, I don't see any effect on the image. 
      Moreover, for 1 resolution, namely 400 dpi, the bank counter seems 
      to behave strangely, and the synchronization get completely lost.
      I guess the software conversion from gray to lineart is good enough,
      so I'll leave it like that.
      
      if (dev->CIS.setParameters)
      {
         chancode |= (dev->desc->mode == MODE_BW) ? 0x20: 0;
      }
   */

   SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, chancode);
   
#ifdef M1015_TRACE_REGS
   dev->CIS.regs.channel = chancode;
#endif
}

static void
cis_config_ccd (Mustek_PP_CIS_dev * dev)
{
   SANE_Int skipCount, byteCount;
   
   if (dev->CIS.res != 0)
     dev->CIS.hres_step =
       SANE_FIX ((float) dev->CIS.hw_hres / (float) dev->CIS.res);

   /* CIS:  <= 300 dpi -> 0x86
             > 300 dpi -> 0x96 */  

   if (dev->CIS.cisRes == 600)
      SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, 0x96);
   else
      SANEI_PA4S2_WRITEBYTE (dev->desc->fd, 6, 0x86);
   
   cis_set_dpi_value(dev);
   
   if (dev->CIS.setParameters)
   {
      dev->CIS.channel = dev->desc->mode == MODE_COLOR ?
                         MUSTEK_PP_CIS_CHANNEL_RED : MUSTEK_PP_CIS_CHANNEL_GRAY;
   }
   else
   { 
      dev->CIS.channel = MUSTEK_PP_CIS_CHANNEL_GRAY;
   }
   
   cis_set_ccd_channel (dev);
   
   Mustek_PP_1015_write_reg (dev, MA1015W_POWER_ON_DELAY, 0xAA);
   Mustek_PP_1015_write_reg (dev, MA1015W_CCD_TIMING,     0x05); 
   Mustek_PP_1015_write_reg (dev, MA1015W_CCD_TIMING_ADJ, 0x00); 

   Mustek_PP_1015_send_command (dev, 0x45);	/* or 0x05 for no 8kbank */

   /*
    * Unknown sequence.
    * Seems to be always the same during configuration, independent of the
    * mode and the resolution. 
    */
   CIS_CLEAR_FULLFLAG(dev);
   CIS_INC_READ(dev);
   CIS_CLEAR_READ_BANK(dev);
   CIS_CLEAR_WRITE_ADDR(dev);
   CIS_CLEAR_WRITE_BANK(dev);
   CIS_CLEAR_TOGGLE(dev);

   /*
    # SkipImage = expressed in max resolution (600 DPI)
    #
    # Formulas
    #
    #  <= 300 DPI:
    #
    #  Skip = 67 + skipimage/2
    #
    #   Skip1 = Skip / 32
    #   Skip2 = Skip % 32
    #
    #  Bytes = Skip2 * hw_hres/300 + (imagebytes * hw_hres/res) + 2
    #
    #  > 300 DPI
    #
    #  Skip = 67 + skipimage
    #  
    #   Skip1 = Skip / 32
    #   Skip2 = Skip % 32
    #
    #  Bytes = Skip2*hw_hres/600 + (imagebytes * hw_hres/res) + 2
    #
   */
   
   skipCount = 67; /* Hardware parameter - fixed */

   if (dev->CIS.setParameters == SANE_TRUE)
   {
      /*
       * It seems that the TWAIN driver always adds 2 mm extra. When I do the
       * inverse calculation from the parameters that driver sends, I always
       * get a difference of exactly 2mm, at every resolution and for
       * different positions of the scan area. Moreover, when I don't add this
       * offset, the resulting scan seems to start 2mm to soon.
       * I can't find this back in the backend of the TWAIN driver, but I
       * assume that this 2mm offset is taken care off at the higher levels.
       */
      DBG(4, "cis_config_ccd: Skip count: %d\n",  skipCount);
      skipCount += max2cis_hres(dev, dev->CIS.skipimagebytes);
      DBG(4, "cis_config_ccd: Skip count: %d (cis res: %d)\n",  skipCount, 
             dev->CIS.cisRes);
      skipCount += (int)(2.0/25.4*dev->CIS.cisRes);
      DBG(4, "cis_config_ccd: Skip count: %d\n",  skipCount);
      
      Mustek_PP_1015_write_reg (dev, MA1015W_SKIP_COUNT, skipCount / 32);
      DBG(4, "cis_config_ccd: Skip count: %d (x32)\n",  skipCount / 32);
   }
   else
   {
      Mustek_PP_1015_write_reg (dev, MA1015W_SKIP_COUNT, 0);
      DBG(4, "cis_config_ccd: Skip count: 67 (x32)\n");
   }
      
   skipCount %= 32;
   skipCount = cis2max_res(dev, skipCount);  /* Back to max res */
   
   Mustek_PP_1015_write_reg(dev, MA1015W_EXPOSE_TIME, dev->CIS.exposeTime); 
      
   DBG(4, "cis_config_ccd: skipcount: %d imagebytes: %d\n", skipCount, dev->CIS.imagebytes);
   /* set_initial_skip_1015 (dev); */
   if (dev->CIS.setParameters == SANE_TRUE)
   {
      Mustek_PP_1015_write_reg(dev, MA1015W_EXPOSE_TIME, dev->CIS.exposeTime);
      Mustek_PP_1015_write_reg(dev, MA1015W_POWER_ON_DELAY,   0xAA);
      /* The TWAIN drivers always sends the same value: 0x96 */
      Mustek_PP_1015_write_reg3(dev, MA1015W_RED_REF, 0x96, 0x96, 0x96);
      dev->CIS.adjustskip = max2hw_hres(dev, skipCount);
      byteCount = max2hw_hres(dev, skipCount + dev->CIS.imagebytes) + 2; 
      dev->CIS.setParameters = SANE_FALSE;
   }
   else
   {
      dev->CIS.adjustskip = 0;
      byteCount = max2hw_hres(dev, skipCount); 
   }
   DBG(4, "cis_config_ccd: adjust skip: %d bytecount: %d\n", 
          dev->CIS.adjustskip, byteCount);
   
   Mustek_PP_1015_write_reg2(dev, MA1015W_BYTE_COUNT_HB, 
                             byteCount >> 8, byteCount & 0xFF); 
   
   cis_get_bank_count (dev);
   DBG(5, "cis_config_ccd: done\n");
}

static SANE_Bool
cis_wait_motor_stable (Mustek_PP_CIS_dev * dev)
{
   return Mustek_PP_1015_wait_bit (dev, MA1015R_MOTOR, MA1015B_MOTOR_STABLE, 
                                   SANE_FALSE, 0);
}

static void
cis_motor_forward (Mustek_PP_CIS_dev * dev)
{
   SANE_Byte control;
   
   if (dev->model == MUSTEK_PP_CIS600)
   {
      switch (dev->CIS.hw_vres)
      {
         case 150:
            control = 0x7B;
            break;
         case 300:
            control = 0x73;
            break;
         case 600:
            control = 0x13;
            break;
         default:
            exit(1);
      }
   }
   else
   {
      switch (dev->CIS.hw_vres)
      {
         case 300:
            control = 0x7B;
            break;
         case 600:
            control = 0x73;
            break;
         case 1200:
            control = 0x13;
            break;
         default:
            exit(1);
      }
   }

#if MUSTEK_PP_CIS_MOTOR_REVERSE == 1
   control ^= 0x10;
#endif
   
   DBG(4, "cis_motor_forward: @%d dpi: 0x%02X.\n", dev->CIS.hw_vres, control);
   if (!cis_wait_motor_stable (dev))
      return;
   
   Mustek_PP_1015_write_reg(dev, MA1015W_MOTOR_CONTROL, control);
}

static void
cis_move_motor (Mustek_PP_CIS_dev * dev, SANE_Int steps) /* steps @ maxres */
{
   /* Note: steps is expressed at maximum resolution */
   SANE_Byte fullStep = 0x13, biStep = 0x73, quadStep = 0x7B;
   SANE_Int fullSteps, biSteps, quadSteps;
   /*
    * During a multi-step feed, the expose time is fixed. The value depends
    * on the type of the motor (600/1200 CP) 
    */
   SANE_Byte savedExposeTime = dev->CIS.exposeTime;
   dev->CIS.exposeTime = 85;
   
   DBG(4, "cis_move_motor: Moving motor %d steps.\n", steps);
   
   /* Just in case ... */
   if (steps < 0)
   {
      DBG(1, "cis_move_motor: trying to move negative steps: %d\n", steps);
      steps = 0; /* We must go through the configuration procedure */
   }      
   
   /*
    * Using the parameter settings for the 600 CP on a 1200 CP scanner
    * doesn't work: the engine doesn't move and makes a sharp noise, which
    * doesn't sound too healthy. It could be harmful to the motor !
    * Apparently, the same happens on a real 600 CP (reported by Disma
    * Goggia), so it's probably better to always use the 1200 CP settings.
    */
   dev->CIS.exposeTime <<= 1;
   cis_config_ccd(dev);
   dev->CIS.exposeTime = savedExposeTime;
   
   /* 
    * This is a minor speed optimization: when we are using the high
    * resolution mode, long feeds (eg, to move to a scan area at the bottom
    * of the page) can be made almost twice as fast by using double motor
    * steps as much as possible.
    * It is possible, though, that fast skipping (which is the default) is
    * not very accurate on some scanners. Therefore, the user can disable
    * this through the configuration file.
    */  
      
   fullSteps = steps  & 1;
   biSteps = steps >> 1;
   if (dev->fast_skip) {
      quadSteps = biSteps >> 1;
      biSteps &= 1;
   }
   else {
      quadSteps = 0;
   }
   
   M1015_DISPLAY_REGS(dev, "Before move");
   
#if MUSTEK_PP_CIS_MOTOR_REVERSE == 1
   fullStep ^= 0x10;
   biStep ^= 0x10;
   quadStep ^= 0x10;
#endif
   
   DBG(4, "cis_move_motor: 4x%d 2x%d 1x%d\n", quadSteps, biSteps, fullSteps);
   /* Note: the TWAIN driver opens the motor control register only 
      once before the loop, and closes it after the loop. I've tried this
      too, but it resulted in inaccurate skip distances; therefore, the
      motor control register is now opened and closed for each step. */
     
   while (quadSteps-- > 0 && dev->desc->state != STATE_CANCELLED)
   {
      cis_wait_motor_stable (dev);
      Mustek_PP_1015_write_reg(dev, MA1015W_MOTOR_CONTROL, quadStep);
   }
   
   while (biSteps-- > 0 && dev->desc->state != STATE_CANCELLED)
   {
      cis_wait_motor_stable (dev);
      Mustek_PP_1015_write_reg(dev, MA1015W_MOTOR_CONTROL, biStep);
   }
   
   while (fullSteps-- > 0 && dev->desc->state != STATE_CANCELLED)
   {
      cis_wait_motor_stable (dev);
      Mustek_PP_1015_write_reg(dev, MA1015W_MOTOR_CONTROL, fullStep);
   }
}

static void
cis_set_et_pd_sti (Mustek_PP_CIS_dev * dev)
{
   Mustek_PP_1015_write_reg(dev, MA1015W_EXPOSE_TIME, 
                                 dev->CIS.exposeTime);
   Mustek_PP_1015_write_reg(dev, MA1015W_POWER_ON_DELAY, 
                                 dev->CIS.powerOnDelay[dev->CIS.channel]);
   cis_set_ccd_channel (dev);
   cis_set_sti (dev);
}

/*
 * Prepare the scanner for catching the next channel and, if necessary, 
 * move the head one step further.
 */
static SANE_Bool
cis_wait_next_channel (Mustek_PP_CIS_dev * dev)
{
   int moveAtChannel = dev->desc->mode == MODE_COLOR ?
                       MUSTEK_PP_CIS_CHANNEL_BLUE : MUSTEK_PP_CIS_CHANNEL_GRAY;
   
   if (!cis_wait_bank_change (dev, dev->bank_count))
   {
      DBG(2, "cis_wait_next_channel: Could not get next bank.\n");
      return SANE_FALSE;
   }
   
   moveAtChannel = (dev->desc->mode == MODE_COLOR) ?
                    MUSTEK_PP_CIS_CHANNEL_BLUE : MUSTEK_PP_CIS_CHANNEL_GRAY;
      
   if (dev->CIS.channel == moveAtChannel && !dev->CIS.dontMove)
   {
      cis_motor_forward (dev);
   }
   
   cis_set_et_pd_sti (dev);
   
   if (dev->desc->mode == MODE_COLOR)
   {
      ++dev->CIS.channel;
      dev->CIS.channel %= 3;
   }
   
   return SANE_TRUE;
}
   
/*
 * Wait for the device to be ready for scanning. Cycles through the different
 * channels and sets the parameters (only green channel in gray/lineart).
 */
static SANE_Bool
cis_wait_read_ready (Mustek_PP_CIS_dev * dev)
{
   int channel;
   dev->CIS.dontIncRead = SANE_TRUE;
   
   dev->CIS.channel =  dev->desc->mode == MODE_COLOR ?
                       MUSTEK_PP_CIS_CHANNEL_RED : MUSTEK_PP_CIS_CHANNEL_GRAY;
   
   for (channel = 0; channel < 3; ++channel)
   {
      if (!cis_wait_next_channel(dev)) return SANE_FALSE;
   }
   return SANE_TRUE;
}

static int
delay_read (int delay)
{
   /* 
    * A (very) smart compiler may complete optimize the delay loop away. By
    * adding some difficult data dependencies, we can try to prevent this. 
    */
   static int prevent_removal, i;
   for (i = 0; i<delay; ++i)
   {
      prevent_removal = sqrt(prevent_removal+1.); /* Just waste some cycles */
   }
   return prevent_removal; /* another data dependency */
}

/*
** Reads one line of pixels
*/
static void
cis_read_line_low_level (Mustek_PP_CIS_dev * dev, SANE_Byte * buf, 
                         SANE_Int pixel, SANE_Byte * calib_low, 
                         SANE_Byte * calib_hi, SANE_Int * gamma)
{
   u_char color;
   int ctr, skips = dev->CIS.adjustskip, cval;
   int bpos = 0;
   SANE_Byte low_val = 0, hi_val = 255;

   if (pixel <= 0)
      return;
  
   SANEI_PA4S2_READBEGIN (dev->desc->fd, 1);

   while(skips-- >= 0)
   {
      if (dev->CIS.delay) delay_read(dev->CIS.delay);
      SANEI_PA4S2_READBYTE (dev->desc->fd, &color);
   }

   if (dev->CIS.hw_hres == dev->CIS.res)
   {
      /* One-to one mapping */
      DBG (6, "cis_read_line_low_level: one-to-one\n");
      for (ctr = 0; ctr < pixel; ctr++)
      {
         if (dev->CIS.delay) delay_read(dev->CIS.delay);
         SANEI_PA4S2_READBYTE (dev->desc->fd, &color);

	 cval = color;

         if (calib_low) {
           low_val = calib_low[ctr] ;
         }

         if (calib_hi) {
           hi_val = calib_hi[ctr] ;
         }

         cval  -= low_val ;
         cval <<= 8 ;
         cval  /= hi_val-low_val ;

         if (cval < 0)         cval = 0;
         else if (cval > 255)  cval = 255;

         if (gamma)
	    cval = gamma[cval];

	 buf[ctr] = cval;
      }
   }
   else if (dev->CIS.hw_hres > dev->CIS.res)
   {
      /* Sub-sampling */
      
      int pos = 0;
      DBG (6, "cis_read_line_low_level: sub-sampling\n");
      ctr = 0;
      do
      {
         if (dev->CIS.delay) delay_read(dev->CIS.delay);
         SANEI_PA4S2_READBYTE (dev->desc->fd, &color);

         cval = color;
         if (ctr < (pos >> SANE_FIXED_SCALE_SHIFT))
	 {
	    ctr++;
	    continue;
	 }

         ctr++;
         pos += dev->CIS.hres_step;

         if (calib_low) {
           low_val = calib_low[bpos] ;
         }

         if (calib_hi) {
           hi_val = calib_hi[bpos] ;
         }
         cval  -= low_val ;
         cval <<= 8 ;
         cval  /= hi_val-low_val ;

         if (cval < 0)         cval = 0 ;
         else if (cval > 255)  cval = 255 ;

         if (gamma) cval = gamma[cval];

         buf[bpos++] = cval;
      }
      while (bpos < pixel);
   }
   else
   {
      int calctr = 0;
      SANE_Int pos = 0, nextPos = 1;
      /* Step: eg: 600 DPI -> 700 DPI -> hres_step = 6/7 -> step = 1/7 */
      SANE_Int step = SANE_FIX(1) - dev->CIS.hres_step; 
      
      /* Super-sampling */
      DBG (6, "cis_read_line_low_level: super-sampling\n");
      do
      {
         if (dev->CIS.delay) delay_read(dev->CIS.delay);
         SANEI_PA4S2_READBYTE (dev->desc->fd, &color);

	 cval = color;

         if (calib_low) {
           low_val = calib_low[calctr] ;
         }

         if (calib_hi) {
           hi_val = calib_hi[calctr] ;
         }
         
         if (++calctr >= dev->calib_pixels) {
            /* Avoid array boundary violations due to rounding errors 
               (due to the incremental calculation, the current position
               may be inaccurate to up to two pixels, so we may need to 
               read a few extra bytes -> use the last calibration value) */
            calctr = dev->calib_pixels - 1;
            DBG (3, "cis_read_line_low_level: calibration overshoot\n");
         }

         cval  -= low_val ;
         cval <<= 8 ;
         cval  /= hi_val-low_val ;

         if (cval < 0)         cval = 0 ;
         else if (cval > 255)  cval = 255 ;

         if (gamma)
	    cval = gamma[cval];

         pos += step;
         
         if ((pos >> SANE_FIXED_SCALE_SHIFT) >= nextPos)
         {
            nextPos++;
            
            /* Insert an interpolated value */
            buf[bpos] = (buf[bpos-1] + cval)/2; /* Interpolate */
            ++bpos;
            
            /* Store the plain value, but only if we still need pixels */
            if (bpos < pixel)
               buf[bpos++] = cval;
            
            pos += step; /* Take interpolated value into account for pos */
         }
         else
         {
  	    buf[bpos++] = cval;
         }
      }
      while (bpos < pixel);
   }
   
   SANEI_PA4S2_READEND (dev->desc->fd);
   DBG (6, "cis_read_line_low_level: done\n");
}

static SANE_Bool
cis_read_line (Mustek_PP_CIS_dev * dev, SANE_Byte* buf, SANE_Int pixel,
               SANE_Bool raw)
{
   if (!dev->CIS.dontIncRead)
      CIS_INC_READ(dev);
   else
      dev->CIS.dontIncRead = SANE_FALSE;
   
   
   if (raw)
   {
      /* No color correction; raw data */
      cis_read_line_low_level (dev, buf, pixel, NULL, NULL, NULL);
   }
   else
   {
      /* Color correction */
      cis_read_line_low_level (dev, buf, pixel,
                               dev->calib_low[dev->CIS.channel], 
                               dev->calib_hi[dev->CIS.channel], 
                               (dev->desc->val[OPT_CUSTOM_GAMMA].w ? 
                               dev->desc->gamma_table[dev->CIS.channel] : NULL));
   }
   
   return cis_wait_next_channel(dev);
}

static void
cis_get_next_line (Mustek_PP_CIS_dev * dev, SANE_Byte * buf)
{
   SANE_Byte *dest, *tmpbuf = dev->tmpbuf;
   int ctr, channel, first, last, stride, ignore, step = dev->CIS.line_step;
   SANE_Byte gotline;
   
   if (dev->desc->mode == MODE_COLOR)
   {
      first = MUSTEK_PP_CIS_CHANNEL_RED;
      last = MUSTEK_PP_CIS_CHANNEL_BLUE;
      stride = 3;
      ignore = 1; /* 1 * 3 channels */
   }
   else
   {
      first = MUSTEK_PP_CIS_CHANNEL_GRAY;
      last = MUSTEK_PP_CIS_CHANNEL_GRAY;
      stride = 1;
      ignore = 3; /* 3 * 1 channel */
   }
   
   gotline = SANE_FALSE;
   do
   {
      dev->ccd_line++;
      if ((dev->line_diff >> SANE_FIXED_SCALE_SHIFT) != dev->ccd_line)
      {
         cis_motor_forward (dev);
         continue;
      }

      dev->line_diff += step;

      for (channel = first; channel <= last; ++channel)
      {
         if (!cis_read_line(dev, tmpbuf, dev->desc->params.pixels_per_line, 
                            SANE_FALSE)) 
            return;

         dest = buf + channel - first;
         for (ctr = 0; ctr < dev->desc->params.pixels_per_line; ctr++)
         {
	    *dest = tmpbuf[ctr];
            dest += stride;
         }
      }
      gotline = SANE_TRUE;
   }
   while (!gotline && dev->desc->state != STATE_CANCELLED);
}

static void
cis_get_grayscale_line (Mustek_PP_CIS_dev * dev, SANE_Byte * buf)
{
   cis_get_next_line(dev, buf);
}

static void
cis_get_lineart_line (Mustek_PP_CIS_dev * dev, SANE_Byte * buf)
{
   int ctr;
   SANE_Byte gbuf[MUSTEK_PP_CIS_MAX_H_PIXEL * 2];

   cis_get_grayscale_line (dev, gbuf);
   memset (buf, 0xFF, dev->desc->params.bytes_per_line);

   for (ctr = 0; ctr < dev->desc->params.pixels_per_line; ctr++)
      buf[ctr >> 3] ^= ((gbuf[ctr] > dev->bw_limit) ? (1 << (7 - ctr % 8)) : 0);
}

static void
cis_get_color_line (Mustek_PP_CIS_dev * dev, SANE_Byte * buf)
{
   cis_get_next_line(dev, buf);
}


/******************************************************************************
 * Saves the state of the device during reset and calibration.
 *****************************************************************************/
static void 
cis_save_state (Mustek_PP_CIS_dev * dev)
{
   dev->Saved_CIS = dev->CIS;
}

/******************************************************************************
 * Restores the state of the device after reset and calibration.
 *****************************************************************************/
static void 
cis_restore_state (Mustek_PP_CIS_dev * dev)
{
   dev->CIS = dev->Saved_CIS;
}

#define CIS_TOO_BRIGHT 1
#define CIS_OK         0
#define CIS_TOO_DARK  -1

static int
cis_check_result(SANE_Byte* buffer, int pixel)
{
   int i, maxVal = 0;
   
   for (i=0;i<pixel;++i) 
      if (buffer[i] > maxVal) maxVal = buffer[i];
   
   if (maxVal > 250) return CIS_TOO_BRIGHT;
   if (maxVal < 240) return CIS_TOO_DARK;
   return CIS_OK;
}

static SANE_Bool
cis_maximize_dynamic_range(Mustek_PP_CIS_dev * dev)
{
   /* The device is in its final configuration already. */
   int i, j, pixel, channel, minExposeTime, first, last;
   SANE_Byte powerOnDelayLower[3], powerOnDelayUpper[3], exposeTime[3];
   SANE_Byte buf[3][MUSTEK_PP_CIS_MAX_H_PIXEL];
   SANE_Int pixels = dev->calib_pixels;
   
   DBG(3, "cis_maximize_dynamic_range: starting\n");
   
   for (channel = 0; channel < 3; ++channel)
   {
      exposeTime[channel] = 254;
      dev->CIS.powerOnDelay[channel] = 170;
      powerOnDelayLower[channel] = 1;
      powerOnDelayUpper[channel] = 254;
   }         
   dev->CIS.setParameters  = SANE_TRUE;
   dev->CIS.exposeTime     = exposeTime[MUSTEK_PP_CIS_CHANNEL_GREEN];
   cis_config_ccd(dev);
   
   M1015_DISPLAY_REGS(dev, "before maximizing dynamic range");
   dev->CIS.dontMove = SANE_TRUE; /* Don't move while calibrating */
   
   if (!cis_wait_read_ready(dev) && dev->desc->state != STATE_CANCELLED)
   {
      DBG(2, "cis_maximize_dynamic_range: DEVICE NOT READY!\n");
      return SANE_FALSE;
   }
   
   if (dev->desc->mode == MODE_COLOR)
   {
      first = MUSTEK_PP_CIS_CHANNEL_RED;
      last  = MUSTEK_PP_CIS_CHANNEL_BLUE;
   }
   else
   {
      first = MUSTEK_PP_CIS_CHANNEL_GRAY;
      last  = MUSTEK_PP_CIS_CHANNEL_GRAY;
   }
   
   dev->CIS.channel = first;
   
   /* Perform a kind of binary search. In the worst case, we should find 
      the optimal power delay values after 8 iterations */
   for( i=0; i<8; i++)
   {
      for (channel = first; channel <= last; ++channel)
      {
         dev->CIS.powerOnDelay[channel] = (powerOnDelayLower[channel] +
                                           powerOnDelayUpper[channel]) / 2;
      }
      Mustek_PP_1015_write_reg(dev, MA1015W_POWER_ON_DELAY, 
                               dev->CIS.powerOnDelay[1]); /* Green */

      for (pixel = 0; pixel < pixels; ++pixel)
      {
         buf[0][pixel] = buf[1][pixel] = buf[2][pixel] = 255;
      }
      
      /* Scan 4 lines, but ignore the first 3 ones. */
      for (j = 0; j < 4; ++j)
      {
         for (channel = first; channel <= last; ++channel)
         {
            if (!cis_read_line(dev, &buf[channel][0], pixels, 
                               /* raw = */ SANE_TRUE))
               return SANE_FALSE;
         }
      }

      for (channel = first; channel <= last; ++channel)
      {
         switch (cis_check_result(buf[channel], pixels))
         {
            case CIS_TOO_BRIGHT:
               powerOnDelayLower[channel] = dev->CIS.powerOnDelay[channel];
               break;   
               
            case CIS_TOO_DARK:
               powerOnDelayUpper[channel] = dev->CIS.powerOnDelay[channel];
               break;   
               
            default:
               break;   
         }
      } 
      DBG (4, "cis_maximize_dynamic_range: power on delay %3d %3d %3d\n", 
           dev->CIS.powerOnDelay[0], dev->CIS.powerOnDelay[1], 
           dev->CIS.powerOnDelay[2]);
   }
   dev->CIS.dontMove = SANE_FALSE;
   
   DBG (3, "cis_maximize_dynamic_range: power on delay %3d %3d %3d\n", 
           dev->CIS.powerOnDelay[0], dev->CIS.powerOnDelay[1], 
           dev->CIS.powerOnDelay[2]);
           
   minExposeTime = (dev->CIS.hw_hres <= 300) ? 170 : 253;
   
   for (channel = first; channel <= last; ++channel)
   {
      dev->CIS.powerOnDelay[channel] = (powerOnDelayLower[channel] +
                                        powerOnDelayUpper[channel]) / 2;
      exposeTime[channel] -= dev->CIS.powerOnDelay[channel] - 1;
      dev->CIS.powerOnDelay[channel] = 1;
      
      if (exposeTime[channel] < minExposeTime)
      {
         dev->CIS.powerOnDelay[channel] += 
            minExposeTime - exposeTime[channel];
         exposeTime[channel] = minExposeTime;
      }
   }                                      
   
   dev->CIS.exposeTime = exposeTime[MUSTEK_PP_CIS_CHANNEL_GREEN];
   
   DBG (3, "cis_maximize_dynamic_range: expose time: %3d\n", exposeTime[1]);
   DBG (3, "cis_maximize_dynamic_range: power on delay %3d %3d %3d\n", 
           dev->CIS.powerOnDelay[0], dev->CIS.powerOnDelay[1], 
           dev->CIS.powerOnDelay[2]);
   
   /*
    * Short the calibration. Temporary, to find out what is wrong with
    * the calibration on a 600 CP.
    *
   dev->CIS.exposeTime = 170;
   dev->CIS.powerOnDelay[0] = 120;
   dev->CIS.powerOnDelay[1] = 120;
   dev->CIS.powerOnDelay[2] = 120;
   */
   return SANE_TRUE;
}

static SANE_Bool
cis_measure_extremes(Mustek_PP_CIS_dev * dev, SANE_Byte* calib[3],
		     SANE_Int pixels, SANE_Int first, SANE_Int last)
{
   SANE_Byte buf[3][MUSTEK_PP_CIS_MAX_H_PIXEL];
   SANE_Byte min[3][MUSTEK_PP_CIS_MAX_H_PIXEL];
   SANE_Byte max[3][MUSTEK_PP_CIS_MAX_H_PIXEL];
   SANE_Int  sum[3][MUSTEK_PP_CIS_MAX_H_PIXEL];
   int channel, cnt, p;
   
   memset((void*)&min, 255, 3*MUSTEK_PP_CIS_MAX_H_PIXEL*sizeof(SANE_Byte));
   memset((void*)&max,   0, 3*MUSTEK_PP_CIS_MAX_H_PIXEL*sizeof(SANE_Byte));
   memset((void*)&sum,   0, 3*MUSTEK_PP_CIS_MAX_H_PIXEL*sizeof(SANE_Int));
   
   dev->CIS.channel = first;
   
   /* Purge the banks first (there's always a 3-cycle delay) */
   for (channel = first; channel <= last; ++channel)
   {
      if (!cis_read_line(dev, &buf[channel%3][0], pixels, 
                         /* raw = */ SANE_TRUE))
         return SANE_FALSE;
   }
   --dev->CIS.skipsToOrigin;
   
   for (cnt = 0; cnt < MUSTEK_PP_CIS_AVERAGE_COUNT + 2; ++cnt)
   {
      for (channel = first; channel <= last; ++channel)
      {
         DBG(4, "cis_measure_extremes: Reading line %d - channel %d\n", 
                cnt, channel);
         if (!cis_read_line(dev, &buf[channel][0], pixels, 
                            /* raw = */ SANE_TRUE))
            return SANE_FALSE;

         for (p = 0; p < pixels; ++p)
         {
            SANE_Byte val = buf[channel][p];
            if (val < min[channel][p]) min[channel][p] = val;
            if (val > max[channel][p]) max[channel][p] = val;
            sum[channel][p] += val;
         }
      }
      --dev->CIS.skipsToOrigin;
   }
   DBG(4, "cis_measure_extremes: Averaging\n");
   for (channel = first; channel <= last; ++channel)      
   {
      /* Ignore the extreme values and take the average of the others. */
      for (p = 0; p < pixels; ++p)
      {
         sum[channel][p] -= min[channel][p] + max[channel][p];
         sum[channel][p] /= MUSTEK_PP_CIS_AVERAGE_COUNT;
         if (calib[channel]) calib[channel][p] = sum[channel][p];
      }
   }
   DBG(4, "cis_measure_extremes: Done\n");
   return SANE_TRUE;
}   

static SANE_Bool
cis_normalize_ranges(Mustek_PP_CIS_dev * dev)
{
   SANE_Byte cal_low, cal_hi ;
   SANE_Byte powerOnDelay[3] ;
   SANE_Int pixels = dev->calib_pixels;
   SANE_Int channel, p, first, last;
   
   if (dev->desc->mode == MODE_COLOR)
   {
      first = MUSTEK_PP_CIS_CHANNEL_RED;
      last  = MUSTEK_PP_CIS_CHANNEL_BLUE;
   }
   else
   {
      first = MUSTEK_PP_CIS_CHANNEL_GRAY;
      last  = MUSTEK_PP_CIS_CHANNEL_GRAY;
   }
   
   DBG(3, "cis_normalize_ranges: Measuring high extremes\n");
   /* Measure extremes with normal lighting */
   if (!cis_measure_extremes(dev, dev->calib_hi, pixels, first, last)) {
      return SANE_FALSE;
   }

   /* Measure extremes without lighting */
   for (channel=first; channel<=last; ++channel) {
      powerOnDelay[channel] = dev->CIS.powerOnDelay[channel];
      dev->CIS.powerOnDelay[channel] = dev->CIS.exposeTime;
   }
   
   DBG(3, "cis_normalize_ranges: Measuring low extremes\n");
   if (!cis_measure_extremes(dev, dev->calib_low, pixels, first, last)) {
      return SANE_FALSE;
   }
   
   /* Restore settings */
   for (channel=first; channel<=last; ++channel) {
      dev->CIS.powerOnDelay[channel] = powerOnDelay[channel];
   }
   
   /* Make sure calib_hi is greater than calib_low */
   for (channel = first; channel <= last; ++channel) {
     for (p = 0; p<pixels; p++) {
       if (dev->calib_low[channel]) {
         cal_low = dev->calib_low[channel][p];
       } else {
         cal_low = 0;
       }
       if (dev->calib_hi[channel]) {
         cal_hi = dev->calib_hi[channel][p];
       } else {
         cal_hi = 255;
       }
       if (cal_hi <= cal_low) {
         if(cal_hi<255) {
           /* calib_hi exists, else cal_hi would be 255 */
           dev->calib_hi[channel][p] = cal_low+1;
         } else {
           /* calib_low exists, else cal_low would be 0, < 255  */
           dev->calib_low[channel][p] = cal_hi-1;
         }
       }
     }
   }
   DBG(3, "cis_normalize_ranges: calibration done\n");
   return SANE_TRUE;
}   

/*
 * This routine measures the time that we have to wait between reading 
 * to pixels from the scanner. Especially at low resolutions, but also
 * for narrow-width scans at high resolutions, reading too fast cause
 * color stability problems. 
 * This routine sends a test pattern to the scanner memory banks and tries
 * to measure how fast it can be retrieved without errors. 
 * The same is done by the TWAIN driver (TESTIO.CPP:TestDelay). 
 */
static SANE_Bool
cis_measure_delay(Mustek_PP_CIS_dev * dev)
{
   SANE_Byte buf[2][2048];
   unsigned i, j, d;
   int saved_res;
   SANE_Bool error = SANE_FALSE;
   
   CIS_CLEAR_FULLFLAG(dev);
   CIS_CLEAR_WRITE_ADDR(dev);
   CIS_CLEAR_WRITE_BANK(dev);
   CIS_INC_READ(dev);
   CIS_CLEAR_READ_BANK(dev);

   M1015_DISPLAY_REGS(dev, "Before delay measurement");
   assert(dev->CIS.adjustskip == 0);
   
   /* Sawtooth */
   for (i=0; i<2048; ++i)
   {
      buf[0][i] = i % 255; /* Why 255 ? Seems to have no real importance */
   }
   
   Mustek_PP_1015_write_reg_start(dev, MA1015W_SRAM_SOURCE_PC);
   for (i=0; i<2048; ++i)
   {
      Mustek_PP_1015_write_reg_val(dev, buf[0][i]);
   }
   Mustek_PP_1015_write_reg_stop(dev);
   
   /* Bank offset measurement */
   dev->CIS.delay = 0; /* Initialize to zero, measure next */
   
   saved_res = dev->CIS.res;
   dev->CIS.res = dev->CIS.hw_hres;
   
   /*
    * Note: the TWAIN driver seems to have a fast EPP mode too. That one is
    * tried first, and then they try the normal mode. I haven't figured out
    * yet how the fast mode works, so I'll only check the normal mode for now.
    * Moreover, from the behaviour that I've witnessed from the TWAIN driver,
    * I must conclude that the fast mode probably doesn't work on my computer,
    * so I can't test it anyhow.
    */
   /* Gradually increase the delay till we have no more errors */
   for (d = 0; d < 75 /* 255 */  && dev->desc->state != STATE_CANCELLED; d += 5)
   {
      dev->CIS.delay = d;
      
      /*
       * We read the line 5 times to make sure that all garbage is flushed.
       */
      for (i=0; i<5; ++i)
      {
         CIS_INC_READ(dev);
         CIS_CLEAR_READ_BANK(dev);
         cis_read_line_low_level (dev, &buf[1][0], 2048, NULL, NULL, NULL);
         if (dev->desc->state == STATE_CANCELLED) return SANE_FALSE;
      }
      
      error = SANE_FALSE;
      /* Check 100 times whether we can read without errors. */
      for (i=0; i<100 && !error; ++i)
      {
         CIS_INC_READ(dev);
         CIS_CLEAR_READ_BANK(dev);
         cis_read_line_low_level (dev, &buf[1][0], 2048, NULL, NULL, NULL);
         if (dev->desc->state == STATE_CANCELLED) return SANE_FALSE;
         
         for (j=0; j<2048; ++j)
         {
            if (buf[0][j] != buf[1][j]) 
            {
               error = SANE_TRUE;
               break;
            }
         }
      }
      
      DBG (3, "cis_measure_delay: delay %d\n", dev->CIS.delay); 
      if (!error)
         break;
   }
   
   dev->CIS.res = saved_res;
   
   if (error)
   {
      fprintf(stderr, "mustek_pp_cis: failed to measure delay.\n");
      fprintf(stderr, "Buffer contents:\n");
      for (j = 0; j < 20; ++j)
      {
         fprintf(stderr, "%d ", buf[1][j]);
      }
      fprintf(stderr, "\n");
      dev->CIS.delay = 0; 
   }
   
   DBG (3, "cis_measure_delay: delay %d\n", dev->CIS.delay); 
   return SANE_TRUE;
}

static void
cis_motor_control (Mustek_PP_CIS_dev * dev, u_char control)
{
   Mustek_PP_1015_wait_bit(dev, MA1015R_MOTOR, MA1015B_MOTOR_STABLE, 
                           SANE_FALSE, 0);
   Mustek_PP_1015_write_reg(dev, MA1015W_MOTOR_CONTROL, control);
}

static void
cis_return_home (Mustek_PP_CIS_dev * dev, SANE_Bool nowait)
{
   SANE_Byte savedExposeTime = dev->CIS.exposeTime;
   DBG(4, "cis_return_home: returning home; nowait: %d\n", nowait);
   /* During a return-home, the expose time is fixed. */
   dev->CIS.exposeTime = 170;
   cis_config_ccd(dev);
   dev->CIS.exposeTime = savedExposeTime;
   
   cis_motor_control (dev, 0xEB);

   if (nowait == SANE_FALSE)
      Mustek_PP_1015_wait_bit(dev, MA1015R_MOTOR, MA1015B_MOTOR_HOME, 
                              SANE_TRUE, 1000);
}

/******************************************************************************
 * Does a full reset of the device, ie. configures the CIS to a default
 * resolution of 300 DPI (in high or low resolution mode, depending on the
 * resolution requested by the user).
 *****************************************************************************/
static void
cis_reset_device (Mustek_PP_CIS_dev * dev)
{
   DBG(4, "cis_reset_device: resetting device\n");
   dev->CIS.adjustskip         = 0;
   dev->CIS.dontIncRead        = SANE_TRUE;
   dev->CIS.dontMove           = SANE_FALSE;
   
   cis_save_state(dev);

   dev->CIS.hw_hres            = 300;
   dev->CIS.channel            = MUSTEK_PP_CIS_CHANNEL_GREEN;
   dev->CIS.setParameters      = SANE_FALSE;
   dev->CIS.exposeTime         = 0xAA;
   
   cis_config_ccd (dev);
   
   cis_restore_state(dev);
   
}

static SANE_Bool
cis_calibrate (Mustek_PP_CIS_dev * dev)
{
   int i, saved_res = dev->CIS.res, saved_vres = dev->CIS.hw_vres;
   
   /*
    * Flow of operation observed from the twain driver
    * (it is assumed that the lamp is at the origin, and that the CIS is
    * configured for 300 DPI, ie. cis_reset_device has been called.)
    *
    *  - Reset the device and return the lamp to its home position
    *
    *  - Unknown short sequence
    *
    *  - Send a sawtooth-like pattern to one of the memory banks.
    *
    *  - Repetitive read_line of 2048 bytes, interleaved with an unknown
    *    command. The number varies between 102 and 170 times, but there
    *    doesn't seem to be any correlation with the current mode of the
    *    scanner, so I assume that the exact number isn't really relevant.
    *    The values that are read are the one that were sent to the bank,
    *    rotated by 1 byte in my case. 
    *
    *
    *    It seems that the width of the black border is being measured at 
    *    this stage, possibly multiple times till it stabilizes. 
    *    I assume that the buffer is read 100 times to allow the lamp to
    *    warm up and that the the width of the black border is then being 
    *    measured till it stabilizes. That would explain the minimum number
    *    of 102 iterations that I've seen.
    *
    *  - reset the device
    *
    *  - move the motor 110 steps forward. The TWAIN driver moves 90 steps,
    *    and I've used 90 steps for a long time too, but occasionally, 
    *    90 steps is a fraction to short to reach the start of the 
    *    calibration strip (the motor movements are not very accurate;
    *    an offset of 1 mm is not unusual). Therefore, I've increased it to
    *    110 steps. This gives us an additional 1.6 mm slack, which should
    *    prevent calibration errors. 
    *    (Note that the MUSTEK_PP_CIS_????CP_DEFAULT_SKIP constants have to 
    *    be adjusted if the number of steps is altered.)
    *
    *  - configure the CIS : actual resolution + set parameters
    * 
    */
   
   /* 
    * We must make sure that we are in the scanning state; otherwise we may
    * still be in the canceled state from a previous scan (even if terminated
    * normally), and the whole calibration would go wrong. 
    */ 
   dev->desc->state = STATE_SCANNING;
    
   cis_reset_device (dev);
   cis_return_home (dev, SANE_FALSE);  /* Wait till it's home */
   
   /* Use maximum resolution during calibration; otherwise we may calibrate
      past the calibration strip. */
   dev->CIS.hw_vres = dev->desc->dev->maxres;
   /* This field remembers how many steps we still have to go @ max res */
   dev->CIS.skipsToOrigin = dev->top_skip; /*max2hw_vres(dev, dev->top_skip); */
   
   if (!cis_measure_delay(dev))
      return SANE_FALSE;
   
   cis_reset_device (dev);

   /* Move motor 110 steps @ 300 DPI */
   Mustek_PP_1015_write_reg_start(dev, MA1015W_MOTOR_CONTROL);
   for (i=0; i<110; ++i)
   {
      if (dev->model == MUSTEK_PP_CIS600)
      {
         Mustek_PP_1015_write_reg_val (dev, 0x73);
      }
      else
      {
         Mustek_PP_1015_write_reg_val (dev, 0x7B);
      }
      Mustek_PP_1015_wait_bit(dev, MA1015R_MOTOR, MA1015B_MOTOR_STABLE, SANE_FALSE, 0);
   }
   Mustek_PP_1015_write_reg_stop(dev);
   
   /* Next, we maximize the dynamic range of the scanner. During calibration
      we don't want to extrapolate, so we limit the resolution if necessary */
   
   if (dev->CIS.hw_hres < dev->CIS.res) 
      dev->CIS.res = dev->CIS.hw_hres;
   
   if (!cis_maximize_dynamic_range(dev))
      return SANE_FALSE;
   
   if (!cis_normalize_ranges(dev))
      return SANE_FALSE;
   
   dev->CIS.res = saved_res;
   dev->CIS.hw_vres = saved_vres;
   
   /* Convert steps back to max res size, which are used during skipping */
/*   dev->CIS.skipsToOrigin = hw2max_vres(dev, dev->CIS.skipsToOrigin); */
   
   /* Move to the origin */
   DBG(3, "cis_calibrate: remaining skips to origin @maxres: %d\n", 
          dev->CIS.skipsToOrigin);
   cis_move_motor(dev, dev->CIS.skipsToOrigin);
   
   if (dev->calib_mode)
   {
      /* In calibration mode, we scan the interior of the scanner before the
         glass plate in order to find the position of the calibration strip
         and the start of the glass plate. */
      DBG(3, "cis_calibrate: running in calibration mode. Returning home.\n");
      cis_return_home (dev, SANE_FALSE);  /* Wait till it's home */
   }
   
   return dev->desc->state != STATE_CANCELLED ? SANE_TRUE : SANE_FALSE;
   
}  

/******************************************************************************
 ******************************************************************************
 ***                           Mustek PP interface                          ***
 ******************************************************************************
 *****************************************************************************/

/******************************************************************************
* Init                                                                        *
******************************************************************************/

/* Shared initialization routine */
static SANE_Status cis_attach(SANE_String_Const port,
                              SANE_String_Const name, 
                              SANE_Attach_Callback attach,
                              SANE_Int driverNo,
                              SANE_Int info)
{
   int fd;
   SANE_Status status;
   u_char asic;

   status = sanei_pa4s2_open (port, &fd);

   if (status != SANE_STATUS_GOOD)
   {
      SANE_Status altStatus;
      SANE_String_Const altPort;
      
      DBG (2, "cis_attach: couldn't attach to `%s' (%s)\n", port,
           sane_strstatus (status));
      
      /* Make migration to libieee1284 painless for users that used 
         direct io in the past */
           if (strcmp(port, "0x378") == 0) altPort = "parport0";
      else if (strcmp(port, "0x278") == 0) altPort = "parport1";
      else if (strcmp(port, "0x3BC") == 0) altPort = "parport2";
      else return status;
      
      DBG (2, "cis_attach: trying alternative port name: %s\n", altPort);
      
      altStatus = sanei_pa4s2_open (altPort, &fd);
      if (altStatus != SANE_STATUS_GOOD)
      {
           DBG (2, "cis_attach: couldn't attach to alternative port `%s' "
              "(%s)\n", altPort, sane_strstatus (altStatus));
           return status; /* Return original status, not alternative status */
      }
   }
   
   M1015_START_LL;
   M1015_START_HL;


   sanei_pa4s2_enable (fd, SANE_TRUE);
   SANEI_PA4S2_READBEGIN (fd, 0);
   SANEI_PA4S2_READBYTE (fd, &asic);
   SANEI_PA4S2_READEND (fd);
   sanei_pa4s2_enable (fd, SANE_FALSE);

   sanei_pa4s2_close (fd);
   
   if (asic != 0xA5) /* Identifies the MA1015 chipset */
   {
      /* CIS driver only works for MA1015 chipset */
      DBG (2, "cis_attach: asic id (0x%02x) not recognized\n", asic);
      return SANE_STATUS_INVAL; 
   }

   DBG (3, "cis_attach: device %s attached\n", name);
   DBG (3, "cis_attach: asic 0x%02x\n", asic);
   
   return attach(port, name, driverNo, info);
}

SANE_Status cis600_drv_init(SANE_Int options, SANE_String_Const port,
		SANE_String_Const name, SANE_Attach_Callback attach)
{
   if (options != CAP_NOTHING)
      return SANE_STATUS_INVAL;

   return cis_attach(port, name, attach, MUSTEK_PP_CIS600, MUSTEK_PP_CIS600);
}

SANE_Status cis1200_drv_init(SANE_Int options, SANE_String_Const port,
		SANE_String_Const name, SANE_Attach_Callback attach)
{
   if (options != CAP_NOTHING)
      return SANE_STATUS_INVAL;

   return cis_attach(port, name, attach, MUSTEK_PP_CIS1200, MUSTEK_PP_CIS1200);
}

SANE_Status cis1200p_drv_init(SANE_Int options, SANE_String_Const port,
		SANE_String_Const name, SANE_Attach_Callback attach)
{
   if (options != CAP_NOTHING)
      return SANE_STATUS_INVAL;

   return cis_attach(port, name, attach, MUSTEK_PP_CIS1200PLUS, MUSTEK_PP_CIS1200PLUS);
}

/******************************************************************************
* Capabilities                                                                *
******************************************************************************/
void cis_drv_capabilities(SANE_Int info, SANE_String *model,
                          SANE_String *vendor, SANE_String *type,
                          SANE_Int *maxres, SANE_Int *minres,
                          SANE_Int *maxhsize, SANE_Int *maxvsize,
                          SANE_Int *caps)
{
   *vendor = strdup("Mustek");
   *type = strdup("flatbed scanner");
   *caps = CAP_NOTHING;

   switch(info)
   {   
      case MUSTEK_PP_CIS600:
        *model = strdup("600CP");
        *maxres = 600;
        *minres = 50;
        *maxhsize = MUSTEK_PP_CIS_MAX_H_PIXEL;
        *maxvsize = MUSTEK_PP_CIS_MAX_V_PIXEL;
        break;
      case MUSTEK_PP_CIS1200:
        *model = strdup("1200CP");
        *maxres = 1200;
        *minres = 50;
        *maxhsize = MUSTEK_PP_CIS_MAX_H_PIXEL*2;
        *maxvsize = MUSTEK_PP_CIS_MAX_V_PIXEL*2;
        break;
      case MUSTEK_PP_CIS1200PLUS:
        *model = strdup("1200CP+");
        *model = strdup("1200CP");
        *maxres = 1200;
        *minres = 50;
        *maxhsize = MUSTEK_PP_CIS_MAX_H_PIXEL*2;
        *maxvsize = MUSTEK_PP_CIS_MAX_V_PIXEL*2;
        break;
   }
}

/******************************************************************************
* Open                                                                        *
******************************************************************************/
SANE_Status cis_drv_open (SANE_String port, SANE_Int caps, SANE_Int *fd)
{
   SANE_Status status;

   if (caps != CAP_NOTHING)
   {
      DBG (1, "cis_drv_open: called with unknown capabilities (0x%02X)\n", caps);
      return SANE_STATUS_INVAL;
   }

   DBG (3, "cis_drv_open: called for port %s\n", port);

   status = sanei_pa4s2_open (port, fd);

   if (status != SANE_STATUS_GOOD)
   {
      SANE_Status altStatus;
      SANE_String_Const altPort;
      
      DBG (2, "cis_attach: couldn't attach to `%s' (%s)\n", port,
           sane_strstatus (status));
      
      /* Make migration to libieee1284 painless for users that used 
         direct io in the past */
           if (strcmp(port, "0x378") == 0) altPort = "parport0";
      else if (strcmp(port, "0x278") == 0) altPort = "parport1";
      else if (strcmp(port, "0x3BC") == 0) altPort = "parport2";
      else return status;
      
      DBG (2, "cis_attach: trying alternative port name: %s\n", altPort);
      
      altStatus = sanei_pa4s2_open (altPort, fd);
      if (altStatus != SANE_STATUS_GOOD)
      {
           DBG (2, "cis_attach: couldn't attach to alternative port `%s' "
              "(%s)\n", altPort, sane_strstatus (altStatus));
           return status; /* Return original status, not alternative status */
      }
   }

   return SANE_STATUS_GOOD;
}

/******************************************************************************
* Setup                                                                       *
******************************************************************************/
void cis_drv_setup (SANE_Handle hndl)
{
   Mustek_pp_Handle *dev = hndl;
   Mustek_PP_CIS_dev *cisdev;
   cisdev = (Mustek_PP_CIS_dev*)malloc(sizeof(Mustek_PP_CIS_dev));
   if (cisdev == NULL)
   {
      DBG (2, "cis_drv_setup: not enough memory for device descriptor\n");
      sanei_pa4s2_close (dev->fd);
      return;
   }
   memset(cisdev, 0, sizeof(Mustek_PP_CIS_dev));
   DBG(3, "cis_drv_setup: cis device allocated\n");
   
   dev->lamp_on = 0;
   dev->priv = cisdev;
   
   cisdev->desc = dev;
   cisdev->model = dev->dev->info;
   cisdev->CIS.hw_hres = 300;
   cisdev->CIS.cisRes = 300;
   cisdev->CIS.hw_vres = 300;
   
   /* Default values for configurable parameters; configuration file
      may override them. */
   cisdev->fast_skip = SANE_TRUE;
   cisdev->bw_limit = 127;
   cisdev->calib_mode = SANE_FALSE;
   if (cisdev->model == MUSTEK_PP_CIS600)
   {
      cisdev->top_skip = MUSTEK_PP_CIS_600CP_DEFAULT_SKIP;
   }
   else
   {
      cisdev->top_skip = MUSTEK_PP_CIS_1200CP_DEFAULT_SKIP;
   }
}

/******************************************************************************
* Config                                                                      *
******************************************************************************/
SANE_Status cis_drv_config(SANE_Handle hndl, SANE_String_Const optname,
	 		   SANE_String_Const optval)
{
   Mustek_pp_Handle *dev = hndl;
   Mustek_PP_CIS_dev *cisdev = dev->priv;
   int value = 0;
   double dvalue = 0;
   DBG (3, "cis_drv_cfg option: %s=%s\n", optname, optval ? optval : "");
   if (!strcmp(optname, "top_adjust"))
   {
      if (!optval)
      {
         DBG (1, "cis_drv_config: missing value for option top_adjust\n");
         return SANE_STATUS_INVAL;
      }
      dvalue = atof(optval);
      
      /* An adjustment of +/- 5 mm should be sufficient and safe */
      if (dvalue < -5.0)
      {
         DBG (1, "cis_drv_config: value for option top_adjust too small: "
                 "%.2f < -5; limiting to -5 mm\n", dvalue);
         dvalue = -5.0;
      }
      if (dvalue > 5.0)
      {
         DBG (1, "cis_drv_config: value for option top_adjust too large: "
                 "%.2f > 5; limiting to 5 mm\n", dvalue);
         dvalue = 5.0;
      }
      /* In practice, there is a lower bound on the value that can be used,
         but if the top_skip value is smaller than that value, the only result
         will be that the driver tries to move the head a negative number 
         of steps after calibration. The move routine just ignores negative
         steps, so no harm can be done. */
      cisdev->top_skip += MM_TO_PIXEL(dvalue, dev->dev->maxres);
      DBG (3, "cis_drv_config: setting top skip value to %d\n", 
              cisdev->top_skip);
      
      /* Just to be cautious; we don't want the head to hit the bottom */
      if (cisdev->top_skip > 600) cisdev->top_skip = 600;
      if (cisdev->top_skip < -600) cisdev->top_skip = -600;
   }
   else if (!strcmp(optname, "slow_skip"))
   {
      if (optval)
      {
         DBG (1, "cis_drv_config: unexpected value for option slow_skip\n");
         return SANE_STATUS_INVAL;
      }
      DBG (3, "cis_drv_config: disabling fast skipping\n");
      cisdev->fast_skip = SANE_FALSE;
   }
   else if (!strcmp(optname, "bw"))
   {
      if (!optval)
      {
         DBG (1, "cis_drv_config: missing value for option bw\n");
         return SANE_STATUS_INVAL;
      }
      value = atoi(optval);
      if (value < 0 || value > 255)
      {
         DBG (1, "cis_drv_config: valu for option bw out of range: "
                 "%d < 0 or %d > 255\n", value, value);
         return SANE_STATUS_INVAL;
      }
      cisdev->bw_limit = value;
   }
   else if (!strcmp(optname, "calibration_mode"))
   {
      if (optval)
      {
         DBG (1, "cis_drv_config: unexpected value for option calibration_mode\n");
         return SANE_STATUS_INVAL;
      }
      DBG (3, "cis_drv_config: using calibration mode\n");
      cisdev->calib_mode = SANE_TRUE;
   }
   else
   {
      DBG (1, "cis_drv_config: unknown options %s\n", optname);
      return SANE_STATUS_INVAL;
   }
   return SANE_STATUS_GOOD;
}

/******************************************************************************
* Close                                                                       *
******************************************************************************/
void cis_drv_close (SANE_Handle hndl)
{
   Mustek_pp_Handle *dev = hndl;
   Mustek_PP_CIS_dev *cisdev = dev->priv;
   DBG (3, "cis_close: resetting device.\n");
   sanei_pa4s2_enable (dev->fd, SANE_TRUE); 
   cis_reset_device (cisdev);
   DBG (3, "cis_close: returning home.\n");
   cis_return_home (cisdev, SANE_TRUE); /* Don't wait */
   DBG (3, "cis_close: disabling fd.\n");
   sanei_pa4s2_enable (dev->fd, SANE_FALSE);
   DBG (3, "cis_close: closing fd.\n");
   sanei_pa4s2_close (dev->fd);
   DBG (3, "cis_close: done.\n");
   DBG (6, "cis_close: lamp_on: %d\n", (int)dev->lamp_on);
   M1015_STOP_LL;
   M1015_STOP_HL;
}

/******************************************************************************
* Start                                                                       *
******************************************************************************/
SANE_Status cis_drv_start (SANE_Handle hndl)
{
   Mustek_pp_Handle *dev = hndl;
   Mustek_PP_CIS_dev *cisdev = dev->priv;
   SANE_Int pixels = dev->params.pixels_per_line;
   
   if (!cisdev)
   {
      DBG (2, "cis_drv_start: not enough memory for device\n");
      return SANE_STATUS_NO_MEM;
   }

   cisdev->CIS.exposeTime = 0xAA;
   cisdev->CIS.setParameters = SANE_FALSE;
   cisdev->CIS.use8KBank = SANE_TRUE;
   cisdev->CIS.imagebytes = dev->bottomX - dev->topX;
   cisdev->CIS.skipimagebytes = dev->topX;

   cisdev->CIS.res = dev->res;

   DBG (3, "cis_drv_start: %d dpi\n", dev->res);

   if (dev->res <= 50 && cisdev->model != MUSTEK_PP_CIS1200PLUS)
   {
     cisdev->CIS.hw_hres = 50;
   }
   else if (dev->res <= 75 && cisdev->model == MUSTEK_PP_CIS1200PLUS)
   {
     cisdev->CIS.hw_hres = 75;
   }
   else if (dev->res <= 100)
   {
     cisdev->CIS.hw_hres = 100;
   }
   else if (dev->res <= 200)
   {
     cisdev->CIS.hw_hres = 200;
   }
   else if (dev->res <= 300)
   {
     cisdev->CIS.hw_hres = 300;
   }
   else
   {
      if (cisdev->model == MUSTEK_PP_CIS600)
      {
         cisdev->CIS.hw_hres = 300; /* Limit for 600 CP */
      }
      else if (dev->res <= 400)
      {
   	 cisdev->CIS.hw_hres = 400;
      }
      else
      {
	 cisdev->CIS.hw_hres = 600; /* Limit for 1200 CP/CP+ */
      }
   }

   if (cisdev->model == MUSTEK_PP_CIS600)
   {
      if (dev->res <= 150)
      {
         cisdev->CIS.hw_vres = 150;
      }
      else if (dev->res <= 300)
      {
         cisdev->CIS.hw_vres = 300;
      }
      else
      {
         cisdev->CIS.hw_vres = 600;
      }
   }
   else
   {
      if (dev->res <= 300)
      {
         cisdev->CIS.hw_vres = 300;
      }
      else if (dev->res <= 600)
      {
         cisdev->CIS.hw_vres = 600;
      }
      else
      {
         cisdev->CIS.hw_vres = 1200;
      }
   }

   if (cisdev->model == MUSTEK_PP_CIS600 ||
       (cisdev->model == MUSTEK_PP_CIS1200 && dev->res <= 300))
     cisdev->CIS.cisRes = 300;
   else
     cisdev->CIS.cisRes = 600;         

   /* Calibration only makes sense for hardware pixels, not for interpolated
      pixels, so we limit the number of calibration pixels to the maximum
      number of hardware pixels corresponding to the selected area */
   if (dev->res > cisdev->CIS.hw_hres)
      cisdev->calib_pixels = (pixels * cisdev->CIS.hw_hres) / dev->res;
   else
      cisdev->calib_pixels = pixels;
   
   DBG (3, "cis_drv_start: hres: %d vres: %d cisres: %d\n", 
            cisdev->CIS.hw_hres, cisdev->CIS.hw_vres, cisdev->CIS.cisRes);
   
   sanei_pa4s2_enable (dev->fd, SANE_TRUE);

   cis_reset_device (cisdev);
   cis_return_home (cisdev, SANE_TRUE); /* Don't wait here */

#ifdef M1015_TRACE_REGS
   {
      int i, j;

      /*
       * Set all registers to -1 (uninitialized)
       */  
      for (i=0; i<4; ++i)
      {
         cisdev->CIS.regs.in_regs[i] = -1;
         for (j=0; j<4; ++j)
         {
            cisdev->CIS.regs.out_regs[i][j] = -1;
         }
      }

      cisdev->CIS.regs.channel = -1;
      /* These values have been read earlier. */
      cisdev->CIS.regs.in_regs[0] = 0xA5;
   }
#endif

   cis_reset_device (cisdev);
   cis_return_home (cisdev, SANE_TRUE); /* no wait */

   /* Allocate memory for temporary color buffer */
   cisdev->tmpbuf = malloc (pixels);
   if (cisdev->tmpbuf == NULL)
   {
      sanei_pa4s2_enable (dev->fd, SANE_FALSE);
      DBG (2, "cis_drv_start: not enough memory for temporary buffer\n");
      free(cisdev);
      dev->priv = NULL;
      return SANE_STATUS_NO_MEM;
   }
   
   /* Allocate memory for calibration; calibrating interpolated pixels 
      makes no sense */
   if (pixels > (dev->dev->maxhsize >> 1)) 
      pixels = (dev->dev->maxhsize >> 1);
   
   cisdev->calib_low[1] = malloc (pixels);
   cisdev->calib_hi[1] = malloc (pixels);

   if (cisdev->calib_low[1] == NULL || cisdev->calib_hi[1] == NULL)
   {
      free (cisdev->calib_low[1]); cisdev->calib_low[1] = NULL;
      free (cisdev->calib_hi[1]);  cisdev->calib_hi[1] = NULL;
      sanei_pa4s2_enable (dev->fd, SANE_FALSE);
      DBG (2, "cis_drv_start: not enough memory for calibration buffer\n");
      free(cisdev->tmpbuf); cisdev->tmpbuf = NULL;
      free(cisdev); dev->priv = NULL;
      return SANE_STATUS_NO_MEM;
   }

   cisdev->calib_low[0] = NULL;
   cisdev->calib_low[2] = NULL;
   cisdev->calib_hi[0] = NULL;
   cisdev->calib_hi[2] = NULL;
   if (dev->mode == MODE_COLOR)
   {
      cisdev->calib_low[0] = malloc (pixels);
      cisdev->calib_low[2] = malloc (pixels);
      cisdev->calib_hi[0] = malloc (pixels);
      cisdev->calib_hi[2] = malloc (pixels);

      if ((cisdev->calib_low[0] == NULL) || (cisdev->calib_low[2] == NULL) ||
          (cisdev->calib_hi[0] == NULL) || (cisdev->calib_hi[2] == NULL))
      {
         free (cisdev->calib_low[0]); cisdev->calib_low[0] = NULL;
	 free (cisdev->calib_low[1]); cisdev->calib_low[1] = NULL;
         free (cisdev->calib_low[2]); cisdev->calib_low[2] = NULL;
         free (cisdev->calib_hi[0]); cisdev->calib_hi[0] = NULL;
	 free (cisdev->calib_hi[1]); cisdev->calib_hi[1] = NULL;
         free (cisdev->calib_hi[2]); cisdev->calib_hi[2] = NULL;
         free(cisdev->tmpbuf); cisdev->tmpbuf = NULL;
         free(cisdev); dev->priv = NULL;
	 sanei_pa4s2_enable (dev->fd, SANE_FALSE);
	 DBG (2, "cis_drv_start: not enough memory for color calib buffer\n");
	 return SANE_STATUS_NO_MEM;
      }
   }

   DBG (3, "cis_drv_start: executing calibration\n");

   if (!cis_calibrate (cisdev))
   {
      free (cisdev->calib_low[0]); cisdev->calib_low[0] = NULL;
      free (cisdev->calib_low[1]); cisdev->calib_low[1] = NULL;
      free (cisdev->calib_low[2]); cisdev->calib_low[2] = NULL;
      free (cisdev->calib_hi[0]); cisdev->calib_hi[0] = NULL;
      free (cisdev->calib_hi[1]); cisdev->calib_hi[1] = NULL;
      free (cisdev->calib_hi[2]); cisdev->calib_hi[2] = NULL;
      free(cisdev->tmpbuf); cisdev->tmpbuf = NULL;
      free(cisdev); dev->priv = NULL;
      return SANE_STATUS_CANCELLED; /* Most likely cause */
   }

/*   M1015_DISPLAY_REGS(dev, "after calibration"); */

   cis_get_bank_count(cisdev);

   cis_move_motor (cisdev, dev->topY); /* Measured in max resolution */

   /* It is vital to reinitialize the scanner right before we start the 
      real scanning. Otherwise the bank synchronization may have gotten lost
      by the time we reach the top of the scan area */

   cisdev->CIS.setParameters = SANE_TRUE;
   cis_config_ccd(cisdev);
   cis_wait_read_ready(cisdev);

   sanei_pa4s2_enable (dev->fd, SANE_FALSE); 

   cisdev->CIS.line_step =
     SANE_FIX ((float) cisdev->CIS.hw_vres / (float) cisdev->CIS.res);

   /* 
    * It is very important that line_diff is not initialized at zero !
    * If it is set to zero, the motor will keep on moving forever (or better,
    * till the scanner breaks).
    */
   cisdev->line_diff = cisdev->CIS.line_step;
   cisdev->ccd_line = 0;
   cisdev->line = 0;
   cisdev->lines_left = dev->params.lines;

   dev->state = STATE_SCANNING;

   DBG (3, "cis_drv_start: device ready for scanning\n");

   return SANE_STATUS_GOOD;
}

/******************************************************************************
* Read                                                                        *
******************************************************************************/
void cis_drv_read (SANE_Handle hndl, SANE_Byte *buffer)
{
   Mustek_pp_Handle *dev = hndl;
   Mustek_PP_CIS_dev *cisdev = dev->priv;
   DBG(6, "cis_drv_read: Reading line\n");
   sanei_pa4s2_enable (dev->fd, SANE_TRUE); 
   switch (dev->mode)
   {
      case MODE_BW:
         cis_get_lineart_line(cisdev, buffer);
         break;

      case MODE_GRAYSCALE:
         cis_get_grayscale_line(cisdev, buffer);
         break;

      case MODE_COLOR:
         cis_get_color_line(cisdev, buffer);
         break;
   }
   sanei_pa4s2_enable (dev->fd, SANE_FALSE);
}

/******************************************************************************
* Stop                                                                        *
******************************************************************************/
void cis_drv_stop (SANE_Handle hndl)
{
   Mustek_pp_Handle *dev = hndl;
   Mustek_PP_CIS_dev *cisdev = dev->priv;
   
   /* device is scanning: return lamp and free buffers */
   DBG (3, "cis_drv_stop: stopping current scan\n");
   dev->state = STATE_CANCELLED;

   DBG (9, "cis_drv_stop: enabling fd\n");
   sanei_pa4s2_enable (dev->fd, SANE_TRUE); 
   Mustek_PP_1015_write_reg(cisdev, MA1015W_MOTOR_CONTROL, 0); /* stop */
   DBG (9, "cis_drv_stop: resetting device (1)\n");
   cis_reset_device (cisdev);
   DBG (9, "cis_drv_stop: returning home\n");
   cis_return_home (cisdev, SANE_TRUE); /* don't wait */
   DBG (9, "cis_drv_stop: resetting device (2)\n");
   cis_reset_device (cisdev);
   DBG (9, "cis_drv_stop: disabling fd\n");
   sanei_pa4s2_enable (dev->fd, SANE_FALSE); 
   DBG (9, "cis_drv_stop: freeing buffers\n");

   /* This is no good: canceling while the device is scanning and
      freeing the data buffers can result in illegal memory accesses if
      the device is still scanning in another thread. */
   free (cisdev->calib_low[1]); cisdev->calib_low[1] = NULL;
   free (cisdev->calib_hi[1]); cisdev->calib_hi[1] = NULL;
   free (cisdev->tmpbuf); cisdev->tmpbuf = NULL;
   DBG (3, "cis_drv_stop: freed green and temporary buffers\n");

   if (cisdev->CIS.mode == MODE_COLOR)
   {
      free (cisdev->calib_low[0]); cisdev->calib_low[0] = NULL;
      free (cisdev->calib_low[2]); cisdev->calib_low[2] = NULL;
      free (cisdev->calib_hi[0]); cisdev->calib_hi[0] = NULL;
      free (cisdev->calib_hi[2]); cisdev->calib_hi[2] = NULL;
   }
   DBG (3, "cis_drv_stop: freed buffers\n");
   DBG (6, "cis_drv_stop: lamp_on: %d\n", (int)dev->lamp_on);
}
