kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			2855 wiersze
		
	
	
		
			88 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			2855 wiersze
		
	
	
		
			88 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, ignore, step = dev->CIS.line_step;
 | 
						|
   SANE_Byte gotline;
 | 
						|
   
 | 
						|
   if (dev->desc->mode == MODE_COLOR)
 | 
						|
   {
 | 
						|
      first = MUSTEK_PP_CIS_CHANNEL_RED;
 | 
						|
      last = MUSTEK_PP_CIS_CHANNEL_BLUE;
 | 
						|
      stride = 3;
 | 
						|
      ignore = 1; /* 1 * 3 channels */
 | 
						|
   }
 | 
						|
   else
 | 
						|
   {
 | 
						|
      first = MUSTEK_PP_CIS_CHANNEL_GRAY;
 | 
						|
      last = MUSTEK_PP_CIS_CHANNEL_GRAY;
 | 
						|
      stride = 1;
 | 
						|
      ignore = 3; /* 3 * 1 channel */
 | 
						|
   }
 | 
						|
   
 | 
						|
   gotline = SANE_FALSE;
 | 
						|
   do
 | 
						|
   {
 | 
						|
      dev->ccd_line++;
 | 
						|
      if ((dev->line_diff >> SANE_FIXED_SCALE_SHIFT) != dev->ccd_line)
 | 
						|
      {
 | 
						|
         cis_motor_forward (dev);
 | 
						|
         continue;
 | 
						|
      }
 | 
						|
 | 
						|
      dev->line_diff += step;
 | 
						|
 | 
						|
      for (channel = first; channel <= last; ++channel)
 | 
						|
      {
 | 
						|
         if (!cis_read_line(dev, tmpbuf, dev->desc->params.pixels_per_line, 
 | 
						|
                            SANE_FALSE)) 
 | 
						|
            return;
 | 
						|
 | 
						|
         dest = buf + channel - first;
 | 
						|
         for (ctr = 0; ctr < dev->desc->params.pixels_per_line; ctr++)
 | 
						|
         {
 | 
						|
	    *dest = tmpbuf[ctr];
 | 
						|
            dest += stride;
 | 
						|
         }
 | 
						|
      }
 | 
						|
      gotline = SANE_TRUE;
 | 
						|
   }
 | 
						|
   while (!gotline && dev->desc->state != STATE_CANCELLED);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
