summaryrefslogtreecommitdiff
path: root/backend/mustek_pp_cis.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/mustek_pp_cis.c')
-rw-r--r--backend/mustek_pp_cis.c2854
1 files changed, 2854 insertions, 0 deletions
diff --git a/backend/mustek_pp_cis.c b/backend/mustek_pp_cis.c
new file mode 100644
index 0000000..026734f
--- /dev/null
+++ b/backend/mustek_pp_cis.c
@@ -0,0 +1,2854 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2001-2003 Eddy De Greef <eddy_de_greef at scarlet 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 5118
+#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>
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#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)
+{
+ static struct timeval timeoutVal;
+ SANE_Bool ret =
+ Mustek_PP_1015_wait_bit (dev, MA1015R_MOTOR, MA1015B_MOTOR_STABLE,
+ SANE_FALSE, 0);
+#ifdef HAVE_SYS_SELECT_H
+ if (dev->engine_delay > 0)
+ {
+ timeoutVal.tv_sec = 0;
+ timeoutVal.tv_usec = dev->engine_delay*1000;
+ select(0, NULL, NULL, NULL, &timeoutVal);
+ }
+#endif
+ return ret;
+}
+
+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)
+{
+ cis_wait_motor_stable (dev);
+ 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);
+ }
+ cis_wait_motor_stable (dev);
+ }
+ 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+");
+ *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;
+ cisdev->engine_delay = 0;
+ 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: value 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 if (!strcmp(optname, "engine_delay"))
+ {
+ if (!optval)
+ {
+ DBG (1, "cis_drv_config: missing value for option engine_delay\n");
+ return SANE_STATUS_INVAL;
+ }
+ value = atoi(optval);
+ if (value < 0 || value > 100) /* 100 ms is already pretty slow */
+ {
+ DBG (1, "cis_drv_config: value for option engine_delay out of range: "
+ "%d < 0 or %d > 100\n", value, value);
+ return SANE_STATUS_INVAL;
+ }
+ cisdev->engine_delay = value;
+ }
+ 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);
+}