kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			2853 wiersze
		
	
	
		
			87 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			2853 wiersze
		
	
	
		
			87 KiB
		
	
	
	
		
			C
		
	
	
| /* 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, 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;
 | |
|    }
 | |
|    else
 | |
|    {
 | |
|       first = MUSTEK_PP_CIS_CHANNEL_GRAY;
 | |
|       last = MUSTEK_PP_CIS_CHANNEL_GRAY;
 | |
|       stride = 1;
 | |
|    }
 | |
| 
 | |
|    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 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);
 | |
| }
 |