cis_get_grayscale_line (Mustek_PP_CIS_dev * dev, SANE_Byte * buf)
 | 
						|
{
 | 
						|
   cis_get_next_line(dev, buf);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
cis_get_lineart_line (Mustek_PP_CIS_dev * dev, SANE_Byte * buf)
 | 
						|
{
 | 
						|
   int ctr;
 | 
						|
   SANE_Byte gbuf[MUSTEK_PP_CIS_MAX_H_PIXEL * 2];
 | 
						|
 | 
						|
   cis_get_grayscale_line (dev, gbuf);
 | 
						|
   memset (buf, 0xFF, dev->desc->params.bytes_per_line);
 | 
						|
 | 
						|
   for (ctr = 0; ctr < dev->desc->params.pixels_per_line; ctr++)
 | 
						|
      buf[ctr >> 3] ^= ((gbuf[ctr] > dev->bw_limit) ? (1 << (7 - ctr % 8)) : 0);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
cis_get_color_line (Mustek_PP_CIS_dev * dev, SANE_Byte * buf)
 | 
						|
{
 | 
						|
   cis_get_next_line(dev, buf);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/******************************************************************************
 | 
						|
 * Saves the state of the device during reset and calibration.
 | 
						|
 *****************************************************************************/
 | 
						|
static void 
 | 
						|
cis_save_state (Mustek_PP_CIS_dev * dev)
 | 
						|
{
 | 
						|
   dev->Saved_CIS = dev->CIS;
 | 
						|
}
 | 
						|
 | 
						|
/******************************************************************************
 | 
						|
 * Restores the state of the device after reset and calibration.
 | 
						|
 *****************************************************************************/
 | 
						|
static void 
 | 
						|
cis_restore_state (Mustek_PP_CIS_dev * dev)
 | 
						|
{
 | 
						|
   dev->CIS = dev->Saved_CIS;
 | 
						|
}
 | 
						|
 | 
						|
#define CIS_TOO_BRIGHT 1
 | 
						|
#define CIS_OK         0
 | 
						|
#define CIS_TOO_DARK  -1
 | 
						|
 | 
						|
static int
 | 
						|
cis_check_result(SANE_Byte* buffer, int pixel)
 | 
						|
{
 | 
						|
   int i, maxVal = 0;
 | 
						|
   
 | 
						|
   for (i=0;i<pixel;++i) 
 | 
						|
      if (buffer[i] > maxVal) maxVal = buffer[i];
 | 
						|
   
 | 
						|
   if (maxVal > 250) return CIS_TOO_BRIGHT;
 | 
						|
   if (maxVal < 240) return CIS_TOO_DARK;
 | 
						|
   return CIS_OK;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Bool
 | 
						|
cis_maximize_dynamic_range(Mustek_PP_CIS_dev * dev)
 | 
						|
{
 | 
						|
   /* The device is in its final configuration already. */
 | 
						|
   int i, j, pixel, channel, minExposeTime, first, last;
 | 
						|
   SANE_Byte powerOnDelayLower[3], powerOnDelayUpper[3], exposeTime[3];
 | 
						|
   SANE_Byte buf[3][MUSTEK_PP_CIS_MAX_H_PIXEL];
 | 
						|
   SANE_Int pixels = dev->calib_pixels;
 | 
						|
   
 | 
						|
   DBG(3, "cis_maximize_dynamic_range: starting\n");
 | 
						|
   
 | 
						|
   for (channel = 0; channel < 3; ++channel)
 | 
						|
   {
 | 
						|
      exposeTime[channel] = 254;
 | 
						|
      dev->CIS.powerOnDelay[channel] = 170;
 | 
						|
      powerOnDelayLower[channel] = 1;
 | 
						|
      powerOnDelayUpper[channel] = 254;
 | 
						|
   }         
 | 
						|
   dev->CIS.setParameters  = SANE_TRUE;
 | 
						|
   dev->CIS.exposeTime     = exposeTime[MUSTEK_PP_CIS_CHANNEL_GREEN];
 | 
						|
   cis_config_ccd(dev);
 | 
						|
   
 | 
						|
   M1015_DISPLAY_REGS(dev, "before maximizing dynamic range");
 | 
						|
   dev->CIS.dontMove = SANE_TRUE; /* Don't move while calibrating */
 | 
						|
   
 | 
						|
   if (!cis_wait_read_ready(dev) && dev->desc->state != STATE_CANCELLED)
 | 
						|
   {
 | 
						|
      DBG(2, "cis_maximize_dynamic_range: DEVICE NOT READY!\n");
 | 
						|
      return SANE_FALSE;
 | 
						|
   }
 | 
						|
   
 | 
						|
   if (dev->desc->mode == MODE_COLOR)
 | 
						|
   {
 | 
						|
      first = MUSTEK_PP_CIS_CHANNEL_RED;
 | 
						|
      last  = MUSTEK_PP_CIS_CHANNEL_BLUE;
 | 
						|
   }
 | 
						|
   else
 | 
						|
   {
 | 
						|
      first = MUSTEK_PP_CIS_CHANNEL_GRAY;
 | 
						|
      last  = MUSTEK_PP_CIS_CHANNEL_GRAY;
 | 
						|
   }
 | 
						|
   
 | 
						|
   dev->CIS.channel = first;
 | 
						|
   
 | 
						|
   /* Perform a kind of binary search. In the worst case, we should find 
 | 
						|
      the optimal power delay values after 8 iterations */
 | 
						|
   for( i=0; i<8; i++)
 | 
						|
   {
 | 
						|
      for (channel = first; channel <= last; ++channel)
 | 
						|
      {
 | 
						|
         dev->CIS.powerOnDelay[channel] = (powerOnDelayLower[channel] +
 | 
						|
                                           powerOnDelayUpper[channel]) / 2;
 | 
						|
      }
 | 
						|
      Mustek_PP_1015_write_reg(dev, MA1015W_POWER_ON_DELAY, 
 | 
						|
                               dev->CIS.powerOnDelay[1]); /* Green */
 | 
						|
 | 
						|
      for (pixel = 0; pixel < pixels; ++pixel)
 | 
						|
      {
 | 
						|
         buf[0][pixel] = buf[1][pixel] = buf[2][pixel] = 255;
 | 
						|
      }
 | 
						|
      
 | 
						|
      /* Scan 4 lines, but ignore the first 3 ones. */
 | 
						|
      for (j = 0; j < 4; ++j)
 | 
						|
      {
 | 
						|
         for (channel = first; channel <= last; ++channel)
 | 
						|
         {
 | 
						|
            if (!cis_read_line(dev, &buf[channel][0], pixels, 
 | 
						|
                               /* raw = */ SANE_TRUE))
 | 
						|
               return SANE_FALSE;
 | 
						|
         }
 | 
						|
      }
 | 
						|
 | 
						|
      for (channel = first; channel <= last; ++channel)
 | 
						|
      {
 | 
						|
         switch (cis_check_result(buf[channel], pixels))
 | 
						|
         {
 | 
						|
            case CIS_TOO_BRIGHT:
 | 
						|
               powerOnDelayLower[channel] = dev->CIS.powerOnDelay[channel];
 | 
						|
               break;   
 | 
						|
               
 | 
						|
            case CIS_TOO_DARK:
 | 
						|
               powerOnDelayUpper[channel] = dev->CIS.powerOnDelay[channel];
 | 
						|
               break;   
 | 
						|
               
 | 
						|
            default:
 | 
						|
               break;   
 | 
						|
         }
 | 
						|
      } 
 | 
						|
      DBG (4, "cis_maximize_dynamic_range: power on delay %3d %3d %3d\n", 
 | 
						|
           dev->CIS.powerOnDelay[0], dev->CIS.powerOnDelay[1], 
 | 
						|
           dev->CIS.powerOnDelay[2]);
 | 
						|
   }
 | 
						|
   dev->CIS.dontMove = SANE_FALSE;
 | 
						|
   
 | 
						|
   DBG (3, "cis_maximize_dynamic_range: power on delay %3d %3d %3d\n", 
 | 
						|
           dev->CIS.powerOnDelay[0], dev->CIS.powerOnDelay[1], 
 | 
						|
           dev->CIS.powerOnDelay[2]);
 | 
						|
           
 | 
						|
   minExposeTime = (dev->CIS.hw_hres <= 300) ? 170 : 253;
 | 
						|
   
 | 
						|
   for (channel = first; channel <= last; ++channel)
 | 
						|
   {
 | 
						|
      dev->CIS.powerOnDelay[channel] = (powerOnDelayLower[channel] +
 | 
						|
                                        powerOnDelayUpper[channel]) / 2;
 | 
						|
      exposeTime[channel] -= dev->CIS.powerOnDelay[channel] - 1;
 | 
						|
      dev->CIS.powerOnDelay[channel] = 1;
 | 
						|
      
 | 
						|
      if (exposeTime[channel] < minExposeTime)
 | 
						|
      {
 | 
						|
         dev->CIS.powerOnDelay[channel] += 
 | 
						|
            minExposeTime - exposeTime[channel];
 | 
						|
         exposeTime[channel] = minExposeTime;
 | 
						|
      }
 | 
						|
   }                                      
 | 
						|
   
 | 
						|
   dev->CIS.exposeTime = exposeTime[MUSTEK_PP_CIS_CHANNEL_GREEN];
 | 
						|
   
 | 
						|
   DBG (3, "cis_maximize_dynamic_range: expose time: %3d\n", exposeTime[1]);
 | 
						|
   DBG (3, "cis_maximize_dynamic_range: power on delay %3d %3d %3d\n", 
 | 
						|
           dev->CIS.powerOnDelay[0], dev->CIS.powerOnDelay[1], 
 | 
						|
           dev->CIS.powerOnDelay[2]);
 | 
						|
   
 | 
						|
   /*
 | 
						|
    * Short the calibration. Temporary, to find out what is wrong with
 | 
						|
    * the calibration on a 600 CP.
 | 
						|
    *
 | 
						|
   dev->CIS.exposeTime = 170;
 | 
						|
   dev->CIS.powerOnDelay[0] = 120;
 | 
						|
   dev->CIS.powerOnDelay[1] = 120;
 | 
						|
   dev->CIS.powerOnDelay[2] = 120;
 | 
						|
   */
 | 
						|
   return SANE_TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Bool
 | 
						|
cis_measure_extremes(Mustek_PP_CIS_dev * dev, SANE_Byte* calib[3],
 | 
						|
		     SANE_Int pixels, SANE_Int first, SANE_Int last)
 | 
						|
{
 | 
						|
   SANE_Byte buf[3][MUSTEK_PP_CIS_MAX_H_PIXEL];
 | 
						|
   SANE_Byte min[3][MUSTEK_PP_CIS_MAX_H_PIXEL];
 | 
						|
   SANE_Byte max[3][MUSTEK_PP_CIS_MAX_H_PIXEL];
 | 
						|
   SANE_Int  sum[3][MUSTEK_PP_CIS_MAX_H_PIXEL];
 | 
						|
   int channel, cnt, p;
 | 
						|
   
 | 
						|
   memset((void*)&min, 255, 3*MUSTEK_PP_CIS_MAX_H_PIXEL*sizeof(SANE_Byte));
 | 
						|
   memset((void*)&max,   0, 3*MUSTEK_PP_CIS_MAX_H_PIXEL*sizeof(SANE_Byte));
 | 
						|
   memset((void*)&sum,   0, 3*MUSTEK_PP_CIS_MAX_H_PIXEL*sizeof(SANE_Int));
 | 
						|
   
 | 
						|
   dev->CIS.channel = first;
 | 
						|
   
 | 
						|
   /* Purge the banks first (there's always a 3-cycle delay) */
 | 
						|
   for (channel = first; channel <= last; ++channel)
 | 
						|
   {
 | 
						|
      if (!cis_read_line(dev, &buf[channel%3][0], pixels, 
 | 
						|
                         /* raw = */ SANE_TRUE))
 | 
						|
         return SANE_FALSE;
 | 
						|
   }
 | 
						|
   --dev->CIS.skipsToOrigin;
 | 
						|
   
 | 
						|
   for (cnt = 0; cnt < MUSTEK_PP_CIS_AVERAGE_COUNT + 2; ++cnt)
 | 
						|
   {
 | 
						|
      for (channel = first; channel <= last; ++channel)
 | 
						|
      {
 | 
						|
         DBG(4, "cis_measure_extremes: Reading line %d - channel %d\n", 
 | 
						|
                cnt, channel);
 | 
						|
         if (!cis_read_line(dev, &buf[channel][0], pixels, 
 | 
						|
                            /* raw = */ SANE_TRUE))
 | 
						|
            return SANE_FALSE;
 | 
						|
 | 
						|
         for (p = 0; p < pixels; ++p)
 | 
						|
         {
 | 
						|
            SANE_Byte val = buf[channel][p];
 | 
						|
            if (val < min[channel][p]) min[channel][p] = val;
 | 
						|
            if (val > max[channel][p]) max[channel][p] = val;
 | 
						|
            sum[channel][p] += val;
 | 
						|
         }
 | 
						|
      }
 | 
						|
      --dev->CIS.skipsToOrigin;
 | 
						|
   }
 | 
						|
   DBG(4, "cis_measure_extremes: Averaging\n");
 | 
						|
   for (channel = first; channel <= last; ++channel)      
 | 
						|
   {
 | 
						|
      /* Ignore the extreme values and take the average of the others. */
 | 
						|
      for (p = 0; p < pixels; ++p)
 | 
						|
      {
 | 
						|
         sum[channel][p] -= min[channel][p] + max[channel][p];
 | 
						|
         sum[channel][p] /= MUSTEK_PP_CIS_AVERAGE_COUNT;
 | 
						|
         if (calib[channel]) calib[channel][p] = sum[channel][p];
 | 
						|
      }
 | 
						|
   }
 | 
						|
   DBG(4, "cis_measure_extremes: Done\n");
 | 
						|
   return SANE_TRUE;
 | 
						|
}   
 | 
						|
 | 
						|
static SANE_Bool
 | 
						|
cis_normalize_ranges(Mustek_PP_CIS_dev * dev)
 | 
						|
{
 | 
						|
   SANE_Byte cal_low, cal_hi ;
 | 
						|
   SANE_Byte powerOnDelay[3] ;
 | 
						|
   SANE_Int pixels = dev->calib_pixels;
 | 
						|
   SANE_Int channel, p, first, last;
 | 
						|
   
 | 
						|
   if (dev->desc->mode == MODE_COLOR)
 | 
						|
   {
 | 
						|
      first = MUSTEK_PP_CIS_CHANNEL_RED;
 | 
						|
      last  = MUSTEK_PP_CIS_CHANNEL_BLUE;
 | 
						|
   }
 | 
						|
   else
 | 
						|
   {
 | 
						|
      first = MUSTEK_PP_CIS_CHANNEL_GRAY;
 | 
						|
      last  = MUSTEK_PP_CIS_CHANNEL_GRAY;
 | 
						|
   }
 | 
						|
   
 | 
						|
   DBG(3, "cis_normalize_ranges: Measuring high extremes\n");
 | 
						|
   /* Measure extremes with normal lighting */
 | 
						|
   if (!cis_measure_extremes(dev, dev->calib_hi, pixels, first, last)) {
 | 
						|
      return SANE_FALSE;
 | 
						|
   }
 | 
						|
 | 
						|
   /* Measure extremes without lighting */
 | 
						|
   for (channel=first; channel<=last; ++channel) {
 | 
						|
      powerOnDelay[channel] = dev->CIS.powerOnDelay[channel];
 | 
						|
      dev->CIS.powerOnDelay[channel] = dev->CIS.exposeTime;
 | 
						|
   }
 | 
						|
   
 | 
						|
   DBG(3, "cis_normalize_ranges: Measuring low extremes\n");
 | 
						|
   if (!cis_measure_extremes(dev, dev->calib_low, pixels, first, last)) {
 | 
						|
      return SANE_FALSE;
 | 
						|
   }
 | 
						|
   
 | 
						|
   /* Restore settings */
 | 
						|
   for (channel=first; channel<=last; ++channel) {
 | 
						|
      dev->CIS.powerOnDelay[channel] = powerOnDelay[channel];
 | 
						|
   }
 | 
						|
   
 | 
						|
   /* Make sure calib_hi is greater than calib_low */
 | 
						|
   for (channel = first; channel <= last; ++channel) {
 | 
						|
     for (p = 0; p<pixels; p++) {
 | 
						|
       if (dev->calib_low[channel]) {
 | 
						|
         cal_low = dev->calib_low[channel][p];
 | 
						|
       } else {
 | 
						|
         cal_low = 0;
 | 
						|
       }
 | 
						|
       if (dev->calib_hi[channel]) {
 | 
						|
         cal_hi = dev->calib_hi[channel][p];
 | 
						|
       } else {
 | 
						|
         cal_hi = 255;
 | 
						|
       }
 | 
						|
       if (cal_hi <= cal_low) {
 | 
						|
         if(cal_hi<255) {
 | 
						|
           /* calib_hi exists, else cal_hi would be 255 */
 | 
						|
           dev->calib_hi[channel][p] = cal_low+1;
 | 
						|
         } else {
 | 
						|
           /* calib_low exists, else cal_low would be 0, < 255  */
 | 
						|
           dev->calib_low[channel][p] = cal_hi-1;
 | 
						|
         }
 | 
						|
       }
 | 
						|
     }
 | 
						|
   }
 | 
						|
   DBG(3, "cis_normalize_ranges: calibration done\n");
 | 
						|
   return SANE_TRUE;
 | 
						|
}   
 | 
						|
 | 
						|
/*
 | 
						|
 * This routine measures the time that we have to wait between reading 
 | 
						|
 * to pixels from the scanner. Especially at low resolutions, but also
 | 
						|
 * for narrow-width scans at high resolutions, reading too fast cause
 | 
						|
 * color stability problems. 
 | 
						|
 * This routine sends a test pattern to the scanner memory banks and tries
 | 
						|
 * to measure how fast it can be retrieved without errors. 
 | 
						|
 * The same is done by the TWAIN driver (TESTIO.CPP:TestDelay). 
 | 
						|
 */
 | 
						|
static SANE_Bool
 | 
						|
cis_measure_delay(Mustek_PP_CIS_dev * dev)
 | 
						|
{
 | 
						|
   SANE_Byte buf[2][2048];
 | 
						|
   unsigned i, j, d;
 | 
						|
   int saved_res;
 | 
						|
   SANE_Bool error = SANE_FALSE;
 | 
						|
   
 | 
						|
   CIS_CLEAR_FULLFLAG(dev);
 | 
						|
   CIS_CLEAR_WRITE_ADDR(dev);
 | 
						|
   CIS_CLEAR_WRITE_BANK(dev);
 | 
						|
   CIS_INC_READ(dev);
 | 
						|
   CIS_CLEAR_READ_BANK(dev);
 | 
						|
 | 
						|
   M1015_DISPLAY_REGS(dev, "Before delay measurement");
 | 
						|
   assert(dev->CIS.adjustskip == 0);
 | 
						|
   
 | 
						|
   /* Sawtooth */
 | 
						|
   for (i=0; i<2048; ++i)
 | 
						|
   {
 | 
						|
      buf[0][i] = i % 255; /* Why 255 ? Seems to have no real importance */
 | 
						|
   }
 | 
						|
   
 | 
						|
   Mustek_PP_1015_write_reg_start(dev, MA1015W_SRAM_SOURCE_PC);
 | 
						|
   for (i=0; i<2048; ++i)
 | 
						|
   {
 | 
						|
      Mustek_PP_1015_write_reg_val(dev, buf[0][i]);
 | 
						|
   }
 | 
						|
   Mustek_PP_1015_write_reg_stop(dev);
 | 
						|
   
 | 
						|
   /* Bank offset measurement */
 | 
						|
   dev->CIS.delay = 0; /* Initialize to zero, measure next */
 | 
						|
   
 | 
						|
   saved_res = dev->CIS.res;
 | 
						|
   dev->CIS.res = dev->CIS.hw_hres;
 | 
						|
   
 | 
						|
   /*
 | 
						|
    * Note: the TWAIN driver seems to have a fast EPP mode too. That one is
 | 
						|
    * tried first, and then they try the normal mode. I haven't figured out
 | 
						|
    * yet how the fast mode works, so I'll only check the normal mode for now.
 | 
						|
    * Moreover, from the behaviour that I've witnessed from the TWAIN driver,
 | 
						|
    * I must conclude that the fast mode probably doesn't work on my computer,
 | 
						|
    * so I can't test it anyhow.
 | 
						|
    */
 | 
						|
   /* Gradually increase the delay till we have no more errors */
 | 
						|
   for (d = 0; d < 75 /* 255 */  && dev->desc->state != STATE_CANCELLED; d += 5)
 | 
						|
   {
 | 
						|
      dev->CIS.delay = d;
 | 
						|
      
 | 
						|
      /*
 | 
						|
       * We read the line 5 times to make sure that all garbage is flushed.
 | 
						|
       */
 | 
						|
      for (i=0; i<5; ++i)
 | 
						|
      {
 | 
						|
         CIS_INC_READ(dev);
 | 
						|
         CIS_CLEAR_READ_BANK(dev);
 | 
						|
         cis_read_line_low_level (dev, &buf[1][0], 2048, NULL, NULL, NULL);
 | 
						|
         if (dev->desc->state == STATE_CANCELLED) return SANE_FALSE;
 | 
						|
      }
 | 
						|
      
 | 
						|
      error = SANE_FALSE;
 | 
						|
      /* Check 100 times whether we can read without errors. */
 | 
						|
      for (i=0; i<100 && !error; ++i)
 | 
						|
      {
 | 
						|
         CIS_INC_READ(dev);
 | 
						|
         CIS_CLEAR_READ_BANK(dev);
 | 
						|
         cis_read_line_low_level (dev, &buf[1][0], 2048, NULL, NULL, NULL);
 | 
						|
         if (dev->desc->state == STATE_CANCELLED) return SANE_FALSE;
 | 
						|
         
 | 
						|
         for (j=0; j<2048; ++j)
 | 
						|
         {
 | 
						|
            if (buf[0][j] != buf[1][j]) 
 | 
						|
            {
 | 
						|
               error = SANE_TRUE;
 | 
						|
               break;
 | 
						|
            }
 | 
						|
         }
 | 
						|
      }
 | 
						|
      
 | 
						|
      DBG (3, "cis_measure_delay: delay %d\n", dev->CIS.delay); 
 | 
						|
      if (!error)
 | 
						|
         break;
 | 
						|
   }
 | 
						|
   
 | 
						|
   dev->CIS.res = saved_res;
 | 
						|
   
 | 
						|
   if (error)
 | 
						|
   {
 | 
						|
      fprintf(stderr, "mustek_pp_cis: failed to measure delay.\n");
 | 
						|
      fprintf(stderr, "Buffer contents:\n");
 | 
						|
      for (j = 0; j < 20; ++j)
 | 
						|
      {
 | 
						|
         fprintf(stderr, "%d ", buf[1][j]);
 | 
						|
      }
 | 
						|
      fprintf(stderr, "\n");
 | 
						|
      dev->CIS.delay = 0; 
 | 
						|
   }
 | 
						|
   
 | 
						|
   DBG (3, "cis_measure_delay: delay %d\n", dev->CIS.delay); 
 | 
						|
   return SANE_TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
cis_motor_control (Mustek_PP_CIS_dev * dev, u_char control)
 | 
						|
{
 | 
						|
   cis_wait_motor_stable (dev);
 | 
						|
   Mustek_PP_1015_write_reg(dev, MA1015W_MOTOR_CONTROL, control);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
cis_return_home (Mustek_PP_CIS_dev * dev, SANE_Bool nowait)
 | 
						|
{
 | 
						|
   SANE_Byte savedExposeTime = dev->CIS.exposeTime;
 | 
						|
   DBG(4, "cis_return_home: returning home; nowait: %d\n", nowait);
 | 
						|
   /* During a return-home, the expose time is fixed. */
 | 
						|
   dev->CIS.exposeTime = 170;
 | 
						|
   cis_config_ccd(dev);
 | 
						|
   dev->CIS.exposeTime = savedExposeTime;
 | 
						|
   
 | 
						|
   cis_motor_control (dev, 0xEB);
 | 
						|
 | 
						|
   if (nowait == SANE_FALSE)
 | 
						|
      Mustek_PP_1015_wait_bit(dev, MA1015R_MOTOR, MA1015B_MOTOR_HOME, 
 | 
						|
                              SANE_TRUE, 1000);
 | 
						|
}
 | 
						|
 | 
						|
/******************************************************************************
 | 
						|
 * Does a full reset of the device, ie. configures the CIS to a default
 | 
						|
 * resolution of 300 DPI (in high or low resolution mode, depending on the
 | 
						|
 * resolution requested by the user).
 | 
						|
 *****************************************************************************/
 | 
						|
static void
 | 
						|
cis_reset_device (Mustek_PP_CIS_dev * dev)
 | 
						|
{
 | 
						|
   DBG(4, "cis_reset_device: resetting device\n");
 | 
						|
   dev->CIS.adjustskip         = 0;
 | 
						|
   dev->CIS.dontIncRead        = SANE_TRUE;
 | 
						|
   dev->CIS.dontMove           = SANE_FALSE;
 | 
						|
   
 | 
						|
   cis_save_state(dev);
 | 
						|
 | 
						|
   dev->CIS.hw_hres            = 300;
 | 
						|
   dev->CIS.channel            = MUSTEK_PP_CIS_CHANNEL_GREEN;
 | 
						|
   dev->CIS.setParameters      = SANE_FALSE;
 | 
						|
   dev->CIS.exposeTime         = 0xAA;
 | 
						|
   
 | 
						|
   cis_config_ccd (dev);
 | 
						|
   
 | 
						|
   cis_restore_state(dev);
 | 
						|
   
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Bool
 | 
						|
cis_calibrate (Mustek_PP_CIS_dev * dev)
 | 
						|
{
 | 
						|
   int i, saved_res = dev->CIS.res, saved_vres = dev->CIS.hw_vres;
 | 
						|
   
 | 
						|
   /*
 | 
						|
    * Flow of operation observed from the twain driver
 | 
						|
    * (it is assumed that the lamp is at the origin, and that the CIS is
 | 
						|
    * configured for 300 DPI, ie. cis_reset_device has been called.)
 | 
						|
    *
 | 
						|
    *  - Reset the device and return the lamp to its home position
 | 
						|
    *
 | 
						|
    *  - Unknown short sequence
 | 
						|
    *
 | 
						|
    *  - Send a sawtooth-like pattern to one of the memory banks.
 | 
						|
    *
 | 
						|
    *  - Repetitive read_line of 2048 bytes, interleaved with an unknown
 | 
						|
    *    command. The number varies between 102 and 170 times, but there
 | 
						|
    *    doesn't seem to be any correlation with the current mode of the
 | 
						|
    *    scanner, so I assume that the exact number isn't really relevant.
 | 
						|
    *    The values that are read are the one that were sent to the bank,
 | 
						|
    *    rotated by 1 byte in my case. 
 | 
						|
    *
 | 
						|
    *
 | 
						|
    *    It seems that the width of the black border is being measured at 
 | 
						|
    *    this stage, possibly multiple times till it stabilizes. 
 | 
						|
    *    I assume that the buffer is read 100 times to allow the lamp to
 | 
						|
    *    warm up and that the the width of the black border is then being 
 | 
						|
    *    measured till it stabilizes. That would explain the minimum number
 | 
						|
    *    of 102 iterations that I've seen.
 | 
						|
    *
 | 
						|
    *  - reset the device
 | 
						|
    *
 | 
						|
    *  - move the motor 110 steps forward. The TWAIN driver moves 90 steps,
 | 
						|
    *    and I've used 90 steps for a long time too, but occasionally, 
 | 
						|
    *    90 steps is a fraction to short to reach the start of the 
 | 
						|
    *    calibration strip (the motor movements are not very accurate;
 | 
						|
    *    an offset of 1 mm is not unusual). Therefore, I've increased it to
 | 
						|
    *    110 steps. This gives us an additional 1.6 mm slack, which should
 | 
						|
    *    prevent calibration errors. 
 | 
						|
    *    (Note that the MUSTEK_PP_CIS_????CP_DEFAULT_SKIP constants have to 
 | 
						|
    *    be adjusted if the number of steps is altered.)
 | 
						|
    *
 | 
						|
    *  - configure the CIS : actual resolution + set parameters
 | 
						|
    * 
 | 
						|
    */
 | 
						|
   
 | 
						|
   /* 
 | 
						|
    * We must make sure that we are in the scanning state; otherwise we may
 | 
						|
    * still be in the canceled state from a previous scan (even if terminated
 | 
						|
    * normally), and the whole calibration would go wrong. 
 | 
						|
    */ 
 | 
						|
   dev->desc->state = STATE_SCANNING;
 | 
						|
    
 | 
						|
   cis_reset_device (dev);
 | 
						|
   cis_return_home (dev, SANE_FALSE);  /* Wait till it's home */
 | 
						|
   
 | 
						|
   /* Use maximum resolution during calibration; otherwise we may calibrate
 | 
						|
      past the calibration strip. */
 | 
						|
   dev->CIS.hw_vres = dev->desc->dev->maxres;
 | 
						|
   /* This field remembers how many steps we still have to go @ max res */
 | 
						|
   dev->CIS.skipsToOrigin = dev->top_skip; /*max2hw_vres(dev, dev->top_skip); */
 | 
						|
   
 | 
						|
   if (!cis_measure_delay(dev))
 | 
						|
      return SANE_FALSE;
 | 
						|
   
 | 
						|
   cis_reset_device (dev);
 | 
						|
 | 
						|
   /* Move motor 110 steps @ 300 DPI */
 | 
						|
   Mustek_PP_1015_write_reg_start(dev, MA1015W_MOTOR_CONTROL);
 | 
						|
   for (i=0; i<110; ++i)
 | 
						|
   {
 | 
						|
      if (dev->model == MUSTEK_PP_CIS600)
 | 
						|
      {
 | 
						|
         Mustek_PP_1015_write_reg_val (dev, 0x73);
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
         Mustek_PP_1015_write_reg_val (dev, 0x7B);
 | 
						|
      }
 | 
						|
      cis_wait_motor_stable (dev);
 | 
						|
   }
 | 
						|
   Mustek_PP_1015_write_reg_stop(dev);
 | 
						|
   
 | 
						|
   /* Next, we maximize the dynamic range of the scanner. During calibration
 | 
						|
      we don't want to extrapolate, so we limit the resolution if necessary */
 | 
						|
   
 | 
						|
   if (dev->CIS.hw_hres < dev->CIS.res) 
 | 
						|
      dev->CIS.res = dev->CIS.hw_hres;
 | 
						|
   
 | 
						|
   if (!cis_maximize_dynamic_range(dev))
 | 
						|
      return SANE_FALSE;
 | 
						|
   
 | 
						|
   if (!cis_normalize_ranges(dev))
 | 
						|
      return SANE_FALSE;
 | 
						|
   
 | 
						|
   dev->CIS.res = saved_res;
 | 
						|
   dev->CIS.hw_vres = saved_vres;
 | 
						|
   
 | 
						|
   /* Convert steps back to max res size, which are used during skipping */
 | 
						|
/*   dev->CIS.skipsToOrigin = hw2max_vres(dev, dev->CIS.skipsToOrigin); */
 | 
						|
   
 | 
						|
   /* Move to the origin */
 | 
						|
   DBG(3, "cis_calibrate: remaining skips to origin @maxres: %d\n", 
 | 
						|
          dev->CIS.skipsToOrigin);
 | 
						|
   cis_move_motor(dev, dev->CIS.skipsToOrigin);
 | 
						|
   
 | 
						|
   if (dev->calib_mode)
 | 
						|
   {
 | 
						|
      /* In calibration mode, we scan the interior of the scanner before the
 | 
						|
         glass plate in order to find the position of the calibration strip
 | 
						|
         and the start of the glass plate. */
 | 
						|
      DBG(3, "cis_calibrate: running in calibration mode. Returning home.\n");
 | 
						|
      cis_return_home (dev, SANE_FALSE);  /* Wait till it's home */
 | 
						|
   }
 | 
						|
   
 | 
						|
   return dev->desc->state != STATE_CANCELLED ? SANE_TRUE : SANE_FALSE;
 | 
						|
   
 | 
						|
}  
 | 
						|
 | 
						|
/******************************************************************************
 | 
						|
 ******************************************************************************
 | 
						|
 ***                           Mustek PP interface                          ***
 | 
						|
 ******************************************************************************
 | 
						|
 *****************************************************************************/
 | 
						|
 | 
						|
/******************************************************************************
 | 
						|
* Init                                                                        *
 | 
						|
******************************************************************************/
 | 
						|
 | 
						|
/* Shared initialization routine */
 | 
						|
static SANE_Status cis_attach(SANE_String_Const port,
 | 
						|
                              SANE_String_Const name, 
 | 
						|
                              SANE_Attach_Callback attach,
 | 
						|
                              SANE_Int driverNo,
 | 
						|
                              SANE_Int info)
 | 
						|
{
 | 
						|
   int fd;
 | 
						|
   SANE_Status status;
 | 
						|
   u_char asic;
 | 
						|
 | 
						|
   status = sanei_pa4s2_open (port, &fd);
 | 
						|
 | 
						|
   if (status != SANE_STATUS_GOOD)
 | 
						|
   {
 | 
						|
      SANE_Status altStatus;
 | 
						|
      SANE_String_Const altPort;
 | 
						|
      
 | 
						|
      DBG (2, "cis_attach: couldn't attach to `%s' (%s)\n", port,
 | 
						|
           sane_strstatus (status));
 | 
						|
      
 | 
						|
      /* Make migration to libieee1284 painless for users that used 
 | 
						|
         direct io in the past */
 | 
						|
           if (strcmp(port, "0x378") == 0) altPort = "parport0";
 | 
						|
      else if (strcmp(port, "0x278") == 0) altPort = "parport1";
 | 
						|
      else if (strcmp(port, "0x3BC") == 0) altPort = "parport2";
 | 
						|
      else return status;
 | 
						|
      
 | 
						|
      DBG (2, "cis_attach: trying alternative port name: %s\n", altPort);
 | 
						|
      
 | 
						|
      altStatus = sanei_pa4s2_open (altPort, &fd);
 | 
						|
      if (altStatus != SANE_STATUS_GOOD)
 | 
						|
      {
 | 
						|
           DBG (2, "cis_attach: couldn't attach to alternative port `%s' "
 | 
						|
              "(%s)\n", altPort, sane_strstatus (altStatus));
 | 
						|
           return status; /* Return original status, not alternative status */
 | 
						|
      }
 | 
						|
   }
 | 
						|
   
 | 
						|
   M1015_START_LL;
 | 
						|
   M1015_START_HL;
 | 
						|
 | 
						|
 | 
						|
   sanei_pa4s2_enable (fd, SANE_TRUE);
 | 
						|
   SANEI_PA4S2_READBEGIN (fd, 0);
 | 
						|
   SANEI_PA4S2_READBYTE (fd, &asic);
 | 
						|
   SANEI_PA4S2_READEND (fd);
 | 
						|
   sanei_pa4s2_enable (fd, SANE_FALSE);
 | 
						|
 | 
						|
   sanei_pa4s2_close (fd);
 | 
						|
   
 | 
						|
   if (asic != 0xA5) /* Identifies the MA1015 chipset */
 | 
						|
   {
 | 
						|
      /* CIS driver only works for MA1015 chipset */
 | 
						|
      DBG (2, "cis_attach: asic id (0x%02x) not recognized\n", asic);
 | 
						|
      return SANE_STATUS_INVAL; 
 | 
						|
   }
 | 
						|
 | 
						|
   DBG (3, "cis_attach: device %s attached\n", name);
 | 
						|
   DBG (3, "cis_attach: asic 0x%02x\n", asic);
 | 
						|
   
 | 
						|
   return attach(port, name, driverNo, info);
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status cis600_drv_init(SANE_Int options, SANE_String_Const port,
 | 
						|
		SANE_String_Const name, SANE_Attach_Callback attach)
 | 
						|
{
 | 
						|
   if (options != CAP_NOTHING)
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
   return cis_attach(port, name, attach, MUSTEK_PP_CIS600, MUSTEK_PP_CIS600);
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status cis1200_drv_init(SANE_Int options, SANE_String_Const port,
 | 
						|
		SANE_String_Const name, SANE_Attach_Callback attach)
 | 
						|
{
 | 
						|
   if (options != CAP_NOTHING)
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
   return cis_attach(port, name, attach, MUSTEK_PP_CIS1200, MUSTEK_PP_CIS1200);
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status cis1200p_drv_init(SANE_Int options, SANE_String_Const port,
 | 
						|
		SANE_String_Const name, SANE_Attach_Callback attach)
 | 
						|
{
 | 
						|
   if (options != CAP_NOTHING)
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
   return cis_attach(port, name, attach, MUSTEK_PP_CIS1200PLUS, MUSTEK_PP_CIS1200PLUS);
 | 
						|
}
 | 
						|
 | 
						|
/******************************************************************************
 | 
						|
* Capabilities                                                                *
 | 
						|
******************************************************************************/
 | 
						|
void cis_drv_capabilities(SANE_Int info, SANE_String *model,
 | 
						|
                          SANE_String *vendor, SANE_String *type,
 | 
						|
                          SANE_Int *maxres, SANE_Int *minres,
 | 
						|
                          SANE_Int *maxhsize, SANE_Int *maxvsize,
 | 
						|
                          SANE_Int *caps)
 | 
						|
{
 | 
						|
   *vendor = strdup("Mustek");
 | 
						|
   *type = strdup("flatbed scanner");
 | 
						|
   *caps = CAP_NOTHING;
 | 
						|
 | 
						|
   switch(info)
 | 
						|
   {   
 | 
						|
      case MUSTEK_PP_CIS600:
 | 
						|
        *model = strdup("600CP");
 | 
						|
        *maxres = 600;
 | 
						|
        *minres = 50;
 | 
						|
        *maxhsize = MUSTEK_PP_CIS_MAX_H_PIXEL;
 | 
						|
        *maxvsize = MUSTEK_PP_CIS_MAX_V_PIXEL;
 | 
						|
        break;
 | 
						|
      case MUSTEK_PP_CIS1200:
 | 
						|
        *model = strdup("1200CP");
 | 
						|
        *maxres = 1200;
 | 
						|
        *minres = 50;
 | 
						|
        *maxhsize = MUSTEK_PP_CIS_MAX_H_PIXEL*2;
 | 
						|
        *maxvsize = MUSTEK_PP_CIS_MAX_V_PIXEL*2;
 | 
						|
        break;
 | 
						|
      case MUSTEK_PP_CIS1200PLUS:
 | 
						|
        *model = strdup("1200CP+");
 | 
						|
        *maxres = 1200;
 | 
						|
        *minres = 50;
 | 
						|
        *maxhsize = MUSTEK_PP_CIS_MAX_H_PIXEL*2;
 | 
						|
        *maxvsize = MUSTEK_PP_CIS_MAX_V_PIXEL*2;
 | 
						|
        break;
 | 
						|
   }
 | 
						|
}
 | 
						|
 | 
						|
/******************************************************************************
 | 
						|
* Open                                                                        *
 | 
						|
******************************************************************************/
 | 
						|
SANE_Status cis_drv_open (SANE_String port, SANE_Int caps, SANE_Int *fd)
 | 
						|
{
 | 
						|
   SANE_Status status;
 | 
						|
 | 
						|
   if (caps != CAP_NOTHING)
 | 
						|
   {
 | 
						|
      DBG (1, "cis_drv_open: called with unknown capabilities (0x%02X)\n", caps);
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
   }
 | 
						|
 | 
						|
   DBG (3, "cis_drv_open: called for port %s\n", port);
 | 
						|
 | 
						|
   status = sanei_pa4s2_open (port, fd);
 | 
						|
 | 
						|
   if (status != SANE_STATUS_GOOD)
 | 
						|
   {
 | 
						|
      SANE_Status altStatus;
 | 
						|
      SANE_String_Const altPort;
 | 
						|
      
 | 
						|
      DBG (2, "cis_attach: couldn't attach to `%s' (%s)\n", port,
 | 
						|
           sane_strstatus (status));
 | 
						|
      
 | 
						|
      /* Make migration to libieee1284 painless for users that used 
 | 
						|
         direct io in the past */
 | 
						|
           if (strcmp(port, "0x378") == 0) altPort = "parport0";
 | 
						|
      else if (strcmp(port, "0x278") == 0) altPort = "parport1";
 | 
						|
      else if (strcmp(port, "0x3BC") == 0) altPort = "parport2";
 | 
						|
      else return status;
 | 
						|
      
 | 
						|
      DBG (2, "cis_attach: trying alternative port name: %s\n", altPort);
 | 
						|
      
 | 
						|
      altStatus = sanei_pa4s2_open (altPort, fd);
 | 
						|
      if (altStatus != SANE_STATUS_GOOD)
 | 
						|
      {
 | 
						|
           DBG (2, "cis_attach: couldn't attach to alternative port `%s' "
 | 
						|
              "(%s)\n", altPort, sane_strstatus (altStatus));
 | 
						|
           return status; /* Return original status, not alternative status */
 | 
						|
      }
 | 
						|
   }
 | 
						|
 | 
						|
   return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
/******************************************************************************
 | 
						|
* Setup                                                                       *
 | 
						|
******************************************************************************/
 | 
						|
void cis_drv_setup (SANE_Handle hndl)
 | 
						|
{
 | 
						|
   Mustek_pp_Handle *dev = hndl;
 | 
						|
   Mustek_PP_CIS_dev *cisdev;
 | 
						|
   cisdev = (Mustek_PP_CIS_dev*)malloc(sizeof(Mustek_PP_CIS_dev));
 | 
						|
   if (cisdev == NULL)
 | 
						|
   {
 | 
						|
      DBG (2, "cis_drv_setup: not enough memory for device descriptor\n");
 | 
						|
      sanei_pa4s2_close (dev->fd);
 | 
						|
      return;
 | 
						|
   }
 | 
						|
   memset(cisdev, 0, sizeof(Mustek_PP_CIS_dev));
 | 
						|
   DBG(3, "cis_drv_setup: cis device allocated\n");
 | 
						|
   
 | 
						|
   dev->lamp_on = 0;
 | 
						|
   dev->priv = cisdev;
 | 
						|
   
 | 
						|
   cisdev->desc = dev;
 | 
						|
   cisdev->model = dev->dev->info;
 | 
						|
   cisdev->CIS.hw_hres = 300;
 | 
						|
   cisdev->CIS.cisRes = 300;
 | 
						|
   cisdev->CIS.hw_vres = 300;
 | 
						|
   
 | 
						|
   /* Default values for configurable parameters; configuration file
 | 
						|
      may override them. */
 | 
						|
   cisdev->fast_skip = SANE_TRUE;
 | 
						|
   cisdev->bw_limit = 127;
 | 
						|
   cisdev->calib_mode = SANE_FALSE;
 | 
						|
   cisdev->engine_delay = 0;
 | 
						|
   if (cisdev->model == MUSTEK_PP_CIS600)
 | 
						|
   {
 | 
						|
      cisdev->top_skip = MUSTEK_PP_CIS_600CP_DEFAULT_SKIP;
 | 
						|
   }
 | 
						|
   else
 | 
						|
   {
 | 
						|
      cisdev->top_skip = MUSTEK_PP_CIS_1200CP_DEFAULT_SKIP;
 | 
						|
   }
 | 
						|
}
 | 
						|
 | 
						|
/******************************************************************************
 | 
						|
* Config                                                                      *
 | 
						|
******************************************************************************/
 | 
						|
SANE_Status cis_drv_config(SANE_Handle hndl, SANE_String_Const optname,
 | 
						|
	 		   SANE_String_Const optval)
 | 
						|
{
 | 
						|
   Mustek_pp_Handle *dev = hndl;
 | 
						|
   Mustek_PP_CIS_dev *cisdev = dev->priv;
 | 
						|
   int value = 0;
 | 
						|
   double dvalue = 0;
 | 
						|
   DBG (3, "cis_drv_cfg option: %s=%s\n", optname, optval ? optval : "");
 | 
						|
   if (!strcmp(optname, "top_adjust"))
 | 
						|
   {
 | 
						|
      if (!optval)
 | 
						|
      {
 | 
						|
         DBG (1, "cis_drv_config: missing value for option top_adjust\n");
 | 
						|
         return SANE_STATUS_INVAL;
 | 
						|
      }
 | 
						|
      dvalue = atof(optval);
 | 
						|
      
 | 
						|
      /* An adjustment of +/- 5 mm should be sufficient and safe */
 | 
						|
      if (dvalue < -5.0)
 | 
						|
      {
 | 
						|
         DBG (1, "cis_drv_config: value for option top_adjust too small: "
 | 
						|
                 "%.2f < -5; limiting to -5 mm\n", dvalue);
 | 
						|
         dvalue = -5.0;
 | 
						|
      }
 | 
						|
      if (dvalue > 5.0)
 | 
						|
      {
 | 
						|
         DBG (1, "cis_drv_config: value for option top_adjust too large: "
 | 
						|
                 "%.2f > 5; limiting to 5 mm\n", dvalue);
 | 
						|
         dvalue = 5.0;
 | 
						|
      }
 | 
						|
      /* In practice, there is a lower bound on the value that can be used,
 | 
						|
         but if the top_skip value is smaller than that value, the only result
 | 
						|
         will be that the driver tries to move the head a negative number 
 | 
						|
         of steps after calibration. The move routine just ignores negative
 | 
						|
         steps, so no harm can be done. */
 | 
						|
      cisdev->top_skip += MM_TO_PIXEL(dvalue, dev->dev->maxres);
 | 
						|
      DBG (3, "cis_drv_config: setting top skip value to %d\n", 
 | 
						|
              cisdev->top_skip);
 | 
						|
      
 | 
						|
      /* Just to be cautious; we don't want the head to hit the bottom */
 | 
						|
      if (cisdev->top_skip > 600) cisdev->top_skip = 600;
 | 
						|
      if (cisdev->top_skip < -600) cisdev->top_skip = -600;
 | 
						|
   }
 | 
						|
   else if (!strcmp(optname, "slow_skip"))
 | 
						|
   {
 | 
						|
      if (optval)
 | 
						|
      {
 | 
						|
         DBG (1, "cis_drv_config: unexpected value for option slow_skip\n");
 | 
						|
         return SANE_STATUS_INVAL;
 | 
						|
      }
 | 
						|
      DBG (3, "cis_drv_config: disabling fast skipping\n");
 | 
						|
      cisdev->fast_skip = SANE_FALSE;
 | 
						|
   }
 | 
						|
   else if (!strcmp(optname, "bw"))
 | 
						|
   {
 | 
						|
      if (!optval)
 | 
						|
      {
 | 
						|
         DBG (1, "cis_drv_config: missing value for option bw\n");
 | 
						|
         return SANE_STATUS_INVAL;
 | 
						|
      }
 | 
						|
      value = atoi(optval);
 | 
						|
      if (value < 0 || value > 255)
 | 
						|
      {
 | 
						|
         DBG (1, "cis_drv_config: value for option bw out of range: "
 | 
						|
                 "%d < 0 or %d > 255\n", value, value);
 | 
						|
         return SANE_STATUS_INVAL;
 | 
						|
      }
 | 
						|
      cisdev->bw_limit = value;
 | 
						|
   }
 | 
						|
   else if (!strcmp(optname, "calibration_mode"))
 | 
						|
   {
 | 
						|
      if (optval)
 | 
						|
      {
 | 
						|
         DBG (1, "cis_drv_config: unexpected value for option calibration_mode\n");
 | 
						|
         return SANE_STATUS_INVAL;
 | 
						|
      }
 | 
						|
      DBG (3, "cis_drv_config: using calibration mode\n");
 | 
						|
      cisdev->calib_mode = SANE_TRUE;
 | 
						|
   }
 | 
						|
   else if (!strcmp(optname, "engine_delay"))
 | 
						|
   {
 | 
						|
      if (!optval)
 | 
						|
      {
 | 
						|
         DBG (1, "cis_drv_config: missing value for option engine_delay\n");
 | 
						|
         return SANE_STATUS_INVAL;
 | 
						|
      }
 | 
						|
      value = atoi(optval);
 | 
						|
      if (value < 0 || value > 100) /* 100 ms is already pretty slow */
 | 
						|
      {
 | 
						|
         DBG (1, "cis_drv_config: value for option engine_delay out of range: "
 | 
						|
                 "%d < 0 or %d > 100\n", value, value);
 | 
						|
         return SANE_STATUS_INVAL;
 | 
						|
      }
 | 
						|
      cisdev->engine_delay = value;
 | 
						|
   }
 | 
						|
   else
 | 
						|
   {
 | 
						|
      DBG (1, "cis_drv_config: unknown options %s\n", optname);
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
   }
 | 
						|
   return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
/******************************************************************************
 | 
						|
* Close                                                                       *
 | 
						|
******************************************************************************/
 | 
						|
void cis_drv_close (SANE_Handle hndl)
 | 
						|
{
 | 
						|
   Mustek_pp_Handle *dev = hndl;
 | 
						|
   Mustek_PP_CIS_dev *cisdev = dev->priv;
 | 
						|
   DBG (3, "cis_close: resetting device.\n");
 | 
						|
   sanei_pa4s2_enable (dev->fd, SANE_TRUE); 
 | 
						|
   cis_reset_device (cisdev);
 | 
						|
   DBG (3, "cis_close: returning home.\n");
 | 
						|
   cis_return_home (cisdev, SANE_TRUE); /* Don't wait */
 | 
						|
   DBG (3, "cis_close: disabling fd.\n");
 | 
						|
   sanei_pa4s2_enable (dev->fd, SANE_FALSE);
 | 
						|
   DBG (3, "cis_close: closing fd.\n");
 | 
						|
   sanei_pa4s2_close (dev->fd);
 | 
						|
   DBG (3, "cis_close: done.\n");
 | 
						|
   DBG (6, "cis_close: lamp_on: %d\n", (int)dev->lamp_on);
 | 
						|
   M1015_STOP_LL;
 | 
						|
   M1015_STOP_HL;
 | 
						|
}
 | 
						|
 | 
						|
/******************************************************************************
 | 
						|
* Start                                                                       *
 | 
						|
******************************************************************************/
 | 
						|
SANE_Status cis_drv_start (SANE_Handle hndl)
 | 
						|
{
 | 
						|
   Mustek_pp_Handle *dev = hndl;
 | 
						|
   Mustek_PP_CIS_dev *cisdev = dev->priv;
 | 
						|
   SANE_Int pixels = dev->params.pixels_per_line;
 | 
						|
   
 | 
						|
   if (!cisdev)
 | 
						|
   {
 | 
						|
      DBG (2, "cis_drv_start: not enough memory for device\n");
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
   }
 | 
						|
 | 
						|
   cisdev->CIS.exposeTime = 0xAA;
 | 
						|
   cisdev->CIS.setParameters = SANE_FALSE;
 | 
						|
   cisdev->CIS.use8KBank = SANE_TRUE;
 | 
						|
   cisdev->CIS.imagebytes = dev->bottomX - dev->topX;
 | 
						|
   cisdev->CIS.skipimagebytes = dev->topX;
 | 
						|
 | 
						|
   cisdev->CIS.res = dev->res;
 | 
						|
 | 
						|
   DBG (3, "cis_drv_start: %d dpi\n", dev->res);
 | 
						|
 | 
						|
   if (dev->res <= 50 && cisdev->model != MUSTEK_PP_CIS1200PLUS)
 | 
						|
   {
 | 
						|
     cisdev->CIS.hw_hres = 50;
 | 
						|
   }
 | 
						|
   else if (dev->res <= 75 && cisdev->model == MUSTEK_PP_CIS1200PLUS)
 | 
						|
   {
 | 
						|
     cisdev->CIS.hw_hres = 75;
 | 
						|
   }
 | 
						|
   else if (dev->res <= 100)
 | 
						|
   {
 | 
						|
     cisdev->CIS.hw_hres = 100;
 | 
						|
   }
 | 
						|
   else if (dev->res <= 200)
 | 
						|
   {
 | 
						|
     cisdev->CIS.hw_hres = 200;
 | 
						|
   }
 | 
						|
   else if (dev->res <= 300)
 | 
						|
   {
 | 
						|
     cisdev->CIS.hw_hres = 300;
 | 
						|
   }
 | 
						|
   else
 | 
						|
   {
 | 
						|
      if (cisdev->model == MUSTEK_PP_CIS600)
 | 
						|
      {
 | 
						|
         cisdev->CIS.hw_hres = 300; /* Limit for 600 CP */
 | 
						|
      }
 | 
						|
      else if (dev->res <= 400)
 | 
						|
      {
 | 
						|
   	 cisdev->CIS.hw_hres = 400;
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
	 cisdev->CIS.hw_hres = 600; /* Limit for 1200 CP/CP+ */
 | 
						|
      }
 | 
						|
   }
 | 
						|
 | 
						|
   if (cisdev->model == MUSTEK_PP_CIS600)
 | 
						|
   {
 | 
						|
      if (dev->res <= 150)
 | 
						|
      {
 | 
						|
         cisdev->CIS.hw_vres = 150;
 | 
						|
      }
 | 
						|
      else if (dev->res <= 300)
 | 
						|
      {
 | 
						|
         cisdev->CIS.hw_vres = 300;
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
         cisdev->CIS.hw_vres = 600;
 | 
						|
      }
 | 
						|
   }
 | 
						|
   else
 | 
						|
   {
 | 
						|
      if (dev->res <= 300)
 | 
						|
      {
 | 
						|
         cisdev->CIS.hw_vres = 300;
 | 
						|
      }
 | 
						|
      else if (dev->res <= 600)
 | 
						|
      {
 | 
						|
         cisdev->CIS.hw_vres = 600;
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
         cisdev->CIS.hw_vres = 1200;
 | 
						|
      }
 | 
						|
   }
 | 
						|
 | 
						|
   if (cisdev->model == MUSTEK_PP_CIS600 ||
 | 
						|
       (cisdev->model == MUSTEK_PP_CIS1200 && dev->res <= 300))
 | 
						|
     cisdev->CIS.cisRes = 300;
 | 
						|
   else
 | 
						|
     cisdev->CIS.cisRes = 600;         
 | 
						|
 | 
						|
   /* Calibration only makes sense for hardware pixels, not for interpolated
 | 
						|
      pixels, so we limit the number of calibration pixels to the maximum
 | 
						|
      number of hardware pixels corresponding to the selected area */
 | 
						|
   if (dev->res > cisdev->CIS.hw_hres)
 | 
						|
      cisdev->calib_pixels = (pixels * cisdev->CIS.hw_hres) / dev->res;
 | 
						|
   else
 | 
						|
      cisdev->calib_pixels = pixels;
 | 
						|
   
 | 
						|
   DBG (3, "cis_drv_start: hres: %d vres: %d cisres: %d\n", 
 | 
						|
            cisdev->CIS.hw_hres, cisdev->CIS.hw_vres, cisdev->CIS.cisRes);
 | 
						|
   
 | 
						|
   sanei_pa4s2_enable (dev->fd, SANE_TRUE);
 | 
						|
 | 
						|
   cis_reset_device (cisdev);
 | 
						|
   cis_return_home (cisdev, SANE_TRUE); /* Don't wait here */
 | 
						|
 | 
						|
#ifdef M1015_TRACE_REGS
 | 
						|
   {
 | 
						|
      int i, j;
 | 
						|
 | 
						|
      /*
 | 
						|
       * Set all registers to -1 (uninitialized)
 | 
						|
       */  
 | 
						|
      for (i=0; i<4; ++i)
 | 
						|
      {
 | 
						|
         cisdev->CIS.regs.in_regs[i] = -1;
 | 
						|
         for (j=0; j<4; ++j)
 | 
						|
         {
 | 
						|
            cisdev->CIS.regs.out_regs[i][j] = -1;
 | 
						|
         }
 | 
						|
      }
 | 
						|
 | 
						|
      cisdev->CIS.regs.channel = -1;
 | 
						|
      /* These values have been read earlier. */
 | 
						|
      cisdev->CIS.regs.in_regs[0] = 0xA5;
 | 
						|
   }
 | 
						|
#endif
 | 
						|
 | 
						|
   cis_reset_device (cisdev);
 | 
						|
   cis_return_home (cisdev, SANE_TRUE); /* no wait */
 | 
						|
 | 
						|
   /* Allocate memory for temporary color buffer */
 | 
						|
   cisdev->tmpbuf = malloc (pixels);
 | 
						|
   if (cisdev->tmpbuf == NULL)
 | 
						|
   {
 | 
						|
      sanei_pa4s2_enable (dev->fd, SANE_FALSE);
 | 
						|
      DBG (2, "cis_drv_start: not enough memory for temporary buffer\n");
 | 
						|
      free(cisdev);
 | 
						|
      dev->priv = NULL;
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
   }
 | 
						|
   
 | 
						|
   /* Allocate memory for calibration; calibrating interpolated pixels 
 | 
						|
      makes no sense */
 | 
						|
   if (pixels > (dev->dev->maxhsize >> 1)) 
 | 
						|
      pixels = (dev->dev->maxhsize >> 1);
 | 
						|
   
 | 
						|
   cisdev->calib_low[1] = malloc (pixels);
 | 
						|
   cisdev->calib_hi[1] = malloc (pixels);
 | 
						|
 | 
						|
   if (cisdev->calib_low[1] == NULL || cisdev->calib_hi[1] == NULL)
 | 
						|
   {
 | 
						|
      free (cisdev->calib_low[1]); cisdev->calib_low[1] = NULL;
 | 
						|
      free (cisdev->calib_hi[1]);  cisdev->calib_hi[1] = NULL;
 | 
						|
      sanei_pa4s2_enable (dev->fd, SANE_FALSE);
 | 
						|
      DBG (2, "cis_drv_start: not enough memory for calibration buffer\n");
 | 
						|
      free(cisdev->tmpbuf); cisdev->tmpbuf = NULL;
 | 
						|
      free(cisdev); dev->priv = NULL;
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
   }
 | 
						|
 | 
						|
   cisdev->calib_low[0] = NULL;
 | 
						|
   cisdev->calib_low[2] = NULL;
 | 
						|
   cisdev->calib_hi[0] = NULL;
 | 
						|
   cisdev->calib_hi[2] = NULL;
 | 
						|
   if (dev->mode == MODE_COLOR)
 | 
						|
   {
 | 
						|
      cisdev->calib_low[0] = malloc (pixels);
 | 
						|
      cisdev->calib_low[2] = malloc (pixels);
 | 
						|
      cisdev->calib_hi[0] = malloc (pixels);
 | 
						|
      cisdev->calib_hi[2] = malloc (pixels);
 | 
						|
 | 
						|
      if ((cisdev->calib_low[0] == NULL) || (cisdev->calib_low[2] == NULL) ||
 | 
						|
          (cisdev->calib_hi[0] == NULL) || (cisdev->calib_hi[2] == NULL))
 | 
						|
      {
 | 
						|
         free (cisdev->calib_low[0]); cisdev->calib_low[0] = NULL;
 | 
						|
	 free (cisdev->calib_low[1]); cisdev->calib_low[1] = NULL;
 | 
						|
         free (cisdev->calib_low[2]); cisdev->calib_low[2] = NULL;
 | 
						|
         free (cisdev->calib_hi[0]); cisdev->calib_hi[0] = NULL;
 | 
						|
	 free (cisdev->calib_hi[1]); cisdev->calib_hi[1] = NULL;
 | 
						|
         free (cisdev->calib_hi[2]); cisdev->calib_hi[2] = NULL;
 | 
						|
         free(cisdev->tmpbuf); cisdev->tmpbuf = NULL;
 | 
						|
         free(cisdev); dev->priv = NULL;
 | 
						|
	 sanei_pa4s2_enable (dev->fd, SANE_FALSE);
 | 
						|
	 DBG (2, "cis_drv_start: not enough memory for color calib buffer\n");
 | 
						|
	 return SANE_STATUS_NO_MEM;
 | 
						|
      }
 | 
						|
   }
 | 
						|
 | 
						|
   DBG (3, "cis_drv_start: executing calibration\n");
 | 
						|
 | 
						|
   if (!cis_calibrate (cisdev))
 | 
						|
   {
 | 
						|
      free (cisdev->calib_low[0]); cisdev->calib_low[0] = NULL;
 | 
						|
      free (cisdev->calib_low[1]); cisdev->calib_low[1] = NULL;
 | 
						|
      free (cisdev->calib_low[2]); cisdev->calib_low[2] = NULL;
 | 
						|
      free (cisdev->calib_hi[0]); cisdev->calib_hi[0] = NULL;
 | 
						|
      free (cisdev->calib_hi[1]); cisdev->calib_hi[1] = NULL;
 | 
						|
      free (cisdev->calib_hi[2]); cisdev->calib_hi[2] = NULL;
 | 
						|
      free(cisdev->tmpbuf); cisdev->tmpbuf = NULL;
 | 
						|
      free(cisdev); dev->priv = NULL;
 | 
						|
      return SANE_STATUS_CANCELLED; /* Most likely cause */
 | 
						|
   }
 | 
						|
 | 
						|
/*   M1015_DISPLAY_REGS(dev, "after calibration"); */
 | 
						|
 | 
						|
   cis_get_bank_count(cisdev);
 | 
						|
 | 
						|
   cis_move_motor (cisdev, dev->topY); /* Measured in max resolution */
 | 
						|
 | 
						|
   /* It is vital to reinitialize the scanner right before we start the 
 | 
						|
      real scanning. Otherwise the bank synchronization may have gotten lost
 | 
						|
      by the time we reach the top of the scan area */
 | 
						|
 | 
						|
   cisdev->CIS.setParameters = SANE_TRUE;
 | 
						|
   cis_config_ccd(cisdev);
 | 
						|
   cis_wait_read_ready(cisdev);
 | 
						|
 | 
						|
   sanei_pa4s2_enable (dev->fd, SANE_FALSE); 
 | 
						|
 | 
						|
   cisdev->CIS.line_step =
 | 
						|
     SANE_FIX ((float) cisdev->CIS.hw_vres / (float) cisdev->CIS.res);
 | 
						|
 | 
						|
   /* 
 | 
						|
    * It is very important that line_diff is not initialized at zero !
 | 
						|
    * If it is set to zero, the motor will keep on moving forever (or better,
 | 
						|
    * till the scanner breaks).
 | 
						|
    */
 | 
						|
   cisdev->line_diff = cisdev->CIS.line_step;
 | 
						|
   cisdev->ccd_line = 0;
 | 
						|
   cisdev->line = 0;
 | 
						|
   cisdev->lines_left = dev->params.lines;
 | 
						|
 | 
						|
   dev->state = STATE_SCANNING;
 | 
						|
 | 
						|
   DBG (3, "cis_drv_start: device ready for scanning\n");
 | 
						|
 | 
						|
   return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
/******************************************************************************
 | 
						|
* Read                                                                        *
 | 
						|
******************************************************************************/
 | 
						|
void cis_drv_read (SANE_Handle hndl, SANE_Byte *buffer)
 | 
						|
{
 | 
						|
   Mustek_pp_Handle *dev = hndl;
 | 
						|
   Mustek_PP_CIS_dev *cisdev = dev->priv;
 | 
						|
   DBG(6, "cis_drv_read: Reading line\n");
 | 
						|
   sanei_pa4s2_enable (dev->fd, SANE_TRUE); 
 | 
						|
   switch (dev->mode)
 | 
						|
   {
 | 
						|
      case MODE_BW:
 | 
						|
         cis_get_lineart_line(cisdev, buffer);
 | 
						|
         break;
 | 
						|
 | 
						|
      case MODE_GRAYSCALE:
 | 
						|
         cis_get_grayscale_line(cisdev, buffer);
 | 
						|
         break;
 | 
						|
 | 
						|
      case MODE_COLOR:
 | 
						|
         cis_get_color_line(cisdev, buffer);
 | 
						|
         break;
 | 
						|
   }
 | 
						|
   sanei_pa4s2_enable (dev->fd, SANE_FALSE);
 | 
						|
}
 | 
						|
 | 
						|
/******************************************************************************
 | 
						|
* Stop                                                                        *
 | 
						|
******************************************************************************/
 | 
						|
void cis_drv_stop (SANE_Handle hndl)
 | 
						|
{
 | 
						|
   Mustek_pp_Handle *dev = hndl;
 | 
						|
   Mustek_PP_CIS_dev *cisdev = dev->priv;
 | 
						|
   
 | 
						|
   /* device is scanning: return lamp and free buffers */
 | 
						|
   DBG (3, "cis_drv_stop: stopping current scan\n");
 | 
						|
   dev->state = STATE_CANCELLED;
 | 
						|
 | 
						|
   DBG (9, "cis_drv_stop: enabling fd\n");
 | 
						|
   sanei_pa4s2_enable (dev->fd, SANE_TRUE); 
 | 
						|
   Mustek_PP_1015_write_reg(cisdev, MA1015W_MOTOR_CONTROL, 0); /* stop */
 | 
						|
   DBG (9, "cis_drv_stop: resetting device (1)\n");
 | 
						|
   cis_reset_device (cisdev);
 | 
						|
   DBG (9, "cis_drv_stop: returning home\n");
 | 
						|
   cis_return_home (cisdev, SANE_TRUE); /* don't wait */
 | 
						|
   DBG (9, "cis_drv_stop: resetting device (2)\n");
 | 
						|
   cis_reset_device (cisdev);
 | 
						|
   DBG (9, "cis_drv_stop: disabling fd\n");
 | 
						|
   sanei_pa4s2_enable (dev->fd, SANE_FALSE); 
 | 
						|
   DBG (9, "cis_drv_stop: freeing buffers\n");
 | 
						|
 | 
						|
   /* This is no good: canceling while the device is scanning and
 | 
						|
      freeing the data buffers can result in illegal memory accesses if
 | 
						|
      the device is still scanning in another thread. */
 | 
						|
   free (cisdev->calib_low[1]); cisdev->calib_low[1] = NULL;
 | 
						|
   free (cisdev->calib_hi[1]); cisdev->calib_hi[1] = NULL;
 | 
						|
   free (cisdev->tmpbuf); cisdev->tmpbuf = NULL;
 | 
						|
   DBG (3, "cis_drv_stop: freed green and temporary buffers\n");
 | 
						|
 | 
						|
   if (cisdev->CIS.mode == MODE_COLOR)
 | 
						|
   {
 | 
						|
      free (cisdev->calib_low[0]); cisdev->calib_low[0] = NULL;
 | 
						|
      free (cisdev->calib_low[2]); cisdev->calib_low[2] = NULL;
 | 
						|
      free (cisdev->calib_hi[0]); cisdev->calib_hi[0] = NULL;
 | 
						|
      free (cisdev->calib_hi[2]); cisdev->calib_hi[2] = NULL;
 | 
						|
   }
 | 
						|
   DBG (3, "cis_drv_stop: freed buffers\n");
 | 
						|
   DBG (6, "cis_drv_stop: lamp_on: %d\n", (int)dev->lamp_on);
 | 
						|
}
 |