kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			6785 wiersze
		
	
	
		
			198 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			6785 wiersze
		
	
	
		
			198 KiB
		
	
	
	
		
			C
		
	
	
/* sane - Scanner Access Now Easy.
 | 
						|
   Copyright (C) 1996, 1997 David Mosberger-Tang and Andreas Czechanowski,
 | 
						|
   1998 Andreas Bolsch for extension to ScanExpress models version 0.6,
 | 
						|
   2000-2005 Henning Meier-Geinitz,
 | 
						|
   2003 James Perry (600 EP).
 | 
						|
 | 
						|
   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 and some Trust flatbed
 | 
						|
   scanners with SCSI, parallel port (600 EP)  or proprietary interface.  */
 | 
						|
 | 
						|
 | 
						|
/**************************************************************************/
 | 
						|
/* Mustek backend version                                                 */
 | 
						|
#define BUILD 138
 | 
						|
/**************************************************************************/
 | 
						|
 | 
						|
#include "../include/sane/config.h"
 | 
						|
 | 
						|
#include <ctype.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <limits.h>
 | 
						|
#include <signal.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <unistd.h>
 | 
						|
 | 
						|
#include <sys/time.h>
 | 
						|
#include <sys/types.h>
 | 
						|
 | 
						|
#include "../include/_stdint.h"
 | 
						|
 | 
						|
#include "../include/sane/sane.h"
 | 
						|
#include "../include/sane/sanei.h"
 | 
						|
#include "../include/sane/saneopts.h"
 | 
						|
#include "../include/sane/sanei_scsi.h"
 | 
						|
#include "../include/sane/sanei_ab306.h"
 | 
						|
#include "../include/sane/sanei_thread.h"
 | 
						|
 | 
						|
#define BACKEND_NAME	mustek
 | 
						|
#include "../include/sane/sanei_backend.h"
 | 
						|
#include "../include/sane/sanei_config.h"
 | 
						|
 | 
						|
#include "mustek.h"
 | 
						|
#include "mustek_scsi_pp.h"
 | 
						|
 | 
						|
#ifndef SANE_I18N
 | 
						|
#define SANE_I18N(text) text
 | 
						|
#endif
 | 
						|
 | 
						|
/* Debug level from sanei_init_debug */
 | 
						|
static SANE_Int debug_level;
 | 
						|
 | 
						|
/* Maximum # of inches to scan in one swoop.  0 means "unlimited."
 | 
						|
   This is here to be nice on the SCSI bus---Mustek scanners don't
 | 
						|
   disconnect while scanning is in progress, which confuses some
 | 
						|
   drivers that expect no reasonable SCSI request would take more than
 | 
						|
   10 seconds.  That's not really true for Mustek scanners operating
 | 
						|
   in certain modes, hence this limit. Usually you don't need to set it. */
 | 
						|
static double strip_height;
 | 
						|
 | 
						|
/* Should we wait for the scan slider to return after each scan? */
 | 
						|
static SANE_Bool force_wait;
 | 
						|
 | 
						|
/* Should we disable double buffering when reading data from the scanner? */
 | 
						|
static SANE_Bool disable_double_buffering;
 | 
						|
 | 
						|
static SANE_Int num_devices;
 | 
						|
static Mustek_Device *first_dev;
 | 
						|
static Mustek_Scanner *first_handle;
 | 
						|
static const SANE_Device **devlist = 0;
 | 
						|
 | 
						|
/* Array of newly attached devices */
 | 
						|
static Mustek_Device **new_dev;
 | 
						|
 | 
						|
/* Length of new_dev array */
 | 
						|
static SANE_Int new_dev_len;
 | 
						|
 | 
						|
/* Number of entries alloced for new_dev */
 | 
						|
static SANE_Int new_dev_alloced;
 | 
						|
 | 
						|
static SANE_Int lamp_off_time = 60;
 | 
						|
 | 
						|
/* Used for line-distance correction: */
 | 
						|
static const SANE_Int color_seq[] = {
 | 
						|
  1, 2, 0			/* green, blue, red */
 | 
						|
};
 | 
						|
 | 
						|
/* Which modes are supported? */
 | 
						|
static SANE_String_Const mode_list_paragon[] = {
 | 
						|
  SANE_VALUE_SCAN_MODE_LINEART,
 | 
						|
  SANE_VALUE_SCAN_MODE_HALFTONE,
 | 
						|
  SANE_VALUE_SCAN_MODE_GRAY,
 | 
						|
  SANE_VALUE_SCAN_MODE_COLOR,
 | 
						|
  0
 | 
						|
};
 | 
						|
static SANE_String_Const mode_list_se[] = {
 | 
						|
  SANE_VALUE_SCAN_MODE_LINEART,
 | 
						|
  SANE_VALUE_SCAN_MODE_GRAY,
 | 
						|
  SANE_VALUE_SCAN_MODE_COLOR,
 | 
						|
  0
 | 
						|
};
 | 
						|
 | 
						|
static SANE_String_Const bit_depth_list_pro[] = {
 | 
						|
  "8", "12",
 | 
						|
  0
 | 
						|
};
 | 
						|
 | 
						|
/* Some scanners support setting speed manually */
 | 
						|
static SANE_String_Const speed_list[] = {
 | 
						|
  SANE_I18N ("Slowest"), SANE_I18N ("Slower"), SANE_I18N ("Normal"),
 | 
						|
  SANE_I18N ("Faster"), SANE_I18N ("Fastest"),
 | 
						|
  0
 | 
						|
};
 | 
						|
 | 
						|
/* Which scan-sources are supported? */
 | 
						|
static const SANE_String_Const source_list[] = {
 | 
						|
  SANE_I18N ("Flatbed"),
 | 
						|
  0
 | 
						|
};
 | 
						|
static SANE_String_Const adf_source_list[] = {
 | 
						|
  SANE_I18N ("Flatbed"), SANE_I18N ("Automatic Document Feeder"),
 | 
						|
  0
 | 
						|
};
 | 
						|
static SANE_String_Const ta_source_list[] = {
 | 
						|
  SANE_I18N ("Flatbed"), SANE_I18N ("Transparency Adapter"),
 | 
						|
  0
 | 
						|
};
 | 
						|
 | 
						|
/* Range used for gamma and halftone pattern */
 | 
						|
static const SANE_Range u8_range = {
 | 
						|
  0,				/* minimum */
 | 
						|
  255,				/* maximum */
 | 
						|
  0				/* quantization */
 | 
						|
};
 | 
						|
 | 
						|
/* Which kind of halftone patterns are available? */
 | 
						|
static SANE_String_Const halftone_list[] = {
 | 
						|
  SANE_I18N ("8x8 coarse"), SANE_I18N ("8x8 normal"), SANE_I18N ("8x8 fine"),
 | 
						|
  SANE_I18N ("8x8 very fine"), SANE_I18N ("6x6 normal"),
 | 
						|
  SANE_I18N ("5x5 coarse"), SANE_I18N ("5x5 fine"), SANE_I18N ("4x4 coarse"),
 | 
						|
  SANE_I18N ("4x4 normal"), SANE_I18N ("4x4 fine"), SANE_I18N ("3x3 normal"),
 | 
						|
  SANE_I18N ("2x2 normal"), SANE_I18N ("8x8 custom"),
 | 
						|
  SANE_I18N ("6x6 custom"),
 | 
						|
  SANE_I18N ("5x5 custom"), SANE_I18N ("4x4 custom"),
 | 
						|
  SANE_I18N ("3x3 custom"),
 | 
						|
  SANE_I18N ("2x2 custom"),
 | 
						|
  0
 | 
						|
};
 | 
						|
 | 
						|
/* Range used for brightness and contrast */
 | 
						|
static const SANE_Range percentage_range = {
 | 
						|
  SANE_FIX(-100),	/* minimum */
 | 
						|
  SANE_FIX(100),	/* maximum */
 | 
						|
  SANE_FIX(1)           /* quantization */
 | 
						|
};
 | 
						|
 | 
						|
/* SCSI command buffers used by the backend */
 | 
						|
static const SANE_Byte scsi_inquiry[] = {
 | 
						|
  MUSTEK_SCSI_INQUIRY, 0x00, 0x00, 0x00, INQ_LEN, 0x00
 | 
						|
};
 | 
						|
static const SANE_Byte scsi_test_unit_ready[] = {
 | 
						|
  MUSTEK_SCSI_TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00
 | 
						|
};
 | 
						|
/* Remove #ifdef and this comment when this SCSI command is used for
 | 
						|
   something.  Keeping this definition around so we don't loose info
 | 
						|
   about the protocol.
 | 
						|
 */
 | 
						|
#ifdef ENABLE_MUSTEK_SCSI_AREA_AND_WINDOWS
 | 
						|
static const SANE_Byte scsi_area_and_windows[] = {
 | 
						|
  MUSTEK_SCSI_AREA_AND_WINDOWS, 0x00, 0x00, 0x00, 0x09, 0x00
 | 
						|
};
 | 
						|
#endif
 | 
						|
static const SANE_Byte scsi_request_sense[] = {
 | 
						|
  MUSTEK_SCSI_REQUEST_SENSE, 0x00, 0x00, 0x00, 0x04, 0x00
 | 
						|
};
 | 
						|
static const SANE_Byte scsi_start_stop[] = {
 | 
						|
  MUSTEK_SCSI_START_STOP, 0x00, 0x00, 0x00, 0x00, 0x00
 | 
						|
};
 | 
						|
static const SANE_Byte scsi_ccd_distance[] = {
 | 
						|
  MUSTEK_SCSI_CCD_DISTANCE, 0x00, 0x00, 0x00, 0x05, 0x00
 | 
						|
};
 | 
						|
static const SANE_Byte scsi_get_image_status[] = {
 | 
						|
  MUSTEK_SCSI_GET_IMAGE_STATUS, 0x00, 0x00, 0x00, 0x06, 0x00
 | 
						|
};
 | 
						|
static const SANE_Byte scsi_set_window[] = {
 | 
						|
  MUSTEK_SCSI_SET_WINDOW, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00
 | 
						|
};
 | 
						|
static const SANE_Byte scsi_get_window[] = {
 | 
						|
  MUSTEK_SCSI_GET_WINDOW, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00
 | 
						|
};
 | 
						|
static const SANE_Byte scsi_read_data[] = {
 | 
						|
  MUSTEK_SCSI_READ_DATA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 | 
						|
};
 | 
						|
static const SANE_Byte scsi_send_data[] = {
 | 
						|
  MUSTEK_SCSI_SEND_DATA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 | 
						|
};
 | 
						|
/* Remove #ifdef and this comment when this SCSI command is used for
 | 
						|
   something.  Keeping this definition around so we don't loose info
 | 
						|
   about the protocol.
 | 
						|
 */
 | 
						|
#ifdef ENABLE_MUSTEK_SCSI_LOOKUP_TABLE
 | 
						|
static const SANE_Byte scsi_lookup_table[] = {
 | 
						|
  MUSTEK_SCSI_LOOKUP_TABLE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
						|
  0x00
 | 
						|
};
 | 
						|
#endif
 | 
						|
 | 
						|
/* prototypes */
 | 
						|
static SANE_Status area_and_windows (Mustek_Scanner * s);
 | 
						|
static SANE_Status inquiry (Mustek_Scanner * s);
 | 
						|
 | 
						|
/* Test if this machine is little endian (from coolscan.c) */
 | 
						|
static SANE_Bool
 | 
						|
little_endian (void)
 | 
						|
{
 | 
						|
  SANE_Int testvalue = 255;
 | 
						|
  uint8_t *firstbyte = (uint8_t *) & testvalue;
 | 
						|
 | 
						|
  if (*firstbyte == 255)
 | 
						|
    return SANE_TRUE;
 | 
						|
  return SANE_FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/* Used for Pro series. First value seems to be always 85, second one varies.
 | 
						|
   First bit of second value clear == device ready (?) */
 | 
						|
static SANE_Status
 | 
						|
scsi_sense_wait_ready (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  struct timeval now, start;
 | 
						|
  SANE_Status status;
 | 
						|
  size_t len;
 | 
						|
  SANE_Byte sense_buffer[4];
 | 
						|
  SANE_Byte bytetxt[300], dbgtxt[300], *pp;
 | 
						|
 | 
						|
  gettimeofday (&start, 0);
 | 
						|
 | 
						|
  while (1)
 | 
						|
    {
 | 
						|
      len = sizeof (sense_buffer);
 | 
						|
 | 
						|
      DBG (5, "scsi_sense_wait_ready: command size = %ld, sense size = %ld\n",
 | 
						|
	   (long int) sizeof (scsi_request_sense), (long int) len);
 | 
						|
      status = sanei_scsi_cmd (s->fd, scsi_request_sense,
 | 
						|
			       sizeof (scsi_request_sense), sense_buffer,
 | 
						|
			       &len);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	{
 | 
						|
	  DBG (1, "scsi_sense_wait_ready: failed: %s\n",
 | 
						|
	       sane_strstatus (status));
 | 
						|
	  return status;
 | 
						|
	}
 | 
						|
 | 
						|
      dbgtxt[0] = '\0';
 | 
						|
      for (pp = sense_buffer; pp < (sense_buffer + 4); pp++)
 | 
						|
	{
 | 
						|
	  sprintf ((SANE_String) bytetxt, " %02x", *pp);
 | 
						|
	  strcat ((SANE_String) dbgtxt, (SANE_String) bytetxt);
 | 
						|
	}
 | 
						|
      DBG (5, "scsi_sense_wait_ready: sensebuffer: %s\n", dbgtxt);
 | 
						|
 | 
						|
      if (!(sense_buffer[1] & 0x01))
 | 
						|
	{
 | 
						|
	  DBG (4, "scsi_sense_wait_ready: ok\n");
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  gettimeofday (&now, 0);
 | 
						|
	  if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME)
 | 
						|
	    {
 | 
						|
	      DBG (1, "scsi_sense_wait_ready: timed out after %lu seconds\n",
 | 
						|
		   (u_long) (now.tv_sec - start.tv_sec));
 | 
						|
	      return SANE_STATUS_INVAL;
 | 
						|
	    }
 | 
						|
	  usleep (100000);	/* retry after 100ms */
 | 
						|
	}
 | 
						|
    }
 | 
						|
  return SANE_STATUS_INVAL;
 | 
						|
}
 | 
						|
 | 
						|
/* Used for 3pass series */
 | 
						|
static SANE_Status
 | 
						|
scsi_area_wait_ready (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  struct timeval now, start;
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  gettimeofday (&start, 0);
 | 
						|
 | 
						|
  DBG (5, "scsi_area_wait_ready\n");
 | 
						|
  while (1)
 | 
						|
    {
 | 
						|
      status = area_and_windows (s);
 | 
						|
      switch (status)
 | 
						|
	{
 | 
						|
	default:
 | 
						|
	  /* Ignore errors while waiting for scanner to become ready.
 | 
						|
	     Some SCSI drivers return EIO while the scanner is
 | 
						|
	     returning to the home position.  */
 | 
						|
	  DBG (3, "scsi_area_wait_ready: failed (%s)\n",
 | 
						|
	       sane_strstatus (status));
 | 
						|
	  /* fall through */
 | 
						|
	case SANE_STATUS_DEVICE_BUSY:
 | 
						|
	  gettimeofday (&now, 0);
 | 
						|
	  if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME)
 | 
						|
	    {
 | 
						|
	      DBG (1, "scsi_area_wait_ready: timed out after %lu seconds\n",
 | 
						|
		   (u_long) (now.tv_sec - start.tv_sec));
 | 
						|
	      return SANE_STATUS_INVAL;
 | 
						|
	    }
 | 
						|
	  usleep (100000);	/* retry after 100ms */
 | 
						|
	  break;
 | 
						|
 | 
						|
	case SANE_STATUS_GOOD:
 | 
						|
	  return status;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  return SANE_STATUS_INVAL;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
scsi_unit_wait_ready (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  struct timeval now, start;
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  gettimeofday (&start, 0);
 | 
						|
 | 
						|
  while (1)
 | 
						|
    {
 | 
						|
      DBG (5, "scsi_unit_wait_ready: sending TEST_UNIT_READY\n");
 | 
						|
      status = sanei_scsi_cmd (s->fd, scsi_test_unit_ready,
 | 
						|
			       sizeof (scsi_test_unit_ready), 0, 0);
 | 
						|
      DBG (5, "scsi_unit_wait_ready: TEST_UNIT_READY finished\n");
 | 
						|
      switch (status)
 | 
						|
	{
 | 
						|
	default:
 | 
						|
	  /* Ignore errors while waiting for scanner to become ready.
 | 
						|
	     Some SCSI drivers return EIO while the scanner is
 | 
						|
	     returning to the home position.  */
 | 
						|
	  DBG (3, "scsi_unit_wait_ready: test unit ready failed (%s)\n",
 | 
						|
	       sane_strstatus (status));
 | 
						|
	  /* fall through */
 | 
						|
	case SANE_STATUS_DEVICE_BUSY:
 | 
						|
	  gettimeofday (&now, 0);
 | 
						|
	  if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME)
 | 
						|
	    {
 | 
						|
	      DBG (1, "scsi_unit_wait_ready: timed out after %lu seconds\n",
 | 
						|
		   (u_long) (now.tv_sec - start.tv_sec));
 | 
						|
	      return SANE_STATUS_INVAL;
 | 
						|
	    }
 | 
						|
	  usleep (100000);	/* retry after 100ms */
 | 
						|
	  break;
 | 
						|
 | 
						|
	case SANE_STATUS_GOOD:
 | 
						|
	  return status;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  return SANE_STATUS_INVAL;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
scsi_inquiry_wait_ready (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  struct timeval now, start;
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  gettimeofday (&start, 0);
 | 
						|
 | 
						|
  while (1)
 | 
						|
    {
 | 
						|
      DBG (5, "scsi_inquiry_wait_ready: sending INQUIRY\n");
 | 
						|
      status = inquiry (s);
 | 
						|
      DBG (5, "scsi_inquiry_wait_ready: INQUIRY finished\n");
 | 
						|
      switch (status)
 | 
						|
	{
 | 
						|
	default:
 | 
						|
	  /* Ignore errors while waiting for scanner to become ready.
 | 
						|
	     Some SCSI drivers return EIO while the scanner is
 | 
						|
	     returning to the home position.  */
 | 
						|
	  DBG (3, "scsi_unit_wait_ready: inquiry failed (%s)\n",
 | 
						|
	       sane_strstatus (status));
 | 
						|
	  /* fall through */
 | 
						|
	case SANE_STATUS_DEVICE_BUSY:
 | 
						|
	  gettimeofday (&now, 0);
 | 
						|
	  if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME)
 | 
						|
	    {
 | 
						|
	      DBG (1, "scsi_unit_wait_ready: timed out after %lu seconds\n",
 | 
						|
		   (u_long) (now.tv_sec - start.tv_sec));
 | 
						|
	      return SANE_STATUS_INVAL;
 | 
						|
	    }
 | 
						|
	  usleep (500000);	/* retry after 500ms */
 | 
						|
	  break;
 | 
						|
 | 
						|
	case SANE_STATUS_GOOD:
 | 
						|
	  return status;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  return SANE_STATUS_INVAL;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
n_wait_ready (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  struct timeval now, start;
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  gettimeofday (&start, 0);
 | 
						|
 | 
						|
  DBG (5, "n_wait_ready\n");
 | 
						|
  while (1)
 | 
						|
    {
 | 
						|
      status = sanei_ab306_test_ready (s->fd);
 | 
						|
      if (status == SANE_STATUS_GOOD)
 | 
						|
	return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
      gettimeofday (&now, 0);
 | 
						|
      if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME)
 | 
						|
	{
 | 
						|
	  DBG (1, "n_wait_ready: timed out after %lu seconds\n",
 | 
						|
	       (u_long) (now.tv_sec - start.tv_sec));
 | 
						|
	  return SANE_STATUS_INVAL;
 | 
						|
	}
 | 
						|
      usleep (100000);		/* retry after 100ms */
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
scsi_pp_wait_ready (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  struct timeval now, start;
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  gettimeofday (&start, 0);
 | 
						|
 | 
						|
  DBG (5, "scsi_pp_wait_ready\n");
 | 
						|
  while (1)
 | 
						|
    {
 | 
						|
      status = mustek_scsi_pp_test_ready (s->fd);
 | 
						|
      if (status == SANE_STATUS_GOOD)
 | 
						|
	return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
      gettimeofday (&now, 0);
 | 
						|
      if (now.tv_sec - start.tv_sec >= MAX_WAITING_TIME)
 | 
						|
	{
 | 
						|
	  DBG (1, "scsi_pp_wait_ready: timed out after %lu seconds\n",
 | 
						|
	       (u_long) (now.tv_sec - start.tv_sec));
 | 
						|
	  return SANE_STATUS_INVAL;
 | 
						|
	}
 | 
						|
      usleep (100000);		/* retry after 100ms */
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
dev_wait_ready (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  if (s->hw->flags & MUSTEK_FLAG_N)
 | 
						|
    return n_wait_ready (s);
 | 
						|
  else if (s->hw->flags & MUSTEK_FLAG_SCSI_PP)
 | 
						|
    return scsi_pp_wait_ready (s);
 | 
						|
  else if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
 | 
						|
    {
 | 
						|
      SANE_Status status;
 | 
						|
 | 
						|
      /* some 3-pass scanners seem to need the inquiry wait, too */
 | 
						|
      status = scsi_area_wait_ready (s);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	return status;
 | 
						|
      return scsi_inquiry_wait_ready (s);
 | 
						|
    }
 | 
						|
  else if ((s->hw->flags & MUSTEK_FLAG_PARAGON_1)
 | 
						|
	   || (s->hw->flags & MUSTEK_FLAG_PARAGON_2))
 | 
						|
    return scsi_inquiry_wait_ready (s);
 | 
						|
  else if (s->hw->flags & MUSTEK_FLAG_PRO)
 | 
						|
    return scsi_sense_wait_ready (s);
 | 
						|
  else
 | 
						|
    return scsi_unit_wait_ready (s);
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
dev_open (SANE_String_Const devname, Mustek_Scanner * s,
 | 
						|
	  SANEI_SCSI_Sense_Handler handler)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  DBG (5, "dev_open %s\n", devname);
 | 
						|
 | 
						|
#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
 | 
						|
  s->hw->buffer_size = s->hw->max_buffer_size;
 | 
						|
  status = sanei_scsi_open_extended (devname, &s->fd, handler, 0,
 | 
						|
				     &s->hw->buffer_size);
 | 
						|
#else
 | 
						|
  s->hw->buffer_size = MIN (sanei_scsi_max_request_size,
 | 
						|
			    s->hw->max_buffer_size);
 | 
						|
  status = sanei_scsi_open (devname, &s->fd, handler, 0);
 | 
						|
#endif
 | 
						|
 | 
						|
  if (status == SANE_STATUS_GOOD)
 | 
						|
    {
 | 
						|
      DBG (3, "dev_open: %s is a SCSI device\n", devname);
 | 
						|
      DBG (4, "dev_open: wanted %d kbytes, got %d kbytes buffer\n",
 | 
						|
	   s->hw->max_buffer_size / 1024, s->hw->buffer_size / 1024);
 | 
						|
      if (s->hw->buffer_size < 4096)
 | 
						|
	{
 | 
						|
	  DBG (1, "dev_open: sanei_scsi_open buffer too small\n");
 | 
						|
	  sanei_scsi_close (s->fd);
 | 
						|
	  return SANE_STATUS_NO_MEM;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      DBG (3, "dev_open: %s: can't open %s as a SCSI device\n",
 | 
						|
	   sane_strstatus (status), devname);
 | 
						|
 | 
						|
      status = sanei_ab306_open (devname, &s->fd);
 | 
						|
      if (status == SANE_STATUS_GOOD)
 | 
						|
	{
 | 
						|
	  s->hw->flags |= MUSTEK_FLAG_N;
 | 
						|
	  DBG (3, "dev_open: %s is an AB306N device\n", devname);
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  DBG (3, "dev_open: %s: can't open %s as an AB306N device\n",
 | 
						|
	       sane_strstatus (status), devname);
 | 
						|
 | 
						|
	  status = mustek_scsi_pp_open (devname, &s->fd);
 | 
						|
	  if (status == SANE_STATUS_GOOD)
 | 
						|
	    {
 | 
						|
	      s->hw->flags |= MUSTEK_FLAG_SCSI_PP;
 | 
						|
	      DBG (3, "dev_open: %s is a SCSI-over-parallel device\n",
 | 
						|
		   devname);
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      DBG (3,
 | 
						|
		   "dev_open: %s: can't open %s as a SCSI-over-parallel device\n",
 | 
						|
		   sane_strstatus (status), devname);
 | 
						|
	      DBG (1, "dev_open: can't open %s\n", devname);
 | 
						|
	      return SANE_STATUS_INVAL;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
dev_cmd (Mustek_Scanner * s, const void *src, size_t src_size,
 | 
						|
	 void *dst, size_t * dst_size)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  SANE_Byte cmd_byte_list[50];
 | 
						|
  SANE_Byte cmd_byte[5];
 | 
						|
  const SANE_Byte *pp;
 | 
						|
 | 
						|
  DBG (5, "dev_cmd: fd=%d, src=%p, src_size=%ld, dst=%p, dst_size=%ld\n",
 | 
						|
       s->fd, src, (long int) src_size, dst,
 | 
						|
       (long int) (dst_size ? *dst_size : 0));
 | 
						|
 | 
						|
  if (src && (debug_level >= 5))	/* output data sent to SCSI device */
 | 
						|
    {
 | 
						|
      cmd_byte_list[0] = '\0';
 | 
						|
      for (pp = (const SANE_Byte *) src;
 | 
						|
	   pp < (((const SANE_Byte *) src) + src_size); pp++)
 | 
						|
	{
 | 
						|
	  sprintf ((SANE_String) cmd_byte, " %02x", *pp);
 | 
						|
	  strcat ((SANE_String) cmd_byte_list, (SANE_String) cmd_byte);
 | 
						|
	  if (((pp - (const SANE_Byte *) src) % 0x10 == 0x0f)
 | 
						|
	      || (pp >= (((const SANE_Byte *) src) + src_size - 1)))
 | 
						|
	    {
 | 
						|
	      DBG (5, "dev_cmd: sending: %s\n", cmd_byte_list);
 | 
						|
	      cmd_byte_list[0] = '\0';
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
  if (s->hw->flags & MUSTEK_FLAG_N)
 | 
						|
    status = sanei_ab306_cmd (s->fd, src, src_size, dst, dst_size);
 | 
						|
  else if (s->hw->flags & MUSTEK_FLAG_SCSI_PP)
 | 
						|
    status = mustek_scsi_pp_cmd (s->fd, src, src_size, dst, dst_size);
 | 
						|
  else
 | 
						|
    status = sanei_scsi_cmd (s->fd, src, src_size, dst, dst_size);
 | 
						|
 | 
						|
  if (dst && dst_size && (debug_level >= 5))
 | 
						|
    /* output data received from SCSI device */
 | 
						|
    {
 | 
						|
      cmd_byte_list[0] = '\0';
 | 
						|
      for (pp = (const SANE_Byte *) dst;
 | 
						|
	   pp < (((const SANE_Byte *) dst) + *dst_size); pp++)
 | 
						|
	{
 | 
						|
	  sprintf ((SANE_String) cmd_byte, " %02x", *pp);
 | 
						|
	  strcat ((SANE_String) cmd_byte_list, (SANE_String) cmd_byte);
 | 
						|
	  if (((pp - (const SANE_Byte *) dst) % 0x10 == 0x0f)
 | 
						|
	      || (pp >= (((const SANE_Byte *) dst) + *dst_size - 1)))
 | 
						|
	    {
 | 
						|
	      DBG (5, "dev_cmd: receiving: %s\n", cmd_byte_list);
 | 
						|
	      cmd_byte_list[0] = '\0';
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (5, "dev_cmd: finished: dst_size=%ld, status=%s\n",
 | 
						|
       (long int) (dst_size ? *dst_size : 0), sane_strstatus (status));
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
dev_req_wait (void *id)
 | 
						|
{
 | 
						|
  if (!id)
 | 
						|
    return SANE_STATUS_GOOD;
 | 
						|
  else
 | 
						|
    return sanei_scsi_req_wait (id);
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
dev_block_read_start (Mustek_Scanner * s, SANE_Int lines)
 | 
						|
{
 | 
						|
  DBG (4, "dev_block_read_start: entering block for %d lines\n", lines);
 | 
						|
  if (s->hw->flags & MUSTEK_FLAG_N)
 | 
						|
    {
 | 
						|
      SANE_Byte readlines[6];
 | 
						|
 | 
						|
      memset (readlines, 0, sizeof (readlines));
 | 
						|
      readlines[0] = MUSTEK_SCSI_READ_SCANNED_DATA;
 | 
						|
      readlines[2] = (lines >> 16) & 0xff;
 | 
						|
      readlines[3] = (lines >> 8) & 0xff;
 | 
						|
      readlines[4] = (lines >> 0) & 0xff;
 | 
						|
      return sanei_ab306_cmd (s->fd, readlines, sizeof (readlines), 0, 0);
 | 
						|
    }
 | 
						|
  else if (s->hw->flags & MUSTEK_FLAG_SCSI_PP)
 | 
						|
    {
 | 
						|
      SANE_Byte readlines[6];
 | 
						|
 | 
						|
      memset (readlines, 0, sizeof (readlines));
 | 
						|
      readlines[0] = MUSTEK_SCSI_READ_SCANNED_DATA;
 | 
						|
      readlines[2] = (lines >> 16) & 0xff;
 | 
						|
      readlines[3] = (lines >> 8) & 0xff;
 | 
						|
      readlines[4] = (lines >> 0) & 0xff;
 | 
						|
      return mustek_scsi_pp_cmd (s->fd, readlines, sizeof (readlines), 0, 0);
 | 
						|
    }
 | 
						|
  else if (s->hw->flags & MUSTEK_FLAG_PARAGON_2)
 | 
						|
    {
 | 
						|
      SANE_Byte buffer[6];
 | 
						|
      size_t len;
 | 
						|
      SANE_Int color;
 | 
						|
      SANE_Status status;
 | 
						|
 | 
						|
      /* reset line-distance values */
 | 
						|
      if (s->mode & MUSTEK_MODE_COLOR)
 | 
						|
	{
 | 
						|
	  for (color = 0; color < 3; ++color)
 | 
						|
	    {
 | 
						|
	      s->ld.index[color] = -s->ld.dist[color];
 | 
						|
	    }
 | 
						|
	  s->ld.lmod3 = -1;
 | 
						|
	  s->ld.ld_line = 0;
 | 
						|
	}
 | 
						|
 | 
						|
      /* Get image status (necessary to start new block) */
 | 
						|
      len = sizeof (buffer);
 | 
						|
      status = dev_cmd (s, scsi_get_image_status,
 | 
						|
			sizeof (scsi_get_image_status), buffer, &len);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	return status;
 | 
						|
 | 
						|
      /* tell scanner how many lines to scan in one block */
 | 
						|
      memset (buffer, 0, sizeof (buffer));
 | 
						|
      buffer[0] = MUSTEK_SCSI_READ_SCANNED_DATA;
 | 
						|
      buffer[2] = (lines >> 16) & 0xff;
 | 
						|
      buffer[3] = (lines >> 8) & 0xff;
 | 
						|
      buffer[4] = (lines >> 0) & 0xff;
 | 
						|
      buffer[5] = 0x04;
 | 
						|
      return sanei_scsi_cmd (s->fd, buffer, sizeof (buffer), 0, 0);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
dev_read_req_enter (Mustek_Scanner * s, SANE_Byte * buf, SANE_Int lines,
 | 
						|
		    SANE_Int bpl, size_t * lenp, void **idp, SANE_Int bank,
 | 
						|
		    SANE_Byte * command)
 | 
						|
{
 | 
						|
  *lenp = lines * bpl;
 | 
						|
 | 
						|
  if (s->hw->flags & MUSTEK_FLAG_N)
 | 
						|
    {
 | 
						|
      SANE_Int planes;
 | 
						|
 | 
						|
      *idp = 0;
 | 
						|
      planes = (s->mode & MUSTEK_MODE_COLOR) ? 3 : 1;
 | 
						|
 | 
						|
      return sanei_ab306_rdata (s->fd, planes, buf, lines, bpl);
 | 
						|
    }
 | 
						|
  else if (s->hw->flags & MUSTEK_FLAG_SCSI_PP)
 | 
						|
    {
 | 
						|
      SANE_Int planes;
 | 
						|
 | 
						|
      *idp = 0;
 | 
						|
      planes = (s->mode & MUSTEK_MODE_COLOR) ? 3 : 1;
 | 
						|
 | 
						|
      return mustek_scsi_pp_rdata (s->fd, planes, buf, lines, bpl);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      if (s->hw->flags & MUSTEK_FLAG_SE)
 | 
						|
	{
 | 
						|
	  if (s->mode & MUSTEK_MODE_COLOR)
 | 
						|
	    lines *= 3;
 | 
						|
 | 
						|
	  memset (command, 0, 10);
 | 
						|
	  command[0] = MUSTEK_SCSI_READ_DATA;
 | 
						|
	  command[6] = bank;	/* buffer bank not used ??? */
 | 
						|
	  command[7] = (lines >> 8) & 0xff;
 | 
						|
	  command[8] = (lines >> 0) & 0xff;
 | 
						|
	  return sanei_scsi_req_enter (s->fd, command, 10, buf, lenp, idp);
 | 
						|
	}
 | 
						|
      else if (s->hw->flags & MUSTEK_FLAG_PRO)
 | 
						|
	{
 | 
						|
	  memset (command, 0, 6);
 | 
						|
	  command[0] = MUSTEK_SCSI_READ_SCANNED_DATA;
 | 
						|
	  command[2] = ((lines * bpl) >> 16) & 0xff;
 | 
						|
	  command[3] = ((lines * bpl) >> 8) & 0xff;
 | 
						|
	  command[4] = ((lines * bpl) >> 0) & 0xff;
 | 
						|
 | 
						|
	  return sanei_scsi_req_enter (s->fd, command, 6, buf, lenp, idp);
 | 
						|
	}
 | 
						|
      else			/* Paragon series */
 | 
						|
	{
 | 
						|
	  memset (command, 0, 6);
 | 
						|
	  command[0] = MUSTEK_SCSI_READ_SCANNED_DATA;
 | 
						|
	  command[2] = (lines >> 16) & 0xff;
 | 
						|
	  command[3] = (lines >> 8) & 0xff;
 | 
						|
	  command[4] = (lines >> 0) & 0xff;
 | 
						|
	  return sanei_scsi_req_enter (s->fd, command, 6, buf, lenp, idp);
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
dev_close (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  if (s->hw->flags & MUSTEK_FLAG_N)
 | 
						|
    sanei_ab306_close (s->fd);
 | 
						|
  else if (s->hw->flags & MUSTEK_FLAG_SCSI_PP)
 | 
						|
    mustek_scsi_pp_close (s->fd);
 | 
						|
  else
 | 
						|
    sanei_scsi_close (s->fd);
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
sense_handler (SANE_Int scsi_fd, SANE_Byte * result, void *arg)
 | 
						|
{
 | 
						|
  if (!result)
 | 
						|
    {
 | 
						|
      DBG (5, "sense_handler: no sense buffer\n");
 | 
						|
      return SANE_STATUS_IO_ERROR;
 | 
						|
    }
 | 
						|
  if (!arg)
 | 
						|
    DBG (5, "sense_handler: got sense code %02x for fd %d (arg = null)\n",
 | 
						|
	 result[0], scsi_fd);
 | 
						|
  else
 | 
						|
    DBG (5, "sense_handler: got sense code %02x for fd %d (arg = %uc)\n",
 | 
						|
	 result[0], scsi_fd, *(SANE_Byte *) arg);
 | 
						|
  switch (result[0])
 | 
						|
    {
 | 
						|
    case 0x00:
 | 
						|
      break;
 | 
						|
 | 
						|
    case 0x82:
 | 
						|
      if (result[1] & 0x80)
 | 
						|
	{
 | 
						|
	  DBG (3, "sense_handler: ADF is jammed\n");
 | 
						|
	  return SANE_STATUS_JAMMED;	/* ADF is jammed */
 | 
						|
	}
 | 
						|
      break;
 | 
						|
 | 
						|
    case 0x83:
 | 
						|
      if (result[2] & 0x02)
 | 
						|
	{
 | 
						|
	  DBG (3, "sense_handler: ADF is out of documents\n");
 | 
						|
	  return SANE_STATUS_NO_DOCS;	/* ADF out of documents */
 | 
						|
	}
 | 
						|
      break;
 | 
						|
 | 
						|
    case 0x84:
 | 
						|
      if (result[1] & 0x10)
 | 
						|
	{
 | 
						|
	  DBG (3, "sense_handler: transparency adapter cover open\n");
 | 
						|
	  return SANE_STATUS_COVER_OPEN;	/* open transparency adapter cover */
 | 
						|
	}
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      DBG (1, "sense_handler: got unknown sense code %02x for fd %d\n",
 | 
						|
	   result[0], scsi_fd);
 | 
						|
      return SANE_STATUS_IO_ERROR;
 | 
						|
    }
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
inquiry (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Byte result[INQ_LEN];
 | 
						|
  size_t size;
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  DBG (5, "inquiry: sending INQUIRY\n");
 | 
						|
  size = sizeof (result);
 | 
						|
 | 
						|
  memset (result, 0, size);
 | 
						|
 | 
						|
  status = dev_cmd (s, scsi_inquiry, sizeof (scsi_inquiry), result, &size);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    return status;
 | 
						|
 | 
						|
  /* checking ADF status */
 | 
						|
  if (s->hw->flags & MUSTEK_FLAG_ADF)
 | 
						|
    {
 | 
						|
      if (result[63] & (1 << 3))
 | 
						|
	{
 | 
						|
	  s->hw->flags |= MUSTEK_FLAG_ADF_READY;
 | 
						|
	  DBG (4, "inquiry: ADF ready\n");
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  s->hw->flags &= ~MUSTEK_FLAG_ADF_READY;
 | 
						|
	  DBG (4, "inquiry: ADF not ready (out of paper)\n");
 | 
						|
	}
 | 
						|
    }
 | 
						|
  if (!result[0])
 | 
						|
    return SANE_STATUS_DEVICE_BUSY;
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
paragon_2_get_adf_status (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  size_t len;
 | 
						|
  SANE_Byte sense_buffer[4];
 | 
						|
 | 
						|
  len = sizeof (sense_buffer);
 | 
						|
 | 
						|
  status = sanei_scsi_cmd (s->fd, scsi_request_sense,
 | 
						|
			   sizeof (scsi_request_sense), sense_buffer, &len);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    {
 | 
						|
      DBG (1, "paragon_2_get_adf_status: %s\n", sane_strstatus (status));
 | 
						|
      return status;
 | 
						|
    }
 | 
						|
  DBG (5, "paragon_2_get_adf_status: sense_buffer: %x %x %x %x\n",
 | 
						|
       sense_buffer[0], sense_buffer[1], sense_buffer[3], sense_buffer[3]);
 | 
						|
 | 
						|
  if (sense_buffer[0] == 0x00 && sense_buffer[1] == 0x00)
 | 
						|
    return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
  return SANE_STATUS_NO_DOCS;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Bool
 | 
						|
ta_available_pro (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  size_t len;
 | 
						|
  SANE_Byte sense_buffer[4];
 | 
						|
 | 
						|
  len = sizeof (sense_buffer);
 | 
						|
 | 
						|
  status = sanei_scsi_cmd (s->fd, scsi_request_sense,
 | 
						|
			   sizeof (scsi_request_sense), sense_buffer, &len);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    {
 | 
						|
      DBG (1, "ta_available_pro: failed: %s\n", sane_strstatus (status));
 | 
						|
      return status;
 | 
						|
    }
 | 
						|
  DBG (5, "ta_available_pro: sense_buffer[2] = %x\n", sense_buffer[2]);
 | 
						|
 | 
						|
  scsi_unit_wait_ready (s);
 | 
						|
  if (sense_buffer[2] == 0x40)
 | 
						|
    return SANE_TRUE;
 | 
						|
 | 
						|
  return SANE_FALSE;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
attach (SANE_String_Const devname, Mustek_Device ** devp, SANE_Bool may_wait)
 | 
						|
{
 | 
						|
  SANE_Int mustek_scanner, fw_revision;
 | 
						|
  SANE_Byte result[INQ_LEN];
 | 
						|
  SANE_Byte inquiry_byte_list[50], inquiry_text_list[17];
 | 
						|
  SANE_Byte inquiry_byte[5], inquiry_text[5];
 | 
						|
  SANE_Byte *model_name = result + 44;
 | 
						|
  Mustek_Scanner s;
 | 
						|
  Mustek_Device *dev, new_dev;
 | 
						|
  SANE_Status status;
 | 
						|
  size_t size;
 | 
						|
  SANE_String scsi_device_type[] = {
 | 
						|
    "Direct-Access", "Sequential-Access", "Printer", "Processor",
 | 
						|
    "Write-Once", "CD-ROM", "Scanner", "Optical Memory", "Medium Changer",
 | 
						|
    "Communications"
 | 
						|
  };
 | 
						|
  SANE_Byte scsi_vendor[9];
 | 
						|
  SANE_Byte scsi_product[17];
 | 
						|
  SANE_Byte scsi_revision[5];
 | 
						|
  SANE_Byte *pp;
 | 
						|
  SANE_Bool warning = SANE_FALSE;
 | 
						|
  SANE_Int firmware_format = 0;
 | 
						|
  SANE_Int firmware_revision_system = 0;
 | 
						|
 | 
						|
  if (devp)
 | 
						|
    *devp = 0;
 | 
						|
 | 
						|
  for (dev = first_dev; dev; dev = dev->next)
 | 
						|
    if (strcmp (dev->sane.name, devname) == 0)
 | 
						|
      {
 | 
						|
	if (devp)
 | 
						|
	  *devp = dev;
 | 
						|
	return SANE_STATUS_GOOD;
 | 
						|
      }
 | 
						|
 | 
						|
  memset (&new_dev, 0, sizeof (new_dev));
 | 
						|
  memset (&s, 0, sizeof (s));
 | 
						|
  s.hw = &new_dev;
 | 
						|
  s.hw->max_buffer_size = 8 * 1024;
 | 
						|
 | 
						|
  DBG (3, "attach: trying device %s\n", devname);
 | 
						|
 | 
						|
  status = dev_open (devname, &s, sense_handler);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    return status;
 | 
						|
 | 
						|
  if (may_wait || force_wait)
 | 
						|
    dev_wait_ready (&s);
 | 
						|
 | 
						|
  DBG (5, "attach: sending INQUIRY\n");
 | 
						|
  size = sizeof (result);
 | 
						|
  memset (result, 0, sizeof (result));
 | 
						|
  status = dev_cmd (&s, scsi_inquiry, sizeof (scsi_inquiry), result, &size);
 | 
						|
  if (status != SANE_STATUS_GOOD || size != INQ_LEN)
 | 
						|
    {
 | 
						|
      DBG (1, "attach: inquiry for device %s failed (%s)\n", devname,
 | 
						|
	   sane_strstatus (status));
 | 
						|
      dev_close (&s);
 | 
						|
      return status;
 | 
						|
    }
 | 
						|
 | 
						|
  status = dev_wait_ready (&s);
 | 
						|
  dev_close (&s);
 | 
						|
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    return status;
 | 
						|
 | 
						|
  if ((result[0] & 0x1f) != 0x06)
 | 
						|
    {
 | 
						|
      DBG (1, "attach: device %s doesn't look like a scanner at all (%d)\n",
 | 
						|
	   devname, result[0] & 0x1f);
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  if (debug_level >= 3)
 | 
						|
    {
 | 
						|
      /* clear spaces and special chars */
 | 
						|
      strncpy ((SANE_String) scsi_vendor, (SANE_String) result + 8, 8);
 | 
						|
      scsi_vendor[8] = '\0';
 | 
						|
      pp = scsi_vendor + 7;
 | 
						|
      while (pp >= scsi_vendor && (*pp == ' ' || *pp >= 127))
 | 
						|
	*pp-- = '\0';
 | 
						|
      strncpy ((SANE_String) scsi_product, (SANE_String) result + 16, 16);
 | 
						|
      scsi_product[16] = '\0';
 | 
						|
      pp = scsi_product + 15;
 | 
						|
      while (pp >= scsi_product && (*pp == ' ' || *pp >= 127))
 | 
						|
	*pp-- = '\0';
 | 
						|
      strncpy ((SANE_String) scsi_revision, (SANE_String) result + 32, 4);
 | 
						|
      scsi_revision[4] = '\0';
 | 
						|
      pp = scsi_revision + 3;
 | 
						|
      while (pp >= scsi_revision && (*pp == ' ' || *pp >= 127))
 | 
						|
	*pp-- = '\0';
 | 
						|
      DBG (3, "attach: SCSI Vendor: `%-8s' Model: `%-16s' Rev.: `%-4s'\n",
 | 
						|
	   scsi_vendor, scsi_product, scsi_revision);
 | 
						|
      DBG (3, "attach: SCSI Type: %s; ANSI rev.: %d\n",
 | 
						|
	   ((result[0] & 0x1f) < 0x10) ?
 | 
						|
	   scsi_device_type[result[0] & 0x1f] : "Unknown", result[2] & 0x03);
 | 
						|
      DBG (3, "attach: SCSI flags: %s%s%s%s%s%s%s\n",
 | 
						|
	   (result[7] & 0x80) ? "RelAdr " : "",
 | 
						|
	   (result[7] & 0x40) ? "WBus32 " : "",
 | 
						|
	   (result[7] & 0x20) ? "WBus16 " : "",
 | 
						|
	   (result[7] & 0x10) ? "Sync " : "",
 | 
						|
	   (result[7] & 0x08) ? "Linked " : "",
 | 
						|
	   (result[7] & 0x02) ? "CmdQue " : "",
 | 
						|
	   (result[7] & 0x01) ? "SftRe " : "");
 | 
						|
    }
 | 
						|
 | 
						|
  if (debug_level >= 4)
 | 
						|
    {
 | 
						|
      /* print out inquiry */
 | 
						|
      DBG (4, "attach: inquiry output:\n");
 | 
						|
      inquiry_byte_list[0] = '\0';
 | 
						|
      inquiry_text_list[0] = '\0';
 | 
						|
      for (pp = result; pp < (result + INQ_LEN); pp++)
 | 
						|
	{
 | 
						|
	  sprintf ((SANE_String) inquiry_text, "%c",
 | 
						|
		   (*pp < 127) && (*pp > 31) ? *pp : '.');
 | 
						|
	  strcat ((SANE_String) inquiry_text_list,
 | 
						|
		  (SANE_String) inquiry_text);
 | 
						|
	  sprintf ((SANE_String) inquiry_byte, " %02x", *pp);
 | 
						|
	  strcat ((SANE_String) inquiry_byte_list,
 | 
						|
		  (SANE_String) inquiry_byte);
 | 
						|
	  if ((pp - result) % 0x10 == 0x0f)
 | 
						|
	    {
 | 
						|
	      DBG (4, "%s  %s\n", inquiry_byte_list, inquiry_text_list);
 | 
						|
	      inquiry_byte_list[0] = '\0';
 | 
						|
	      inquiry_text_list[0] = '\0';
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  /* first check for new firmware format: */
 | 
						|
  mustek_scanner = (strncmp ((SANE_String) result + 36, "MUSTEK", 6) == 0);
 | 
						|
  if (mustek_scanner)
 | 
						|
    {
 | 
						|
      if (result[43] == 'M')
 | 
						|
	{
 | 
						|
	  DBG (3, "attach: found Mustek scanner (pro series firmware "
 | 
						|
	       "format)\n");
 | 
						|
	  firmware_format = 2;
 | 
						|
	  model_name = result + 43;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  DBG (3, "attach: found Mustek scanner (new firmware format)\n");
 | 
						|
	  firmware_format = 1;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      /* check for old format: */
 | 
						|
      mustek_scanner = (strncmp ((SANE_String) result + 8, "MUSTEK", 6) == 0);
 | 
						|
      if (mustek_scanner)
 | 
						|
	{
 | 
						|
	  model_name = result + 16;
 | 
						|
	  DBG (3, "attach: found Mustek scanner (old firmware format)\n");
 | 
						|
	  firmware_format = 0;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  /* Check for some non-Mustek scanners an print warning */
 | 
						|
	  if (strncmp ((SANE_String) result + 8, "Trust", 5) == 0)
 | 
						|
	    DBG (1, "attach: this is a real Trust scanner. It is not "
 | 
						|
		 " supported by this backend.\n");
 | 
						|
	  if (strncmp ((SANE_String) result + 8, "Aashima", 7) == 0)
 | 
						|
	    DBG (1, "attach: this is an Aashima/Teco scanner. It is not "
 | 
						|
		 " supported by this backend.\n");
 | 
						|
	  if (strncmp ((SANE_String) result + 16, "Flatbed Scanner", 15) == 0
 | 
						|
	      && strncmp ((SANE_String) result + 42, "TECO", 4) == 0)
 | 
						|
	    DBG (1, "attach: this is a Relysis/Teco scanner. It is not "
 | 
						|
		 " supported by this backend.\n");
 | 
						|
	  DBG (1, "attach: device %s doesn't look like a Mustek scanner\n",
 | 
						|
	       devname);
 | 
						|
	  return SANE_STATUS_INVAL;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  /* get firmware revision as BCD number:             */
 | 
						|
  /* General format: x.yz                             */
 | 
						|
  /* Newer ScanExpress scanners (ID XC06): Vxyz       */
 | 
						|
  if (result[33] == '.')
 | 
						|
    {
 | 
						|
      fw_revision =
 | 
						|
	(result[32] - '0') << 8 | (result[34] - '0') << 4 | (result[35] -
 | 
						|
							     '0');
 | 
						|
      firmware_revision_system = 0;
 | 
						|
      DBG (4, "attach: old firmware revision system\n");
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      fw_revision =
 | 
						|
	(result[33] - '0') << 8 | (result[34] - '0') << 4 | (result[35] -
 | 
						|
							     '0');
 | 
						|
      firmware_revision_system = 1;
 | 
						|
      DBG (4, "attach: new firmware revision system\n");
 | 
						|
    }
 | 
						|
  DBG (3, "attach: firmware revision %d.%02x\n",
 | 
						|
       fw_revision >> 8, fw_revision & 0xff);
 | 
						|
 | 
						|
  dev = malloc (sizeof (*dev));
 | 
						|
  if (!dev)
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
 | 
						|
  memcpy (dev, &new_dev, sizeof (*dev));
 | 
						|
 | 
						|
  dev->name = strdup (devname);
 | 
						|
  if (!dev->name)
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
  dev->sane.name = (SANE_String_Const) dev->name;
 | 
						|
  dev->sane.vendor = "Mustek";
 | 
						|
  dev->sane.type = "flatbed scanner";
 | 
						|
 | 
						|
  dev->x_range.min = 0;
 | 
						|
  dev->y_range.min = 0;
 | 
						|
  dev->x_range.quant = 0;
 | 
						|
  dev->y_range.quant = 0;
 | 
						|
  dev->x_trans_range.min = 0;
 | 
						|
  dev->y_trans_range.min = 0;
 | 
						|
  /* default to something really small to be on the safe side: */
 | 
						|
  dev->x_trans_range.max = SANE_FIX (8.0 * MM_PER_INCH);
 | 
						|
  dev->y_trans_range.max = SANE_FIX (5.0 * MM_PER_INCH);
 | 
						|
  dev->x_trans_range.quant = 0;
 | 
						|
  dev->y_trans_range.quant = 0;
 | 
						|
  dev->dpi_range.min = SANE_FIX (72);	/* some scanners don't like low dpi */
 | 
						|
  dev->dpi_range.quant = SANE_FIX (1);
 | 
						|
  /* default to 128 kB */
 | 
						|
  dev->max_buffer_size = 128 * 1024;	/* SCSI buffer -> use 64 k per buffer */
 | 
						|
  dev->max_block_buffer_size = 1024 * 1024 * 1024;
 | 
						|
  dev->firmware_format = firmware_format;
 | 
						|
  dev->firmware_revision_system = firmware_revision_system;
 | 
						|
 | 
						|
  DBG (3, "attach: scanner id: %.11s\n", model_name);
 | 
						|
  if (strncmp ((SANE_String) model_name + 10, "PRO", 3) == 0)
 | 
						|
    DBG (3, "attach: this is probably a Paragon Pro series scanner\n");
 | 
						|
  else if (strncmp ((SANE_String) model_name, "MFC", 3) == 0)
 | 
						|
    DBG (3, "attach: this is probably a Paragon series II scanner\n");
 | 
						|
  else if (strncmp ((SANE_String) model_name, "M", 1) == 0)
 | 
						|
    DBG (3,
 | 
						|
	 "attach: this is probably a Paragon series I or 3-pass scanner\n");
 | 
						|
  else if (strncmp ((SANE_String) model_name, " C", 2) == 0)
 | 
						|
    DBG (3, "attach: this is probably a ScanExpress series A4 scanner\n");
 | 
						|
  else if (strncmp ((SANE_String) model_name, " L", 2) == 0)
 | 
						|
    DBG (3, "attach: this is probably a ScanExpress series A3 scanner\n");
 | 
						|
  else if (strncmp ((SANE_String) model_name, "XC", 2) == 0)
 | 
						|
    DBG (3,
 | 
						|
	 "attach: this is probably a ScanExpress Plus series A4 scanner\n");
 | 
						|
  else
 | 
						|
    DBG (3, "attach: I am not sure what type of scanner this is\n");
 | 
						|
 | 
						|
  /* Paragon 3-pass series */
 | 
						|
  if (strncmp ((SANE_String) model_name, "MFS-12000CX", 11) == 0)
 | 
						|
    {
 | 
						|
      /* These values were measured and compared to those from the Windows
 | 
						|
         driver. Tested with a Paragon MFS-12000CX v4.00 */
 | 
						|
      dev->x_range.min = SANE_FIX (0.0);
 | 
						|
      dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH);
 | 
						|
      dev->y_range.min = SANE_FIX (0.0);
 | 
						|
      dev->y_range.max = SANE_FIX (14.00 * MM_PER_INCH);
 | 
						|
      dev->x_trans_range.min = SANE_FIX (1.0);
 | 
						|
      dev->y_trans_range.min = SANE_FIX (1.0);
 | 
						|
      dev->x_trans_range.max = SANE_FIX (205.0);
 | 
						|
      dev->y_trans_range.max = SANE_FIX (255.0);
 | 
						|
      dev->dpi_range.max = SANE_FIX (1200);
 | 
						|
      dev->sane.model = "MFS-12000CX";
 | 
						|
    }
 | 
						|
  /* There are two different versions of the MFS-6000CX, one has the model
 | 
						|
     name "MFS-06000CX", the other one is "MSF-06000CZ"   */
 | 
						|
  else if (strncmp ((SANE_String) model_name, "MFS-06000CX", 11) == 0)
 | 
						|
    {
 | 
						|
      /* These values were measured and tested with a Paragon MFS-6000CX
 | 
						|
         v4.06 */
 | 
						|
      dev->x_range.min = SANE_FIX (0.0);
 | 
						|
      dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH);
 | 
						|
      dev->y_range.min = SANE_FIX (0.0);
 | 
						|
      dev->y_range.max = SANE_FIX (13.86 * MM_PER_INCH);
 | 
						|
      dev->x_trans_range.min = SANE_FIX (1.0);
 | 
						|
      dev->y_trans_range.min = SANE_FIX (2.0);
 | 
						|
      dev->x_trans_range.max = SANE_FIX (203.0);
 | 
						|
      dev->y_trans_range.max = SANE_FIX (255.0);
 | 
						|
 | 
						|
      dev->dpi_range.max = SANE_FIX (600);
 | 
						|
      dev->sane.model = "MFS-6000CX";
 | 
						|
    }
 | 
						|
  else if (strncmp ((SANE_String) model_name, "MSF-06000CZ", 11) == 0)
 | 
						|
    {
 | 
						|
      /* These values were measured and compared to those from the Windows
 | 
						|
         driver. Tested with a Paragon MFS-6000CX v4.00 */
 | 
						|
      dev->x_range.min = SANE_FIX (0.0);
 | 
						|
      dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH);
 | 
						|
      dev->y_range.min = SANE_FIX (0.0);
 | 
						|
      dev->y_range.max = SANE_FIX (13.85 * MM_PER_INCH);
 | 
						|
      dev->x_trans_range.min = SANE_FIX (1.0);
 | 
						|
      dev->y_trans_range.min = SANE_FIX (2.0);
 | 
						|
      dev->x_trans_range.max = SANE_FIX (205.0);
 | 
						|
      dev->y_trans_range.max = SANE_FIX (255.0);
 | 
						|
 | 
						|
      dev->dpi_range.max = SANE_FIX (600);
 | 
						|
      dev->sane.model = "MFS-6000CX";
 | 
						|
    }
 | 
						|
 | 
						|
  /* Paragon 1-pass 14" series I */
 | 
						|
 | 
						|
  /* I haven't seen a single report for this, but it is mentioned in
 | 
						|
     the old man page. All reported Paragon 1200SP had a model name
 | 
						|
     "MFS-12000SP" */
 | 
						|
  else if (strncmp ((SANE_String) model_name, "MSF-12000SP", 11) == 0)
 | 
						|
    {
 | 
						|
      /* These values are not tested and mostly guessed. */
 | 
						|
      dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH);
 | 
						|
      dev->y_range.max = SANE_FIX (13.85 * MM_PER_INCH);
 | 
						|
      dev->x_trans_range.min = SANE_FIX (1.0);
 | 
						|
      dev->y_trans_range.min = SANE_FIX (1.0);
 | 
						|
      dev->x_trans_range.max = SANE_FIX (200.0);
 | 
						|
      dev->y_trans_range.max = SANE_FIX (250.0);
 | 
						|
      dev->dpi_range.max = SANE_FIX (1200);
 | 
						|
      dev->flags |= MUSTEK_FLAG_LD_NONE;
 | 
						|
      dev->flags |= MUSTEK_FLAG_PARAGON_1;
 | 
						|
      dev->flags |= MUSTEK_FLAG_USE_BLOCK;
 | 
						|
      dev->sane.model = "MFS-12000SP";
 | 
						|
      warning = SANE_TRUE;
 | 
						|
    }
 | 
						|
  /* MFS-8000 SP v 1.x */
 | 
						|
  else if (strncmp ((SANE_String) model_name, "MSF-08000SP", 11) == 0)
 | 
						|
    {
 | 
						|
      /* These values were measured and compared to those from the Windows
 | 
						|
         driver. Tested with a Paragon MFS-8000SP v1.20 */
 | 
						|
      dev->x_range.min = SANE_FIX (0.0);
 | 
						|
      dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH);
 | 
						|
      dev->y_range.min = SANE_FIX (0);
 | 
						|
      dev->y_range.max = SANE_FIX (355.6);
 | 
						|
      dev->x_trans_range.min = SANE_FIX (1.0);
 | 
						|
      dev->y_trans_range.min = SANE_FIX (1.0);
 | 
						|
      dev->x_trans_range.max = SANE_FIX (205.0);
 | 
						|
      dev->y_trans_range.max = SANE_FIX (255.0);
 | 
						|
 | 
						|
      dev->dpi_range.max = SANE_FIX (800);
 | 
						|
      /* At least scanners with firmware 1.20 need a gamma table upload
 | 
						|
         in color mode, otherwise the image is red */
 | 
						|
      if (fw_revision == 0x120)
 | 
						|
	dev->flags |= MUSTEK_FLAG_FORCE_GAMMA;
 | 
						|
      dev->flags |= MUSTEK_FLAG_PARAGON_1;
 | 
						|
      dev->sane.model = "MFS-8000SP";
 | 
						|
    }
 | 
						|
  /* This model name exists */
 | 
						|
  else if (strncmp ((SANE_String) model_name, "MSF-06000SP", 11) == 0)
 | 
						|
    {
 | 
						|
      /* These values were measured and compared to those from the Windows
 | 
						|
         driver. Tested with a Paragon MFS-6000SP v3.12 */
 | 
						|
      dev->x_range.min = SANE_FIX (0.0);
 | 
						|
      dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH);
 | 
						|
      dev->y_range.min = SANE_FIX (0.0);
 | 
						|
      dev->y_range.max = SANE_FIX (355.6);
 | 
						|
      dev->x_trans_range.min = SANE_FIX (1.0);
 | 
						|
      dev->y_trans_range.min = SANE_FIX (1.0);
 | 
						|
      dev->x_trans_range.max = SANE_FIX (205.0);
 | 
						|
      dev->y_trans_range.max = SANE_FIX (255.0);
 | 
						|
      dev->dpi_range.max = SANE_FIX (600);
 | 
						|
      /* Looks like at least some versions of this scanner produce black images
 | 
						|
         in gray and color mode if MUSTEK_FORCE_GAMMA is not set. At least
 | 
						|
         versions 2.01, 2.02 and 2.10 are reported as having this bug. 3.12
 | 
						|
         doesn't need this workaround but it doesn't harm either. */
 | 
						|
      dev->flags |= MUSTEK_FLAG_FORCE_GAMMA;
 | 
						|
      dev->flags |= MUSTEK_FLAG_PARAGON_1;
 | 
						|
      dev->sane.model = "MFS-6000SP";
 | 
						|
    }
 | 
						|
 | 
						|
  /* This one was reported multiple times */
 | 
						|
  else if (strncmp ((SANE_String) model_name, "MFS-12000SP", 11) == 0)
 | 
						|
    {
 | 
						|
      /* These values were measured and compared to those from the Windows
 | 
						|
         driver. Tested with a Paragon MFS-12000SP v1.02 and v1.00 */
 | 
						|
      dev->x_range.min = SANE_FIX (0.0);
 | 
						|
      dev->x_range.max = SANE_FIX (217.0);
 | 
						|
      dev->y_range.min = SANE_FIX (2.0);
 | 
						|
      dev->y_range.max = SANE_FIX (352.0);
 | 
						|
      dev->x_trans_range.min = SANE_FIX (0.0);
 | 
						|
      dev->y_trans_range.min = SANE_FIX (0.0);
 | 
						|
      dev->x_trans_range.max = SANE_FIX (205.0);
 | 
						|
      dev->y_trans_range.max = SANE_FIX (250.0);
 | 
						|
 | 
						|
      dev->dpi_range.max = SANE_FIX (1200);
 | 
						|
      /* Earlier versions of this source code used MUSTEK_FLAG_LD_MFS
 | 
						|
         for firmware versions < 1.02 and LD_NONE for the rest. This
 | 
						|
         didn't work for my scanners.  1.00 doesn't need any LD
 | 
						|
         correction, 1.02, 1.07 and 1.11 do need normal LD
 | 
						|
         corrections.  Maybe all != 1.00 need normal LD */
 | 
						|
      dev->flags |= MUSTEK_FLAG_PARAGON_1;
 | 
						|
      dev->flags |= MUSTEK_FLAG_LD_BLOCK;
 | 
						|
      dev->flags |= MUSTEK_FLAG_USE_BLOCK;
 | 
						|
      dev->sane.model = "MFS-12000SP";
 | 
						|
    }
 | 
						|
  /* MFS-8000 SP v2.x */
 | 
						|
  else if (strncmp ((SANE_String) model_name, "MFS-08000SP", 11) == 0)
 | 
						|
    {
 | 
						|
      /* These values are tested with a MFS-08000SP v 2.04 */
 | 
						|
      dev->x_range.min = SANE_FIX (0.0);
 | 
						|
      dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH);
 | 
						|
      dev->y_range.min = SANE_FIX (0);
 | 
						|
      dev->y_range.max = SANE_FIX (355.6);
 | 
						|
      dev->x_trans_range.min = SANE_FIX (1.0);
 | 
						|
      dev->y_trans_range.min = SANE_FIX (1.0);
 | 
						|
      dev->x_trans_range.max = SANE_FIX (205.0);
 | 
						|
      dev->y_trans_range.max = SANE_FIX (255.0);
 | 
						|
 | 
						|
      dev->dpi_range.max = SANE_FIX (800);
 | 
						|
      /* At least scanners with firmware 1.20 need a gamma table upload
 | 
						|
         in color mode, otherwise the image is red */
 | 
						|
      if (fw_revision == 0x120)
 | 
						|
	dev->flags |= MUSTEK_FLAG_FORCE_GAMMA;
 | 
						|
      dev->flags |= MUSTEK_FLAG_PARAGON_1;
 | 
						|
      dev->sane.model = "MFS-8000SP";
 | 
						|
    }
 | 
						|
  /* I have never seen one of those */
 | 
						|
  else if (strncmp ((SANE_String) model_name, "MFS-06000SP", 11) == 0)
 | 
						|
    {
 | 
						|
      /* These values are not tested. */
 | 
						|
      dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH);
 | 
						|
      dev->y_range.max = SANE_FIX (13.84 * MM_PER_INCH);
 | 
						|
      /* copied from MSF-06000SP */
 | 
						|
      dev->x_trans_range.min = SANE_FIX (1.0);
 | 
						|
      dev->y_trans_range.min = SANE_FIX (1.0);
 | 
						|
      dev->x_trans_range.max = SANE_FIX (205.0);
 | 
						|
      dev->y_trans_range.max = SANE_FIX (255.0);
 | 
						|
      dev->dpi_range.max = SANE_FIX (600);
 | 
						|
      dev->flags |= MUSTEK_FLAG_PARAGON_1;
 | 
						|
      dev->sane.model = "MFS-6000SP";
 | 
						|
      warning = SANE_TRUE;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Paragon 1-pass A4 series II */
 | 
						|
  else if (strncmp ((SANE_String) model_name, "MFC-08000CZ", 11) == 0)
 | 
						|
    {
 | 
						|
      /* These values were measured and compared to those from the Windows
 | 
						|
         driver. Tested with a Paragon 800 II SP v1.06. */
 | 
						|
      dev->x_range.min = SANE_FIX (1.5);
 | 
						|
      dev->x_range.max = SANE_FIX (218.0);
 | 
						|
      dev->y_range.min = SANE_FIX (0.0);
 | 
						|
      dev->y_range.max = SANE_FIX (293.0);
 | 
						|
      dev->x_trans_range.min = SANE_FIX (0.0);
 | 
						|
      dev->y_trans_range.min = SANE_FIX (0.0);
 | 
						|
      dev->x_trans_range.max = SANE_FIX (205.0);
 | 
						|
      dev->y_trans_range.max = SANE_FIX (254.0);
 | 
						|
 | 
						|
      dev->dpi_range.max = SANE_FIX (800);
 | 
						|
      dev->max_block_buffer_size = 2 * 1024 * 1024;
 | 
						|
 | 
						|
      dev->flags |= MUSTEK_FLAG_PARAGON_2;
 | 
						|
      dev->flags |= MUSTEK_FLAG_LD_BLOCK;
 | 
						|
      dev->flags |= MUSTEK_FLAG_USE_BLOCK;
 | 
						|
      dev->sane.model = "800S/800 II SP";
 | 
						|
    }
 | 
						|
  else if (strncmp ((SANE_String) model_name, "MFC-06000CZ", 11) == 0)
 | 
						|
    {
 | 
						|
      /* These values were measured and compared to those from the
 | 
						|
         Windows driver. Tested with a Paragon 600 II CD, a Paragon
 | 
						|
         MFC-600S and a Paragon 600 II N. */
 | 
						|
      dev->x_range.min = SANE_FIX (0.0);
 | 
						|
      dev->x_range.max = SANE_FIX (218.0);
 | 
						|
      dev->y_range.min = SANE_FIX (0.0);
 | 
						|
      dev->y_range.max = SANE_FIX (293.0);
 | 
						|
      dev->x_trans_range.min = SANE_FIX (0.0);
 | 
						|
      dev->y_trans_range.min = SANE_FIX (0.0);
 | 
						|
      dev->x_trans_range.max = SANE_FIX (201.0);
 | 
						|
      dev->y_trans_range.max = SANE_FIX (257.0);
 | 
						|
 | 
						|
      dev->dpi_range.max = SANE_FIX (600);
 | 
						|
      /* This model comes in a non-scsi version, too. It is supplied
 | 
						|
         with its own parallel-port like adapter, an AB306N. Two
 | 
						|
         firmware revisions are known: 1.01 and 2.00. Each needs its
 | 
						|
         own line-distance correction code. */
 | 
						|
      if (dev->flags & MUSTEK_FLAG_N)
 | 
						|
	{
 | 
						|
	  if (fw_revision < 0x200)
 | 
						|
	    dev->flags |= MUSTEK_FLAG_LD_N1;
 | 
						|
	  else
 | 
						|
	    dev->flags |= MUSTEK_FLAG_LD_N2;
 | 
						|
	  dev->x_trans_range.min = SANE_FIX (33.0);
 | 
						|
	  dev->y_trans_range.min = SANE_FIX (62.0);
 | 
						|
	  dev->x_trans_range.max = SANE_FIX (183.0);
 | 
						|
	  dev->y_trans_range.max = SANE_FIX (238.0);
 | 
						|
	  dev->max_block_buffer_size = 1024 * 1024 * 1024;
 | 
						|
	  dev->sane.model = "600 II N";
 | 
						|
	}
 | 
						|
      else if (dev->flags & MUSTEK_FLAG_SCSI_PP)
 | 
						|
	{
 | 
						|
	  /* FIXME; experiment with different line distance codes later */
 | 
						|
	  dev->dpi_range.min = SANE_FIX (75.0);
 | 
						|
	  dev->flags |= MUSTEK_FLAG_LD_NONE;
 | 
						|
	  dev->max_block_buffer_size = 2 * 1024 * 1024;
 | 
						|
	  dev->sane.model = "600 II EP";
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  dev->sane.model = "600S/600 II CD";
 | 
						|
	  dev->flags |= MUSTEK_FLAG_PARAGON_2;
 | 
						|
	  dev->flags |= MUSTEK_FLAG_LD_BLOCK;
 | 
						|
	  dev->flags |= MUSTEK_FLAG_USE_BLOCK;
 | 
						|
	  dev->max_block_buffer_size = 2 * 1024 * 1024;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  /* ScanExpress and ScanMagic series */
 | 
						|
  else if (strncmp ((SANE_String) model_name, " C03", 4) == 0)
 | 
						|
    {
 | 
						|
      /* These values were measured and compared to those from the Windows
 | 
						|
         driver. Tested with a ScannExpress 6000SP 1.00 */
 | 
						|
      dev->x_range.max = SANE_FIX (215);
 | 
						|
      dev->y_range.min = SANE_FIX (0);
 | 
						|
      dev->y_range.max = SANE_FIX (293);
 | 
						|
 | 
						|
      dev->x_trans_range.min = SANE_FIX (0);
 | 
						|
      dev->y_trans_range.min = SANE_FIX (0);
 | 
						|
      dev->x_trans_range.max = SANE_FIX (150.0);
 | 
						|
      dev->y_trans_range.max = SANE_FIX (175.0);
 | 
						|
 | 
						|
      dev->dpi_range.max = SANE_FIX (600);
 | 
						|
      dev->dpi_range.min = SANE_FIX (60);
 | 
						|
      dev->flags |= MUSTEK_FLAG_SE;
 | 
						|
      /* At least the SE 6000SP with firmware 1.00 limits its
 | 
						|
         x-resolution to 300 dpi and does *no* interpolation at higher
 | 
						|
         resolutions. So this has to be done in software. */
 | 
						|
      dev->flags |= MUSTEK_FLAG_ENLARGE_X;
 | 
						|
      dev->sane.model = "ScanExpress 6000SP";
 | 
						|
    }
 | 
						|
  /* There are two different versions of the ScanExpress 12000SP, one
 | 
						|
     has the model name " C06", the other one is "XC06". The latter
 | 
						|
     seems to be used in the newer "Plus" models.
 | 
						|
     Also there is the Mustek ScanExpress 1200 FS, which looks similar to the
 | 
						|
     ScanExpress 12000 SP but has an "F" instead of the "V" in the
 | 
						|
     firmware version.
 | 
						|
  */
 | 
						|
  else if (strncmp ((SANE_String) model_name, " C06", 4) == 0)
 | 
						|
    {
 | 
						|
      if (result[32] == 'F')
 | 
						|
	{
 | 
						|
	  /* Mustek ScanExpress 1200 FS. Completely untested. */
 | 
						|
	  dev->x_range.min = SANE_FIX (0);
 | 
						|
	  dev->y_range.min = SANE_FIX (0);
 | 
						|
	  dev->x_range.max = SANE_FIX (215.9);
 | 
						|
	  dev->y_range.max = SANE_FIX (291.2);
 | 
						|
 | 
						|
	  dev->x_trans_range.min = SANE_FIX (0);
 | 
						|
	  dev->y_trans_range.min = SANE_FIX (0);
 | 
						|
	  dev->x_trans_range.max = SANE_FIX (150.0);
 | 
						|
	  dev->y_trans_range.max = SANE_FIX (175.0);
 | 
						|
 | 
						|
	  dev->dpi_range.max = SANE_FIX (1200);
 | 
						|
	  dev->dpi_range.min = SANE_FIX (60);
 | 
						|
	  dev->flags |= MUSTEK_FLAG_SE;
 | 
						|
	  /* The ScanExpress models limit their x-resolution to 600 dpi
 | 
						|
	     and do *no* interpolation at higher resolutions. So this has
 | 
						|
	     to be done in software. */
 | 
						|
	  dev->flags |= MUSTEK_FLAG_ENLARGE_X;
 | 
						|
	  dev->flags |= MUSTEK_FLAG_COVER_SENSOR;
 | 
						|
	  dev->sane.model = "ScanExpress 12000 FS (untested)";
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  /* These values were measured and compared to those from the Windows
 | 
						|
	     driver. Tested with a ScaneExpress 12000SP 2.02 and a ScanMagic
 | 
						|
	     9636S v 1.01 */
 | 
						|
	  dev->x_range.min = SANE_FIX (0);
 | 
						|
	  dev->y_range.min = SANE_FIX (0);
 | 
						|
	  dev->x_range.max = SANE_FIX (215.9);
 | 
						|
	  dev->y_range.max = SANE_FIX (291.2);
 | 
						|
 | 
						|
	  dev->x_trans_range.min = SANE_FIX (0);
 | 
						|
	  dev->y_trans_range.min = SANE_FIX (0);
 | 
						|
	  dev->x_trans_range.max = SANE_FIX (150.0);
 | 
						|
	  dev->y_trans_range.max = SANE_FIX (175.0);
 | 
						|
 | 
						|
	  dev->dpi_range.max = SANE_FIX (1200);
 | 
						|
	  dev->dpi_range.min = SANE_FIX (60);
 | 
						|
	  dev->flags |= MUSTEK_FLAG_SE;
 | 
						|
	  /* The ScanExpress models limit their x-resolution to 600 dpi
 | 
						|
	     and do *no* interpolation at higher resolutions. So this has
 | 
						|
	     to be done in software. */
 | 
						|
	  dev->flags |= MUSTEK_FLAG_ENLARGE_X;
 | 
						|
	  dev->flags |= MUSTEK_FLAG_COVER_SENSOR;
 | 
						|
	  dev->sane.model = "ScanExpress 12000SP";
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else if (strncmp ((SANE_String) model_name, "XC06", 4) == 0)
 | 
						|
    {
 | 
						|
      /* These values are tested with a SE 12000 SP Plus v 1.01 */
 | 
						|
      dev->x_range.max = SANE_FIX (216);
 | 
						|
      dev->y_range.min = SANE_FIX (0);
 | 
						|
      dev->y_range.max = SANE_FIX (294.5);
 | 
						|
 | 
						|
      dev->x_trans_range.min = SANE_FIX (0);
 | 
						|
      dev->y_trans_range.min = SANE_FIX (0);
 | 
						|
      dev->x_trans_range.max = SANE_FIX (152.0);
 | 
						|
      dev->y_trans_range.max = SANE_FIX (177.0);
 | 
						|
 | 
						|
      dev->dpi_range.max = SANE_FIX (1200);
 | 
						|
      dev->dpi_range.min = SANE_FIX (60);
 | 
						|
 | 
						|
      dev->flags |= MUSTEK_FLAG_SE;
 | 
						|
      dev->flags |= MUSTEK_FLAG_SE_PLUS;
 | 
						|
      /* The ScanExpress models limit their x-resolution to 600 dpi
 | 
						|
         and do *no* interpolation at higher resolutions. So this has
 | 
						|
         to be done in software. */
 | 
						|
      dev->flags |= MUSTEK_FLAG_ENLARGE_X;
 | 
						|
      dev->flags |= MUSTEK_FLAG_COVER_SENSOR;
 | 
						|
      dev->sane.model = "ScanExpress 12000SP Plus";
 | 
						|
    }
 | 
						|
  /* ScanExpress A3 SP */
 | 
						|
  else if (strncmp ((SANE_String) model_name, " L03", 4) == 0)
 | 
						|
    {
 | 
						|
      /* These values were measured with a ScannExpress A3 SP 2.00 */
 | 
						|
      dev->x_range.max = SANE_FIX (297);
 | 
						|
      dev->y_range.min = SANE_FIX (0);
 | 
						|
      dev->y_range.max = SANE_FIX (430);
 | 
						|
 | 
						|
      /* TA couldn't be tested due to lack of equipment. So At least
 | 
						|
         the TA IV (A4 size) is supported */
 | 
						|
      dev->x_trans_range.min = SANE_FIX (0);
 | 
						|
      dev->y_trans_range.min = SANE_FIX (0);
 | 
						|
      dev->x_trans_range.max = SANE_FIX (150.0);
 | 
						|
      dev->y_trans_range.max = SANE_FIX (175.0);
 | 
						|
 | 
						|
      dev->dpi_range.max = SANE_FIX (600);
 | 
						|
      dev->dpi_range.min = SANE_FIX (60);
 | 
						|
      dev->flags |= MUSTEK_FLAG_SE;
 | 
						|
      /* The ScanExpress models limit their x-resolution to 300 dpi
 | 
						|
         and do *no* interpolation at higher resolutions. So this has
 | 
						|
         to be done in software. */
 | 
						|
      dev->flags |= MUSTEK_FLAG_ENLARGE_X;
 | 
						|
      dev->flags |= MUSTEK_FLAG_COVER_SENSOR;
 | 
						|
      dev->sane.model = "ScanExpress A3 SP";
 | 
						|
    }
 | 
						|
  /* Paragon 1200 SP Pro */
 | 
						|
  else if (strncmp ((SANE_String) model_name, "MFS-1200SPPRO", 13) == 0)
 | 
						|
    {
 | 
						|
      /* These values were measured with a Paragon 1200 SP Pro v2.01 */
 | 
						|
      dev->x_range.max = SANE_FIX (8.6 * MM_PER_INCH);
 | 
						|
      dev->y_range.max = SANE_FIX (13.70 * MM_PER_INCH);
 | 
						|
      dev->dpi_range.max = SANE_FIX (1200);
 | 
						|
      dev->sane.model = "1200 SP PRO";
 | 
						|
      dev->flags |= MUSTEK_FLAG_LD_NONE;
 | 
						|
      dev->flags |= MUSTEK_FLAG_ENLARGE_X;
 | 
						|
    }
 | 
						|
  /* No documentation, but it works: Paragon 1200 A3 PRO  */
 | 
						|
  else if (strncmp ((SANE_String) model_name, "MFS-1200A3PRO", 13) == 0)
 | 
						|
    {
 | 
						|
      /* These values were measured and compared to those from the Windows
 | 
						|
         driver. Tested with a Paragon 1200 A3 Pro v1.10 */
 | 
						|
      dev->x_range.max = SANE_FIX (11.7 * MM_PER_INCH);
 | 
						|
      dev->y_range.max = SANE_FIX (424);
 | 
						|
      dev->dpi_range.max = SANE_FIX (1200);
 | 
						|
      dev->sane.model = "1200 A3 PRO";
 | 
						|
      dev->flags |= MUSTEK_FLAG_LD_NONE;
 | 
						|
      dev->flags |= MUSTEK_FLAG_ENLARGE_X;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      DBG (0, "attach: this Mustek scanner (ID: %s) is not supported yet\n",
 | 
						|
	   model_name);
 | 
						|
      DBG (0, "attach: please set the debug level to 5 and send a debug "
 | 
						|
	   "report\n");
 | 
						|
      DBG (0, "attach: to henning@meier-geinitz.de (export "
 | 
						|
	   "SANE_DEBUG_MUSTEK=5\n");
 | 
						|
      DBG (0, "attach: scanimage -L 2>debug.txt). Thank you.\n");
 | 
						|
      free (dev);
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  if (dev->flags & MUSTEK_FLAG_SE)
 | 
						|
    {
 | 
						|
      DBG (3, "attach: this is a single-pass scanner\n");
 | 
						|
      if (result[63] & (1 << 6))
 | 
						|
	{
 | 
						|
	  dev->flags |= MUSTEK_FLAG_TA;
 | 
						|
	  DBG (3, "attach: scanner supports transparency adapter (TA)\n");
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      if (result[57] & (1 << 6))
 | 
						|
	{
 | 
						|
	  DBG (3, "attach: this is a single-pass scanner\n");
 | 
						|
	  if (dev->flags & MUSTEK_FLAG_LD_NONE)
 | 
						|
	    DBG (4,
 | 
						|
		 "attach: scanner doesn't need line-distance correction\n");
 | 
						|
	  else if (dev->flags & MUSTEK_FLAG_LD_N1)
 | 
						|
	    DBG (4, "attach: scanner has N1 line-distance correction\n");
 | 
						|
	  else if (dev->flags & MUSTEK_FLAG_LD_N2)
 | 
						|
	    DBG (4, "attach: scanner has N2 line-distance correction\n");
 | 
						|
	  else if (dev->flags & MUSTEK_FLAG_LD_BLOCK)
 | 
						|
	    DBG (4, "attach: scanner has block line-distance correction\n");
 | 
						|
	  else
 | 
						|
	    DBG (4, "attach: scanner has normal line-distance correction\n");
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  dev->flags |= MUSTEK_FLAG_THREE_PASS;
 | 
						|
	  /* three-pass scanners quantize to 0.5% of the maximum resolution: */
 | 
						|
	  dev->dpi_range.quant = dev->dpi_range.max / 200;
 | 
						|
	  dev->dpi_range.min = dev->dpi_range.quant;
 | 
						|
	  DBG (3, "attach: this is a three-pass scanner\n");
 | 
						|
	}
 | 
						|
      if (result[57] & (1 << 5))
 | 
						|
	{
 | 
						|
	  DBG (3, "attach: this is a professional series scanner\n");
 | 
						|
	  dev->flags |= MUSTEK_FLAG_PRO;
 | 
						|
	  status = dev_open (devname, &s, sense_handler);
 | 
						|
	  if (status == SANE_STATUS_GOOD)
 | 
						|
	    {
 | 
						|
	      if (ta_available_pro (&s))
 | 
						|
		{
 | 
						|
		  dev->flags |= MUSTEK_FLAG_TA;
 | 
						|
		  DBG (3, "attach: found transparency adapter (TA)\n");
 | 
						|
		}
 | 
						|
	      dev_close (&s);
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      DBG (1, "attach: couldn't open device: %s\n",
 | 
						|
		   sane_strstatus (status));
 | 
						|
	      return status;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      if (result[63] & (1 << 2))
 | 
						|
	{
 | 
						|
	  dev->flags |= MUSTEK_FLAG_ADF;
 | 
						|
	  DBG (3, "attach: found automatic document feeder (ADF)\n");
 | 
						|
	  if (result[63] & (1 << 3))
 | 
						|
	    {
 | 
						|
	      dev->flags |= MUSTEK_FLAG_ADF_READY;
 | 
						|
	      DBG (4, "attach: automatic document feeder is ready\n");
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      DBG (4, "attach: automatic document feeder is out of "
 | 
						|
		   "documents\n");
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
      if (result[63] & (1 << 6))
 | 
						|
	{
 | 
						|
	  dev->flags |= MUSTEK_FLAG_TA;
 | 
						|
	  DBG (3, "attach: found transparency adapter (TA)\n");
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  if (dev->flags & MUSTEK_FLAG_COVER_SENSOR)
 | 
						|
    {
 | 
						|
      if (result[62] & (1 << 0))
 | 
						|
	DBG (4, "attach: scanner cover is closed\n");
 | 
						|
      else
 | 
						|
	DBG (4, "attach: scanner cover is open\n");
 | 
						|
    }
 | 
						|
 | 
						|
  if (warning == SANE_TRUE)
 | 
						|
    {
 | 
						|
      DBG (0,
 | 
						|
	   "WARNING: Your scanner was detected by the SANE Mustek backend, "
 | 
						|
	   "but\n  it is not fully tested. It may or may not work. Be "
 | 
						|
	   "careful and read\n  the PROBLEMS file in the sane directory. "
 | 
						|
	   "Please set the debug level of this\n  backend to maximum "
 | 
						|
	   "(export SANE_DEBUG_MUSTEK=255) and send the output of\n  "
 | 
						|
	   "scanimage -L to the SANE mailing list sane-devel@alioth-lists.debian.net. "
 | 
						|
	   "Please include\n  the exact model name of your scanner and to "
 | 
						|
	   "which extend it works.\n");
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (2, "attach: found Mustek %s %s, %s%s%s%s\n",
 | 
						|
       dev->sane.model, dev->sane.type,
 | 
						|
       (dev->flags & MUSTEK_FLAG_THREE_PASS) ? "3-pass" : "1-pass",
 | 
						|
       (dev->flags & MUSTEK_FLAG_ADF) ? ", ADF" : "",
 | 
						|
       (dev->flags & MUSTEK_FLAG_TA) ? ", TA" : "",
 | 
						|
       (dev->flags & MUSTEK_FLAG_SE) ? ", SE" : "");
 | 
						|
 | 
						|
  ++num_devices;
 | 
						|
  dev->next = first_dev;
 | 
						|
  first_dev = dev;
 | 
						|
 | 
						|
  if (devp)
 | 
						|
    *devp = dev;
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static size_t
 | 
						|
max_string_size (const SANE_String_Const strings[])
 | 
						|
{
 | 
						|
  size_t size, max_size = 0;
 | 
						|
  SANE_Int i;
 | 
						|
 | 
						|
  for (i = 0; strings[i]; ++i)
 | 
						|
    {
 | 
						|
      size = strlen (strings[i]) + 1;
 | 
						|
      if (size > max_size)
 | 
						|
	max_size = size;
 | 
						|
    }
 | 
						|
  return max_size;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
constrain_value (Mustek_Scanner * s, SANE_Int option, void *value,
 | 
						|
		 SANE_Int * info)
 | 
						|
{
 | 
						|
  SANE_Fixed w, dpi;
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  if (value)
 | 
						|
    w = *(SANE_Fixed *) value;
 | 
						|
  else
 | 
						|
    w = 0;
 | 
						|
 | 
						|
  if (option == OPT_RESOLUTION)
 | 
						|
    {
 | 
						|
      if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
 | 
						|
	{
 | 
						|
	  /* The three pass scanners use a 0.5% of the maximum resolution
 | 
						|
	     increment for resolutions less than or equal to half of the
 | 
						|
	     maximum resolution. The MFS-06000CX uses a 5% of the maximum
 | 
						|
	     resolution increment for larger resolutions.  The models
 | 
						|
	     MFS-12000CX and MSF-06000CZ use 1% of the maximum resolution.
 | 
						|
	     We can't represent this easily in SANE, so the constraint is
 | 
						|
	     simply for 0.5% and then we round to the 5% or 1% increments
 | 
						|
	     if necessary.  */
 | 
						|
	  SANE_Fixed max_dpi, quant, half_res;
 | 
						|
 | 
						|
	  /*w = *(SANE_Word *) value; */
 | 
						|
	  max_dpi = s->hw->dpi_range.max;
 | 
						|
	  half_res = max_dpi / 2;
 | 
						|
 | 
						|
	  if (w > half_res)
 | 
						|
	    {
 | 
						|
	      /* quantizize to 1% step */
 | 
						|
	      quant = max_dpi / 100;
 | 
						|
 | 
						|
	      dpi = (w + quant / 2) / quant;
 | 
						|
	      dpi *= quant;
 | 
						|
	      if (dpi != w)
 | 
						|
		{
 | 
						|
		  *(SANE_Word *) value = dpi;
 | 
						|
		  if (info)
 | 
						|
		    *info |= SANE_INFO_INEXACT;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  status = sanei_constrain_value (s->opt + option, value, info);
 | 
						|
  if (s->opt[option].type == SANE_TYPE_FIXED)
 | 
						|
    DBG (5, "constrain_value: %s = %.2f (was %.2f)\n", s->opt[option].name,
 | 
						|
	 SANE_UNFIX (*(SANE_Word *) value), SANE_UNFIX (w));
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
/* Quantize s->val[OPT_RESOLUTION].w and return the resolution code for the
 | 
						|
   quantized resolution.  Quantization depends on scanner type (single
 | 
						|
   pass vs. three-pass) and resolution */
 | 
						|
static SANE_Int
 | 
						|
encode_resolution (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Fixed max_dpi, dpi;
 | 
						|
  SANE_Int code, mode = 0;
 | 
						|
 | 
						|
  dpi = s->val[OPT_RESOLUTION].w;
 | 
						|
 | 
						|
  if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS))
 | 
						|
    {
 | 
						|
      code = dpi >> SANE_FIXED_SCALE_SHIFT;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      SANE_Fixed quant, half_res;
 | 
						|
 | 
						|
      max_dpi = s->hw->dpi_range.max;
 | 
						|
      half_res = max_dpi / 2;
 | 
						|
 | 
						|
      if (dpi <= half_res)
 | 
						|
	{
 | 
						|
	  /* quantizize to 0.5% step */
 | 
						|
	  quant = max_dpi / 200;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  /* quantizize to 1% step */
 | 
						|
	  quant = max_dpi / 100;
 | 
						|
	  mode = 0x100;		/* indicate 5% or 1% quantization */
 | 
						|
	}
 | 
						|
 | 
						|
      code = (dpi + quant / 2) / quant;
 | 
						|
      if (code < 1)
 | 
						|
	code = 1;
 | 
						|
 | 
						|
    }
 | 
						|
  DBG (5, "encode_resolution: code = 0x%x (%d); mode = %x\n", code, code,
 | 
						|
       mode);
 | 
						|
  return code | mode;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Int
 | 
						|
encode_percentage (Mustek_Scanner * s, double value)
 | 
						|
{
 | 
						|
  SANE_Int max, code, sign = 0;
 | 
						|
 | 
						|
  if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
 | 
						|
    {
 | 
						|
      code = (int) ((value / 100.0 * 12) + 12.5);
 | 
						|
      max = 0x18;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      if (value < 0.0)
 | 
						|
	{
 | 
						|
	  value = -value;
 | 
						|
	  sign = 0x80;
 | 
						|
	}
 | 
						|
      code = (int) (value / 100.0 * 127 + 0.5);
 | 
						|
      code |= sign;
 | 
						|
      max = 0xff;
 | 
						|
    }
 | 
						|
  if (code > max)
 | 
						|
    code = max;
 | 
						|
  if (code < 0)
 | 
						|
    code = 0x00;
 | 
						|
  return code;
 | 
						|
}
 | 
						|
 | 
						|
/* encode halftone pattern type and size */
 | 
						|
static SANE_Status
 | 
						|
encode_halftone (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_String selection = s->val[OPT_HALFTONE_DIMENSION].s;
 | 
						|
  SANE_Int i = 0;
 | 
						|
 | 
						|
  while ((halftone_list[i] != 0) && (strcmp (selection, halftone_list[i]) != 0))
 | 
						|
    {
 | 
						|
      i++;
 | 
						|
    }
 | 
						|
  if (halftone_list[i] == 0)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  if (i < 0x0c)			/* standard pattern */
 | 
						|
    {
 | 
						|
      s->custom_halftone_pattern = SANE_FALSE;
 | 
						|
      s->halftone_pattern_type = i;
 | 
						|
    }
 | 
						|
  else				/* custom pattern */
 | 
						|
    {
 | 
						|
      s->custom_halftone_pattern = SANE_TRUE;
 | 
						|
      i -= 0x0c;
 | 
						|
      i = 8 - i;
 | 
						|
      if (i < 8)
 | 
						|
	i--;
 | 
						|
      i = i + (i << 4);
 | 
						|
      s->halftone_pattern_type = i;
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (5, "encode_halftone: %s pattern type %x\n",
 | 
						|
       s->custom_halftone_pattern ? "custom" : "standard",
 | 
						|
       s->halftone_pattern_type);
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
/* Paragon series */
 | 
						|
static SANE_Status
 | 
						|
area_and_windows (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Byte cmd[117], *cp;
 | 
						|
  SANE_Int i, offset;
 | 
						|
 | 
						|
  /* setup SCSI command (except length): */
 | 
						|
  memset (cmd, 0, sizeof (cmd));
 | 
						|
  cmd[0] = MUSTEK_SCSI_AREA_AND_WINDOWS;
 | 
						|
 | 
						|
  cp = cmd + 6;
 | 
						|
 | 
						|
  /* Some scanners need a larger scanarea for line-distance correction */
 | 
						|
  offset = 0;
 | 
						|
  if (((s->hw->flags & MUSTEK_FLAG_LD_N1)
 | 
						|
       || ((s->hw->flags & MUSTEK_FLAG_LD_BLOCK)
 | 
						|
	   && (s->hw->flags & MUSTEK_FLAG_PARAGON_1)))
 | 
						|
      && (s->mode & MUSTEK_MODE_COLOR))
 | 
						|
    offset = MAX_LINE_DIST;
 | 
						|
 | 
						|
  /* fill in frame header: */
 | 
						|
 | 
						|
  if (s->hw->flags & MUSTEK_FLAG_USE_EIGHTS)
 | 
						|
    {
 | 
						|
      double eights_per_mm = 8 / MM_PER_INCH;
 | 
						|
      SANE_Int tlx, tly, brx, bry;
 | 
						|
      /*
 | 
						|
       * The MSF-06000CZ seems to lock-up if the pixel-unit is used.
 | 
						|
       * Using 1/8" works.
 | 
						|
       * This doesn't seem to be true with the current scheme.
 | 
						|
       * This code isn't used at the moment.  <henning@meier-geinitz.de>
 | 
						|
       */
 | 
						|
      *cp++ = ((s->mode & MUSTEK_MODE_LINEART) ? 0x00 : 0x01);
 | 
						|
 | 
						|
      tlx = SANE_UNFIX (s->val[OPT_TL_X].w) * eights_per_mm + 0.5;
 | 
						|
      tly = SANE_UNFIX (s->val[OPT_TL_Y].w) * eights_per_mm + 0.5;
 | 
						|
      brx = SANE_UNFIX (s->val[OPT_BR_X].w) * eights_per_mm + 0.5;
 | 
						|
      bry = SANE_UNFIX (s->val[OPT_BR_Y].w) * eights_per_mm + 0.5;
 | 
						|
      STORE16L (cp, tlx);
 | 
						|
      STORE16L (cp, tly);
 | 
						|
      STORE16L (cp, brx);
 | 
						|
      STORE16L (cp, bry);
 | 
						|
      DBG (5, "area_and_windows: tlx=%d (%d mm); tly=%d (%d mm); "
 | 
						|
	   "brx=%d (%d mm); bry=%d (%d mm)\n", tlx,
 | 
						|
	   (int) (tlx / eights_per_mm), tly, (int) (tly / eights_per_mm), brx,
 | 
						|
	   (int) (brx / eights_per_mm), bry, (int) (bry / eights_per_mm));
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      double pixels_per_mm = SANE_UNFIX (s->hw->dpi_range.max) / MM_PER_INCH;
 | 
						|
      SANE_Int tlx, tly, brx, bry;
 | 
						|
 | 
						|
      if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
 | 
						|
	/* 3pass scanners use 1/2 of the max resolution as base */
 | 
						|
	pixels_per_mm /= 2;
 | 
						|
 | 
						|
      /* pixel unit and halftoning: */
 | 
						|
      *cp++ = 0x8 | ((s->mode & MUSTEK_MODE_LINEART) ? 0x00 : 0x01);
 | 
						|
 | 
						|
      /* fill in scanning area: */
 | 
						|
      if (strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0)
 | 
						|
	{
 | 
						|
	  /* must mirror the x coordinates */
 | 
						|
	  brx = SANE_UNFIX (s->hw->x_range.max - s->val[OPT_TL_X].w)
 | 
						|
	    * pixels_per_mm + 0.5;
 | 
						|
	  tlx = SANE_UNFIX (s->hw->x_range.max - s->val[OPT_BR_X].w)
 | 
						|
	    * pixels_per_mm + 0.5;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  tlx = SANE_UNFIX (s->val[OPT_TL_X].w) * pixels_per_mm + 0.5;
 | 
						|
	  brx = SANE_UNFIX (s->val[OPT_BR_X].w) * pixels_per_mm + 0.5;
 | 
						|
 | 
						|
	}
 | 
						|
      tly = SANE_UNFIX (s->val[OPT_TL_Y].w) * pixels_per_mm + 0.5;
 | 
						|
      bry = SANE_UNFIX (s->val[OPT_BR_Y].w) * pixels_per_mm + 0.5 + offset;
 | 
						|
      STORE16L (cp, tlx);
 | 
						|
      STORE16L (cp, tly);
 | 
						|
      STORE16L (cp, brx);
 | 
						|
      STORE16L (cp, bry);
 | 
						|
      DBG (5, "area_and_windows: tlx=%d (%d mm); tly=%d (%d mm); "
 | 
						|
	   "brx=%d (%d mm); bry=%d (%d mm)\n", tlx,
 | 
						|
	   (int) (tlx / pixels_per_mm), tly, (int) (tly / pixels_per_mm), brx,
 | 
						|
	   (int) (brx / pixels_per_mm), bry, (int) (bry / pixels_per_mm));
 | 
						|
    }
 | 
						|
 | 
						|
  if (s->custom_halftone_pattern)
 | 
						|
    {
 | 
						|
      *cp++ = 0x40;		/* mark presence of user pattern */
 | 
						|
      *cp++ = s->halftone_pattern_type;	/* set pattern length */
 | 
						|
      for (i = 0; i < (s->halftone_pattern_type & 0x0f) *
 | 
						|
	   ((s->halftone_pattern_type >> 4) & 0x0f); ++i)
 | 
						|
	*cp++ = s->val[OPT_HALFTONE_PATTERN].wa[i];
 | 
						|
    }
 | 
						|
 | 
						|
  cmd[4] = (cp - cmd) - 6;
 | 
						|
 | 
						|
  return dev_cmd (s, cmd, (cp - cmd), 0, 0);
 | 
						|
}
 | 
						|
 | 
						|
/* ScanExpress */
 | 
						|
static SANE_Status
 | 
						|
set_window_se (Mustek_Scanner * s, SANE_Int lamp)
 | 
						|
{
 | 
						|
  SANE_Byte cmd[58], *cp;
 | 
						|
  double pixels_per_mm;
 | 
						|
  SANE_Int offset;
 | 
						|
  SANE_Int tlx, tly, width, height;
 | 
						|
 | 
						|
  /* setup SCSI command (except length): */
 | 
						|
  memset (cmd, 0, sizeof (cmd));
 | 
						|
  cmd[0] = MUSTEK_SCSI_SET_WINDOW;
 | 
						|
  cp = cmd + sizeof (scsi_set_window);	/* skip command block           */
 | 
						|
 | 
						|
  if (s->mode & MUSTEK_MODE_COLOR)
 | 
						|
    {
 | 
						|
      /* We have to increase the specified resolution to the next       */
 | 
						|
      /* "standard" resolution due to a firmware bug(?) in color mode   */
 | 
						|
      /* It's possible to scan in 36, 75, 100, 150, 200, 250, 300,      */
 | 
						|
      /* 400, 500, 600, 900, 1200 dpi but the speed is only different   */
 | 
						|
      /* with 36, 150, 300, 600, 1200 dpi.                              */
 | 
						|
      /* Additionally we must increase the window length slightly to    */
 | 
						|
      /* compensate for different line counts for r/g/b                 */
 | 
						|
      const SANE_Int resolution_list[] = { 36, 150, 300, 600, 1200, 0 };
 | 
						|
      SANE_Int entry = 0;
 | 
						|
 | 
						|
      while (resolution_list[entry] < s->resolution_code)
 | 
						|
	entry++;
 | 
						|
      s->ld.peak_res = resolution_list[entry];
 | 
						|
 | 
						|
      offset = MAX_LINE_DIST;	/* distance r/b lines           */
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      /* In gray and lineart modes all resolutions are possible         */
 | 
						|
      s->ld.peak_res = s->resolution_code;
 | 
						|
      offset = 0;
 | 
						|
    }
 | 
						|
  DBG (5, "set_window_se: hardware resolution is %d dpi; offset is %d\n",
 | 
						|
       s->ld.peak_res, offset);
 | 
						|
 | 
						|
  STORE16B (cp, 0);		/* window identifier            */
 | 
						|
  STORE16B (cp, s->ld.peak_res);
 | 
						|
  /* x and y resolution           */
 | 
						|
  STORE16B (cp, 0);		/* not used acc. to specs       */
 | 
						|
 | 
						|
  pixels_per_mm = SANE_UNFIX (s->hw->dpi_range.max) / MM_PER_INCH;
 | 
						|
 | 
						|
  /* fill in scanning area, begin and length(!) */
 | 
						|
  if ((strcmp (s->val[OPT_SOURCE].s, "Transparency Adapter") == 0) &&
 | 
						|
      !(s->hw->flags & MUSTEK_FLAG_TA))
 | 
						|
    {
 | 
						|
      /* need to add the start values of the transparency adapter */
 | 
						|
      tlx = (SANE_UNFIX (s->val[OPT_TL_X].w) + 33.0) * pixels_per_mm + 0.5;
 | 
						|
      tly = (SANE_UNFIX (s->val[OPT_TL_Y].w) + 60.0) * pixels_per_mm + 0.5;
 | 
						|
      DBG (5, "set_window_se: added offset for transparency adapter\n");
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      /* no transparency adapter selected or calculation done in firmware */
 | 
						|
      tlx = SANE_UNFIX (s->val[OPT_TL_X].w) * pixels_per_mm + 0.5;
 | 
						|
      tly = SANE_UNFIX (s->val[OPT_TL_Y].w) * pixels_per_mm + 0.5;
 | 
						|
    }
 | 
						|
  width = (SANE_UNFIX (s->val[OPT_BR_X].w) - SANE_UNFIX (s->val[OPT_TL_X].w))
 | 
						|
    * pixels_per_mm + 0.5;
 | 
						|
  height = (SANE_UNFIX (s->val[OPT_BR_Y].w) - SANE_UNFIX (s->val[OPT_TL_Y].w))
 | 
						|
    * pixels_per_mm + 0.5 + offset;
 | 
						|
 | 
						|
  DBG (5, "set_window_se: tlx=%d (%d mm); tly=%d (%d mm); width=%d (%d mm); "
 | 
						|
       "height=%d (%d mm)\n", tlx, (int) (tlx / pixels_per_mm), tly,
 | 
						|
       (int) (tly / pixels_per_mm), width, (int) (width / pixels_per_mm),
 | 
						|
       height, (int) (height / pixels_per_mm));
 | 
						|
 | 
						|
 | 
						|
  STORE32B (cp, tlx);
 | 
						|
  STORE32B (cp, tly);
 | 
						|
  STORE32B (cp, width);
 | 
						|
  STORE32B (cp, height);
 | 
						|
 | 
						|
  *cp++ = 0x00;			/* brightness, not impl.        */
 | 
						|
  *cp++ = 0x80;			/* threshold, not impl.         */
 | 
						|
  *cp++ = 0x00;			/* contrast, not impl.          */
 | 
						|
 | 
						|
  /* Note that 'image composition' has no meaning for the SE series     */
 | 
						|
  /* Mode selection is accomplished solely by bits/pixel (1, 8, 24)     */
 | 
						|
  if (s->mode & MUSTEK_MODE_COLOR)
 | 
						|
    {
 | 
						|
      *cp++ = 0x05;		/* actually not used!           */
 | 
						|
      *cp++ = 24;		/* 24 bits/pixel in color mode  */
 | 
						|
    }
 | 
						|
  else if (s->mode & MUSTEK_MODE_GRAY)
 | 
						|
    {
 | 
						|
      *cp++ = 0x02;		/* actually not used!           */
 | 
						|
      *cp++ = 8;		/* 8 bits/pixel in gray mode    */
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      *cp++ = 0x00;		/* actually not used!           */
 | 
						|
      *cp++ = 1;		/* 1 bit/pixel in lineart mode  */
 | 
						|
    }
 | 
						|
 | 
						|
  cp += 14;			/* skip reserved bytes          */
 | 
						|
  *cp++ = lamp;			/* 0 = normal, 1 = on, 2 = off  */
 | 
						|
 | 
						|
  if ((s->hw->flags & MUSTEK_FLAG_TA)
 | 
						|
      && (strcmp (s->val[OPT_SOURCE].s, "Transparency Adapter") == 0))
 | 
						|
    *cp++ = 1;
 | 
						|
  else
 | 
						|
    *cp++ = 0;
 | 
						|
  cp += 5;			/* skip reserved bytes          */
 | 
						|
 | 
						|
  cmd[8] = cp - cmd - sizeof (scsi_set_window);
 | 
						|
  return dev_cmd (s, cmd, (cp - cmd), 0, 0);
 | 
						|
}
 | 
						|
 | 
						|
/* Pro series */
 | 
						|
static SANE_Status
 | 
						|
set_window_pro (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Byte cmd[20], *cp;
 | 
						|
  double pixels_per_mm;
 | 
						|
 | 
						|
  memset (cmd, 0, sizeof (cmd));
 | 
						|
  cmd[0] = MUSTEK_SCSI_SET_WINDOW;
 | 
						|
  if (strcmp (s->hw->sane.model, "1200 SP PRO") == 0)
 | 
						|
    cmd[8] = 0x09;
 | 
						|
  else
 | 
						|
    cmd[8] = 0x0a;
 | 
						|
 | 
						|
  cp = cmd + sizeof (scsi_set_window);	/* skip command block           */
 | 
						|
 | 
						|
  *cp++ = 0;			/* what's this? */
 | 
						|
  pixels_per_mm = SANE_UNFIX (s->hw->dpi_range.max) / MM_PER_INCH;
 | 
						|
 | 
						|
  /* The next for 16 bit values are x0, y0, x1, y1 in pixels at max res */
 | 
						|
  STORE16L (cp, SANE_UNFIX (s->val[OPT_TL_X].w) * pixels_per_mm + 0.5);
 | 
						|
  STORE16L (cp, SANE_UNFIX (s->val[OPT_TL_Y].w) * pixels_per_mm + 0.5);
 | 
						|
  STORE16L (cp, SANE_UNFIX (s->val[OPT_BR_X].w) * pixels_per_mm + 0.5);
 | 
						|
  STORE16L (cp, SANE_UNFIX (s->val[OPT_BR_Y].w) * pixels_per_mm + 0.5);
 | 
						|
 | 
						|
  if (strcmp (s->hw->sane.model, "1200 SP PRO") != 0)
 | 
						|
    *cp++ = lamp_off_time;		/* Only needed for A3 Pro, default: 60 minutes until lamp-off */
 | 
						|
  DBG (5, "set_window_pro\n");
 | 
						|
 | 
						|
  return dev_cmd (s, cmd, (cp - cmd), 0, 0);
 | 
						|
}
 | 
						|
 | 
						|
/* Pro series calibration */
 | 
						|
static SANE_Status
 | 
						|
get_calibration_size_pro (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  SANE_Byte cmd[6];
 | 
						|
  SANE_Byte result[6];
 | 
						|
  size_t len;
 | 
						|
 | 
						|
  memset (cmd, 0, sizeof (cmd));
 | 
						|
  memset (result, 0, sizeof (result));
 | 
						|
  cmd[0] = MUSTEK_SCSI_GET_IMAGE_STATUS;
 | 
						|
  cmd[4] = 0x06;		/* size of result */
 | 
						|
  cmd[5] = 0x80;		/* get back buffer size and number of buffers */
 | 
						|
  len = sizeof (result);
 | 
						|
  status = dev_cmd (s, cmd, sizeof (cmd), result, &len);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    return status;
 | 
						|
 | 
						|
  s->hw->cal.bytes = result[1] | (result[2] << 8);
 | 
						|
  s->hw->cal.lines = result[3] | (result[4] << 8);
 | 
						|
 | 
						|
  DBG (4, "get_calibration_size_pro: bytes=%d, lines=%d\n", s->hw->cal.bytes,
 | 
						|
       s->hw->cal.lines);
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
get_calibration_lines_pro (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  SANE_Byte cmd[10];
 | 
						|
  size_t len;
 | 
						|
  SANE_Int line;
 | 
						|
 | 
						|
  DBG (2, "get_calibration_lines_pro: please wait for warmup\n");
 | 
						|
  memset (cmd, 0, sizeof (cmd));
 | 
						|
  cmd[0] = MUSTEK_SCSI_READ_DATA;
 | 
						|
  len = s->hw->cal.bytes;
 | 
						|
  cmd[6] = (len >> 16) & 0xff;
 | 
						|
  cmd[7] = (len >> 8) & 0xff;
 | 
						|
  cmd[8] = (len >> 0) & 0xff;
 | 
						|
 | 
						|
  for (line = 0; line < s->hw->cal.lines; line++)
 | 
						|
    {
 | 
						|
      status = dev_cmd (s, cmd, sizeof (scsi_read_data),
 | 
						|
			s->hw->cal.buffer + line * len, &len);
 | 
						|
 | 
						|
      if ((status != SANE_STATUS_GOOD)
 | 
						|
	  || (len != (unsigned int) s->hw->cal.bytes))
 | 
						|
	{
 | 
						|
	  DBG (1, "get_calibration_lines_pro: read failed\n");
 | 
						|
	  return status;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  DBG (5, "get_calibration_lines_pro finished. Assuming 12 bits per color\n");
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
send_calibration_lines_pro (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  SANE_Byte *cmd1, *cmd2;
 | 
						|
  size_t buf_size;
 | 
						|
  SANE_Word column, line, color;
 | 
						|
 | 
						|
  DBG (5, "send_calibration_lines_pro\n");
 | 
						|
 | 
						|
  buf_size = s->hw->cal.bytes / 2;
 | 
						|
  cmd1 = (SANE_Byte *) malloc (buf_size + sizeof (scsi_send_data));
 | 
						|
  cmd2 = (SANE_Byte *) malloc (buf_size + sizeof (scsi_send_data));
 | 
						|
  if (!cmd1 || !cmd2)
 | 
						|
    {
 | 
						|
      DBG (1, "send_calibration_lines_pro: failed to malloc %ld bytes for "
 | 
						|
	   "sending lines\n",
 | 
						|
	   (long int) (buf_size + sizeof (scsi_send_data)));
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
  memset (cmd1, 0, sizeof (scsi_send_data));
 | 
						|
  memset (cmd2, 0, sizeof (scsi_send_data));
 | 
						|
 | 
						|
  cmd1[0] = cmd2[0] = MUSTEK_SCSI_SEND_DATA;
 | 
						|
  cmd1[6] = cmd2[6] = (buf_size >> 16) & 0xff;
 | 
						|
  cmd1[7] = cmd2[7] = (buf_size >> 8) & 0xff;
 | 
						|
  cmd1[8] = cmd2[8] = (buf_size >> 0) & 0xff;
 | 
						|
  cmd1[9] = 0;			/* Least significant 8 bits */
 | 
						|
  cmd2[9] = 0x80;		/* Most significant 2 bits */
 | 
						|
 | 
						|
  for (color = 0; color < 3; color++)
 | 
						|
    {
 | 
						|
      for (column = 0; column < s->hw->cal.bytes / 6; column++)
 | 
						|
	{
 | 
						|
	  SANE_Word calibration_word = 0;
 | 
						|
	  for (line = 0; line < s->hw->cal.lines; line++)
 | 
						|
	    {
 | 
						|
	      calibration_word +=
 | 
						|
		*(s->hw->cal.buffer + column * 6 + color_seq[color] * 2 + 0)
 | 
						|
		+
 | 
						|
		(*(s->hw->cal.buffer + column * 6 + color_seq[color] * 2 + 1)
 | 
						|
		 << 8);
 | 
						|
	    }
 | 
						|
	  if (!calibration_word)
 | 
						|
	    calibration_word = 1;
 | 
						|
	  calibration_word = (1024 * 65536 / calibration_word) - 1024;
 | 
						|
	  if (calibration_word > 1023)
 | 
						|
	    calibration_word = 1023;
 | 
						|
	  *(cmd1 + sizeof (scsi_send_data) + (buf_size / 3) * color + column)
 | 
						|
	    = calibration_word & 0xff;
 | 
						|
	  *(cmd2 + sizeof (scsi_send_data) + (buf_size / 3) * color + column)
 | 
						|
	    = (calibration_word >> 8) & 0xff;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  status = dev_cmd (s, cmd1, buf_size + sizeof (scsi_send_data), 0, 0);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    {
 | 
						|
      DBG (1, "send_calibration_lines_pro: send failed\n");
 | 
						|
      return status;
 | 
						|
    }
 | 
						|
 | 
						|
  status = dev_cmd (s, cmd2, buf_size + sizeof (scsi_send_data), 0, 0);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    {
 | 
						|
      DBG (1, "send_calibration_lines_pro: send failed\n");
 | 
						|
      return status;
 | 
						|
    }
 | 
						|
  free (cmd1);
 | 
						|
  free (cmd2);
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
calibration_pro (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  if (s->val[OPT_QUALITY_CAL].w)
 | 
						|
    DBG (4, "calibration_pro: doing calibration\n");
 | 
						|
  else
 | 
						|
    {
 | 
						|
      DBG (4, "calibration_pro: calibration not necessary\n");
 | 
						|
      return SANE_STATUS_GOOD;
 | 
						|
    }
 | 
						|
 | 
						|
  status = get_calibration_size_pro (s);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    return status;
 | 
						|
 | 
						|
  s->hw->cal.buffer = (SANE_Byte *) malloc (s->hw->cal.bytes *
 | 
						|
					    s->hw->cal.lines);
 | 
						|
  if (!s->hw->cal.buffer)
 | 
						|
    {
 | 
						|
      DBG (1, "calibration_pro: failed to malloc %d bytes for buffer\n",
 | 
						|
	   s->hw->cal.bytes * s->hw->cal.lines);
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
 | 
						|
  status = get_calibration_lines_pro (s);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    return status;
 | 
						|
 | 
						|
  status = send_calibration_lines_pro (s);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    return status;
 | 
						|
 | 
						|
  free (s->hw->cal.buffer);
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ScanExpress series calibration */
 | 
						|
static SANE_Status
 | 
						|
get_calibration_lines_se (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  SANE_Byte cmd[10];
 | 
						|
  size_t len;
 | 
						|
  SANE_Word lines, bytes_per_color;
 | 
						|
 | 
						|
  if (s->mode == MUSTEK_MODE_COLOR)
 | 
						|
    {
 | 
						|
      lines = s->hw->cal.lines * 3;
 | 
						|
      bytes_per_color = s->hw->cal.bytes / 3;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      lines = s->hw->cal.lines;
 | 
						|
      bytes_per_color = s->hw->cal.bytes;
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (4, "get_calibration_lines_se: reading %d lines (%d bytes per color)\n",
 | 
						|
       lines, bytes_per_color);
 | 
						|
  memset (cmd, 0, sizeof (cmd));
 | 
						|
  cmd[0] = MUSTEK_SCSI_READ_DATA;
 | 
						|
  cmd[2] = 1;
 | 
						|
  cmd[7] = (lines >> 8) & 0xff;
 | 
						|
  cmd[8] = (lines >> 0) & 0xff;
 | 
						|
  len = lines * bytes_per_color;
 | 
						|
  status = dev_cmd (s, cmd, sizeof (scsi_read_data), s->hw->cal.buffer, &len);
 | 
						|
  if ((status != SANE_STATUS_GOOD)
 | 
						|
      || (len != (unsigned int) (lines * bytes_per_color)))
 | 
						|
    {
 | 
						|
      DBG (1, "get_calibration_lines_se: read failed\n");
 | 
						|
      return status;
 | 
						|
    }
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
send_calibration_lines_se (Mustek_Scanner * s, SANE_Word color)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  SANE_Byte *cmd;
 | 
						|
  size_t buf_size;
 | 
						|
  SANE_Word column;
 | 
						|
  SANE_Word bytes_per_color;
 | 
						|
 | 
						|
  if (s->mode == MUSTEK_MODE_COLOR)
 | 
						|
    {
 | 
						|
      bytes_per_color = s->hw->cal.bytes / 3;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      bytes_per_color = s->hw->cal.bytes;
 | 
						|
    }
 | 
						|
 | 
						|
  buf_size = bytes_per_color;
 | 
						|
 | 
						|
  DBG (5, "send_calibration_lines_se: %d bytes, color: %d\n",
 | 
						|
       bytes_per_color, color + 1);
 | 
						|
 | 
						|
  cmd = (SANE_Byte *) malloc (buf_size + sizeof (scsi_send_data));
 | 
						|
  if (!cmd)
 | 
						|
    {
 | 
						|
      DBG (1, "send_calibration_lines_se: failed to malloc %ld bytes for "
 | 
						|
	   "sending lines\n",
 | 
						|
	   (long int) (buf_size + sizeof (scsi_send_data)));
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
  memset (cmd, 0, sizeof (scsi_send_data));
 | 
						|
 | 
						|
  for (column = 0; column < bytes_per_color; column++)
 | 
						|
    {
 | 
						|
      SANE_Word line;
 | 
						|
      SANE_Word cali_word = 0;
 | 
						|
      SANE_Int color_seq[] = { 2, 0, 1 };
 | 
						|
 | 
						|
      for (line = 0; line < s->hw->cal.lines; line++)
 | 
						|
	cali_word += *(s->hw->cal.buffer
 | 
						|
		       + line * bytes_per_color
 | 
						|
		       + bytes_per_color * color_seq[color] + column);
 | 
						|
      if (!cali_word)
 | 
						|
	cali_word = 1;
 | 
						|
      cali_word = 256 * s->hw->cal.lines * 255 / cali_word - 256;
 | 
						|
      if (cali_word > 255)
 | 
						|
	cali_word = 255;
 | 
						|
      *(cmd + sizeof (scsi_send_data) + column) = cali_word;
 | 
						|
    }
 | 
						|
 | 
						|
  cmd[0] = MUSTEK_SCSI_SEND_DATA;
 | 
						|
  cmd[2] = 1;
 | 
						|
  cmd[6] = color + 1;
 | 
						|
  cmd[7] = (buf_size >> 8) & 0xff;
 | 
						|
  cmd[8] = (buf_size >> 0) & 0xff;
 | 
						|
 | 
						|
  status = dev_cmd (s, cmd, buf_size + sizeof (scsi_send_data), 0, 0);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    {
 | 
						|
      DBG (1, "send_calibration_lines_se: send failed\n");
 | 
						|
      return status;
 | 
						|
    }
 | 
						|
  free (cmd);
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
calibration_se (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  if (!s->val[OPT_QUALITY_CAL].w || s->val[OPT_PREVIEW].w
 | 
						|
      || s->mode == MUSTEK_MODE_LINEART)
 | 
						|
    return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
  DBG (4, "calibration_se: doing calibration\n");
 | 
						|
 | 
						|
  s->hw->cal.lines = MIN (s->hw->cal.lines,
 | 
						|
			  s->hw->buffer_size / s->hw->cal.bytes);
 | 
						|
 | 
						|
  s->hw->cal.buffer = (SANE_Byte *) malloc (s->hw->cal.bytes
 | 
						|
					    * s->hw->cal.lines);
 | 
						|
  if (!s->hw->cal.buffer)
 | 
						|
    {
 | 
						|
      DBG (1, "calibration_se: failed to malloc %d bytes for buffer\n",
 | 
						|
	   s->hw->cal.bytes * s->hw->cal.lines);
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
 | 
						|
  status = get_calibration_lines_se (s);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    return status;
 | 
						|
 | 
						|
  if (s->mode == MUSTEK_MODE_GRAY)
 | 
						|
    status = send_calibration_lines_se (s, 0);
 | 
						|
  else
 | 
						|
    {
 | 
						|
      status = send_calibration_lines_se (s, 0);
 | 
						|
      status = send_calibration_lines_se (s, 1);
 | 
						|
      status = send_calibration_lines_se (s, 2);
 | 
						|
    }
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    return status;
 | 
						|
 | 
						|
  free (s->hw->cal.buffer);
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
/* ScanExpress series */
 | 
						|
static SANE_Status
 | 
						|
send_gamma_table_se (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  SANE_Byte gamma[10 + 4096], *cp;
 | 
						|
  SANE_Int color, factor, val_a, val_b;
 | 
						|
  SANE_Int i, j;
 | 
						|
# define CLIP(x)	((x) < 0 ? 0 : ((x) > 255 ? 255 : (x)))
 | 
						|
 | 
						|
  memset (gamma, 0, sizeof (scsi_send_data));
 | 
						|
 | 
						|
  gamma[0] = MUSTEK_SCSI_SEND_DATA;
 | 
						|
  gamma[2] = 0x03;		/* indicates gamma table */
 | 
						|
 | 
						|
  if ((s->mode & MUSTEK_MODE_GRAY) || (s->mode & MUSTEK_MODE_COLOR))
 | 
						|
    {
 | 
						|
      if (s->hw->gamma_length + sizeof (scsi_send_data) > sizeof (gamma))
 | 
						|
	return SANE_STATUS_NO_MEM;
 | 
						|
      gamma[7] = (s->hw->gamma_length >> 8) & 0xff;
 | 
						|
      gamma[8] = (s->hw->gamma_length >> 0) & 0xff;
 | 
						|
 | 
						|
      factor = s->hw->gamma_length / 256;
 | 
						|
      color = (s->mode & MUSTEK_MODE_COLOR) ? 1 : 0;
 | 
						|
 | 
						|
      do
 | 
						|
	{
 | 
						|
	  gamma[6] = color;
 | 
						|
 | 
						|
	  if (color == 0)
 | 
						|
	    {
 | 
						|
	      val_a = s->gamma_table[0][1];
 | 
						|
	      val_b = s->gamma_table[0][0];
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      /* compose intensity gamma and color channel gamma: */
 | 
						|
	      val_a = s->gamma_table[0][s->gamma_table[color][1]];
 | 
						|
	      val_b = s->gamma_table[0][s->gamma_table[color][0]];
 | 
						|
	    }
 | 
						|
	  /* Now val_a is extrapolated from [0] and [1]      */
 | 
						|
	  val_a = MAX (2 * val_b - val_a, 0);
 | 
						|
 | 
						|
	  /* Interpolate first entries from 256 entry table  */
 | 
						|
	  cp = gamma + sizeof (scsi_send_data);
 | 
						|
	  for (j = 0; j < factor; j++)
 | 
						|
	    *cp++ = CLIP (((factor - j) * val_a + j * val_b
 | 
						|
			   + factor / 2) / factor);
 | 
						|
 | 
						|
	  for (i = 1; i < 256; i++)
 | 
						|
	    {
 | 
						|
	      if (color == 0)
 | 
						|
		{
 | 
						|
		  val_a = s->gamma_table[0][i - 1];
 | 
						|
		  val_b = s->gamma_table[0][i];
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  /* compose intensity gamma and color channel gamma: */
 | 
						|
		  val_a = s->gamma_table[0][s->gamma_table[color][i - 1]];
 | 
						|
		  val_b = s->gamma_table[0][s->gamma_table[color][i]];
 | 
						|
		}
 | 
						|
 | 
						|
	      /* Interpolate next entries from the 256 entry table */
 | 
						|
	      for (j = 0; j < factor; j++)
 | 
						|
		*cp++ = CLIP (((factor - j) * val_a + j * val_b
 | 
						|
			       + factor / 2) / factor);
 | 
						|
	    }
 | 
						|
 | 
						|
	  DBG (5, "send_gamma_table_se: sending table for color %d\n",
 | 
						|
	       gamma[6]);
 | 
						|
	  status = dev_cmd (s, gamma, sizeof (scsi_send_data)
 | 
						|
			    + s->hw->gamma_length, 0, 0);
 | 
						|
	  ++color;
 | 
						|
	}
 | 
						|
      while ((color != 1) & (color < 4) & (status == SANE_STATUS_GOOD));
 | 
						|
 | 
						|
      return status;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      /* In lineart mode the threshold is encoded in byte 8 as follows */
 | 
						|
      /* brightest -> 00 01 02 ... 7F 80 81 82 ... FF <- darkest image */
 | 
						|
      gamma[6] = 0x04;
 | 
						|
      gamma[8] = 128 - 127 * SANE_UNFIX (s->val[OPT_BRIGHTNESS].w) / 100.0;
 | 
						|
 | 
						|
      DBG (5, "send_gamma_table_se: sending lineart threshold %2X\n",
 | 
						|
	   gamma[8]);
 | 
						|
      return dev_cmd (s, gamma, sizeof (scsi_send_data), 0, 0);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* Paragon series */
 | 
						|
static SANE_Status
 | 
						|
mode_select_paragon (Mustek_Scanner * s, SANE_Int color_code)
 | 
						|
{
 | 
						|
  SANE_Int speed_code;
 | 
						|
  SANE_Byte mode[19], *cp;
 | 
						|
 | 
						|
  /* calculate funky speed code: */
 | 
						|
  for (speed_code = 0; speed_list[speed_code]; ++speed_code)
 | 
						|
    {
 | 
						|
      if (strcmp (speed_list[speed_code], s->val[OPT_SPEED].s) == 0)
 | 
						|
	break;
 | 
						|
    }
 | 
						|
  if (speed_code > 4)
 | 
						|
    speed_code = 4;
 | 
						|
  else if (speed_code < 0)
 | 
						|
    speed_code = 0;
 | 
						|
  if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
 | 
						|
    {
 | 
						|
      speed_code = 5 - speed_code;	/* 1 is fast, 5 is slow */
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      speed_code = 4 - speed_code;	/* 0 is fast, 4 is slow */
 | 
						|
    }
 | 
						|
  memset (mode, 0, sizeof (mode));
 | 
						|
  mode[0] = MUSTEK_SCSI_MODE_SELECT;
 | 
						|
 | 
						|
  /* set command length and resolution code: */
 | 
						|
  if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
 | 
						|
    {
 | 
						|
      mode[4] = 0x0b;
 | 
						|
      mode[7] = s->resolution_code;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      mode[4] = 0x0d;
 | 
						|
      cp = mode + 17;
 | 
						|
      STORE16L (cp, s->resolution_code);
 | 
						|
    }
 | 
						|
  /* set mode byte: */
 | 
						|
  mode[6] = 0x83 | (color_code << 5);
 | 
						|
  if (!(s->hw->flags & MUSTEK_FLAG_USE_EIGHTS))
 | 
						|
    mode[6] |= 0x08;
 | 
						|
  if (s->custom_halftone_pattern)
 | 
						|
    mode[6] |= 0x10;
 | 
						|
  if (s->hw->flags & MUSTEK_FLAG_PARAGON_1)
 | 
						|
    {
 | 
						|
      if ((s->mode == MUSTEK_MODE_LINEART)
 | 
						|
	  || (s->mode == MUSTEK_MODE_HALFTONE))
 | 
						|
	{
 | 
						|
	  mode[8] =
 | 
						|
	    encode_percentage (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS].w));
 | 
						|
	  mode[9] =
 | 
						|
	    encode_percentage (s, SANE_UNFIX (s->val[OPT_CONTRAST].w));
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  mode[8] = 0x0c;
 | 
						|
	  mode[9] = 0x0c;
 | 
						|
	}
 | 
						|
      mode[10] = 2;		/* grain */
 | 
						|
      if (s->val[OPT_PREVIEW].w && s->val[OPT_FAST_PREVIEW].w)
 | 
						|
	mode[11] = 0x01;
 | 
						|
      else if ((s->mode == MUSTEK_MODE_COLOR)
 | 
						|
	       || (s->mode == MUSTEK_MODE_HALFTONE))
 | 
						|
	mode[11] = 0x00;	/* speed */
 | 
						|
      else
 | 
						|
	mode[11] = 0x02;	/* speed */
 | 
						|
      mode[12] = 0x00;		/* shadow param not used by Mustek */
 | 
						|
      mode[13] = 0xff;		/* highlight only used by some scanners */
 | 
						|
      mode[14] = 0x70;		/* paper- */
 | 
						|
      mode[15] = 0x00;		/* length */
 | 
						|
      mode[16] = 0x53;		/* midtone param not used by Mustek */
 | 
						|
    }
 | 
						|
  else if (s->hw->flags & MUSTEK_FLAG_PARAGON_2)
 | 
						|
    {
 | 
						|
      mode[8] = encode_percentage (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS].w));
 | 
						|
      mode[9] = encode_percentage (s, SANE_UNFIX (s->val[OPT_CONTRAST].w));
 | 
						|
      mode[10] = 2;		/* grain */
 | 
						|
      if ((s->mode == MUSTEK_MODE_COLOR) || (s->mode == MUSTEK_MODE_HALFTONE))
 | 
						|
	mode[11] = 0x00;	/* speed */
 | 
						|
      else
 | 
						|
	mode[11] = 0x02;	/* speed */
 | 
						|
      mode[12] = 0x00;		/* shadow param not used by Mustek */
 | 
						|
      mode[13] = 0x00;		/* highlight param not used by Mustek */
 | 
						|
      mode[14] = 0x5c;		/* paper- */
 | 
						|
      mode[15] = 0x00;		/* length */
 | 
						|
      mode[16] = 0x41;		/* midtone param not used by Mustek */
 | 
						|
    }
 | 
						|
  else if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
 | 
						|
    {
 | 
						|
      if (s->mode & MUSTEK_MODE_COLOR)
 | 
						|
	{
 | 
						|
	  mode[8] = encode_percentage
 | 
						|
	    (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS + s->pass + 1].w - 1));
 | 
						|
	  mode[9] = encode_percentage
 | 
						|
	    (s, SANE_UNFIX (s->val[OPT_CONTRAST + s->pass + 1].w - 1));
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  mode[8] = encode_percentage
 | 
						|
	    (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS].w - 1));
 | 
						|
	  mode[9] = encode_percentage
 | 
						|
	    (s, SANE_UNFIX (s->val[OPT_CONTRAST].w - 1));
 | 
						|
	}
 | 
						|
      mode[10] = s->halftone_pattern_type;
 | 
						|
      mode[11] = speed_code;	/* lamp setting not supported yet */
 | 
						|
      mode[12] = 0;		/* shadow param not used by Mustek */
 | 
						|
      mode[13] = 0;		/* highlight param not used by Mustek */
 | 
						|
      mode[14] = mode[15] = 0;	/* paperlength not used by Mustek */
 | 
						|
      mode[16] = 0;		/* midtone param not used by Mustek */
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      mode[8] = encode_percentage (s, SANE_UNFIX (s->val[OPT_BRIGHTNESS].w));
 | 
						|
      mode[9] = encode_percentage (s, SANE_UNFIX (s->val[OPT_CONTRAST].w));
 | 
						|
      mode[10] = s->halftone_pattern_type;
 | 
						|
      mode[11] = speed_code;	/* lamp setting not supported yet */
 | 
						|
      mode[12] = 0;		/* shadow param not used by Mustek */
 | 
						|
      mode[13] = 0;		/* highlight param not used by Mustek */
 | 
						|
      mode[14] = mode[15] = 0;	/* paperlength not used by Mustek */
 | 
						|
      mode[16] = 0;		/* midtone param not used by Mustek */
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (5, "mode_select: resolution_code=%d (0x%x)\n", s->resolution_code,
 | 
						|
       s->resolution_code);
 | 
						|
  return dev_cmd (s, mode, 6 + mode[4], 0, 0);
 | 
						|
}
 | 
						|
 | 
						|
/* Pro series */
 | 
						|
static SANE_Status
 | 
						|
mode_select_pro (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Byte mode[19], *cp;
 | 
						|
 | 
						|
  memset (mode, 0, sizeof (mode));
 | 
						|
 | 
						|
  mode[0] = MUSTEK_SCSI_MODE_SELECT;
 | 
						|
  mode[4] = 0x0d;
 | 
						|
 | 
						|
  if (s->mode & MUSTEK_MODE_COLOR)
 | 
						|
    {
 | 
						|
      if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0)
 | 
						|
	mode[6] = 0xE0;
 | 
						|
      else
 | 
						|
	mode[6] = 0x60;
 | 
						|
    }
 | 
						|
  else if (s->mode & MUSTEK_MODE_GRAY)
 | 
						|
    {
 | 
						|
      if (s->val[OPT_FAST_GRAY_MODE].w)
 | 
						|
	mode[6] = 0x20;
 | 
						|
      else
 | 
						|
	mode[6] = 0x40;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    mode[6] = 0x00;		/* lineart */
 | 
						|
 | 
						|
  mode[7] = 0;
 | 
						|
  mode[8] = 0;
 | 
						|
  mode[9] = 0;
 | 
						|
  mode[10] = 0;
 | 
						|
  mode[11] = 0x00;
 | 
						|
  mode[12] = 0x27;
 | 
						|
  mode[13] = 0xb0;
 | 
						|
  mode[14] = 0x04;
 | 
						|
  mode[15] = 0x43;
 | 
						|
  mode[16] = 0x41;
 | 
						|
 | 
						|
  cp = mode + 17;
 | 
						|
  STORE16L (cp, s->resolution_code);
 | 
						|
 | 
						|
  DBG (5, "mode_select_pro: resolution_code=%d (0x%x), mode=0x%x\n",
 | 
						|
       s->resolution_code, s->resolution_code, mode[6]);
 | 
						|
  return dev_cmd (s, mode, 6 + mode[4], 0, 0);
 | 
						|
}
 | 
						|
 | 
						|
/* Paragon and Pro series. According to Mustek, the only builtin gamma
 | 
						|
   table is a linear table, so all we support here is user-defined
 | 
						|
   gamma tables.  */
 | 
						|
static SANE_Status
 | 
						|
gamma_correction (Mustek_Scanner * s, SANE_Int color_code)
 | 
						|
{
 | 
						|
  SANE_Int i, j, table = 0, len = 0, bytes_per_channel, num_channels = 1;
 | 
						|
  SANE_Byte gamma[4096 + 10], val, *cp;	/* for Paragon models 3 x 256 is the
 | 
						|
					   maximum. Pro needs 4096 bytes */
 | 
						|
 | 
						|
  if ((s->hw->flags & MUSTEK_FLAG_N)
 | 
						|
      && ((s->mode & MUSTEK_MODE_LINEART)
 | 
						|
	  || (s->mode & MUSTEK_MODE_HALFTONE)))
 | 
						|
    {
 | 
						|
      /* sigh! - the 600 II N needs a (dummy) table download even for
 | 
						|
         lineart and halftone mode, else it produces a completely
 | 
						|
         white image. Thank Mustek for their buggy firmware !  */
 | 
						|
      memset (gamma, 0, sizeof (gamma));
 | 
						|
      gamma[0] = MUSTEK_SCSI_LOOKUP_TABLE;
 | 
						|
      gamma[2] = 0x0;		/* indicate any preloaded gamma table */
 | 
						|
      DBG (5, "gamma_correction: sending dummy gamma table\n");
 | 
						|
      return dev_cmd (s, gamma, 6, 0, 0);
 | 
						|
    }
 | 
						|
 | 
						|
  if (((s->mode & MUSTEK_MODE_LINEART) || (s->mode & MUSTEK_MODE_HALFTONE))
 | 
						|
      && !(s->hw->flags & MUSTEK_FLAG_PRO))
 | 
						|
    {
 | 
						|
      DBG (5, "gamma_correction: nothing to do in lineart mode -- exiting\n");
 | 
						|
      return SANE_STATUS_GOOD;
 | 
						|
    }
 | 
						|
 | 
						|
  if ((!s->val[OPT_CUSTOM_GAMMA].w) && (!(s->hw->flags & MUSTEK_FLAG_PRO)))
 | 
						|
    {
 | 
						|
      /* Do we need to upload a gamma table even if the user didn't select
 | 
						|
         this option? Some scanners need this work around. */
 | 
						|
      if (!(s->hw->flags & MUSTEK_FLAG_FORCE_GAMMA) ||
 | 
						|
	  !((s->mode & MUSTEK_MODE_COLOR) || (s->mode & MUSTEK_MODE_GRAY)))
 | 
						|
	{
 | 
						|
	  DBG (5,
 | 
						|
	       "gamma_correction: no custom table selected -- exititing\n");
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  if (s->mode & MUSTEK_MODE_COLOR)
 | 
						|
    {
 | 
						|
      table = 1;
 | 
						|
      if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
 | 
						|
	table += s->pass;
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  if ((color_code == MUSTEK_CODE_GRAY)
 | 
						|
	      && !(s->hw->flags & MUSTEK_FLAG_PRO))
 | 
						|
	    num_channels = 3;
 | 
						|
	  else
 | 
						|
	    table = color_code;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else if (s->hw->flags & MUSTEK_FLAG_N)
 | 
						|
    {
 | 
						|
      /* it seems 600 II N (firmware 1.x at least) wants 768 bytes in
 | 
						|
       * gray mode too */
 | 
						|
      num_channels = 3;
 | 
						|
    }
 | 
						|
 | 
						|
  memset (gamma, 0, sizeof (gamma));
 | 
						|
  gamma[0] = MUSTEK_SCSI_LOOKUP_TABLE;
 | 
						|
 | 
						|
  if (s->hw->flags & MUSTEK_FLAG_PRO)
 | 
						|
    {
 | 
						|
      bytes_per_channel = 4096;
 | 
						|
      len = bytes_per_channel;
 | 
						|
      if (s->mode == MUSTEK_MODE_COLOR)
 | 
						|
	{
 | 
						|
	  gamma[9] = (color_code << 6);	/* color */
 | 
						|
	  if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0)
 | 
						|
	    gamma[2] = 0x7f;	/* medium brightness */
 | 
						|
	}
 | 
						|
      else if (s->mode == MUSTEK_MODE_GRAY)
 | 
						|
	{
 | 
						|
	  gamma[9] = 0x80;	/* grayscale */
 | 
						|
	  if (s->val[OPT_FAST_GRAY_MODE].w)
 | 
						|
	    gamma[2] = 0x7f;	/* medium brightness */
 | 
						|
	}
 | 
						|
      else			/* lineart */
 | 
						|
	{
 | 
						|
	  gamma[2] =
 | 
						|
	    128 - 127 * SANE_UNFIX (s->val[OPT_BRIGHTNESS].w) / 100.0;
 | 
						|
	  gamma[9] = 0x80;	/* grayscale/lineart */
 | 
						|
	  DBG (5, "gamma_correction: sending brightness information\n");
 | 
						|
	}
 | 
						|
      gamma[7] = (len >> 8) & 0xff;	/* big endian! */
 | 
						|
      gamma[8] = (len >> 0) & 0xff;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      bytes_per_channel = 256;
 | 
						|
      len = num_channels * bytes_per_channel;
 | 
						|
      gamma[2] = 0x27;		/* indicate user-selected gamma table */
 | 
						|
      if (s->hw->flags & MUSTEK_FLAG_N)
 | 
						|
	{
 | 
						|
	  /* 600 II N always uses 6-byte cdb */
 | 
						|
	  gamma[3] = (len >> 8) & 0xff;	/* big endian! */
 | 
						|
	  gamma[4] = (len >> 0) & 0xff;
 | 
						|
	  /* no way to pass color_code (?) */
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  gamma[7] = (len >> 8) & 0xff;	/* big endian! */
 | 
						|
	  gamma[8] = (len >> 0) & 0xff;
 | 
						|
	  gamma[9] = (color_code << 6);
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  if (len > 0)
 | 
						|
    {
 | 
						|
      cp = gamma + 10;
 | 
						|
      for (j = 0; j < num_channels; ++j)
 | 
						|
	{
 | 
						|
	  for (i = 0; i < bytes_per_channel; ++i)
 | 
						|
	    {
 | 
						|
	      if (s->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE)
 | 
						|
		val = s->gamma_table[table][i * 256 / bytes_per_channel];
 | 
						|
	      else
 | 
						|
		val = i * 256 / bytes_per_channel;
 | 
						|
	      if ((s->mode & MUSTEK_MODE_COLOR)
 | 
						|
		  && (s->val[OPT_CUSTOM_GAMMA].w == SANE_TRUE))
 | 
						|
		/* compose intensity gamma and color channel gamma: */
 | 
						|
		val = s->gamma_table[0][val];
 | 
						|
	      *cp++ = val;
 | 
						|
	    }
 | 
						|
	  if (!(s->hw->flags & MUSTEK_FLAG_N)
 | 
						|
	      || !(s->mode & MUSTEK_MODE_GRAY))
 | 
						|
	    table++;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  DBG (5, "gamma_correction: sending gamma table of %d bytes\n", len);
 | 
						|
  return dev_cmd (s, gamma, 10 + len, 0, 0);
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
send_gamma_table (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  if (s->one_pass_color_scan)
 | 
						|
    {
 | 
						|
      if (s->hw->flags & MUSTEK_FLAG_N)
 | 
						|
	/* This _should_ work for all one-pass scanners (not just
 | 
						|
	   AB306N scanners), but it doesn't work for my Paragon
 | 
						|
	   600 II SP with firmware rev 1.01.  Too bad, since it would
 | 
						|
	   simplify the gamma correction code quite a bit.  */
 | 
						|
	status = gamma_correction (s, MUSTEK_CODE_GRAY);
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  status = gamma_correction (s, MUSTEK_CODE_RED);
 | 
						|
	  if (status != SANE_STATUS_GOOD)
 | 
						|
	    return status;
 | 
						|
 | 
						|
	  status = gamma_correction (s, MUSTEK_CODE_GREEN);
 | 
						|
	  if (status != SANE_STATUS_GOOD)
 | 
						|
	    return status;
 | 
						|
 | 
						|
	  status = gamma_correction (s, MUSTEK_CODE_BLUE);
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    status = gamma_correction (s, MUSTEK_CODE_GRAY);
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ScanExpress and Paragon series */
 | 
						|
static SANE_Status
 | 
						|
start_scan (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Byte start[6];
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  memset (start, 0, sizeof (start));
 | 
						|
  start[0] = MUSTEK_SCSI_START_STOP;
 | 
						|
  start[4] = 0x01;
 | 
						|
 | 
						|
  DBG (4, "start_scan\n");
 | 
						|
  /* ScanExpress and Pro models don't have any variants */
 | 
						|
  if (!(s->hw->flags & MUSTEK_FLAG_SE) && !(s->hw->flags & MUSTEK_FLAG_PRO))
 | 
						|
    {
 | 
						|
      if (s->mode & MUSTEK_MODE_COLOR)
 | 
						|
	{
 | 
						|
	  if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
 | 
						|
	    start[4] |= ((s->pass + 1) << 3);
 | 
						|
	  else
 | 
						|
	    start[4] |= 0x20;
 | 
						|
	}
 | 
						|
      /* or in single/multi bit: */
 | 
						|
      start[4] |= ((s->mode & MUSTEK_MODE_LINEART)
 | 
						|
		   || (s->mode & MUSTEK_MODE_HALFTONE)) ? 0 : (1 << 6);
 | 
						|
 | 
						|
      /* or in expanded resolution bit: */
 | 
						|
      if (s->val[OPT_RESOLUTION].w > (s->hw->dpi_range.max / 2)
 | 
						|
	  && ((s->hw->flags & MUSTEK_FLAG_THREE_PASS)
 | 
						|
	      || (s->hw->flags & MUSTEK_FLAG_PARAGON_1)
 | 
						|
	      || (s->hw->flags & MUSTEK_FLAG_PARAGON_2)))
 | 
						|
	start[4] |= 1 << 7;
 | 
						|
 | 
						|
      /* block mode (or whatever) */
 | 
						|
      if (s->hw->flags & MUSTEK_FLAG_USE_BLOCK)
 | 
						|
	{
 | 
						|
	  start[5] = 0x08;
 | 
						|
	  DBG (4, "start_scan: using block mode\n");
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  status = dev_cmd (s, start, sizeof (start), 0, 0);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    DBG (1, "start_scan returned status %s\n", sane_strstatus (status));
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
do_eof (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  if (s->pipe >= 0)
 | 
						|
    {
 | 
						|
      close (s->pipe);
 | 
						|
      s->pipe = -1;
 | 
						|
      DBG (5, "do_eof: closing pipe\n");
 | 
						|
    }
 | 
						|
  return SANE_STATUS_EOF;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
do_stop (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Status status = SANE_STATUS_GOOD;
 | 
						|
 | 
						|
  DBG (5, "do_stop\n");
 | 
						|
 | 
						|
  if (s->cancelled)
 | 
						|
    status = SANE_STATUS_CANCELLED;
 | 
						|
 | 
						|
  s->scanning = SANE_FALSE;
 | 
						|
  s->pass = 0;
 | 
						|
 | 
						|
  if (sanei_thread_is_valid (s->reader_pid))
 | 
						|
    {
 | 
						|
      SANE_Int exit_status;
 | 
						|
      struct timeval now;
 | 
						|
      long int scan_time;
 | 
						|
      long int scan_size;
 | 
						|
      SANE_Pid pid;
 | 
						|
 | 
						|
      /* print scanning time */
 | 
						|
      gettimeofday (&now, 0);
 | 
						|
      scan_time = now.tv_sec - s->start_time;
 | 
						|
      if (scan_time < 1)
 | 
						|
	scan_time = 1;
 | 
						|
      scan_size = s->hw->bpl * s->hw->lines / 1024;
 | 
						|
      DBG (2, "Scanning time was %ld seconds, %ld kB/s\n", scan_time,
 | 
						|
	   scan_size / scan_time);
 | 
						|
 | 
						|
      if (s->total_bytes == s->params.lines * s->params.bytes_per_line)
 | 
						|
	DBG (3, "Scanned %d bytes as expected\n", s->total_bytes);
 | 
						|
      else if (s->total_bytes < s->params.lines * s->params.bytes_per_line)
 | 
						|
	DBG (3, "Scanned %d bytes, expected %d bytes\n", s->total_bytes,
 | 
						|
	     s->params.lines * s->params.bytes_per_line);
 | 
						|
      else
 | 
						|
	DBG (1, "Warning: Scanned %d bytes, but expected only %d bytes\n",
 | 
						|
	     s->total_bytes, s->params.lines * s->params.bytes_per_line);
 | 
						|
 | 
						|
      /* ensure child knows it's time to stop: */
 | 
						|
      DBG (5, "do_stop: terminating reader process\n");
 | 
						|
      sanei_thread_kill (s->reader_pid);
 | 
						|
 | 
						|
      pid = sanei_thread_waitpid (s->reader_pid, &exit_status);
 | 
						|
      if (!sanei_thread_is_valid (pid))
 | 
						|
	{
 | 
						|
	  DBG (1,
 | 
						|
	       "do_stop: sanei_thread_waitpid failed, already terminated? (%s)\n",
 | 
						|
	       strerror (errno));
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  DBG (2, "do_stop: reader process terminated with status %s\n",
 | 
						|
	       sane_strstatus (exit_status));
 | 
						|
	  if (status != SANE_STATUS_CANCELLED
 | 
						|
	      && exit_status != SANE_STATUS_GOOD)
 | 
						|
	    status = exit_status;
 | 
						|
	}
 | 
						|
 | 
						|
      sanei_thread_invalidate (s->reader_pid);
 | 
						|
    }
 | 
						|
 | 
						|
  if (s->fd >= 0)
 | 
						|
    {
 | 
						|
      if (!sanei_thread_is_forked ())
 | 
						|
	sanei_scsi_req_flush_all ();	/* flush SCSI queue */
 | 
						|
 | 
						|
      if (s->hw->flags & MUSTEK_FLAG_PRO)
 | 
						|
	{
 | 
						|
	  if (s->total_bytes < s->params.lines * s->params.bytes_per_line)
 | 
						|
	    status = dev_cmd (s, scsi_start_stop, sizeof (scsi_start_stop),
 | 
						|
			      0, 0);
 | 
						|
	  dev_wait_ready (s);
 | 
						|
	}
 | 
						|
      else if ((s->hw->flags & MUSTEK_FLAG_PARAGON_1)
 | 
						|
	       || (s->hw->flags & MUSTEK_FLAG_PARAGON_2)
 | 
						|
	       || (s->hw->flags & MUSTEK_FLAG_THREE_PASS))
 | 
						|
	{
 | 
						|
	  if (s->cancelled &&
 | 
						|
	      (s->total_bytes < s->params.lines * s->params.bytes_per_line))
 | 
						|
	    status = dev_cmd (s, scsi_start_stop, sizeof (scsi_start_stop),
 | 
						|
			      0, 0);
 | 
						|
	}
 | 
						|
      else
 | 
						|
	status = dev_cmd (s, scsi_start_stop, sizeof (scsi_start_stop), 0, 0);
 | 
						|
 | 
						|
      if (force_wait)
 | 
						|
	{
 | 
						|
	  DBG (5, "do_stop: waiting for scanner to be ready\n");
 | 
						|
	  dev_wait_ready (s);
 | 
						|
	}
 | 
						|
 | 
						|
      do_eof (s);
 | 
						|
      DBG (5, "do_stop: closing scanner\n");
 | 
						|
      dev_close (s);
 | 
						|
      s->fd = -1;
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (5, "do_stop: finished\n");
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
/* Paragon I + II: Determine the CCD's distance between the primary color
 | 
						|
   lines.  */
 | 
						|
static SANE_Status
 | 
						|
line_distance (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Int factor, color, res, peak_res;
 | 
						|
  SANE_Status status;
 | 
						|
  SANE_Byte result[5];
 | 
						|
  size_t len;
 | 
						|
 | 
						|
  memset (result, 0, 5);
 | 
						|
 | 
						|
  res = SANE_UNFIX (s->val[OPT_RESOLUTION].w) + 0.5;
 | 
						|
  peak_res = SANE_UNFIX (s->hw->dpi_range.max) + 0.5;
 | 
						|
 | 
						|
  s->ld.buf[0] = NULL;
 | 
						|
 | 
						|
  len = sizeof (result);
 | 
						|
  status = dev_cmd (s, scsi_ccd_distance, sizeof (scsi_ccd_distance),
 | 
						|
		    result, &len);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    return status;
 | 
						|
 | 
						|
  DBG (3, "line_distance: got factor=%d, (r/g/b)=(%d/%d/%d)\n",
 | 
						|
       result[0] | (result[1] << 8), result[2], result[3], result[4]);
 | 
						|
 | 
						|
  if (s->hw->flags & MUSTEK_FLAG_LD_FIX)
 | 
						|
    {
 | 
						|
      result[0] = 0xff;
 | 
						|
      result[1] = 0xff;
 | 
						|
      if (s->mode & MUSTEK_MODE_COLOR)
 | 
						|
	{
 | 
						|
	  if (s->hw->flags & MUSTEK_FLAG_N)
 | 
						|
	    {
 | 
						|
	      /* According to Andreas Czechanowski, the line-distance values
 | 
						|
		 returned for the AB306N scanners are garbage, so we have to
 | 
						|
		 fix things up manually.  Not good.
 | 
						|
		 This seems to be true only for firmware 2.00 which is
 | 
						|
		 extremely seldom.. AB306N scanners with firmware 1.01 don't
 | 
						|
		 need this fix. <henning@meier-geinitz.de> */
 | 
						|
	      if (peak_res == 600)
 | 
						|
		{
 | 
						|
		  if (res < 51)
 | 
						|
		    {
 | 
						|
		      result[0] = 8;
 | 
						|
		      result[1] = 0;
 | 
						|
		      result[2] = 0;
 | 
						|
		      result[3] = 2;
 | 
						|
		      result[4] = 3;
 | 
						|
		    }
 | 
						|
		  else if (res < 75 || (res > 90 && res < 150))
 | 
						|
		    {
 | 
						|
		      /* 51-74 and 91-149 dpi: */
 | 
						|
		      result[0] = 4;
 | 
						|
		      result[1] = 0;
 | 
						|
		      result[2] = 0;
 | 
						|
		      result[3] = 3;
 | 
						|
		      result[4] = 5;
 | 
						|
		    }
 | 
						|
		  else if (res <= 90 || (res >= 150 && res <= 300))
 | 
						|
		    {
 | 
						|
		      /* 75-90 and 150-300 dpi: */
 | 
						|
		      result[0] = 2;
 | 
						|
		      result[1] = 0;
 | 
						|
		      result[2] = 0;
 | 
						|
		      result[3] = 5;
 | 
						|
		      result[4] = 9;
 | 
						|
		    }
 | 
						|
		  else
 | 
						|
		    {
 | 
						|
		      /* 301-600 dpi: */
 | 
						|
		      result[0] = 1;
 | 
						|
		      result[1] = 0;
 | 
						|
		      result[2] = 0;
 | 
						|
		      result[3] = 9;
 | 
						|
		      result[4] = 23;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		DBG (1, "don't know how to fix up line-distance for %d dpi\n",
 | 
						|
		     peak_res);
 | 
						|
	    }
 | 
						|
	  else if (!(s->hw->flags & MUSTEK_FLAG_LD_NONE))
 | 
						|
	    {
 | 
						|
	      if (peak_res == 600)
 | 
						|
		{
 | 
						|
		  if (res < 51)
 | 
						|
		    {
 | 
						|
		      /* 1-50 dpi: */
 | 
						|
		      result[0] = 4;
 | 
						|
		      result[1] = 0;
 | 
						|
		      result[2] = 0;
 | 
						|
		      result[3] = 3;
 | 
						|
		      result[4] = 5;
 | 
						|
		    }
 | 
						|
		  else if (res <= 300)
 | 
						|
		    {
 | 
						|
		      /* 51-300 dpi: */
 | 
						|
		      result[0] = 2;
 | 
						|
		      result[1] = 0;
 | 
						|
		      result[2] = 0;
 | 
						|
		      result[3] = 5;
 | 
						|
		      result[4] = 9;
 | 
						|
		    }
 | 
						|
		  else
 | 
						|
		    {
 | 
						|
		      /*301-600 dpi: */
 | 
						|
		      result[0] = 1;
 | 
						|
		      result[1] = 0;
 | 
						|
		      result[2] = 0;
 | 
						|
		      result[3] = 9;
 | 
						|
		      result[4] = 17;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
	      else if (peak_res == 800)
 | 
						|
		{
 | 
						|
		  if (res < 72)
 | 
						|
		    {
 | 
						|
		      /* 1-71 dpi: */
 | 
						|
		      result[0] = 4;
 | 
						|
		      result[1] = 0;
 | 
						|
		      result[2] = 0;
 | 
						|
		      result[3] = 3;
 | 
						|
		      result[4] = 5;
 | 
						|
		    }
 | 
						|
		  else if (res <= 400)
 | 
						|
		    {
 | 
						|
		      /* 72-400 dpi: */
 | 
						|
		      result[0] = 2;
 | 
						|
		      result[1] = 0;
 | 
						|
		      result[2] = 0;
 | 
						|
		      result[3] = 9;
 | 
						|
		      result[4] = 17;
 | 
						|
		    }
 | 
						|
		  else
 | 
						|
		    {
 | 
						|
		      /*401-800 dpi: */
 | 
						|
		      result[0] = 1;
 | 
						|
		      result[1] = 0;
 | 
						|
		      result[2] = 0;
 | 
						|
		      result[3] = 16;
 | 
						|
		      result[4] = 32;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      DBG (4, "line_distance: fixed up to factor=%d, (r/g/b)=(%d/%d/%d)\n",
 | 
						|
	   result[0] | (result[1] << 8), result[2], result[3], result[4]);
 | 
						|
    }
 | 
						|
 | 
						|
  factor = result[0] | (result[1] << 8);
 | 
						|
  if (factor != 0xffff)
 | 
						|
    {
 | 
						|
      /* need to do line-distance adjustment ourselves... */
 | 
						|
 | 
						|
      s->ld.max_value = peak_res;
 | 
						|
 | 
						|
      if (factor == 0)
 | 
						|
	{
 | 
						|
	  if (res <= peak_res / 2)
 | 
						|
	    res *= 2;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	res *= factor;
 | 
						|
      s->ld.peak_res = res;
 | 
						|
      for (color = 0; color < 3; ++color)
 | 
						|
	{
 | 
						|
	  s->ld.dist[color] = result[2 + color];
 | 
						|
	  s->ld.quant[color] = s->ld.max_value;
 | 
						|
	  s->ld.index[color] = -s->ld.dist[color];
 | 
						|
	}
 | 
						|
      s->ld.lmod3 = -1;
 | 
						|
 | 
						|
      DBG (4, "line_distance: max_value = %d, peak_res = %d, ld.quant = "
 | 
						|
	   "(%d, %d, %d)\n", s->ld.max_value, s->ld.peak_res, s->ld.quant[0],
 | 
						|
	   s->ld.quant[1], s->ld.quant[2]);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    s->ld.max_value = 0;
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
/* Paragon + Pro series */
 | 
						|
static SANE_Status
 | 
						|
get_image_status (Mustek_Scanner * s, SANE_Int * bpl, SANE_Int * lines)
 | 
						|
{
 | 
						|
  SANE_Byte result[6];
 | 
						|
  SANE_Status status;
 | 
						|
  size_t len;
 | 
						|
  SANE_Int busy, offset;
 | 
						|
  long res, half_res;
 | 
						|
 | 
						|
  memset (result, 0, 6);
 | 
						|
 | 
						|
  /* The 600 II N v1.01 and Paragon 12000SP need a larger scan-area for
 | 
						|
     line-distance correction in color mode */
 | 
						|
  offset = 0;
 | 
						|
  if ((s->hw->flags & MUSTEK_FLAG_LD_N1) && (s->mode & MUSTEK_MODE_COLOR))
 | 
						|
    offset = s->ld.dist[1];
 | 
						|
  else if ((s->hw->flags & MUSTEK_FLAG_LD_BLOCK)
 | 
						|
	   && (s->hw->flags & MUSTEK_FLAG_PARAGON_1)
 | 
						|
	   && (s->mode & MUSTEK_MODE_COLOR))
 | 
						|
    offset = MAX_LINE_DIST * SANE_UNFIX (s->val[OPT_RESOLUTION].w)
 | 
						|
      / SANE_UNFIX (s->hw->dpi_range.max);
 | 
						|
 | 
						|
  do
 | 
						|
    {
 | 
						|
      len = sizeof (result);
 | 
						|
      status = dev_cmd (s, scsi_get_image_status,
 | 
						|
			sizeof (scsi_get_image_status), result, &len);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	return status;
 | 
						|
 | 
						|
      busy = result[0];
 | 
						|
      if (busy)
 | 
						|
	usleep (100000);
 | 
						|
 | 
						|
      if (!s->scanning)		/* ? */
 | 
						|
	if (!(s->hw->flags & MUSTEK_FLAG_PRO))
 | 
						|
	  return do_stop (s);
 | 
						|
    }
 | 
						|
  while (busy);
 | 
						|
 | 
						|
  s->hw->bpl = result[1] | (result[2] << 8);
 | 
						|
  s->hw->lines = result[3] | (result[4] << 8) | (result[5] << 16);
 | 
						|
 | 
						|
  res = SANE_UNFIX (s->val[OPT_RESOLUTION].w);
 | 
						|
  half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2;
 | 
						|
  /* Need to interpolate resolutions > max x-resolution? */
 | 
						|
  if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res))
 | 
						|
    {
 | 
						|
      *bpl = (s->hw->bpl * res) / half_res / 3;
 | 
						|
      *bpl *= 3;
 | 
						|
      DBG (4, "get_image_status: resolution > x-max; enlarge %d bpl to "
 | 
						|
	   "%d bpl\n", s->hw->bpl, *bpl);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    *bpl = s->hw->bpl;
 | 
						|
 | 
						|
  *lines = s->hw->lines - offset;
 | 
						|
 | 
						|
  DBG (3, "get_image_status: bytes_per_line=%d, lines=%d (offset = %d)\n",
 | 
						|
       *bpl, *lines, offset);
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
/* ScanExpress models */
 | 
						|
static SANE_Status
 | 
						|
get_window (Mustek_Scanner * s, SANE_Int * bpl, SANE_Int * lines,
 | 
						|
	    SANE_Int * pixels)
 | 
						|
{
 | 
						|
  SANE_Byte result[48];
 | 
						|
  SANE_Status status;
 | 
						|
  size_t len;
 | 
						|
  SANE_Int color;
 | 
						|
  long res, half_res;
 | 
						|
 | 
						|
  res = s->resolution_code;
 | 
						|
  half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2;
 | 
						|
 | 
						|
  DBG (5, "get_window: resolution: %ld dpi (hardware: %d dpi)\n",
 | 
						|
       res, s->ld.peak_res);
 | 
						|
 | 
						|
  len = sizeof (result);
 | 
						|
  status = dev_cmd (s, scsi_get_window, sizeof (scsi_get_window), result,
 | 
						|
		    &len);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    return status;
 | 
						|
 | 
						|
  if (!s->scanning)
 | 
						|
    return do_stop (s);
 | 
						|
 | 
						|
  s->hw->cal.bytes = (result[6] << 24) | (result[7] << 16) |
 | 
						|
    (result[8] << 8) | (result[9] << 0);
 | 
						|
  s->hw->cal.lines = (result[10] << 24) | (result[11] << 16) |
 | 
						|
    (result[12] << 8) | (result[13] << 0);
 | 
						|
 | 
						|
  DBG (4, "get_window: calibration bpl=%d, lines=%d\n",
 | 
						|
       s->hw->cal.bytes, s->hw->cal.lines);
 | 
						|
 | 
						|
  s->hw->bpl = (result[14] << 24) | (result[15] << 16) |
 | 
						|
    (result[16] << 8) | result[17];
 | 
						|
 | 
						|
  s->hw->lines = (result[18] << 24) | (result[19] << 16) |
 | 
						|
    (result[20] << 8) | result[21];
 | 
						|
 | 
						|
  DBG (4, "get_window: scan bpl=%d, lines=%d\n", s->hw->bpl, s->hw->lines);
 | 
						|
 | 
						|
  if ((s->hw->cal.bytes == 0) || (s->hw->cal.lines == 0)
 | 
						|
      || (s->hw->bpl == 0) || (s->hw->lines == 0))
 | 
						|
    {
 | 
						|
      DBG (1, "get_window: oops, none of these values should be 0 "
 | 
						|
	   "-- exiting\n");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  s->hw->gamma_length = 1 << result[26];
 | 
						|
  DBG (4, "get_window: gamma length=%d\n", s->hw->gamma_length);
 | 
						|
 | 
						|
  if (s->mode & MUSTEK_MODE_COLOR)
 | 
						|
    {
 | 
						|
      s->ld.buf[0] = NULL;
 | 
						|
      for (color = 0; color < 3; ++color)
 | 
						|
	{
 | 
						|
	  s->ld.dist[color] = result[42 + color];
 | 
						|
	}
 | 
						|
 | 
						|
      DBG (4, "get_window: LD res=%d, (r/g/b)=(%d/%d/%d)\n",
 | 
						|
	   (result[40] << 8) | result[41], s->ld.dist[0],
 | 
						|
	   s->ld.dist[1], s->ld.dist[2]);
 | 
						|
      s->ld.max_value = (result[40] << 8) | result[41];
 | 
						|
      if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res))
 | 
						|
	{
 | 
						|
	  /* We must interpolate resolutions > max x-resolution */
 | 
						|
	  *bpl = *pixels = (((s->hw->bpl / 3) * res) / half_res) * 3;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  /* Scale down the image according to desired resolution */
 | 
						|
	  *bpl = *pixels = (((s->hw->bpl / 3) * res) / s->ld.peak_res) * 3;
 | 
						|
	}
 | 
						|
      *lines = (s->hw->lines - s->ld.dist[2]) * res / s->ld.peak_res;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res))
 | 
						|
	{
 | 
						|
	  /* We must interpolate resolutions > max x-resolution */
 | 
						|
	  *bpl = s->hw->bpl * res / half_res;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  *bpl = s->hw->bpl;
 | 
						|
	}
 | 
						|
      *lines = s->hw->lines;
 | 
						|
    }
 | 
						|
  DBG (4, "get_window: bpl = %d (hardware: %d), lines = %d (hardware: %d)\n",
 | 
						|
       *bpl, s->hw->bpl, *lines, s->hw->lines);
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
adf_and_backtrack (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Byte backtrack[6];
 | 
						|
  SANE_Int code = 0x80;
 | 
						|
 | 
						|
  if (!(s->hw->flags & MUSTEK_FLAG_NO_BACKTRACK))
 | 
						|
    code |= 0x02;		/* enable backtracking */
 | 
						|
 | 
						|
  if (strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0)
 | 
						|
    code |= 0x01;
 | 
						|
  else if (strcmp (s->val[OPT_SOURCE].s, "Transparency Adapter") == 0)
 | 
						|
    code |= 0x04;
 | 
						|
  memset (backtrack, 0, sizeof (backtrack));
 | 
						|
  backtrack[0] = MUSTEK_SCSI_ADF_AND_BACKTRACK;
 | 
						|
  backtrack[4] = code;
 | 
						|
 | 
						|
  DBG (4, "adf_and_backtrack: backtrack: %s; ADF: %s; TA: %s\n",
 | 
						|
       code & 0x02 ? "yes" : "no", code & 0x01 ? "yes" : "no",
 | 
						|
       code & 0x04 ? "yes" : "no");
 | 
						|
  return dev_cmd (s, backtrack, sizeof (backtrack), 0, 0);
 | 
						|
}
 | 
						|
 | 
						|
/* 600 II N firmware 2.x */
 | 
						|
static SANE_Int
 | 
						|
fix_line_distance_n_2 (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl,
 | 
						|
		       SANE_Byte * raw, SANE_Byte * out)
 | 
						|
{
 | 
						|
  SANE_Byte *out_end, *out_ptr, *raw_end = raw + num_lines * bpl;
 | 
						|
  SANE_Int c, num_saved_lines, line;
 | 
						|
 | 
						|
  if (!s->ld.buf[0])
 | 
						|
    {
 | 
						|
      /* This buffer must be big enough to hold maximum line distance
 | 
						|
         times max_bpl bytes.  The maximum line distance for the
 | 
						|
         Paragon 600 II N scanner is 23, so 40 should be safe.  */
 | 
						|
      DBG (5,
 | 
						|
	   "fix_line_distance_n_2: allocating temp buffer of %d*%d bytes\n",
 | 
						|
	   MAX_LINE_DIST, bpl);
 | 
						|
      s->ld.buf[0] = malloc (MAX_LINE_DIST * (long) bpl);
 | 
						|
      if (!s->ld.buf[0])
 | 
						|
	{
 | 
						|
	  DBG (1,
 | 
						|
	       "fix_line_distance_n_2: failed to malloc temporary buffer\n");
 | 
						|
	  return 0;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  num_saved_lines = s->ld.index[0] - s->ld.index[2];
 | 
						|
  if (num_saved_lines > 0)
 | 
						|
    /* restore the previously saved lines: */
 | 
						|
    memcpy (out, s->ld.buf[0], num_saved_lines * bpl);
 | 
						|
 | 
						|
  while (1)
 | 
						|
    {
 | 
						|
      if (++s->ld.lmod3 >= 3)
 | 
						|
	s->ld.lmod3 = 0;
 | 
						|
 | 
						|
      c = color_seq[s->ld.lmod3];
 | 
						|
      if (s->ld.index[c] < 0)
 | 
						|
	++s->ld.index[c];
 | 
						|
      else if (s->ld.index[c] < s->params.lines)
 | 
						|
	{
 | 
						|
	  s->ld.quant[c] += s->ld.peak_res;
 | 
						|
	  if (s->ld.quant[c] > s->ld.max_value)
 | 
						|
	    {
 | 
						|
	      s->ld.quant[c] -= s->ld.max_value;
 | 
						|
	      line = s->ld.index[c]++ - s->ld.ld_line;
 | 
						|
	      out_ptr = out + line * bpl + c;
 | 
						|
	      out_end = out_ptr + bpl;
 | 
						|
	      while (out_ptr != out_end)
 | 
						|
		{
 | 
						|
		  *out_ptr = *raw++;
 | 
						|
		  out_ptr += 3;
 | 
						|
		}
 | 
						|
 | 
						|
	      if (raw >= raw_end)
 | 
						|
		{
 | 
						|
		  DBG (3, "fix_line_distance_n_2: lmod3=%d, "
 | 
						|
		       "index=(%d,%d,%d)\n", s->ld.lmod3,
 | 
						|
		       s->ld.index[0], s->ld.index[1], s->ld.index[2]);
 | 
						|
		  num_lines = s->ld.index[2] - s->ld.ld_line;
 | 
						|
 | 
						|
		  /* copy away the lines with at least one missing
 | 
						|
		     color component, so that we can interleave them
 | 
						|
		     with new scan data on the next call */
 | 
						|
		  num_saved_lines = s->ld.index[0] - s->ld.index[2];
 | 
						|
		  memcpy (s->ld.buf[0], out + num_lines * bpl,
 | 
						|
			  num_saved_lines * bpl);
 | 
						|
 | 
						|
		  /* notice the number of lines we processed */
 | 
						|
		  s->ld.ld_line = s->ld.index[2];
 | 
						|
		  /* return number of complete (r+g+b) lines */
 | 
						|
		  return num_lines;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* 600 II N firmware 1.x */
 | 
						|
static SANE_Int
 | 
						|
fix_line_distance_n_1 (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl,
 | 
						|
		       SANE_Byte * raw, SANE_Byte * out)
 | 
						|
{
 | 
						|
  SANE_Byte *out_end, *out_ptr, *raw_end = raw + num_lines * bpl;
 | 
						|
  SANE_Int c, num_saved_lines, line;
 | 
						|
 | 
						|
  /* For firmware 1.x the scanarea must be soemwhat bigger than needed
 | 
						|
     because of the linedistance correction */
 | 
						|
 | 
						|
  if (!s->ld.buf[0])
 | 
						|
    {
 | 
						|
      /* This buffer must be big enough to hold maximum line distance
 | 
						|
         times max_bpl bytes.  The maximum line distance for the 600 II N
 | 
						|
         is 23, so 40 is safe. */
 | 
						|
      DBG (5,
 | 
						|
	   "fix_line_distance_n_1: allocating temp buffer of %d*%d bytes\n",
 | 
						|
	   MAX_LINE_DIST, bpl);
 | 
						|
      s->ld.buf[0] = malloc (MAX_LINE_DIST * (long) bpl);
 | 
						|
      if (!s->ld.buf[0])
 | 
						|
	{
 | 
						|
	  DBG (1,
 | 
						|
	       "fix_line_distance_n_1: failed to malloc temporary buffer\n");
 | 
						|
	  return 0;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  num_saved_lines = s->ld.index[0] - s->ld.index[1];
 | 
						|
  DBG (5, "fix_line_distance_n_1: got %d lines, %d bpl\n", num_lines, bpl);
 | 
						|
  DBG (5, "fix_line_distance_n_1: num_saved_lines = %d; peak_res = %d; "
 | 
						|
       "max_value = %d\n", num_saved_lines, s->ld.peak_res, s->ld.max_value);
 | 
						|
  if (num_saved_lines > 0)
 | 
						|
    /* restore the previously saved lines: */
 | 
						|
    memcpy (out, s->ld.buf[0], num_saved_lines * bpl);
 | 
						|
 | 
						|
  while (1)
 | 
						|
    {
 | 
						|
      if (++s->ld.lmod3 >= 3)
 | 
						|
	s->ld.lmod3 = 0;
 | 
						|
      c = s->ld.lmod3;
 | 
						|
      if (s->ld.index[c] < 0)
 | 
						|
	++s->ld.index[c];
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  s->ld.quant[c] += s->ld.peak_res;
 | 
						|
	  if (s->ld.quant[c] > s->ld.max_value)
 | 
						|
	    {
 | 
						|
	      s->ld.quant[c] -= s->ld.max_value;
 | 
						|
	      line = s->ld.index[c]++ - s->ld.ld_line;
 | 
						|
	      out_ptr = out + line * bpl + c;
 | 
						|
	      out_end = out_ptr + bpl;
 | 
						|
	      while (out_ptr != out_end)
 | 
						|
		{
 | 
						|
		  *out_ptr = *raw++;
 | 
						|
		  out_ptr += 3;
 | 
						|
		}
 | 
						|
	      DBG (5, "fix_line_distance_n_1: copied line %d (color %d)\n",
 | 
						|
		   line, c);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      if ((raw >= raw_end) || ((s->ld.index[0] >= s->params.lines) &&
 | 
						|
			       (s->ld.index[1] >= s->params.lines) &&
 | 
						|
			       (s->ld.index[2] >= s->params.lines)))
 | 
						|
	{
 | 
						|
	  DBG (3, "fix_line_distance_n_1: lmod3=%d, index=(%d,%d,%d)%s\n",
 | 
						|
	       s->ld.lmod3,
 | 
						|
	       s->ld.index[0], s->ld.index[1], s->ld.index[2],
 | 
						|
	       raw >= raw_end ? " raw >= raw_end" : "");
 | 
						|
	  num_lines = s->ld.index[1] - s->ld.ld_line;
 | 
						|
	  if (num_lines < 0)
 | 
						|
	    num_lines = 0;
 | 
						|
	  DBG (4, "fix_line_distance_n_1: lines ready: %d\n", num_lines);
 | 
						|
 | 
						|
	  /* copy away the lines with at least one missing
 | 
						|
	     color component, so that we can interleave them
 | 
						|
	     with new scan data on the next call */
 | 
						|
	  num_saved_lines = s->ld.index[0] - s->ld.index[1];
 | 
						|
	  DBG (4, "fix_line_distance_n_1: copied %d lines to "
 | 
						|
	       "ld.buf\n", num_saved_lines);
 | 
						|
	  memcpy (s->ld.buf[0], out + num_lines * bpl, num_saved_lines * bpl);
 | 
						|
	  /* notice the number of lines we processed */
 | 
						|
	  s->ld.ld_line = s->ld.index[1];
 | 
						|
	  if (s->ld.ld_line < 0)
 | 
						|
	    s->ld.ld_line = 0;
 | 
						|
	  /* return number of complete (r+g+b) lines */
 | 
						|
	  return num_lines;
 | 
						|
	}
 | 
						|
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* For ScanExpress models */
 | 
						|
static SANE_Int
 | 
						|
fix_line_distance_se (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl,
 | 
						|
		      SANE_Byte * raw, SANE_Byte * out)
 | 
						|
{
 | 
						|
  SANE_Byte *raw_end = raw + num_lines * bpl;
 | 
						|
  SANE_Byte *out_ptr[3], *ptr;
 | 
						|
  SANE_Int index[3], lines[3], quant[3], dist[3];
 | 
						|
  SANE_Int max_value;
 | 
						|
  SANE_Int color, pixel, res, half_res, scale;
 | 
						|
  SANE_Int bpc = bpl / 3;	/* bytes per color (per line) */
 | 
						|
  SANE_Bool preview = SANE_FALSE;
 | 
						|
 | 
						|
  res = s->resolution_code;
 | 
						|
  half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2;
 | 
						|
  max_value = s->ld.max_value;
 | 
						|
  if ((s->val[OPT_PREVIEW].w == SANE_TRUE)
 | 
						|
      && (s->val[OPT_FAST_PREVIEW].w == SANE_TRUE))
 | 
						|
    {
 | 
						|
      preview = SANE_TRUE;
 | 
						|
      /*max_value = 75; */
 | 
						|
      dist[0] = s->ld.dist[0];
 | 
						|
      dist[1] = s->ld.dist[1];
 | 
						|
      dist[2] = s->ld.dist[2];
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      dist[0] = s->ld.dist[0];
 | 
						|
      dist[1] = s->ld.dist[1];
 | 
						|
      dist[2] = s->ld.dist[2];
 | 
						|
    }
 | 
						|
 | 
						|
  if (!s->ld.buf[0])
 | 
						|
    {
 | 
						|
      /* This buffer must be big enough to hold maximum line distance times
 | 
						|
         3*bpl bytes.  The maximum line distance for 1200 dpi is 32 */
 | 
						|
      DBG (5, "fix_line_distance_se: allocating temp buffer of %d*%d bytes\n",
 | 
						|
	   3 * MAX_LINE_DIST, bpc);
 | 
						|
      s->ld.buf[0] = malloc (3 * MAX_LINE_DIST * (long) bpc);
 | 
						|
 | 
						|
      if (!s->ld.buf[0])
 | 
						|
	{
 | 
						|
	  DBG (1,
 | 
						|
	       "fix_line_distance_se: failed to malloc temporary buffer\n");
 | 
						|
	  return 0;
 | 
						|
	}
 | 
						|
 | 
						|
      /* Note that either s->ld.buf[1] or  s->ld.buf[2] is never used. */
 | 
						|
      s->ld.buf[1] = s->ld.buf[2] =
 | 
						|
	s->ld.buf[0] + 2 * MAX_LINE_DIST * (long) bpc;
 | 
						|
 | 
						|
      /* Since the blocks don't start necessarily with red note color. */
 | 
						|
      s->ld.color = 0;
 | 
						|
 | 
						|
      /* The scan area must be longer than desired because of the line
 | 
						|
         distance. So me must count complete (r+g+b) lines already
 | 
						|
         submitted to the fronted. */
 | 
						|
      s->ld.ld_line = s->params.lines;
 | 
						|
 | 
						|
      for (color = 0; color < 3; ++color)
 | 
						|
	{
 | 
						|
	  s->ld.index[color] = -dist[color];
 | 
						|
	  s->ld.quant[color] = 0;
 | 
						|
	  s->ld.saved[color] = 0;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  num_lines *= 3;
 | 
						|
  DBG (5, "fix_line_distance_se: start color: %d; %d lines \n",
 | 
						|
       s->ld.color, num_lines);
 | 
						|
 | 
						|
  /* First scan the lines read and count red, green and blue ones.
 | 
						|
     Since we will step through the lines a second time we must not
 | 
						|
     alter any global variables here! */
 | 
						|
  for (color = 0; color < 3; ++color)
 | 
						|
    {
 | 
						|
      index[color] = s->ld.index[color];
 | 
						|
      lines[color] = s->ld.saved[color];
 | 
						|
      quant[color] = s->ld.quant[color];
 | 
						|
    }
 | 
						|
 | 
						|
  color = s->ld.color;
 | 
						|
  while (num_lines > 0)
 | 
						|
    {
 | 
						|
      if (index[color] < 0)
 | 
						|
	++index[color];
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  quant[color] += res;
 | 
						|
	  if (quant[color] >= max_value)
 | 
						|
	    {
 | 
						|
	      /* This line must be processed, not dropped. */
 | 
						|
	      quant[color] -= max_value;
 | 
						|
	      ++lines[color];
 | 
						|
	      --num_lines;
 | 
						|
	    }
 | 
						|
	  else if (!preview)
 | 
						|
	    --num_lines;
 | 
						|
 | 
						|
	}
 | 
						|
      if (++color > 2)
 | 
						|
	color = 0;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Calculate how many triples of color lines we can output now.
 | 
						|
     Because the number of available red lines is always greater
 | 
						|
     than for the other colors we may ignore the red ones here. */
 | 
						|
  num_lines = MIN (lines[1], lines[2]);
 | 
						|
 | 
						|
  DBG (5, "fix_line_distance_se: saved lines: %d/%d/%d\n", s->ld.saved[0],
 | 
						|
       s->ld.saved[1], s->ld.saved[2]);
 | 
						|
  DBG (5, "fix_line_distance_se: available:  %d/%d/%d --> triples: %d\n",
 | 
						|
       lines[0], lines[1], lines[2], num_lines);
 | 
						|
 | 
						|
  lines[0] = lines[1] = lines[2] = num_lines;
 | 
						|
 | 
						|
  /* Output the color lines saved in previous call first.
 | 
						|
     Note that data is converted in r/g/b interleave on the fly. */
 | 
						|
  for (color = 0; color < 3; ++color)
 | 
						|
    {
 | 
						|
      out_ptr[color] = out + color;
 | 
						|
      ptr = s->ld.buf[color];
 | 
						|
      while ((s->ld.saved[color] > 0) && (lines[color] > 0))
 | 
						|
	{
 | 
						|
	  scale = 0;
 | 
						|
	  if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res))
 | 
						|
	    {
 | 
						|
	      /* Need to enlarge x-resolution */
 | 
						|
	      SANE_Byte *ptr_start = ptr;
 | 
						|
	      for (pixel = 0; pixel < s->params.pixels_per_line; ++pixel)
 | 
						|
		{
 | 
						|
		  *out_ptr[color] = *ptr;
 | 
						|
		  out_ptr[color] += 3;
 | 
						|
		  scale += half_res;
 | 
						|
		  if (scale >= half_res)
 | 
						|
		    {
 | 
						|
		      scale -= res;
 | 
						|
		      ++ptr;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
	      DBG (5, "fix_line_distance_se: got saved line: %d; line: %d; "
 | 
						|
		   "color: %d; raw bytes: %lu; out bytes: %d\n",
 | 
						|
		   s->ld.saved[color], lines[color], color, (u_long) (ptr - ptr_start),
 | 
						|
		   s->params.pixels_per_line);
 | 
						|
	      ptr = ptr_start + bpc;
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      if (preview)
 | 
						|
		{
 | 
						|
		  for (pixel = 0; pixel < bpc; ++pixel)
 | 
						|
		    {
 | 
						|
		      *out_ptr[color] = *ptr++;
 | 
						|
		      out_ptr[color] += 3;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  for (pixel = 0; pixel < bpc; ++pixel)
 | 
						|
		    {
 | 
						|
		      scale += res;
 | 
						|
		      if (scale >= max_value)
 | 
						|
			{
 | 
						|
			  scale -= max_value;
 | 
						|
			  *out_ptr[color] = *ptr;
 | 
						|
			  out_ptr[color] += 3;
 | 
						|
			}
 | 
						|
		      ++ptr;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
	      DBG (5, "fix_line_distance_se: got saved line: %d; line: %d; "
 | 
						|
		   "color: %d\n", s->ld.saved[color], lines[color], color);
 | 
						|
	    }
 | 
						|
	  --(s->ld.saved[color]);
 | 
						|
	  --lines[color];
 | 
						|
	}
 | 
						|
      if (s->ld.saved[color] > 0)
 | 
						|
	memmove (s->ld.buf[color], ptr, s->ld.saved[color] * bpc);
 | 
						|
    }
 | 
						|
 | 
						|
  while (1)
 | 
						|
    {
 | 
						|
      if (s->ld.index[s->ld.color] < 0)
 | 
						|
	++(s->ld.index[s->ld.color]);
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  s->ld.quant[s->ld.color] += res;
 | 
						|
	  if (s->ld.quant[s->ld.color] >= max_value)
 | 
						|
	    {
 | 
						|
	      /* This line must be processed, not dropped. */
 | 
						|
	      s->ld.quant[s->ld.color] -= max_value;
 | 
						|
 | 
						|
	      if (lines[s->ld.color] > 0)
 | 
						|
		{
 | 
						|
		  /* There's still a line to be output for current color.
 | 
						|
		     Then shuffle current color line to output buffer. */
 | 
						|
		  scale = 0;
 | 
						|
		  /* need to enlarge x-resolution? */
 | 
						|
		  if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X)
 | 
						|
		      && (res > half_res))
 | 
						|
		    {
 | 
						|
		      SANE_Byte *raw_start = raw;
 | 
						|
		      for (pixel = 0; pixel < s->params.pixels_per_line;
 | 
						|
			   ++pixel)
 | 
						|
			{
 | 
						|
			  *out_ptr[s->ld.color] = *raw;
 | 
						|
			  out_ptr[s->ld.color] += 3;
 | 
						|
			  scale += half_res;
 | 
						|
			  if (scale >= half_res)
 | 
						|
			    {
 | 
						|
			      scale -= res;
 | 
						|
			      ++raw;
 | 
						|
			    }
 | 
						|
 | 
						|
			}
 | 
						|
		      DBG (5,
 | 
						|
			   "fix_line_distance_se: got line: %d; color: %d; "
 | 
						|
			   "raw bytes: %lu; out bytes: %d\n",
 | 
						|
			   lines[s->ld.color], s->ld.color, (u_long) (raw - raw_start),
 | 
						|
			   s->params.pixels_per_line);
 | 
						|
		      raw = raw_start + bpc;
 | 
						|
		    }
 | 
						|
		  else
 | 
						|
		    {
 | 
						|
		      if (preview)
 | 
						|
			{
 | 
						|
			  for (pixel = 0; pixel < bpc; ++pixel)
 | 
						|
			    {
 | 
						|
			      *out_ptr[s->ld.color] = *raw++;
 | 
						|
			      out_ptr[s->ld.color] += 3;
 | 
						|
			    }
 | 
						|
			}
 | 
						|
		      else
 | 
						|
			{
 | 
						|
			  for (pixel = 0; pixel < bpc; ++pixel)
 | 
						|
			    {
 | 
						|
			      scale += res;
 | 
						|
			      if (scale >= max_value)
 | 
						|
				{
 | 
						|
				  scale -= max_value;
 | 
						|
				  *out_ptr[s->ld.color] = *raw;
 | 
						|
				  out_ptr[s->ld.color] += 3;
 | 
						|
				}
 | 
						|
			      ++raw;
 | 
						|
			    }
 | 
						|
			}
 | 
						|
 | 
						|
		      DBG (5, "fix_line_distance_se: got line: %d; color: "
 | 
						|
			   "%d\n", lines[s->ld.color], s->ld.color);
 | 
						|
		    }
 | 
						|
		  --lines[s->ld.color];
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  /* At least one component missing, so save this line. */
 | 
						|
		  memcpy (s->ld.buf[s->ld.color] + s->ld.saved[s->ld.color]
 | 
						|
			  * bpc, raw, bpc);
 | 
						|
		  DBG (5, "fix_line_distance_se: saved line %d; color %d\n",
 | 
						|
		       s->ld.saved[s->ld.color], s->ld.color);
 | 
						|
		  ++(s->ld.saved[s->ld.color]);
 | 
						|
		  raw += bpc;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      if (!preview)
 | 
						|
		raw += bpc;
 | 
						|
	      DBG (5, "fix_line_distance_se: ignored line; color: %d\n",
 | 
						|
		   s->ld.color);
 | 
						|
	    }
 | 
						|
 | 
						|
	  if (raw >= raw_end)
 | 
						|
	    {
 | 
						|
	      /* Reduce num_lines if we encounter excess lines. */
 | 
						|
	      if (num_lines > s->ld.ld_line)
 | 
						|
		num_lines = s->ld.ld_line;
 | 
						|
	      s->ld.ld_line -= num_lines;
 | 
						|
 | 
						|
	      if (++s->ld.color > 2)
 | 
						|
		s->ld.color = 0;
 | 
						|
	      return num_lines;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      if (++s->ld.color > 2)
 | 
						|
	s->ld.color = 0;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* For Pro models. Not really a linedistance correction (they don't need one)
 | 
						|
   only enlarging x-res here */
 | 
						|
static void
 | 
						|
fix_line_distance_pro (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl,
 | 
						|
		       SANE_Byte * raw, SANE_Byte * out)
 | 
						|
{
 | 
						|
  SANE_Byte *out_addr, *in_addr;
 | 
						|
  SANE_Int res, half_res, y, x_out, x_in;
 | 
						|
 | 
						|
 | 
						|
  res = SANE_UNFIX (s->val[OPT_RESOLUTION].w);
 | 
						|
  half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2;
 | 
						|
 | 
						|
  DBG (5, "fix_line_distance_pro: res=%d; halfres=%d; num_lines=%d; bpl=%d\n",
 | 
						|
       res, half_res, num_lines, bpl);
 | 
						|
 | 
						|
  if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0)
 | 
						|
    {
 | 
						|
      if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res))
 | 
						|
	{
 | 
						|
	  /*12 bit, need to enlarge x-resolution */
 | 
						|
	  DBG (5, "fix_line_distance_pro: res > half_res --> need to "
 | 
						|
	       "enlarge x\n");
 | 
						|
	  if (little_endian ())
 | 
						|
	    for (y = 0; y < num_lines; y++)
 | 
						|
	      {
 | 
						|
		for (x_out = 0; x_out < s->params.pixels_per_line; x_out++)
 | 
						|
		  {
 | 
						|
		    x_in = x_out * bpl / s->params.bytes_per_line / 2;
 | 
						|
		    x_in *= 2;
 | 
						|
		    out_addr = out + y * s->params.bytes_per_line + x_out * 6;
 | 
						|
		    in_addr = raw + y * bpl + x_in * 6;
 | 
						|
		    *(out_addr) = *(in_addr) << 4;
 | 
						|
		    *(out_addr + 1) = (*(in_addr) >> 4) +
 | 
						|
		      (*(in_addr + 1) << 4);
 | 
						|
		    *(out_addr + 2) = *(in_addr + 2) << 4;
 | 
						|
		    *(out_addr + 3) = (*(in_addr + 2) >> 4) +
 | 
						|
		      (*(in_addr + 3) << 4);
 | 
						|
		    *(out_addr + 4) = *(in_addr + 4) << 4;
 | 
						|
		    *(out_addr + 5) = (*(in_addr + 4) >> 4) +
 | 
						|
		      (*(in_addr + 5) << 4);
 | 
						|
		  }
 | 
						|
	      }
 | 
						|
	  else			/* big endian */
 | 
						|
	    for (y = 0; y < num_lines; y++)
 | 
						|
	      {
 | 
						|
		for (x_out = 0; x_out < s->params.pixels_per_line; x_out++)
 | 
						|
		  {
 | 
						|
		    x_in = x_out * bpl / s->params.bytes_per_line / 2;
 | 
						|
		    out_addr = out + y * s->params.bytes_per_line + x_out * 6;
 | 
						|
		    in_addr = raw + y * bpl + x_in * 6;
 | 
						|
		    *(out_addr) = (*(in_addr) >> 4) + (*(in_addr + 1) << 4);
 | 
						|
		    *(out_addr + 1) = *(in_addr) << 4;
 | 
						|
		    *(out_addr + 2) = (*(in_addr + 2) >> 4) +
 | 
						|
		      (*(in_addr + 3) << 4);
 | 
						|
		    *(out_addr + 3) = *(in_addr + 2) << 4;
 | 
						|
		    *(out_addr + 4) = (*(in_addr + 4) >> 4) +
 | 
						|
		      (*(in_addr + 5) << 4);
 | 
						|
		    *(out_addr + 5) = *(in_addr + 4) << 4;
 | 
						|
		  }
 | 
						|
	      }
 | 
						|
	}
 | 
						|
      else			/* 12 bit, no need to enlarge x */
 | 
						|
	{
 | 
						|
	  SANE_Word pixel;
 | 
						|
 | 
						|
	  if (little_endian ())
 | 
						|
	    for (pixel = 0; pixel < (num_lines * bpl / 2); pixel++)
 | 
						|
	      {
 | 
						|
		*(out + pixel * 2) = *(raw + pixel * 2) << 4;
 | 
						|
		*(out + pixel * 2 + 1) = (*(raw + pixel * 2) >> 4) +
 | 
						|
		  (*(raw + pixel * 2 + 1) << 4);
 | 
						|
	      }
 | 
						|
	  else			/* big endian */
 | 
						|
	    for (pixel = 0; pixel < (num_lines * bpl / 2); pixel++)
 | 
						|
	      {
 | 
						|
		*(out + pixel * 2) = (*(raw + pixel * 2) >> 4) +
 | 
						|
		  (*(raw + pixel * 2 + 1) << 4);
 | 
						|
		*(out + pixel * 2 + 1) = *(raw + pixel * 2) << 4;
 | 
						|
	      }
 | 
						|
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else				/* 8 bit */
 | 
						|
    {
 | 
						|
      /* need to enlarge x-resolution? */
 | 
						|
      if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) && (res > half_res))
 | 
						|
	{
 | 
						|
	  DBG (5, "fix_line_distance_pro: res > half_res --> need to "
 | 
						|
	       "enlarge x\n");
 | 
						|
 | 
						|
	  for (y = 0; y < num_lines; y++)
 | 
						|
	    {
 | 
						|
	      for (x_out = 0; x_out < s->params.pixels_per_line; x_out++)
 | 
						|
		{
 | 
						|
		  x_in = x_out * bpl / s->params.bytes_per_line;
 | 
						|
		  out_addr = out + y * s->params.bytes_per_line + x_out * 3;
 | 
						|
		  in_addr = raw + y * bpl + x_in * 3;
 | 
						|
		  *(out_addr) = *(in_addr);
 | 
						|
		  *(out_addr + 1) = *(in_addr + 1);
 | 
						|
		  *(out_addr + 2) = *(in_addr + 2);
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      else
 | 
						|
	memcpy (out, raw, num_lines * bpl);
 | 
						|
    }
 | 
						|
  return;
 | 
						|
}
 | 
						|
 | 
						|
/* For MFS-08000SP, MFS-06000SP. MFC-08000CZ, MFC-06000CZ */
 | 
						|
static void
 | 
						|
fix_line_distance_normal (Mustek_Scanner * s, SANE_Int num_lines,
 | 
						|
			  SANE_Int bpl, SANE_Byte * raw, SANE_Byte * out)
 | 
						|
{
 | 
						|
  SANE_Byte *out_end, *out_ptr, *raw_end = raw + num_lines * bpl;
 | 
						|
  SANE_Int index[3];		/* index of the next output line for color C */
 | 
						|
  SANE_Int i, color;
 | 
						|
 | 
						|
  /* Initialize the indices with the line distances that were returned
 | 
						|
     by the CCD linedistance command or set manually (option
 | 
						|
     linedistance-fix).  We want to skip data for the first OFFSET
 | 
						|
     rounds, so we initialize the indices to the negative of this
 | 
						|
     offset.  */
 | 
						|
 | 
						|
  DBG (5, "fix_line_distance_normal: %d lines, %d bpl\n", num_lines, bpl);
 | 
						|
 | 
						|
  for (color = 0; color < 3; ++color)
 | 
						|
    index[color] = -s->ld.dist[color];
 | 
						|
 | 
						|
  while (1)
 | 
						|
    {
 | 
						|
      for (i = 0; i < 3; ++i)
 | 
						|
	{
 | 
						|
	  color = color_seq[i];
 | 
						|
	  if (index[color] < 0)
 | 
						|
	    ++index[color];
 | 
						|
	  else if (index[color] < num_lines)
 | 
						|
	    {
 | 
						|
	      s->ld.quant[color] += s->ld.peak_res;
 | 
						|
	      if (s->ld.quant[color] > s->ld.max_value)
 | 
						|
		{
 | 
						|
		  s->ld.quant[color] -= s->ld.max_value;
 | 
						|
		  out_ptr = out + index[color] * bpl + color;
 | 
						|
		  out_end = out_ptr + bpl;
 | 
						|
		  while (out_ptr != out_end)
 | 
						|
		    {
 | 
						|
		      *out_ptr = *raw++;
 | 
						|
		      out_ptr += 3;
 | 
						|
		    }
 | 
						|
		  ++index[color];
 | 
						|
		  if (raw >= raw_end)
 | 
						|
		    return;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* Paragon series I + II. */
 | 
						|
static SANE_Int
 | 
						|
fix_line_distance_block (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl,
 | 
						|
			 SANE_Byte * raw, SANE_Byte * out,
 | 
						|
			 SANE_Int num_lines_total)
 | 
						|
{
 | 
						|
  SANE_Byte *out_end, *out_ptr, *raw_end = raw + num_lines * bpl;
 | 
						|
  SANE_Int c, num_saved_lines, line, max_index, min_index;
 | 
						|
 | 
						|
  if (!s->ld.buf[0])
 | 
						|
    {
 | 
						|
      DBG (5, "fix_line_distance_block: allocating temp buffer of %d*%d "
 | 
						|
	   "bytes\n", MAX_LINE_DIST, bpl);
 | 
						|
      s->ld.buf[0] = malloc (MAX_LINE_DIST * (long) bpl);
 | 
						|
      if (!s->ld.buf[0])
 | 
						|
	{
 | 
						|
	  DBG (1, "fix_line_distance_block: failed to malloc temporary "
 | 
						|
	       "buffer\n");
 | 
						|
	  return 0;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  DBG (5, "fix_line_distance_block: s->ld.index = {%d, %d, %d}, "
 | 
						|
       "s->ld.lmod3 = %d\n", s->ld.index[0], s->ld.index[1], s->ld.index[2],
 | 
						|
       s->ld.lmod3);
 | 
						|
  DBG (5, "fix_line_distance_block: s->ld.quant = {%d, %d, %d}, "
 | 
						|
       "s->ld.max_value = %d\n", s->ld.quant[0], s->ld.quant[1],
 | 
						|
       s->ld.quant[2], s->ld.max_value);
 | 
						|
  DBG (5,
 | 
						|
       "fix_line_distance_block: s->ld.peak_res = %d, s->ld.ld_line = %d\n",
 | 
						|
       s->ld.peak_res, s->ld.ld_line);
 | 
						|
 | 
						|
  /* search maximum and minimum index */
 | 
						|
  max_index = MAX (s->ld.index[0], MAX (s->ld.index[1], s->ld.index[2]));
 | 
						|
  min_index = MIN (s->ld.index[0], MIN (s->ld.index[1], s->ld.index[2]));
 | 
						|
  num_saved_lines = max_index - min_index;
 | 
						|
  if (s->ld.index[0] == 0)
 | 
						|
    num_saved_lines = 0;
 | 
						|
  /* restore the previously saved lines: */
 | 
						|
  memcpy (out, s->ld.buf[0], num_saved_lines * bpl);
 | 
						|
  DBG (5, "fix_line_distance_block: copied %d lines from "
 | 
						|
       "ld.buf to buffer (max=%d, min=%d)\n", num_saved_lines, max_index,
 | 
						|
       min_index);
 | 
						|
  while (1)
 | 
						|
    {
 | 
						|
      if (++s->ld.lmod3 >= 3)
 | 
						|
	s->ld.lmod3 = 0;
 | 
						|
 | 
						|
      c = color_seq[s->ld.lmod3];
 | 
						|
      if (s->ld.index[c] < 0)
 | 
						|
	++s->ld.index[c];
 | 
						|
      else if (s->ld.index[c] < num_lines_total)
 | 
						|
	{
 | 
						|
	  s->ld.quant[c] += s->ld.peak_res;
 | 
						|
	  if (s->ld.quant[c] > s->ld.max_value)
 | 
						|
	    {
 | 
						|
	      s->ld.quant[c] -= s->ld.max_value;
 | 
						|
	      line = s->ld.index[c]++ - s->ld.ld_line;
 | 
						|
	      out_ptr = out + line * bpl + c;
 | 
						|
	      out_end = out_ptr + bpl;
 | 
						|
	      while (out_ptr != out_end)
 | 
						|
		{
 | 
						|
		  *out_ptr = *raw++;
 | 
						|
		  out_ptr += 3;
 | 
						|
		}
 | 
						|
	      DBG (5, "fix_line_distance_block: copied line %d (color %d)\n",
 | 
						|
		   line + s->ld.ld_line, c);
 | 
						|
 | 
						|
	      max_index = MAX (s->ld.index[0],
 | 
						|
			       MAX (s->ld.index[1], s->ld.index[2]));
 | 
						|
	      min_index = MIN (s->ld.index[0],
 | 
						|
			       MIN (s->ld.index[1], s->ld.index[2]));
 | 
						|
	      if ((raw >= raw_end) || ((min_index >= num_lines_total)))
 | 
						|
		{
 | 
						|
		  DBG (5, "fix_line_distance_block: got num_lines: %d\n",
 | 
						|
		       num_lines);
 | 
						|
		  num_lines = min_index - s->ld.ld_line;
 | 
						|
		  if (num_lines < 0)
 | 
						|
		    num_lines = 0;
 | 
						|
		  if ((s->total_lines + num_lines) > s->params.lines)
 | 
						|
		    num_lines = s->params.lines - s->total_lines;
 | 
						|
		  s->total_lines += num_lines;
 | 
						|
 | 
						|
		  /* copy away the lines with at least one missing
 | 
						|
		     color component, so that we can interleave them
 | 
						|
		     with new scan data on the next call */
 | 
						|
		  num_saved_lines = max_index - min_index;
 | 
						|
 | 
						|
		  DBG (5, "fix_line_distance_block: num_saved_lines = %d; "
 | 
						|
		       "num_lines = %d; bpl = %d\n", num_saved_lines,
 | 
						|
		       num_lines, bpl);
 | 
						|
 | 
						|
		  memcpy (s->ld.buf[0], out + num_lines * bpl,
 | 
						|
			  num_saved_lines * bpl);
 | 
						|
 | 
						|
		  DBG (5, "fix_line_distance_block: copied %d lines to "
 | 
						|
		       "ld.buf\n", num_saved_lines);
 | 
						|
 | 
						|
		  /* notice the number of lines we processed */
 | 
						|
		  s->ld.ld_line = min_index;
 | 
						|
		  if (s->ld.ld_line < 0)
 | 
						|
		    s->ld.ld_line = 0;
 | 
						|
 | 
						|
		  DBG (4, "fix_line_distance_block: lmod3=%d, "
 | 
						|
		       "index=(%d,%d,%d), line = %d, lines = %d\n",
 | 
						|
		       s->ld.lmod3,
 | 
						|
		       s->ld.index[0], s->ld.index[1], s->ld.index[2],
 | 
						|
		       s->ld.ld_line, num_lines);
 | 
						|
		  /* return number of complete (r+g+b) lines */
 | 
						|
		  return num_lines;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* For MFS-1200SP 1.00 and others */
 | 
						|
/* No LD correction necessary, just shuffle around data */
 | 
						|
static SANE_Int
 | 
						|
fix_line_distance_none (Mustek_Scanner * s, SANE_Int num_lines, SANE_Int bpl,
 | 
						|
			SANE_Byte * raw, SANE_Byte * out)
 | 
						|
{
 | 
						|
  SANE_Byte *red_ptr, *grn_ptr, *blu_ptr, *ptr, *ptr_end;
 | 
						|
  SANE_Word y;
 | 
						|
 | 
						|
  ptr = out;
 | 
						|
  red_ptr = raw;
 | 
						|
 | 
						|
  DBG (5, "fix_line_distance_none: no ld correction necessary (%d lines)\n",
 | 
						|
       num_lines);
 | 
						|
 | 
						|
  s->ld.ld_line += num_lines;
 | 
						|
 | 
						|
  if (s->ld.ld_line > s->params.lines)
 | 
						|
    num_lines -= (s->ld.ld_line - s->params.lines);
 | 
						|
  if (num_lines < 0)
 | 
						|
    num_lines = 0;
 | 
						|
 | 
						|
  DBG (5, "fix_line_distance_none: using %d lines (ld_line = %d, "
 | 
						|
       "s->params.lines = %d)\n", num_lines, s->ld.ld_line, s->params.lines);
 | 
						|
 | 
						|
  for (y = 0; y < num_lines; ++y)
 | 
						|
    {
 | 
						|
      grn_ptr = red_ptr + bpl / 3;
 | 
						|
      blu_ptr = grn_ptr + bpl / 3;
 | 
						|
      ptr_end = red_ptr + bpl;
 | 
						|
 | 
						|
      while (blu_ptr != ptr_end)
 | 
						|
	{
 | 
						|
	  *ptr++ = *red_ptr++;
 | 
						|
	  *ptr++ = *grn_ptr++;
 | 
						|
	  *ptr++ = *blu_ptr++;
 | 
						|
	}
 | 
						|
      red_ptr = ptr_end;
 | 
						|
    }
 | 
						|
  return num_lines;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
init_options (Mustek_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Int i, j, gammasize;
 | 
						|
 | 
						|
  memset (s->opt, 0, sizeof (s->opt));
 | 
						|
  memset (s->val, 0, sizeof (s->val));
 | 
						|
 | 
						|
  for (i = 0; i < NUM_OPTIONS; ++i)
 | 
						|
    {
 | 
						|
      s->opt[i].size = sizeof (SANE_Word);
 | 
						|
      s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
 | 
						|
    }
 | 
						|
 | 
						|
  s->opt[OPT_NUM_OPTS].name = "";
 | 
						|
  s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
 | 
						|
  s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
 | 
						|
  s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
 | 
						|
  s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
 | 
						|
 | 
						|
  /* "Mode" group: */
 | 
						|
  s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
 | 
						|
  s->opt[OPT_MODE_GROUP].desc = "";
 | 
						|
  s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
 | 
						|
  s->opt[OPT_MODE_GROUP].cap = 0;
 | 
						|
  s->opt[OPT_MODE_GROUP].size = 0;
 | 
						|
  s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | 
						|
 | 
						|
  /* scan mode */
 | 
						|
  s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
 | 
						|
  s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
 | 
						|
  s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
 | 
						|
  s->opt[OPT_MODE].type = SANE_TYPE_STRING;
 | 
						|
  s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | 
						|
  if (s->hw->flags & MUSTEK_FLAG_SE)
 | 
						|
    {
 | 
						|
      s->opt[OPT_MODE].size = max_string_size (mode_list_se);
 | 
						|
      s->opt[OPT_MODE].constraint.string_list = mode_list_se;
 | 
						|
      s->val[OPT_MODE].s = strdup (mode_list_se[1]);
 | 
						|
      if (!s->val[OPT_MODE].s)
 | 
						|
	return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      s->opt[OPT_MODE].size = max_string_size (mode_list_paragon);
 | 
						|
      s->opt[OPT_MODE].constraint.string_list = mode_list_paragon;
 | 
						|
      s->val[OPT_MODE].s = strdup (mode_list_paragon[2]);
 | 
						|
      if (!s->val[OPT_MODE].s)
 | 
						|
	return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
 | 
						|
  /* fast gray mode (pro models) */
 | 
						|
  s->opt[OPT_FAST_GRAY_MODE].name = "fast-gray-mode";
 | 
						|
  s->opt[OPT_FAST_GRAY_MODE].title = SANE_I18N ("Fast gray mode");
 | 
						|
  s->opt[OPT_FAST_GRAY_MODE].desc = SANE_I18N ("Scan in fast gray mode "
 | 
						|
					       "(lower quality).");
 | 
						|
  s->opt[OPT_FAST_GRAY_MODE].type = SANE_TYPE_BOOL;
 | 
						|
  s->val[OPT_FAST_GRAY_MODE].w = SANE_FALSE;
 | 
						|
  s->opt[OPT_FAST_GRAY_MODE].cap |= SANE_CAP_INACTIVE;
 | 
						|
  if (s->hw->flags & MUSTEK_FLAG_PRO)
 | 
						|
    {
 | 
						|
      /* Only Pro models support fast gray mode */
 | 
						|
      s->opt[OPT_FAST_GRAY_MODE].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
    }
 | 
						|
 | 
						|
  /* resolution */
 | 
						|
  s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
 | 
						|
  s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
 | 
						|
  s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
 | 
						|
  s->opt[OPT_RESOLUTION].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
 | 
						|
  s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_RESOLUTION].constraint.range = &s->hw->dpi_range;
 | 
						|
  s->val[OPT_RESOLUTION].w = MAX (SANE_FIX (72), s->hw->dpi_range.min);
 | 
						|
 | 
						|
  /* bit depth */
 | 
						|
  s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
 | 
						|
  s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
 | 
						|
  s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
 | 
						|
  s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_STRING;
 | 
						|
  s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | 
						|
  s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
 | 
						|
  s->opt[OPT_BIT_DEPTH].size = max_string_size (bit_depth_list_pro);
 | 
						|
  s->opt[OPT_BIT_DEPTH].constraint.string_list = bit_depth_list_pro;
 | 
						|
  s->val[OPT_BIT_DEPTH].s = strdup (bit_depth_list_pro[0]);
 | 
						|
  if (!s->val[OPT_BIT_DEPTH].s)
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
 | 
						|
  /* speed */
 | 
						|
  s->opt[OPT_SPEED].name = SANE_NAME_SCAN_SPEED;
 | 
						|
  s->opt[OPT_SPEED].title = SANE_TITLE_SCAN_SPEED;
 | 
						|
  s->opt[OPT_SPEED].desc = SANE_DESC_SCAN_SPEED;
 | 
						|
  s->opt[OPT_SPEED].type = SANE_TYPE_STRING;
 | 
						|
  s->opt[OPT_SPEED].size = max_string_size (speed_list);
 | 
						|
  s->opt[OPT_SPEED].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | 
						|
  s->opt[OPT_SPEED].constraint.string_list = speed_list;
 | 
						|
  s->val[OPT_SPEED].s = strdup (speed_list[4]);
 | 
						|
  if (!s->val[OPT_SPEED].s)
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
  if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS))
 | 
						|
    {
 | 
						|
      /* Speed only supported by 3-pass scanners */
 | 
						|
      s->opt[OPT_SPEED].cap |= SANE_CAP_INACTIVE;
 | 
						|
    }
 | 
						|
 | 
						|
  /* source */
 | 
						|
  s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
 | 
						|
  s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
 | 
						|
  s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
 | 
						|
  s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
 | 
						|
 | 
						|
  if ((s->hw->flags & MUSTEK_FLAG_SE) || (s->hw->flags & MUSTEK_FLAG_N)
 | 
						|
      || (s->hw->flags & MUSTEK_FLAG_TA))
 | 
						|
    {
 | 
						|
      s->opt[OPT_SOURCE].size = max_string_size (ta_source_list);
 | 
						|
      s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | 
						|
      s->opt[OPT_SOURCE].constraint.string_list = ta_source_list;
 | 
						|
      s->val[OPT_SOURCE].s = strdup (ta_source_list[0]);
 | 
						|
      if (!s->val[OPT_SOURCE].s)
 | 
						|
	return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
  else if (s->hw->flags & MUSTEK_FLAG_ADF)
 | 
						|
    {
 | 
						|
      s->opt[OPT_SOURCE].size = max_string_size (adf_source_list);
 | 
						|
      s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | 
						|
      s->opt[OPT_SOURCE].constraint.string_list = adf_source_list;
 | 
						|
      s->val[OPT_SOURCE].s = strdup (adf_source_list[0]);
 | 
						|
      if (!s->val[OPT_SOURCE].s)
 | 
						|
	return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      s->opt[OPT_SOURCE].size = max_string_size (source_list);
 | 
						|
      s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | 
						|
      s->opt[OPT_SOURCE].constraint.string_list = source_list;
 | 
						|
      s->val[OPT_SOURCE].s = strdup (source_list[0]);
 | 
						|
      s->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE;
 | 
						|
      if (!s->val[OPT_SOURCE].s)
 | 
						|
	return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
 | 
						|
  /* preview */
 | 
						|
  s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
 | 
						|
  s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
 | 
						|
  s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
 | 
						|
  s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
 | 
						|
  s->val[OPT_PREVIEW].w = 0;
 | 
						|
 | 
						|
  /* fast preview */
 | 
						|
  s->opt[OPT_FAST_PREVIEW].name = "fast-preview";
 | 
						|
  s->opt[OPT_FAST_PREVIEW].title = SANE_I18N ("Fast preview");
 | 
						|
  s->opt[OPT_FAST_PREVIEW].desc = SANE_I18N ("Request that all previews are "
 | 
						|
					     "done in the fastest (low-quality) mode. This may be a non-color "
 | 
						|
					     "mode or a low resolution mode.");
 | 
						|
  s->opt[OPT_FAST_PREVIEW].type = SANE_TYPE_BOOL;
 | 
						|
  s->val[OPT_FAST_PREVIEW].w = SANE_FALSE;
 | 
						|
 | 
						|
  /* lamp off time*/
 | 
						|
  s->opt[OPT_LAMP_OFF_TIME].name = "lamp-off-time";
 | 
						|
  s->opt[OPT_LAMP_OFF_TIME].title = SANE_I18N ("Lamp off time (minutes)");
 | 
						|
  s->opt[OPT_LAMP_OFF_TIME].desc = SANE_I18N ("Set the time (in minutes) after "
 | 
						|
					      "which the lamp is shut off.");
 | 
						|
  s->opt[OPT_LAMP_OFF_TIME].type = SANE_TYPE_INT;
 | 
						|
  if (strcmp (s->hw->sane.model, "1200 A3 PRO") != 0)
 | 
						|
    s->opt[OPT_LAMP_OFF_TIME].cap |= SANE_CAP_INACTIVE;
 | 
						|
  s->opt[OPT_LAMP_OFF_TIME].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_LAMP_OFF_TIME].constraint.range = &u8_range;
 | 
						|
  s->val[OPT_LAMP_OFF_TIME].w = 60;
 | 
						|
 | 
						|
  /* shut lamp off */
 | 
						|
  s->opt[OPT_LAMP_OFF_BUTTON].name = "lamp-off";
 | 
						|
  s->opt[OPT_LAMP_OFF_BUTTON].title = SANE_I18N ("Turn lamp off");
 | 
						|
  s->opt[OPT_LAMP_OFF_BUTTON].desc = SANE_I18N ("Turns the lamp off immediately.");
 | 
						|
  s->opt[OPT_LAMP_OFF_BUTTON].type = SANE_TYPE_BUTTON;
 | 
						|
  s->opt[OPT_LAMP_OFF_BUTTON].cap = SANE_CAP_SOFT_SELECT;
 | 
						|
  if (strcmp (s->hw->sane.model, "1200 A3 PRO") != 0)
 | 
						|
    s->opt[OPT_LAMP_OFF_BUTTON].cap |= SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
  /* "Geometry" group: */
 | 
						|
  s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
 | 
						|
  s->opt[OPT_GEOMETRY_GROUP].desc = "";
 | 
						|
  s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
 | 
						|
  s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
 | 
						|
  s->opt[OPT_GEOMETRY_GROUP].size = 0;
 | 
						|
  s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | 
						|
 | 
						|
  /* top-left x */
 | 
						|
  s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
 | 
						|
  s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
 | 
						|
  s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
 | 
						|
  s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
 | 
						|
  s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_TL_X].constraint.range = &s->hw->x_range;
 | 
						|
  s->val[OPT_TL_X].w = s->hw->x_range.min;
 | 
						|
 | 
						|
  /* top-left y */
 | 
						|
  s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
 | 
						|
  s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
 | 
						|
  s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
 | 
						|
  s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
 | 
						|
  s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range;
 | 
						|
  s->val[OPT_TL_Y].w = s->hw->y_range.min;
 | 
						|
 | 
						|
  /* bottom-right x */
 | 
						|
  s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
 | 
						|
  s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
 | 
						|
  s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
 | 
						|
  s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
 | 
						|
  s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_BR_X].constraint.range = &s->hw->x_range;
 | 
						|
  s->val[OPT_BR_X].w = s->hw->x_range.max;
 | 
						|
 | 
						|
  /* bottom-right y */
 | 
						|
  s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
 | 
						|
  s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
 | 
						|
  s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
 | 
						|
  s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
 | 
						|
  s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range;
 | 
						|
  s->val[OPT_BR_Y].w = s->hw->y_range.max;
 | 
						|
 | 
						|
  /* "Enhancement" group: */
 | 
						|
  s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
 | 
						|
  s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
 | 
						|
  s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
 | 
						|
  s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
 | 
						|
  s->opt[OPT_ENHANCEMENT_GROUP].size = 0;
 | 
						|
  s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | 
						|
 | 
						|
  /* brightness */
 | 
						|
  s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
 | 
						|
  s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
 | 
						|
  s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
 | 
						|
  s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT;
 | 
						|
  s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_BRIGHTNESS].constraint.range = &percentage_range;
 | 
						|
  if (!s->hw->flags & MUSTEK_FLAG_THREE_PASS)
 | 
						|
    /* 1-pass scanners don't support brightness in multibit mode */
 | 
						|
    s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
 | 
						|
  s->val[OPT_BRIGHTNESS].w = 0;
 | 
						|
 | 
						|
  /* brightness red */
 | 
						|
  s->opt[OPT_BRIGHTNESS_R].name = "brightness-r";
 | 
						|
  s->opt[OPT_BRIGHTNESS_R].title = SANE_I18N ("Red brightness");
 | 
						|
  s->opt[OPT_BRIGHTNESS_R].desc = SANE_I18N ("Controls the brightness of "
 | 
						|
					     "the red channel of the "
 | 
						|
					     "acquired image.");
 | 
						|
  s->opt[OPT_BRIGHTNESS_R].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_BRIGHTNESS_R].unit = SANE_UNIT_PERCENT;
 | 
						|
  s->opt[OPT_BRIGHTNESS_R].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_BRIGHTNESS_R].constraint.range = &percentage_range;
 | 
						|
  s->opt[OPT_BRIGHTNESS_R].cap |= SANE_CAP_INACTIVE;
 | 
						|
  s->val[OPT_BRIGHTNESS_R].w = 0;
 | 
						|
 | 
						|
  /* brightness green */
 | 
						|
  s->opt[OPT_BRIGHTNESS_G].name = "brightness-g";
 | 
						|
  s->opt[OPT_BRIGHTNESS_G].title = SANE_I18N ("Green brightness");
 | 
						|
  s->opt[OPT_BRIGHTNESS_G].desc = SANE_I18N ("Controls the brightness of "
 | 
						|
					     "the green channel of the "
 | 
						|
					     "acquired image.");
 | 
						|
  s->opt[OPT_BRIGHTNESS_G].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_BRIGHTNESS_G].unit = SANE_UNIT_PERCENT;
 | 
						|
  s->opt[OPT_BRIGHTNESS_G].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_BRIGHTNESS_G].constraint.range = &percentage_range;
 | 
						|
  s->opt[OPT_BRIGHTNESS_G].cap |= SANE_CAP_INACTIVE;
 | 
						|
  s->val[OPT_BRIGHTNESS_G].w = 0;
 | 
						|
 | 
						|
  /* brightness blue */
 | 
						|
  s->opt[OPT_BRIGHTNESS_B].name = "brightness-b";
 | 
						|
  s->opt[OPT_BRIGHTNESS_B].title = SANE_I18N ("Blue brightness");
 | 
						|
  s->opt[OPT_BRIGHTNESS_B].desc = SANE_I18N ("Controls the brightness of "
 | 
						|
					     "the blue channel of the "
 | 
						|
					     "acquired image.");
 | 
						|
  s->opt[OPT_BRIGHTNESS_B].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_BRIGHTNESS_B].unit = SANE_UNIT_PERCENT;
 | 
						|
  s->opt[OPT_BRIGHTNESS_B].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_BRIGHTNESS_B].constraint.range = &percentage_range;
 | 
						|
  s->opt[OPT_BRIGHTNESS_B].cap |= SANE_CAP_INACTIVE;
 | 
						|
  s->val[OPT_BRIGHTNESS_B].w = 0;
 | 
						|
 | 
						|
  /* contrast */
 | 
						|
  s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
 | 
						|
  s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
 | 
						|
  s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
 | 
						|
  s->opt[OPT_CONTRAST].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_CONTRAST].unit = SANE_UNIT_PERCENT;
 | 
						|
  s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_CONTRAST].constraint.range = &percentage_range;
 | 
						|
  if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS))
 | 
						|
    /* 1-pass scanners don't support contrast in multibit mode */
 | 
						|
    s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
 | 
						|
  s->val[OPT_CONTRAST].w = 0;
 | 
						|
 | 
						|
  /* contrast red */
 | 
						|
  s->opt[OPT_CONTRAST_R].name = "contrast-r";
 | 
						|
  s->opt[OPT_CONTRAST_R].title = SANE_I18N ("Contrast red channel");
 | 
						|
  s->opt[OPT_CONTRAST_R].desc = SANE_I18N ("Controls the contrast of "
 | 
						|
					   "the red channel of the "
 | 
						|
					   "acquired image.");
 | 
						|
  s->opt[OPT_CONTRAST_R].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_CONTRAST_R].unit = SANE_UNIT_PERCENT;
 | 
						|
  s->opt[OPT_CONTRAST_R].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_CONTRAST_R].constraint.range = &percentage_range;
 | 
						|
  s->opt[OPT_CONTRAST_R].cap |= SANE_CAP_INACTIVE;
 | 
						|
  s->val[OPT_CONTRAST_R].w = 0;
 | 
						|
 | 
						|
  /* contrast green */
 | 
						|
  s->opt[OPT_CONTRAST_G].name = "contrast-g";
 | 
						|
  s->opt[OPT_CONTRAST_G].title = SANE_I18N ("Contrast green channel");
 | 
						|
  s->opt[OPT_CONTRAST_G].desc = SANE_I18N ("Controls the contrast of "
 | 
						|
					   "the green channel of the "
 | 
						|
					   "acquired image.");
 | 
						|
  s->opt[OPT_CONTRAST_G].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_CONTRAST_G].unit = SANE_UNIT_PERCENT;
 | 
						|
  s->opt[OPT_CONTRAST_G].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_CONTRAST_G].constraint.range = &percentage_range;
 | 
						|
  s->opt[OPT_CONTRAST_G].cap |= SANE_CAP_INACTIVE;
 | 
						|
  s->val[OPT_CONTRAST_G].w = 0;
 | 
						|
 | 
						|
  /* contrast blue */
 | 
						|
  s->opt[OPT_CONTRAST_B].name = "contrast-b";
 | 
						|
  s->opt[OPT_CONTRAST_B].title = SANE_I18N ("Contrast blue channel");
 | 
						|
  s->opt[OPT_CONTRAST_B].desc = SANE_I18N ("Controls the contrast of "
 | 
						|
					   "the blue channel of the "
 | 
						|
					   "acquired image.");
 | 
						|
  s->opt[OPT_CONTRAST_B].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_CONTRAST_B].unit = SANE_UNIT_PERCENT;
 | 
						|
  s->opt[OPT_CONTRAST_B].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_CONTRAST_B].constraint.range = &percentage_range;
 | 
						|
  s->opt[OPT_CONTRAST_B].cap |= SANE_CAP_INACTIVE;
 | 
						|
  s->val[OPT_CONTRAST_B].w = 0;
 | 
						|
 | 
						|
  /* gamma */
 | 
						|
  gammasize = 256;
 | 
						|
  for (i = 0; i < 4; ++i)
 | 
						|
    for (j = 0; j < gammasize; ++j)
 | 
						|
      s->gamma_table[i][j] = j;
 | 
						|
 | 
						|
  /* custom-gamma table */
 | 
						|
  s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
 | 
						|
  s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
 | 
						|
  s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
 | 
						|
  s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
 | 
						|
  s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
 | 
						|
 | 
						|
  /* grayscale gamma vector */
 | 
						|
  s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word);
 | 
						|
  s->val[OPT_GAMMA_VECTOR].wa = &s->gamma_table[0][0];
 | 
						|
  s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range;
 | 
						|
 | 
						|
  /* red gamma vector */
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word);
 | 
						|
  s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[1][0];
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
 | 
						|
 | 
						|
  /* green gamma vector */
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word);
 | 
						|
  s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[2][0];
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
 | 
						|
 | 
						|
  /* blue gamma vector */
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word);
 | 
						|
  s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[3][0];
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
 | 
						|
 | 
						|
  /* quality calibration */
 | 
						|
  s->opt[OPT_QUALITY_CAL].name = SANE_NAME_QUALITY_CAL;
 | 
						|
  s->opt[OPT_QUALITY_CAL].title = SANE_TITLE_QUALITY_CAL;
 | 
						|
  s->opt[OPT_QUALITY_CAL].desc = SANE_DESC_QUALITY_CAL;
 | 
						|
  s->opt[OPT_QUALITY_CAL].type = SANE_TYPE_BOOL;
 | 
						|
  if (s->hw->flags & MUSTEK_FLAG_PRO)
 | 
						|
    s->val[OPT_QUALITY_CAL].w = SANE_TRUE;
 | 
						|
  else
 | 
						|
    s->val[OPT_QUALITY_CAL].w = SANE_FALSE;
 | 
						|
  s->opt[OPT_QUALITY_CAL].cap |= SANE_CAP_INACTIVE;
 | 
						|
  if ((s->hw->flags & MUSTEK_FLAG_PRO)
 | 
						|
      || (s->hw->flags & MUSTEK_FLAG_SE_PLUS))
 | 
						|
    {
 | 
						|
      /* Only Pro and SE Plus models support calibration */
 | 
						|
      s->opt[OPT_QUALITY_CAL].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
    }
 | 
						|
 | 
						|
  /* halftone dimension */
 | 
						|
  s->opt[OPT_HALFTONE_DIMENSION].name = SANE_NAME_HALFTONE_DIMENSION;
 | 
						|
  s->opt[OPT_HALFTONE_DIMENSION].title = SANE_TITLE_HALFTONE_DIMENSION;
 | 
						|
  s->opt[OPT_HALFTONE_DIMENSION].desc = SANE_DESC_HALFTONE_DIMENSION;
 | 
						|
  s->opt[OPT_HALFTONE_DIMENSION].type = SANE_TYPE_STRING;
 | 
						|
  s->opt[OPT_HALFTONE_DIMENSION].size = max_string_size (halftone_list);
 | 
						|
  s->opt[OPT_HALFTONE_DIMENSION].constraint_type =
 | 
						|
    SANE_CONSTRAINT_STRING_LIST;
 | 
						|
  s->opt[OPT_HALFTONE_DIMENSION].constraint.string_list = halftone_list;
 | 
						|
  s->val[OPT_HALFTONE_DIMENSION].s = strdup (halftone_list[0]);
 | 
						|
  if (!s->val[OPT_HALFTONE_DIMENSION].s)
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
  s->opt[OPT_HALFTONE_DIMENSION].cap |= SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
  /* halftone pattern */
 | 
						|
  s->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
 | 
						|
  s->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
 | 
						|
  s->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
 | 
						|
  s->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
 | 
						|
  s->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_HALFTONE_PATTERN].constraint.range = &u8_range;
 | 
						|
  s->val[OPT_HALFTONE_PATTERN].wa = s->halftone_pattern;
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
/* The following three functions execute as a child process.  The
 | 
						|
   reason for using a subprocess is that some (most?) generic SCSI
 | 
						|
   interfaces block a SCSI request until it has completed.  With a
 | 
						|
   subprocess, we can let it block waiting for the request to finish
 | 
						|
   while the main process can go about to do more important things
 | 
						|
   (such as recognizing when the user presses a cancel button).
 | 
						|
 | 
						|
   WARNING: Since this is executed as a subprocess, it's NOT possible
 | 
						|
   to update any of the variables in the main process (in particular
 | 
						|
   the scanner state cannot be updated).
 | 
						|
 | 
						|
   NOTE: At least for Linux, it seems that we could get rid of the
 | 
						|
   subprocess.  Linux v2.0 does seem to allow select() on SCSI
 | 
						|
   descriptors.  */
 | 
						|
 | 
						|
static void
 | 
						|
output_data (Mustek_Scanner * s, FILE * fp,
 | 
						|
	     SANE_Byte * data, SANE_Int lines_per_buffer, SANE_Int bpl,
 | 
						|
	     SANE_Byte * extra)
 | 
						|
{
 | 
						|
  SANE_Byte *ptr, *ptr_end;
 | 
						|
  SANE_Int y, num_lines;
 | 
						|
 | 
						|
  DBG (5, "output_data: data=%p, lpb=%d, bpl=%d, extra=%p\n",
 | 
						|
       data, lines_per_buffer, bpl, extra);
 | 
						|
 | 
						|
  /* convert to pixel-interleaved format: */
 | 
						|
  if ((s->mode & MUSTEK_MODE_COLOR)
 | 
						|
      && !(s->hw->flags & MUSTEK_FLAG_THREE_PASS))
 | 
						|
    {
 | 
						|
      num_lines = lines_per_buffer;
 | 
						|
 | 
						|
      /* need to correct for distance between r/g/b sensors: */
 | 
						|
      if (s->hw->flags & MUSTEK_FLAG_PRO)
 | 
						|
	fix_line_distance_pro (s, num_lines, bpl, data, extra);
 | 
						|
      else if (s->hw->flags & MUSTEK_FLAG_SE)
 | 
						|
	{
 | 
						|
	  num_lines = fix_line_distance_se (s, num_lines, bpl, data, extra);
 | 
						|
	}
 | 
						|
      else if (s->hw->flags & MUSTEK_FLAG_N)
 | 
						|
	{
 | 
						|
	  if (s->hw->flags & MUSTEK_FLAG_LD_N2)
 | 
						|
	    num_lines = fix_line_distance_n_2 (s, num_lines, bpl, data,
 | 
						|
					       extra);
 | 
						|
	  else
 | 
						|
	    num_lines = fix_line_distance_n_1 (s, num_lines, bpl, data,
 | 
						|
					       extra);
 | 
						|
	}
 | 
						|
      else if ((s->hw->flags & MUSTEK_FLAG_LD_BLOCK)
 | 
						|
	       && (s->ld.max_value != 0))
 | 
						|
	{
 | 
						|
	  if (s->hw->flags & MUSTEK_FLAG_PARAGON_1)
 | 
						|
	    num_lines = fix_line_distance_block (s, num_lines, bpl, data,
 | 
						|
						 extra, s->hw->lines);
 | 
						|
	  else
 | 
						|
	    num_lines = fix_line_distance_block (s, num_lines, bpl, data,
 | 
						|
						 extra,
 | 
						|
						 s->hw->lines_per_block);
 | 
						|
	}
 | 
						|
      else if (!(s->hw->flags & MUSTEK_FLAG_LD_NONE)
 | 
						|
	       && (s->ld.max_value != 0))
 | 
						|
	fix_line_distance_normal (s, num_lines, bpl, data, extra);
 | 
						|
      else
 | 
						|
	num_lines = fix_line_distance_none (s, num_lines, bpl, data, extra);
 | 
						|
 | 
						|
      if (strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0)
 | 
						|
	{
 | 
						|
	  /* need to revert line direction */
 | 
						|
	  SANE_Int line_number;
 | 
						|
	  SANE_Int byte_number;
 | 
						|
 | 
						|
	  DBG (5, "output_data: ADF found, mirroring lines\n");
 | 
						|
	  for (line_number = 0; line_number < num_lines; line_number++)
 | 
						|
	    {
 | 
						|
	      for (byte_number = bpl - 3; byte_number >= 0; byte_number -= 3)
 | 
						|
		{
 | 
						|
		  fputc (*(extra + line_number * bpl + byte_number), fp);
 | 
						|
		  fputc (*(extra + line_number * bpl + byte_number + 1), fp);
 | 
						|
		  fputc (*(extra + line_number * bpl + byte_number + 2), fp);
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      else
 | 
						|
	fwrite (extra, num_lines, s->params.bytes_per_line, fp);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      DBG (5, "output_data: write %d lpb; %d bpl\n", lines_per_buffer, bpl);
 | 
						|
      /* Scale x-resolution above 1/2 of the maximum resolution for
 | 
						|
         SE and Pro scanners */
 | 
						|
      if ((s->hw->flags & MUSTEK_FLAG_ENLARGE_X) &&
 | 
						|
	  (s->val[OPT_RESOLUTION].w > (s->hw->dpi_range.max / 2)))
 | 
						|
	{
 | 
						|
	  SANE_Int x;
 | 
						|
	  SANE_Int half_res = SANE_UNFIX (s->hw->dpi_range.max) / 2;
 | 
						|
	  SANE_Int res = SANE_UNFIX (s->val[OPT_RESOLUTION].w);
 | 
						|
	  SANE_Int res_counter;
 | 
						|
	  SANE_Int enlarged_x;
 | 
						|
 | 
						|
	  DBG (5, "output_data: enlarge lines from %d bpl to %d bpl\n",
 | 
						|
	       s->hw->bpl, s->params.bytes_per_line);
 | 
						|
 | 
						|
	  for (y = 0; y < lines_per_buffer; y++)
 | 
						|
	    {
 | 
						|
	      SANE_Byte byte = 0;
 | 
						|
 | 
						|
	      x = 0;
 | 
						|
	      res_counter = 0;
 | 
						|
	      enlarged_x = 0;
 | 
						|
 | 
						|
	      while (enlarged_x < s->params.pixels_per_line)
 | 
						|
		{
 | 
						|
		  if (s->mode & MUSTEK_MODE_GRAY)
 | 
						|
		    {
 | 
						|
		      fputc (*(data + y * bpl + x), fp);
 | 
						|
		      res_counter += half_res;
 | 
						|
		      if (res_counter >= half_res)
 | 
						|
			{
 | 
						|
			  res_counter -= res;
 | 
						|
			  x++;
 | 
						|
			}
 | 
						|
		      enlarged_x++;
 | 
						|
		    }
 | 
						|
		  else		/* lineart */
 | 
						|
		    {
 | 
						|
		      /* need to invert image because of funny SANE 1-bit image
 | 
						|
			 polarity */
 | 
						|
		      if (*(data + x / 8 + y * bpl) & (1 << (7 - (x % 8))))
 | 
						|
			byte |= 1 << (7 - (enlarged_x % 8));
 | 
						|
 | 
						|
		      if ((enlarged_x % 8) == 7)
 | 
						|
			{
 | 
						|
			  fputc (~byte, fp);	/* invert image */
 | 
						|
			  byte = 0;
 | 
						|
			}
 | 
						|
		      res_counter += half_res;
 | 
						|
		      if (res_counter >= half_res)
 | 
						|
			{
 | 
						|
			  res_counter -= res;
 | 
						|
			  x++;
 | 
						|
			}
 | 
						|
		      enlarged_x++;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      else			/* lineart, gray or halftone (nothing to scale) */
 | 
						|
	{
 | 
						|
	  if ((s->mode & MUSTEK_MODE_LINEART)
 | 
						|
	      || (s->mode & MUSTEK_MODE_HALFTONE))
 | 
						|
	    {
 | 
						|
	      /* need to invert image because of funny SANE 1-bit image
 | 
						|
		 polarity */
 | 
						|
	      ptr = data;
 | 
						|
	      ptr_end = ptr + lines_per_buffer * bpl;
 | 
						|
 | 
						|
	      if (strcmp (s->val[OPT_SOURCE].s,
 | 
						|
			  "Automatic Document Feeder") == 0)
 | 
						|
		{
 | 
						|
		  while (ptr != ptr_end)
 | 
						|
		    {
 | 
						|
		      (*ptr) = ~(*ptr);
 | 
						|
		      ptr++;
 | 
						|
		      /* need to revert bit direction */
 | 
						|
		      *ptr = ((*ptr & 0x80) >> 7) + ((*ptr & 0x40) >> 5)
 | 
						|
			+ ((*ptr & 0x20) >> 3) + ((*ptr & 0x10) >> 1)
 | 
						|
			+ ((*ptr & 0x08) << 1) + ((*ptr & 0x04) << 3)
 | 
						|
			+ ((*ptr & 0x02) << 5) + ((*ptr & 0x01) << 7);
 | 
						|
		    }
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		while (ptr != ptr_end)
 | 
						|
		  {
 | 
						|
		    (*ptr) = ~(*ptr);
 | 
						|
		    ptr++;
 | 
						|
		  }
 | 
						|
	    }
 | 
						|
	  if (strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0)
 | 
						|
	    {
 | 
						|
	      /* need to revert line direction */
 | 
						|
	      SANE_Int line_number;
 | 
						|
	      SANE_Int byte_number;
 | 
						|
 | 
						|
	      DBG (5, "output_data: ADF found, mirroring lines\n");
 | 
						|
	      for (line_number = 0; line_number < lines_per_buffer;
 | 
						|
		   line_number++)
 | 
						|
		{
 | 
						|
		  for (byte_number = bpl - 1; byte_number >= 0; byte_number--)
 | 
						|
		    {
 | 
						|
		      fputc (*(data + line_number * bpl + byte_number), fp);
 | 
						|
		    }
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      fwrite (data, lines_per_buffer, bpl, fp);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
  DBG (5, "output_data: end\n");
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
sigterm_handler (int signal)
 | 
						|
{
 | 
						|
  DBG (4,
 | 
						|
       "sigterm_handler: started, signal is %d, starting sanei_scsi_req_flush_all()\n",
 | 
						|
       signal);
 | 
						|
  sanei_scsi_req_flush_all ();	/* flush SCSI queue */
 | 
						|
  DBG (4,
 | 
						|
       "sigterm_handler: sanei_scsi_req_flush_all() finisheshed, _exiting()\n");
 | 
						|
  _exit (SANE_STATUS_GOOD);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static SANE_Int
 | 
						|
reader_process (void *data)
 | 
						|
{
 | 
						|
  Mustek_Scanner *s = (Mustek_Scanner *) data;
 | 
						|
  SANE_Int lines_per_buffer, bpl;
 | 
						|
  SANE_Byte *extra = 0, *ptr;
 | 
						|
  sigset_t sigterm_set;
 | 
						|
  struct SIGACTION act;
 | 
						|
  SANE_Status status;
 | 
						|
  FILE *fp;
 | 
						|
  int fd = s->reader_fds;
 | 
						|
  SANE_Int buffernumber = 0;
 | 
						|
  SANE_Int buffer_count, max_buffers;
 | 
						|
  struct
 | 
						|
  {
 | 
						|
    void *id;			/* scsi queue id */
 | 
						|
    SANE_Byte *data;		/* data buffer */
 | 
						|
    SANE_Byte *command;		/* command buffer */
 | 
						|
    SANE_Int lines;		/* # lines in buffer */
 | 
						|
    size_t num_read;		/* # of bytes read (return value) */
 | 
						|
    SANE_Int bank;		/* needed by SE models */
 | 
						|
    SANE_Bool ready;		/* ready to send to application? */
 | 
						|
    SANE_Bool finished;		/* block is finished */
 | 
						|
  }
 | 
						|
  bstat[2];
 | 
						|
 | 
						|
  DBG (3, "reader_process: started\n");
 | 
						|
  if (sanei_thread_is_forked ())
 | 
						|
    {
 | 
						|
      DBG (4, "reader_process: using fork ()\n");
 | 
						|
      close (s->pipe);
 | 
						|
      s->pipe = -1;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      DBG (4, "reader_process: using threads\n");
 | 
						|
    }
 | 
						|
 | 
						|
  if (sanei_thread_is_forked ())
 | 
						|
    {
 | 
						|
      /* ignore SIGTERM while writing SCSI commands */
 | 
						|
      sigemptyset (&sigterm_set);
 | 
						|
      sigaddset (&sigterm_set, SIGTERM);
 | 
						|
 | 
						|
      /* call our sigterm handler to clean up ongoing SCSI requests */
 | 
						|
      memset (&act, 0, sizeof (act));
 | 
						|
      act.sa_handler = sigterm_handler;
 | 
						|
      sigaction (SIGTERM, &act, 0);
 | 
						|
    }
 | 
						|
 | 
						|
  if (disable_double_buffering)
 | 
						|
    DBG (3, "reader_process: disable_double_buffering is set, this may be "
 | 
						|
	 "slow\n");
 | 
						|
 | 
						|
  fp = fdopen (fd, "w");
 | 
						|
  if (!fp)
 | 
						|
    return SANE_STATUS_IO_ERROR;
 | 
						|
 | 
						|
  s->total_lines = 0;
 | 
						|
  bpl = s->hw->bpl;
 | 
						|
 | 
						|
  /* buffer size is scanner dependant */
 | 
						|
  lines_per_buffer = s->hw->buffer_size / bpl / 2;
 | 
						|
 | 
						|
  if (strip_height > 0.0)
 | 
						|
    {
 | 
						|
      SANE_Int max_lines;
 | 
						|
      double dpi;
 | 
						|
 | 
						|
      dpi = SANE_UNFIX (s->val[OPT_RESOLUTION].w);
 | 
						|
      max_lines = (int) (strip_height * dpi + 0.5);
 | 
						|
 | 
						|
      if (lines_per_buffer > max_lines)
 | 
						|
	{
 | 
						|
	  DBG (2, "reader_process: limiting strip height to %g inches "
 | 
						|
	       "(%d lines)\n", strip_height, max_lines);
 | 
						|
	  lines_per_buffer = max_lines;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  if (!lines_per_buffer)
 | 
						|
    {
 | 
						|
      DBG (1, "reader_process: bpl (%d) > SCSI buffer size / 2 (%d)\n",
 | 
						|
	   bpl, s->hw->buffer_size / 2);
 | 
						|
      return SANE_STATUS_NO_MEM;	/* resolution is too high */
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (4, "reader_process: %d lines per buffer, %d bytes per line, "
 | 
						|
       "%d bytes per buffer\n", lines_per_buffer, bpl,
 | 
						|
       lines_per_buffer * bpl);
 | 
						|
 | 
						|
  bstat[0].data = malloc (2 * lines_per_buffer * (long) bpl);
 | 
						|
  if (!bstat[0].data)
 | 
						|
    {
 | 
						|
      DBG (1, "reader_process: failed to malloc %ld bytes for data buffer\n",
 | 
						|
	   lines_per_buffer * (long) bpl);
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
  bstat[1].data = bstat[0].data + lines_per_buffer * (long) bpl;
 | 
						|
 | 
						|
  bstat[0].command = malloc (2 * 10);
 | 
						|
  if (!bstat[0].command)
 | 
						|
    {
 | 
						|
      DBG (1,
 | 
						|
	   "reader_process: failed to malloc %d bytes for command buffer\n",
 | 
						|
	   2 * 10);
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
  bstat[1].command = bstat[0].command + 10;
 | 
						|
 | 
						|
  /* Touch all pages of the buffer to fool the memory management. */
 | 
						|
  ptr = bstat[0].data + 2 * lines_per_buffer * (long) bpl - 1;
 | 
						|
  while (ptr >= bstat[0].data)
 | 
						|
    {
 | 
						|
      *ptr = 0x00;
 | 
						|
      ptr -= 256;
 | 
						|
    }
 | 
						|
 | 
						|
  if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS))
 | 
						|
    {
 | 
						|
      /* get temporary buffer for line-distance correction and/or bit
 | 
						|
         expansion. For some scanners more space is needed because the
 | 
						|
         data must be read in as single big block (cut up into pieces
 | 
						|
         of lines_per_buffer). This requires that the line distance
 | 
						|
         correction continues on every call exactly where it stopped
 | 
						|
         if the image shall be reconstructed without any stripes. */
 | 
						|
 | 
						|
      extra = malloc ((lines_per_buffer + MAX_LINE_DIST)
 | 
						|
		      * (long) s->params.bytes_per_line);
 | 
						|
      if (!extra)
 | 
						|
	{
 | 
						|
	  DBG (1, "reader_process: failed to malloc extra buffer\n");
 | 
						|
	  return SANE_STATUS_NO_MEM;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  if (s->hw->flags & MUSTEK_FLAG_N)
 | 
						|
    {
 | 
						|
      /* reacquire port access rights (lost because of fork()): */
 | 
						|
      sanei_ab306_get_io_privilege (s->fd);
 | 
						|
    }
 | 
						|
 | 
						|
  if ((s->hw->flags & MUSTEK_FLAG_N) || (s->hw->flags & MUSTEK_FLAG_LD_BLOCK))
 | 
						|
    {
 | 
						|
      /* reset counter of line number for line-dictance correction */
 | 
						|
      s->ld.ld_line = 0;
 | 
						|
    }
 | 
						|
 | 
						|
  max_buffers = s->hw->max_block_buffer_size / (lines_per_buffer * bpl);
 | 
						|
  if (max_buffers < 1)
 | 
						|
    {
 | 
						|
      DBG (1, "reader_process: buffersize > blocksize!\n");
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
  DBG (4, "reader_process: limiting block read to %d buffers (%d lines)\n",
 | 
						|
       max_buffers, MIN (s->hw->lines, (max_buffers * lines_per_buffer)));
 | 
						|
 | 
						|
  while (s->line < s->hw->lines)
 | 
						|
    {
 | 
						|
      s->hw->lines_per_block =
 | 
						|
	MIN (s->hw->lines - s->line, (max_buffers * lines_per_buffer));
 | 
						|
      status = dev_block_read_start (s, s->hw->lines_per_block);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	return status;
 | 
						|
 | 
						|
      for (buffernumber = 0; buffernumber < 2; buffernumber++)
 | 
						|
	{
 | 
						|
	  bstat[buffernumber].ready = SANE_FALSE;
 | 
						|
	  bstat[buffernumber].finished = SANE_FALSE;
 | 
						|
	}
 | 
						|
      buffer_count = 0;
 | 
						|
      buffernumber = 0;
 | 
						|
 | 
						|
      while (1)
 | 
						|
	{
 | 
						|
	  /* omit reading first two buffers (not yet ready) */
 | 
						|
	  if (bstat[buffernumber].ready == SANE_TRUE)
 | 
						|
	    {
 | 
						|
	      DBG (4, "reader_process: buffer %d: waiting for request to be "
 | 
						|
		   "ready\n", buffernumber + 1);
 | 
						|
	      status = dev_req_wait (bstat[buffernumber].id);
 | 
						|
	      if (status == SANE_STATUS_GOOD)
 | 
						|
		{
 | 
						|
		  DBG (4, "reader_process: buffer %d is ready, wanted %d, "
 | 
						|
		       "got %ld bytes\n", buffernumber + 1,
 | 
						|
		       bstat[buffernumber].lines * bpl,
 | 
						|
		       (long int) bstat[buffernumber].num_read);
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  DBG (1, "reader_process: failed to read data, status: %s, "
 | 
						|
		       "buffer: %d\n", sane_strstatus (status),
 | 
						|
		       buffernumber + 1);
 | 
						|
		  if (status == SANE_STATUS_NO_MEM)
 | 
						|
		    {
 | 
						|
		      DBG (1,
 | 
						|
			   "Probably the size of the kernel SCSI buffer is "
 | 
						|
			   "too small for the\n         selected buffersize "
 | 
						|
			   "in mustek.conf. Either decrease "
 | 
						|
			   "buffersize in\n         mustek.conf to e.g. 32, "
 | 
						|
			   "increase SG_BIG_BUF in kernel to 130560, "
 | 
						|
			   "or\n         use SANE_SG_BUFFERSIZE variable. "
 | 
						|
			   "See man sane-scsi and README for\n         "
 | 
						|
			   "details.\n");
 | 
						|
		    }
 | 
						|
		  return status;
 | 
						|
		}
 | 
						|
 | 
						|
	      DBG (4, "reader_process: buffer %d: sending %ld bytes to "
 | 
						|
		   "output_data\n", buffernumber + 1,
 | 
						|
		   (long int) bstat[buffernumber].num_read);
 | 
						|
	      output_data (s, fp, bstat[buffernumber].data,
 | 
						|
			   bstat[buffernumber].lines, bpl, extra);
 | 
						|
	      if (bstat[buffernumber].finished)
 | 
						|
		break;		/* everything written; exit loop */
 | 
						|
	    }
 | 
						|
	  if (disable_double_buffering)
 | 
						|
	    {
 | 
						|
	      /* Enter only one buffer at once */
 | 
						|
	      if (buffernumber == 1)
 | 
						|
		buffernumber = 0;
 | 
						|
	      else
 | 
						|
		buffernumber = 1;
 | 
						|
	    }
 | 
						|
 | 
						|
	  /* enter read requests only if data left */
 | 
						|
	  if ((s->line < s->hw->lines) && (buffer_count < max_buffers))
 | 
						|
	    {
 | 
						|
	      if (s->line + lines_per_buffer >= s->hw->lines)
 | 
						|
		{
 | 
						|
		  /* do the last few lines: */
 | 
						|
		  bstat[buffernumber].lines = s->hw->lines - s->line;
 | 
						|
		  bstat[buffernumber].bank = 0x01;
 | 
						|
		  bstat[buffernumber].finished = SANE_TRUE;
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  bstat[buffernumber].lines = lines_per_buffer;
 | 
						|
		  bstat[buffernumber].bank = 0x00;
 | 
						|
		}
 | 
						|
 | 
						|
	      if ((buffer_count + 1) >= max_buffers)
 | 
						|
		bstat[buffernumber].finished = SANE_TRUE;
 | 
						|
 | 
						|
	      s->line += bstat[buffernumber].lines;
 | 
						|
	      bstat[buffernumber].ready = SANE_TRUE;
 | 
						|
 | 
						|
	      buffer_count++;
 | 
						|
 | 
						|
	      DBG (4,
 | 
						|
		   "reader_process: buffer %d: entering read request for %d "
 | 
						|
		   "bytes (buffer %d)\n", buffernumber + 1,
 | 
						|
		   bstat[buffernumber].lines * bpl, buffer_count);
 | 
						|
	      sigprocmask (SIG_BLOCK, &sigterm_set, 0);
 | 
						|
	      status = dev_read_req_enter (s, bstat[buffernumber].data,
 | 
						|
					   bstat[buffernumber].lines, bpl,
 | 
						|
					   &bstat[buffernumber].num_read,
 | 
						|
					   &bstat[buffernumber].id,
 | 
						|
					   bstat[buffernumber].bank,
 | 
						|
					   bstat[buffernumber].command);
 | 
						|
	      sigprocmask (SIG_UNBLOCK, &sigterm_set, 0);
 | 
						|
 | 
						|
 | 
						|
	      if (status == SANE_STATUS_GOOD)
 | 
						|
		{
 | 
						|
		  DBG (5, "reader_process: buffer %d: entered (line %d of %d,"
 | 
						|
		       " buffer %d)\n", buffernumber + 1, s->line,
 | 
						|
		       s->hw->lines, buffer_count);
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  DBG (1, "reader_process: buffer %d: failed to enter read "
 | 
						|
		       "request, status: %s\n", buffernumber + 1,
 | 
						|
		       sane_strstatus (status));
 | 
						|
		  return status;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	  if (!disable_double_buffering)
 | 
						|
	    {
 | 
						|
	      if (buffernumber == 1)
 | 
						|
		buffernumber = 0;
 | 
						|
	      else
 | 
						|
		buffernumber = 1;
 | 
						|
	    }
 | 
						|
	  /* This is said to fix the scanner hangs that reportedly show on
 | 
						|
	     some MFS-12000SP scanners.  */
 | 
						|
	  if (s->mode == 0 && (s->hw->flags & MUSTEK_FLAG_LINEART_FIX))
 | 
						|
	    usleep (200000);
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  fclose (fp);
 | 
						|
  free (bstat[0].data);
 | 
						|
  if (s->ld.buf[0])
 | 
						|
    free (s->ld.buf[0]);
 | 
						|
  s->ld.buf[0] = NULL;
 | 
						|
  if (extra)
 | 
						|
    free (extra);
 | 
						|
  close (fd);
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
attach_one_device (SANE_String_Const devname)
 | 
						|
{
 | 
						|
  Mustek_Device *dev;
 | 
						|
 | 
						|
  attach (devname, &dev, SANE_FALSE);
 | 
						|
  if (dev)
 | 
						|
    {
 | 
						|
      /* Keep track of newly attached devices so we can set options as
 | 
						|
         necessary.  */
 | 
						|
      if (new_dev_len >= new_dev_alloced)
 | 
						|
	{
 | 
						|
	  new_dev_alloced += 4;
 | 
						|
	  if (new_dev)
 | 
						|
	    new_dev =
 | 
						|
	      realloc (new_dev, new_dev_alloced * sizeof (new_dev[0]));
 | 
						|
	  else
 | 
						|
	    new_dev = malloc (new_dev_alloced * sizeof (new_dev[0]));
 | 
						|
	  if (!new_dev)
 | 
						|
	    {
 | 
						|
	      DBG (1, "attach_one_device: out of memory\n");
 | 
						|
	      return SANE_STATUS_NO_MEM;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      new_dev[new_dev_len++] = dev;
 | 
						|
    }
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
/**************************************************************************/
 | 
						|
/*                            SANE API calls                              */
 | 
						|
/**************************************************************************/
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
 | 
						|
{
 | 
						|
  SANE_Char line[PATH_MAX], *word, *end;
 | 
						|
  SANE_String_Const cp;
 | 
						|
  SANE_Int linenumber;
 | 
						|
  FILE *fp;
 | 
						|
 | 
						|
  DBG_INIT ();
 | 
						|
 | 
						|
  sanei_thread_init ();
 | 
						|
 | 
						|
#ifdef DBG_LEVEL
 | 
						|
  debug_level = DBG_LEVEL;
 | 
						|
#else
 | 
						|
  debug_level = 0;
 | 
						|
#endif
 | 
						|
 | 
						|
  DBG (2, "SANE mustek backend version %d.%d build %d from %s\n", SANE_CURRENT_MAJOR,
 | 
						|
       V_MINOR, BUILD, PACKAGE_STRING);
 | 
						|
 | 
						|
  if (version_code)
 | 
						|
    *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
 | 
						|
 | 
						|
  DBG (5, "sane_init: authorize %s null\n", authorize ? "!=" : "==");
 | 
						|
 | 
						|
#ifdef HAVE_SANEI_SCSI_OPEN_EXTENDED
 | 
						|
  DBG (5, "sane_init: using sanei_scsi_open_extended\n");
 | 
						|
#else
 | 
						|
  DBG (5, "sane_init: using sanei_scsi_open with buffer size = %d bytes\n",
 | 
						|
       sanei_scsi_max_request_size);
 | 
						|
#endif
 | 
						|
 | 
						|
  num_devices = 0;
 | 
						|
  force_wait = SANE_FALSE;
 | 
						|
  disable_double_buffering = SANE_FALSE;
 | 
						|
  first_dev = 0;
 | 
						|
  first_handle = 0;
 | 
						|
  devlist = 0;
 | 
						|
  new_dev = 0;
 | 
						|
  new_dev_len = 0;
 | 
						|
  new_dev_alloced = 0;
 | 
						|
 | 
						|
  fp = sanei_config_open (MUSTEK_CONFIG_FILE);
 | 
						|
  if (!fp)
 | 
						|
    {
 | 
						|
      /* default to /dev/scanner instead of insisting on config file */
 | 
						|
      DBG (3, "sane_init: couldn't find config file (%s), trying "
 | 
						|
	   "/dev/scanner directly\n", MUSTEK_CONFIG_FILE);
 | 
						|
      attach ("/dev/scanner", 0, SANE_FALSE);
 | 
						|
      return SANE_STATUS_GOOD;
 | 
						|
    }
 | 
						|
  linenumber = 0;
 | 
						|
  DBG (4, "sane_init: reading config file `%s'\n", MUSTEK_CONFIG_FILE);
 | 
						|
  while (sanei_config_read (line, sizeof (line), fp))
 | 
						|
    {
 | 
						|
      word = 0;
 | 
						|
      linenumber++;
 | 
						|
 | 
						|
      cp = sanei_config_get_string (line, &word);
 | 
						|
      if (!word || cp == line)
 | 
						|
	{
 | 
						|
	  DBG (5, "sane_init: config file line %d: ignoring empty line\n",
 | 
						|
	       linenumber);
 | 
						|
	  if (word)
 | 
						|
	    free (word);
 | 
						|
	  continue;
 | 
						|
	}
 | 
						|
      if (word[0] == '#')
 | 
						|
	{
 | 
						|
	  DBG (5, "sane_init: config file line %d: ignoring comment line\n",
 | 
						|
	       linenumber);
 | 
						|
	  free (word);
 | 
						|
	  continue;
 | 
						|
	}
 | 
						|
 | 
						|
      if (strcmp (word, "option") == 0)
 | 
						|
	{
 | 
						|
	  free (word);
 | 
						|
	  word = 0;
 | 
						|
	  cp = sanei_config_get_string (cp, &word);
 | 
						|
	  if (!word)
 | 
						|
	    {
 | 
						|
	      DBG (1,
 | 
						|
		   "sane_init: config file line %d: missing quotation mark?\n",
 | 
						|
		   linenumber);
 | 
						|
	      continue;
 | 
						|
	    }
 | 
						|
 | 
						|
	  if (strcmp (word, "strip-height") == 0)
 | 
						|
	    {
 | 
						|
	      free (word);
 | 
						|
	      word = 0;
 | 
						|
	      cp = sanei_config_get_string (cp, &word);
 | 
						|
	      if (!word)
 | 
						|
		{
 | 
						|
		  DBG (1,
 | 
						|
		       "sane_init: config file line %d: missing quotation mark?\n",
 | 
						|
		       linenumber);
 | 
						|
		  continue;
 | 
						|
		}
 | 
						|
 | 
						|
	      errno = 0;
 | 
						|
	      strip_height = strtod (word, &end);
 | 
						|
	      if (end == word)
 | 
						|
		{
 | 
						|
		  DBG (3, "sane-init: config file line %d: strip-height "
 | 
						|
		       "must have a parameter; using 1 inch\n", linenumber);
 | 
						|
		  strip_height = 1.0;
 | 
						|
		}
 | 
						|
	      if (errno)
 | 
						|
		{
 | 
						|
		  DBG (3, "sane-init: config file line %d: strip-height `%s' "
 | 
						|
		       "is invalid (%s); using 1 inch\n", linenumber,
 | 
						|
		       word, strerror (errno));
 | 
						|
		  strip_height = 1.0;
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  if (strip_height < 0.1)
 | 
						|
		    strip_height = 0.1;
 | 
						|
		  DBG (3, "sane_init: config file line %d: strip-height set "
 | 
						|
		       "to %g inches\n", linenumber, strip_height);
 | 
						|
		}
 | 
						|
	      if (word)
 | 
						|
		free (word);
 | 
						|
	      word = 0;
 | 
						|
	    }
 | 
						|
	  else if (strcmp (word, "force-wait") == 0)
 | 
						|
	    {
 | 
						|
	      DBG (3, "sane_init: config file line %d: enabling force-wait\n",
 | 
						|
		   linenumber);
 | 
						|
	      force_wait = SANE_TRUE;
 | 
						|
	      if (word)
 | 
						|
		free (word);
 | 
						|
	      word = 0;
 | 
						|
	    }
 | 
						|
	  else if (strcmp (word, "disable-double-buffering") == 0)
 | 
						|
	    {
 | 
						|
	      DBG (3, "sane_init: config file line %d: disabling "
 | 
						|
		   "double-buffering\n", linenumber);
 | 
						|
	      disable_double_buffering = SANE_TRUE;
 | 
						|
	      if (word)
 | 
						|
		free (word);
 | 
						|
	      word = 0;
 | 
						|
	    }
 | 
						|
	  else if (strcmp (word, "legal-size") == 0)
 | 
						|
	    {
 | 
						|
	      if (new_dev_len > 0)
 | 
						|
		{
 | 
						|
		  /* Check for 12000 LS, no way to find out automatically */
 | 
						|
		  if (strcmp (new_dev[new_dev_len - 1]->sane.model,
 | 
						|
			      "ScanExpress 12000SP") == 0)
 | 
						|
		    {
 | 
						|
		      new_dev[new_dev_len - 1]->x_range.max =
 | 
						|
			SANE_FIX (220.0);
 | 
						|
		      new_dev[new_dev_len - 1]->y_range.max =
 | 
						|
			SANE_FIX (360.0);
 | 
						|
		      new_dev[new_dev_len - 1]->sane.model =
 | 
						|
			"Paragon 1200 LS";
 | 
						|
		      DBG (3,
 | 
						|
			   "sane_init: config file line %d: enabling "
 | 
						|
			   "legal-size for %s\n", linenumber,
 | 
						|
			   new_dev[new_dev_len - 1]->sane.name);
 | 
						|
		    }
 | 
						|
		  else
 | 
						|
		    {
 | 
						|
		      DBG (3, "sane_init: config file line %d: option "
 | 
						|
			   "legal-size ignored, device %s is not a "
 | 
						|
			   "Paragon 1200 LS\n", linenumber,
 | 
						|
			   new_dev[new_dev_len - 1]->sane.name);
 | 
						|
		    }
 | 
						|
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  DBG (3, "sane_init: config file line %d: option "
 | 
						|
		       "legal-size ignored, was set before any device "
 | 
						|
		       "name\n", linenumber);
 | 
						|
		}
 | 
						|
	      if (word)
 | 
						|
		free (word);
 | 
						|
	      word = 0;
 | 
						|
	    }
 | 
						|
	  else if (strcmp (word, "linedistance-fix") == 0)
 | 
						|
	    {
 | 
						|
	      if (new_dev_len > 0)
 | 
						|
		{
 | 
						|
		  new_dev[new_dev_len - 1]->flags |= MUSTEK_FLAG_LD_FIX;
 | 
						|
		  DBG (3, "sane_init: config file line %d: enabling "
 | 
						|
		       "linedistance-fix for %s\n", linenumber,
 | 
						|
		       new_dev[new_dev_len - 1]->sane.name);
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  DBG (3, "sane_init: config file line %d: option "
 | 
						|
		       "linedistance-fix ignored, was set before any device "
 | 
						|
		       "name\n", linenumber);
 | 
						|
		}
 | 
						|
	      if (word)
 | 
						|
		free (word);
 | 
						|
	      word = 0;
 | 
						|
	    }
 | 
						|
	  else if (strcmp (word, "disable-backtracking") == 0)
 | 
						|
	    {
 | 
						|
	      if (new_dev_len > 0)
 | 
						|
		{
 | 
						|
		  new_dev[new_dev_len - 1]->flags |= MUSTEK_FLAG_NO_BACKTRACK;
 | 
						|
		  DBG (3, "sane_init: config file line %d: disabling "
 | 
						|
		       "backtracking for %s\n", linenumber,
 | 
						|
		       new_dev[new_dev_len - 1]->sane.name);
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  DBG (3, "sane_init: config file line %d: option "
 | 
						|
		       "disable-backtracking ignored, was set before any "
 | 
						|
		       "device name\n", linenumber);
 | 
						|
		}
 | 
						|
	      if (word)
 | 
						|
		free (word);
 | 
						|
	      word = 0;
 | 
						|
	    }
 | 
						|
	  else if (strcmp (word, "lineart-fix") == 0)
 | 
						|
	    {
 | 
						|
	      if (new_dev_len > 0)
 | 
						|
		{
 | 
						|
		  new_dev[new_dev_len - 1]->flags |= MUSTEK_FLAG_LINEART_FIX;
 | 
						|
		  DBG (3, "sane_init: config file line %d: enabling "
 | 
						|
		       "lineart-fix for %s\n", linenumber,
 | 
						|
		       new_dev[new_dev_len - 1]->sane.name);
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  DBG (3, "sane_init: config file line %d: option "
 | 
						|
		       "lineart-fix ignored, was set before any device name\n",
 | 
						|
		       linenumber);
 | 
						|
		}
 | 
						|
	      if (word)
 | 
						|
		free (word);
 | 
						|
	      word = 0;
 | 
						|
	    }
 | 
						|
	  else if (strcmp (word, "buffersize") == 0)
 | 
						|
	    {
 | 
						|
	      long buffer_size;
 | 
						|
 | 
						|
	      free (word);
 | 
						|
	      word = 0;
 | 
						|
	      cp = sanei_config_get_string (cp, &word);
 | 
						|
	      if (!word)
 | 
						|
		{
 | 
						|
		  DBG (1,
 | 
						|
		       "sane_init: config file line %d: missing quotation mark?\n",
 | 
						|
		       linenumber);
 | 
						|
		  continue;
 | 
						|
		}
 | 
						|
 | 
						|
	      errno = 0;
 | 
						|
	      buffer_size = strtol (word, &end, 0);
 | 
						|
 | 
						|
	      if (end == word)
 | 
						|
		{
 | 
						|
		  DBG (3, "sane-init: config file line %d: buffersize must "
 | 
						|
		       "have a parameter; using default (%d kb)\n",
 | 
						|
		       linenumber, new_dev[new_dev_len - 1]->max_buffer_size);
 | 
						|
		}
 | 
						|
	      if (errno)
 | 
						|
		{
 | 
						|
		  DBG (3, "sane-init: config file line %d: buffersize `%s' "
 | 
						|
		       "is invalid (%s); using default (%d kb)\n", linenumber,
 | 
						|
		       word, strerror (errno),
 | 
						|
		       new_dev[new_dev_len - 1]->max_buffer_size);
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  if (new_dev_len > 0)
 | 
						|
		    {
 | 
						|
		      if (buffer_size < 32.0)
 | 
						|
			buffer_size = 32.0;
 | 
						|
		      new_dev[new_dev_len - 1]->max_buffer_size =
 | 
						|
			buffer_size * 1024;
 | 
						|
		      DBG (3,
 | 
						|
			   "sane_init: config file line %d: buffersize set "
 | 
						|
			   "to %ld kb for %s\n", linenumber, buffer_size,
 | 
						|
			   new_dev[new_dev_len - 1]->sane.name);
 | 
						|
		    }
 | 
						|
		  else
 | 
						|
		    {
 | 
						|
		      DBG (3, "sane_init: config file line %d: option "
 | 
						|
			   "buffersize ignored, was set before any device "
 | 
						|
			   "name\n", linenumber);
 | 
						|
		    }
 | 
						|
		}
 | 
						|
	      if (word)
 | 
						|
		free (word);
 | 
						|
	      word = 0;
 | 
						|
	    }
 | 
						|
	  else if (strcmp (word, "blocksize") == 0)
 | 
						|
	    {
 | 
						|
	      long block_size;
 | 
						|
 | 
						|
	      free (word);
 | 
						|
	      word = 0;
 | 
						|
	      cp = sanei_config_get_string (cp, &word);
 | 
						|
	      if (!word)
 | 
						|
		{
 | 
						|
		  DBG (1,
 | 
						|
		       "sane_init: config file line %d: missing quotation mark?\n",
 | 
						|
		       linenumber);
 | 
						|
		  continue;
 | 
						|
		}
 | 
						|
 | 
						|
	      errno = 0;
 | 
						|
	      block_size = strtol (word, &end, 0);
 | 
						|
 | 
						|
	      if (end == word)
 | 
						|
		{
 | 
						|
		  DBG (3, "sane-init: config file line %d:: blocksize must "
 | 
						|
		       "have a parameter; using default (1 GB)\n",
 | 
						|
		       linenumber);
 | 
						|
		}
 | 
						|
	      if (errno)
 | 
						|
		{
 | 
						|
		  DBG (3, "sane-init: config file line %d: blocksize `%s' "
 | 
						|
		       "is invalid (%s); using default (1 GB)\n", linenumber,
 | 
						|
		       word, strerror (errno));
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  if (new_dev_len > 0)
 | 
						|
		    {
 | 
						|
		      if (block_size < 256.0)
 | 
						|
			block_size = 256.0;
 | 
						|
		      new_dev[new_dev_len - 1]->max_block_buffer_size =
 | 
						|
			block_size * 1024;
 | 
						|
		      DBG (3, "sane_init: config file line %d: blocksize set "
 | 
						|
			   "to %ld kb for %s\n", linenumber, block_size,
 | 
						|
			   new_dev[new_dev_len - 1]->sane.name);
 | 
						|
		    }
 | 
						|
		  else
 | 
						|
		    {
 | 
						|
		      DBG (3, "sane_init: config file line %d: option "
 | 
						|
			   "blocksize ignored, was set before any device "
 | 
						|
			   "name\n", linenumber);
 | 
						|
		    }
 | 
						|
		}
 | 
						|
	      if (word)
 | 
						|
		free (word);
 | 
						|
	      word = 0;
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      DBG (3, "sane_init: config file line %d: ignoring unknown "
 | 
						|
		   "option `%s'\n", linenumber, word);
 | 
						|
	      if (word)
 | 
						|
		free (word);
 | 
						|
	      word = 0;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  new_dev_len = 0;
 | 
						|
	  DBG (4, "sane_init: config file line %d: trying to attach `%s'\n",
 | 
						|
	       linenumber, line);
 | 
						|
	  sanei_config_attach_matching_devices (line, attach_one_device);
 | 
						|
	  if (word)
 | 
						|
	    free (word);
 | 
						|
	  word = 0;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  if (new_dev_alloced > 0)
 | 
						|
    {
 | 
						|
      new_dev_len = new_dev_alloced = 0;
 | 
						|
      free (new_dev);
 | 
						|
    }
 | 
						|
  fclose (fp);
 | 
						|
  DBG (5, "sane_init: end\n");
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
sane_exit (void)
 | 
						|
{
 | 
						|
  Mustek_Device *dev, *next;
 | 
						|
 | 
						|
  DBG (4, "sane_exit\n");
 | 
						|
  for (dev = first_dev; dev; dev = next)
 | 
						|
    {
 | 
						|
      next = dev->next;
 | 
						|
      free (dev->name);
 | 
						|
      free (dev);
 | 
						|
    }
 | 
						|
  if (devlist)
 | 
						|
    free (devlist);
 | 
						|
  devlist = 0;
 | 
						|
  first_dev = 0;
 | 
						|
  sanei_ab306_exit ();		/* may have to do some cleanup */
 | 
						|
  mustek_scsi_pp_exit ();
 | 
						|
  DBG (5, "sane_exit: finished\n");
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
 | 
						|
{
 | 
						|
  Mustek_Device *dev;
 | 
						|
  SANE_Int i;
 | 
						|
 | 
						|
  DBG (4, "sane_get_devices: %d devices %s\n", num_devices,
 | 
						|
       local_only ? "(local only)" : "");
 | 
						|
  if (devlist)
 | 
						|
    free (devlist);
 | 
						|
 | 
						|
  devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
 | 
						|
  if (!devlist)
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
 | 
						|
  i = 0;
 | 
						|
  for (dev = first_dev; i < num_devices; dev = dev->next)
 | 
						|
    devlist[i++] = &dev->sane;
 | 
						|
  devlist[i++] = 0;
 | 
						|
 | 
						|
  *device_list = devlist;
 | 
						|
  DBG (5, "sane_get_devices: end\n");
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_open (SANE_String_Const devicename, SANE_Handle * handle)
 | 
						|
{
 | 
						|
  Mustek_Device *dev;
 | 
						|
  SANE_Status status;
 | 
						|
  Mustek_Scanner *s;
 | 
						|
 | 
						|
  if (!devicename)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_open: devicename is null!\n");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
  if (!handle)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_open: handle is null!\n");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
  DBG (4, "sane_open: devicename=%s\n", devicename);
 | 
						|
 | 
						|
  if (devicename[0])
 | 
						|
    {
 | 
						|
      for (dev = first_dev; dev; dev = dev->next)
 | 
						|
	if (strcmp (dev->sane.name, devicename) == 0)
 | 
						|
	  break;
 | 
						|
 | 
						|
      if (!dev)
 | 
						|
	{
 | 
						|
	  status = attach (devicename, &dev, SANE_TRUE);
 | 
						|
	  if (status != SANE_STATUS_GOOD)
 | 
						|
	    return status;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    /* empty devicname -> use first device */
 | 
						|
    dev = first_dev;
 | 
						|
 | 
						|
  if (!dev)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  s = malloc (sizeof (*s));
 | 
						|
  if (!s)
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
  memset (s, 0, sizeof (*s));
 | 
						|
  s->fd = -1;
 | 
						|
  s->pipe = -1;
 | 
						|
  s->hw = dev;
 | 
						|
  s->ld.ld_line = 0;
 | 
						|
  s->halftone_pattern = malloc (8 * 8 * sizeof (SANE_Int));
 | 
						|
  if (!s->halftone_pattern)
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
  init_options (s);
 | 
						|
 | 
						|
  /* insert newly opened handle into list of open handles: */
 | 
						|
  s->next = first_handle;
 | 
						|
  first_handle = s;
 | 
						|
 | 
						|
  *handle = s;
 | 
						|
  DBG (4, "sane_open: finished (handle=%p)\n", (void *) s);
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
sane_close (SANE_Handle handle)
 | 
						|
{
 | 
						|
  Mustek_Scanner *prev, *s;
 | 
						|
 | 
						|
  DBG (4, "sane_close: handle=%p\n", handle);
 | 
						|
  /* remove handle from list of open handles: */
 | 
						|
  prev = 0;
 | 
						|
  for (s = first_handle; s; s = s->next)
 | 
						|
    {
 | 
						|
      if (s == handle)
 | 
						|
	break;
 | 
						|
      prev = s;
 | 
						|
    }
 | 
						|
  if (!s)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_close: invalid handle %p\n", handle);
 | 
						|
      return;			/* oops, not a handle we know about */
 | 
						|
    }
 | 
						|
 | 
						|
  if (s->scanning)
 | 
						|
    do_stop (handle);
 | 
						|
 | 
						|
  if (s->ld.buf[0])
 | 
						|
    free (s->ld.buf[0]);
 | 
						|
  if (s->val[OPT_MODE].s)
 | 
						|
    free (s->val[OPT_MODE].s);
 | 
						|
  if (s->val[OPT_BIT_DEPTH].s)
 | 
						|
    free (s->val[OPT_BIT_DEPTH].s);
 | 
						|
  if (s->val[OPT_SPEED].s)
 | 
						|
    free (s->val[OPT_SPEED].s);
 | 
						|
  if (s->val[OPT_SOURCE].s)
 | 
						|
    free (s->val[OPT_SOURCE].s);
 | 
						|
  if (s->val[OPT_HALFTONE_DIMENSION].s)
 | 
						|
    free (s->val[OPT_HALFTONE_DIMENSION].s);
 | 
						|
  if (s->halftone_pattern)
 | 
						|
    free (s->halftone_pattern);
 | 
						|
  if (prev)
 | 
						|
    prev->next = s->next;
 | 
						|
  else
 | 
						|
    first_handle = s->next;
 | 
						|
  free (handle);
 | 
						|
  handle = 0;
 | 
						|
  DBG (5, "sane_close: finished\n");
 | 
						|
}
 | 
						|
 | 
						|
const SANE_Option_Descriptor *
 | 
						|
sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
 | 
						|
{
 | 
						|
  Mustek_Scanner *s = handle;
 | 
						|
 | 
						|
  if (((unsigned) option >= NUM_OPTIONS) || (option < 0))
 | 
						|
    {
 | 
						|
      DBG (4, "sane_get_option_descriptor: option %d >= NUM_OPTIONS or < 0\n",
 | 
						|
	   option);
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
  if (!s)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_get_option_descriptor: handle is null!\n");
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
  if (s->opt[option].name && s->opt[option].name[0] != 0)
 | 
						|
    DBG (5, "sane_get_option_descriptor for option %s (%sactive%s)\n",
 | 
						|
	 s->opt[option].name,
 | 
						|
	 s->opt[option].cap & SANE_CAP_INACTIVE ? "in" : "",
 | 
						|
	 s->opt[option].cap & SANE_CAP_ADVANCED ? ", advanced" : "");
 | 
						|
  else
 | 
						|
    DBG (5, "sane_get_option_descriptor for option \"%s\" (%sactive%s)\n",
 | 
						|
	 s->opt[option].title,
 | 
						|
	 s->opt[option].cap & SANE_CAP_INACTIVE ? "in" : "",
 | 
						|
	 s->opt[option].cap & SANE_CAP_ADVANCED ? ", advanced" : "");
 | 
						|
  return s->opt + option;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_control_option (SANE_Handle handle, SANE_Int option,
 | 
						|
		     SANE_Action action, void *val, SANE_Int * info)
 | 
						|
{
 | 
						|
  Mustek_Scanner *s = handle;
 | 
						|
  SANE_Status status;
 | 
						|
  SANE_Word w, cap;
 | 
						|
 | 
						|
  if (((unsigned) option >= NUM_OPTIONS) || (option < 0))
 | 
						|
    {
 | 
						|
      DBG (4, "sane_control_option: option %d < 0 or >= NUM_OPTIONS\n",
 | 
						|
	   option);
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
  if (!s)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_control_option: handle is null!\n");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  if (s->opt[option].type != SANE_TYPE_BUTTON && !val)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_control_option: val is null!\n");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  if (s->opt[option].name && s->opt[option].name[0] != 0)
 | 
						|
    DBG (5, "sane_control_option (%s option %s)\n",
 | 
						|
	 action == SANE_ACTION_GET_VALUE ? "get" :
 | 
						|
	 (action == SANE_ACTION_SET_VALUE ? "set" : "unknown action with"),
 | 
						|
	 s->opt[option].name);
 | 
						|
  else
 | 
						|
    DBG (5, "sane_control_option (%s option \"%s\")\n",
 | 
						|
	 action == SANE_ACTION_GET_VALUE ? "get" :
 | 
						|
	 (action == SANE_ACTION_SET_VALUE ? "set" : "unknown action with"),
 | 
						|
	 s->opt[option].title);
 | 
						|
 | 
						|
  if (info)
 | 
						|
    *info = 0;
 | 
						|
 | 
						|
  if (s->scanning)
 | 
						|
    {
 | 
						|
      DBG (4, "sane_control_option: don't use while scanning (option %s)\n",
 | 
						|
	   s->opt[option].name);
 | 
						|
      return SANE_STATUS_DEVICE_BUSY;
 | 
						|
    }
 | 
						|
 | 
						|
  cap = s->opt[option].cap;
 | 
						|
 | 
						|
  if (!SANE_OPTION_IS_ACTIVE (cap))
 | 
						|
    {
 | 
						|
      DBG (4, "sane_control_option: option %s is inactive\n",
 | 
						|
	   s->opt[option].name);
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  if (action == SANE_ACTION_GET_VALUE)
 | 
						|
    {
 | 
						|
      switch (option)
 | 
						|
	{
 | 
						|
	  /* word options: */
 | 
						|
	case OPT_PREVIEW:
 | 
						|
	case OPT_FAST_PREVIEW:
 | 
						|
	case OPT_RESOLUTION:
 | 
						|
	case OPT_FAST_GRAY_MODE:
 | 
						|
	case OPT_TL_X:
 | 
						|
	case OPT_TL_Y:
 | 
						|
	case OPT_BR_X:
 | 
						|
	case OPT_BR_Y:
 | 
						|
	case OPT_NUM_OPTS:
 | 
						|
	case OPT_BRIGHTNESS:
 | 
						|
	case OPT_BRIGHTNESS_R:
 | 
						|
	case OPT_BRIGHTNESS_G:
 | 
						|
	case OPT_BRIGHTNESS_B:
 | 
						|
	case OPT_CONTRAST:
 | 
						|
	case OPT_CONTRAST_R:
 | 
						|
	case OPT_CONTRAST_G:
 | 
						|
	case OPT_CONTRAST_B:
 | 
						|
	case OPT_CUSTOM_GAMMA:
 | 
						|
	case OPT_QUALITY_CAL:
 | 
						|
	case OPT_LAMP_OFF_TIME:
 | 
						|
	  *(SANE_Word *) val = s->val[option].w;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	  /* word-array options: */
 | 
						|
	case OPT_GAMMA_VECTOR:
 | 
						|
	case OPT_GAMMA_VECTOR_R:
 | 
						|
	case OPT_GAMMA_VECTOR_G:
 | 
						|
	case OPT_GAMMA_VECTOR_B:
 | 
						|
	case OPT_HALFTONE_PATTERN:
 | 
						|
	  memcpy (val, s->val[option].wa, s->opt[option].size);
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	  /* string options: */
 | 
						|
	case OPT_SPEED:
 | 
						|
	case OPT_SOURCE:
 | 
						|
	case OPT_MODE:
 | 
						|
	case OPT_BIT_DEPTH:
 | 
						|
	case OPT_HALFTONE_DIMENSION:
 | 
						|
	  strcpy (val, s->val[option].s);
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else if (action == SANE_ACTION_SET_VALUE)
 | 
						|
    {
 | 
						|
      if (!SANE_OPTION_IS_SETTABLE (cap))
 | 
						|
	{
 | 
						|
	  DBG (4, "sane_control_option: option %s is not setable\n",
 | 
						|
	       s->opt[option].name);
 | 
						|
	  return SANE_STATUS_INVAL;
 | 
						|
	}
 | 
						|
 | 
						|
      status = constrain_value (s, option, val, info);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	{
 | 
						|
	  DBG (4, "sane_control_option: constrain_value error (option %s)\n",
 | 
						|
	       s->opt[option].name);
 | 
						|
	  return status;
 | 
						|
	}
 | 
						|
 | 
						|
      switch (option)
 | 
						|
	{
 | 
						|
	case OPT_LAMP_OFF_BUTTON:
 | 
						|
	  {
 | 
						|
	    SANE_Int old_time = lamp_off_time;
 | 
						|
	    SANE_Status status;
 | 
						|
 | 
						|
	    status = dev_open (s->hw->sane.name, s, sense_handler);
 | 
						|
	    if (status != SANE_STATUS_GOOD)
 | 
						|
	      return status;
 | 
						|
	    lamp_off_time = 0;
 | 
						|
	    set_window_pro (s);
 | 
						|
	    lamp_off_time = old_time;
 | 
						|
	    dev_close (s);
 | 
						|
	    return SANE_STATUS_GOOD;
 | 
						|
	  }
 | 
						|
	  /* (mostly) side-effect-free word options: */
 | 
						|
	case OPT_RESOLUTION:
 | 
						|
	case OPT_TL_X:
 | 
						|
	case OPT_BR_X:
 | 
						|
	case OPT_TL_Y:
 | 
						|
	case OPT_BR_Y:
 | 
						|
	  if (info)
 | 
						|
	    *info |= SANE_INFO_RELOAD_PARAMS;
 | 
						|
	  /* fall through */
 | 
						|
	case OPT_PREVIEW:
 | 
						|
	case OPT_FAST_PREVIEW:
 | 
						|
	case OPT_FAST_GRAY_MODE:
 | 
						|
	case OPT_BRIGHTNESS:
 | 
						|
	case OPT_BRIGHTNESS_R:
 | 
						|
	case OPT_BRIGHTNESS_G:
 | 
						|
	case OPT_BRIGHTNESS_B:
 | 
						|
	case OPT_CONTRAST:
 | 
						|
	case OPT_CONTRAST_R:
 | 
						|
	case OPT_CONTRAST_G:
 | 
						|
	case OPT_CONTRAST_B:
 | 
						|
	case OPT_QUALITY_CAL:
 | 
						|
	case OPT_LAMP_OFF_TIME:
 | 
						|
	  s->val[option].w = *(SANE_Word *) val;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	  /* side-effect-free word-array options: */
 | 
						|
	case OPT_HALFTONE_PATTERN:
 | 
						|
	case OPT_GAMMA_VECTOR:
 | 
						|
	case OPT_GAMMA_VECTOR_R:
 | 
						|
	case OPT_GAMMA_VECTOR_G:
 | 
						|
	case OPT_GAMMA_VECTOR_B:
 | 
						|
	  memcpy (s->val[option].wa, val, s->opt[option].size);
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	  /* side-effect-free single-string options: */
 | 
						|
	case OPT_SPEED:
 | 
						|
	  if (s->val[option].s)
 | 
						|
	    free (s->val[option].s);
 | 
						|
	  s->val[option].s = strdup (val);
 | 
						|
	  if (!s->val[option].s)
 | 
						|
	    return SANE_STATUS_NO_MEM;
 | 
						|
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	  /* side-effect-free string list options: */
 | 
						|
	case OPT_BIT_DEPTH:
 | 
						|
	  {
 | 
						|
	    SANE_Char *old_val = s->val[option].s;
 | 
						|
 | 
						|
	    if (old_val)
 | 
						|
	      {
 | 
						|
		if (strcmp (old_val, val) == 0)
 | 
						|
		  return SANE_STATUS_GOOD;	/* no change */
 | 
						|
		free (old_val);
 | 
						|
	      }
 | 
						|
	    s->val[option].s = strdup (val);
 | 
						|
	    if (!s->val[option].s)
 | 
						|
	      return SANE_STATUS_NO_MEM;
 | 
						|
	    return SANE_STATUS_GOOD;
 | 
						|
	  }
 | 
						|
 | 
						|
	  /* options with side-effects: */
 | 
						|
	case OPT_CUSTOM_GAMMA:
 | 
						|
	  w = *(SANE_Word *) val;
 | 
						|
 | 
						|
	  if (w == s->val[OPT_CUSTOM_GAMMA].w)
 | 
						|
	    return SANE_STATUS_GOOD;	/* no change */
 | 
						|
 | 
						|
	  if (info)
 | 
						|
	    *info |= SANE_INFO_RELOAD_OPTIONS;
 | 
						|
 | 
						|
	  s->val[OPT_CUSTOM_GAMMA].w = w;
 | 
						|
	  if (w)
 | 
						|
	    {
 | 
						|
	      SANE_String_Const mode = s->val[OPT_MODE].s;
 | 
						|
 | 
						|
	      if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
 | 
						|
		s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
	      else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0)
 | 
						|
		{
 | 
						|
		  s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		  s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		  s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		  s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		}
 | 
						|
	      else if ((strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0)
 | 
						|
		       && (s->hw->flags & MUSTEK_FLAG_PRO))
 | 
						|
		{
 | 
						|
		  s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
 | 
						|
	      s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
 | 
						|
	      s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
 | 
						|
	      s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
 | 
						|
	    }
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_MODE:
 | 
						|
	  {
 | 
						|
	    SANE_Char *old_val = s->val[option].s;
 | 
						|
	    SANE_Int halftoning, binary;
 | 
						|
 | 
						|
	    if (old_val)
 | 
						|
	      {
 | 
						|
		if (strcmp (old_val, val) == 0)
 | 
						|
		  return SANE_STATUS_GOOD;	/* no change */
 | 
						|
		free (old_val);
 | 
						|
	      }
 | 
						|
	    if (info)
 | 
						|
	      *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
 | 
						|
 | 
						|
	    s->val[option].s = strdup (val);
 | 
						|
	    if (!s->val[option].s)
 | 
						|
	      return SANE_STATUS_NO_MEM;
 | 
						|
 | 
						|
	    s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
 | 
						|
	    s->opt[OPT_BRIGHTNESS_R].cap |= SANE_CAP_INACTIVE;
 | 
						|
	    s->opt[OPT_BRIGHTNESS_G].cap |= SANE_CAP_INACTIVE;
 | 
						|
	    s->opt[OPT_BRIGHTNESS_B].cap |= SANE_CAP_INACTIVE;
 | 
						|
	    s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
 | 
						|
	    s->opt[OPT_CONTRAST_R].cap |= SANE_CAP_INACTIVE;
 | 
						|
	    s->opt[OPT_CONTRAST_G].cap |= SANE_CAP_INACTIVE;
 | 
						|
	    s->opt[OPT_CONTRAST_B].cap |= SANE_CAP_INACTIVE;
 | 
						|
	    s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
 | 
						|
	    s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
 | 
						|
	    s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
 | 
						|
	    s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
 | 
						|
	    s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
 | 
						|
	    s->opt[OPT_HALFTONE_DIMENSION].cap |= SANE_CAP_INACTIVE;
 | 
						|
	    s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
	    halftoning = strcmp (val, SANE_VALUE_SCAN_MODE_HALFTONE) == 0;
 | 
						|
	    binary = (halftoning || strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0);
 | 
						|
 | 
						|
	    if (binary)
 | 
						|
	      {
 | 
						|
		/* enable brightness/contrast for  when in a binary mode */
 | 
						|
		s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		/* The SE and paragon models support only threshold
 | 
						|
		   in lineart */
 | 
						|
		if (!(s->hw->flags & MUSTEK_FLAG_SE)
 | 
						|
		    && !(s->hw->flags & MUSTEK_FLAG_PRO))
 | 
						|
		  s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
		if (halftoning)
 | 
						|
		  {
 | 
						|
		    s->opt[OPT_HALFTONE_DIMENSION].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		    encode_halftone (s);
 | 
						|
		    if (s->custom_halftone_pattern)
 | 
						|
		      {
 | 
						|
			s->opt[OPT_HALFTONE_PATTERN].cap
 | 
						|
			  &= ~SANE_CAP_INACTIVE;
 | 
						|
		      }
 | 
						|
		    else
 | 
						|
		      {
 | 
						|
			s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
 | 
						|
		      }
 | 
						|
		  }
 | 
						|
	      }
 | 
						|
	    else
 | 
						|
	      {
 | 
						|
		s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
	      }
 | 
						|
 | 
						|
	    if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
 | 
						|
	      {
 | 
						|
		if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
 | 
						|
		  {
 | 
						|
		    s->opt[OPT_BRIGHTNESS_R].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		    s->opt[OPT_BRIGHTNESS_G].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		    s->opt[OPT_BRIGHTNESS_B].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		    s->opt[OPT_CONTRAST_R].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		    s->opt[OPT_CONTRAST_G].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		    s->opt[OPT_CONTRAST_B].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		  }
 | 
						|
		else
 | 
						|
		  {
 | 
						|
		    s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		    s->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		  }
 | 
						|
	      }
 | 
						|
	    else if (s->hw->flags & MUSTEK_FLAG_PRO)
 | 
						|
	      {
 | 
						|
		if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_GRAY) == 0)
 | 
						|
		  s->opt[OPT_FAST_GRAY_MODE].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		else
 | 
						|
		  s->opt[OPT_FAST_GRAY_MODE].cap |= SANE_CAP_INACTIVE;
 | 
						|
		if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
 | 
						|
		  s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		else
 | 
						|
		  s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
 | 
						|
	      }
 | 
						|
	    else if (s->hw->flags & MUSTEK_FLAG_SE_PLUS)
 | 
						|
	      {
 | 
						|
		if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0)
 | 
						|
		  s->opt[OPT_BIT_DEPTH].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		else
 | 
						|
		  s->opt[OPT_BIT_DEPTH].cap |= SANE_CAP_INACTIVE;
 | 
						|
	      }
 | 
						|
 | 
						|
	    if (s->val[OPT_CUSTOM_GAMMA].w)
 | 
						|
	      {
 | 
						|
		if (strcmp (val, SANE_VALUE_SCAN_MODE_GRAY) == 0)
 | 
						|
		  s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		else if (strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) == 0)
 | 
						|
		  {
 | 
						|
		    s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		    s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		    s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		    s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		  }
 | 
						|
	      }
 | 
						|
	    return SANE_STATUS_GOOD;
 | 
						|
	  }
 | 
						|
 | 
						|
	case OPT_HALFTONE_DIMENSION:
 | 
						|
	  /* halftone pattern dimension affects halftone pattern option: */
 | 
						|
	  {
 | 
						|
	    if (strcmp (s->val[option].s, (SANE_String) val) == 0)
 | 
						|
	      return SANE_STATUS_GOOD;	/* no change */
 | 
						|
 | 
						|
	    if (info)
 | 
						|
	      *info |= SANE_INFO_RELOAD_OPTIONS;
 | 
						|
 | 
						|
	    s->val[option].s = strdup (val);
 | 
						|
	    if (!s->val[option].s)
 | 
						|
	      return SANE_STATUS_NO_MEM;
 | 
						|
	    encode_halftone (s);
 | 
						|
	    s->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE;
 | 
						|
	    if (s->custom_halftone_pattern)
 | 
						|
	      {
 | 
						|
		s->opt[OPT_HALFTONE_PATTERN].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		/* BUG: The SANE standard does nor allow to change the option
 | 
						|
		   size at run time */
 | 
						|
		s->opt[OPT_HALFTONE_PATTERN].size =
 | 
						|
		  (s->halftone_pattern_type & 0x0f) * sizeof (SANE_Word);
 | 
						|
	      }
 | 
						|
	    return SANE_STATUS_GOOD;
 | 
						|
	  }
 | 
						|
 | 
						|
	case OPT_SOURCE:
 | 
						|
	  if (info)
 | 
						|
	    *info |= SANE_INFO_RELOAD_OPTIONS;
 | 
						|
	  if (s->val[option].s)
 | 
						|
	    free (s->val[option].s);
 | 
						|
	  s->val[option].s = strdup (val);
 | 
						|
	  if (!s->val[option].s)
 | 
						|
	    return SANE_STATUS_NO_MEM;
 | 
						|
 | 
						|
	  if (strcmp (val, "Transparency Adapter") == 0)
 | 
						|
	    {
 | 
						|
	      s->opt[OPT_TL_X].constraint.range = &s->hw->x_trans_range;
 | 
						|
	      s->opt[OPT_TL_Y].constraint.range = &s->hw->y_trans_range;
 | 
						|
	      s->opt[OPT_BR_X].constraint.range = &s->hw->x_trans_range;
 | 
						|
	      s->opt[OPT_BR_Y].constraint.range = &s->hw->y_trans_range;
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      s->opt[OPT_TL_X].constraint.range = &s->hw->x_range;
 | 
						|
	      s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range;
 | 
						|
	      s->opt[OPT_BR_X].constraint.range = &s->hw->x_range;
 | 
						|
	      s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range;
 | 
						|
	    }
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  DBG (4, "sane_control_option: unknown action for option %s\n",
 | 
						|
       s->opt[option].name);
 | 
						|
  return SANE_STATUS_INVAL;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
 | 
						|
{
 | 
						|
  Mustek_Scanner *s = handle;
 | 
						|
  SANE_String_Const mode;
 | 
						|
 | 
						|
  if (!s)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_get_parameters: handle is null!\n");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  if (!s->scanning)
 | 
						|
    {
 | 
						|
      double width, height, dpi;
 | 
						|
 | 
						|
      memset (&s->params, 0, sizeof (s->params));
 | 
						|
 | 
						|
      width = SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w);
 | 
						|
      height = SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w);
 | 
						|
      dpi = SANE_UNFIX (s->val[OPT_RESOLUTION].w);
 | 
						|
 | 
						|
      /* make best-effort guess at what parameters will look like once
 | 
						|
         scanning starts.  */
 | 
						|
      if (dpi > 0.0 && width > 0.0 && height > 0.0)
 | 
						|
	{
 | 
						|
	  double dots_per_mm = dpi / MM_PER_INCH;
 | 
						|
 | 
						|
	  s->params.pixels_per_line = width * dots_per_mm;
 | 
						|
	  s->params.lines = height * dots_per_mm;
 | 
						|
	}
 | 
						|
      encode_halftone (s);
 | 
						|
      mode = s->val[OPT_MODE].s;
 | 
						|
      if (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0 || strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0)
 | 
						|
	{
 | 
						|
	  s->params.format = SANE_FRAME_GRAY;
 | 
						|
	  s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8;
 | 
						|
	  s->params.depth = 1;
 | 
						|
	}
 | 
						|
      else if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
 | 
						|
	{
 | 
						|
	  s->params.format = SANE_FRAME_GRAY;
 | 
						|
	  s->params.bytes_per_line = s->params.pixels_per_line;
 | 
						|
	  s->params.depth = 8;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  /* it's one of the color modes... */
 | 
						|
 | 
						|
	  if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
 | 
						|
	    {
 | 
						|
	      s->params.format = SANE_FRAME_RED + s->pass;
 | 
						|
	      s->params.bytes_per_line = s->params.pixels_per_line;
 | 
						|
	      s->params.depth = 8;
 | 
						|
	    }
 | 
						|
	  else			/* 1-pass */
 | 
						|
	    {
 | 
						|
	      if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0)
 | 
						|
		{
 | 
						|
		  s->params.bytes_per_line = 6 * s->params.pixels_per_line;
 | 
						|
		  s->params.depth = 16;
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  s->params.bytes_per_line = 3 * s->params.pixels_per_line;
 | 
						|
		  s->params.depth = 8;
 | 
						|
		}
 | 
						|
	      s->params.format = SANE_FRAME_RGB;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else if ((s->mode & MUSTEK_MODE_COLOR)
 | 
						|
	   && (s->hw->flags & MUSTEK_FLAG_THREE_PASS))
 | 
						|
    s->params.format = SANE_FRAME_RED + s->pass;
 | 
						|
  s->params.last_frame = (s->params.format != SANE_FRAME_RED
 | 
						|
			  && s->params.format != SANE_FRAME_GREEN);
 | 
						|
  if (params)
 | 
						|
    *params = s->params;
 | 
						|
  DBG (4, "sane_get_parameters: frame = %d; last_frame = %s; depth = %d\n",
 | 
						|
       s->params.format, s->params.last_frame ? "true" : "false",
 | 
						|
       s->params.depth);
 | 
						|
  DBG (4, "sane_get_parameters: lines = %d; ppl = %d; bpl = %d\n",
 | 
						|
       s->params.lines, s->params.pixels_per_line, s->params.bytes_per_line);
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_start (SANE_Handle handle)
 | 
						|
{
 | 
						|
  Mustek_Scanner *s = handle;
 | 
						|
  SANE_Status status;
 | 
						|
  int fds[2];
 | 
						|
  struct SIGACTION act;
 | 
						|
 | 
						|
  if (!s)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_start: handle is null!\n");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (4, "sane_start\n");
 | 
						|
  /* First make sure we have a current parameter set.  Some of the
 | 
						|
     parameters will be overwritten below, but that's OK.  */
 | 
						|
  status = sane_get_parameters (s, 0);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    return status;
 | 
						|
 | 
						|
  /* Check for inconsistencies */
 | 
						|
 | 
						|
  if (s->val[OPT_TL_X].w > s->val[OPT_BR_X].w)
 | 
						|
    {
 | 
						|
      DBG (0, "sane_start: %s (%.1f mm) is bigger than %s (%.1f mm) "
 | 
						|
	   "-- aborting\n",
 | 
						|
	   s->opt[OPT_TL_X].title, SANE_UNFIX (s->val[OPT_TL_X].w),
 | 
						|
	   s->opt[OPT_BR_X].title, SANE_UNFIX (s->val[OPT_BR_X].w));
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
  if (s->val[OPT_TL_Y].w > s->val[OPT_BR_Y].w)
 | 
						|
    {
 | 
						|
      DBG (0, "sane_start: %s (%.1f mm) is bigger than %s (%.1f mm) "
 | 
						|
	   "-- aborting\n",
 | 
						|
	   s->opt[OPT_TL_Y].title, SANE_UNFIX (s->val[OPT_TL_Y].w),
 | 
						|
	   s->opt[OPT_BR_Y].title, SANE_UNFIX (s->val[OPT_BR_Y].w));
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  s->total_bytes = 0;
 | 
						|
 | 
						|
  if (s->fd < 0)
 | 
						|
    {
 | 
						|
      /* this is the first (and maybe only) pass... */
 | 
						|
      SANE_String_Const mode;
 | 
						|
      struct timeval start;
 | 
						|
 | 
						|
      /* save start time */
 | 
						|
      gettimeofday (&start, 0);
 | 
						|
      s->start_time = start.tv_sec;
 | 
						|
      /* translate options into s->mode for convenient access: */
 | 
						|
      mode = s->val[OPT_MODE].s;
 | 
						|
      if (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0)
 | 
						|
	s->mode = MUSTEK_MODE_LINEART;
 | 
						|
      else if (strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0)
 | 
						|
	s->mode = MUSTEK_MODE_HALFTONE;
 | 
						|
      else if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
 | 
						|
	s->mode = MUSTEK_MODE_GRAY;
 | 
						|
      else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0)
 | 
						|
	s->mode = MUSTEK_MODE_COLOR;
 | 
						|
 | 
						|
      /* scanner dependant specials */
 | 
						|
      s->one_pass_color_scan = SANE_FALSE;
 | 
						|
      if ((s->mode & MUSTEK_MODE_COLOR)
 | 
						|
	  && !(s->hw->flags & MUSTEK_FLAG_THREE_PASS))
 | 
						|
	{
 | 
						|
	  s->one_pass_color_scan = SANE_TRUE;
 | 
						|
	}
 | 
						|
 | 
						|
      s->resolution_code = encode_resolution (s);
 | 
						|
 | 
						|
      if (s->val[OPT_PREVIEW].w && s->val[OPT_FAST_PREVIEW].w)
 | 
						|
	{
 | 
						|
	  if (s->hw->flags & MUSTEK_FLAG_THREE_PASS)
 | 
						|
	    {
 | 
						|
	      if (s->mode & MUSTEK_MODE_COLOR)
 | 
						|
		{
 | 
						|
		  /* Force gray-scale mode when previewing.  */
 | 
						|
		  s->mode = MUSTEK_MODE_GRAY;
 | 
						|
		  s->params.format = SANE_FRAME_GRAY;
 | 
						|
		  s->params.bytes_per_line = s->params.pixels_per_line;
 | 
						|
		  s->params.last_frame = SANE_TRUE;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	  else if (s->hw->flags & MUSTEK_FLAG_SE)
 | 
						|
	    {
 | 
						|
	      /* use 36 dpi color in any case */
 | 
						|
	      s->mode = MUSTEK_MODE_COLOR;
 | 
						|
	      s->params.format = SANE_FRAME_RGB;
 | 
						|
	      s->params.depth = 8;
 | 
						|
	      s->one_pass_color_scan = SANE_TRUE;
 | 
						|
	      s->resolution_code = 36;
 | 
						|
	    }
 | 
						|
	  else if (s->hw->flags & MUSTEK_FLAG_PARAGON_1)
 | 
						|
	    {
 | 
						|
	      /* use 36 dpi */
 | 
						|
	      s->resolution_code = 36;
 | 
						|
	    }
 | 
						|
	  else if (s->hw->flags & MUSTEK_FLAG_PRO)
 | 
						|
	    {
 | 
						|
	      /* use 30 dpi color mode */
 | 
						|
	      s->mode = MUSTEK_MODE_COLOR;
 | 
						|
	      s->params.format = SANE_FRAME_RGB;
 | 
						|
	      s->params.depth = 8;
 | 
						|
	      s->one_pass_color_scan = SANE_TRUE;
 | 
						|
	      s->resolution_code = 30;
 | 
						|
	    }
 | 
						|
	  DBG (4, "sane_start: use fast preview (res=%d dpi)\n",
 | 
						|
	       s->resolution_code);
 | 
						|
	}
 | 
						|
 | 
						|
      status = dev_open (s->hw->sane.name, s, sense_handler);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	return status;
 | 
						|
    }
 | 
						|
 | 
						|
  status = dev_wait_ready (s);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_start: wait_ready() failed: %s\n",
 | 
						|
	   sane_strstatus (status));
 | 
						|
      goto stop_scanner_and_return;
 | 
						|
    }
 | 
						|
 | 
						|
  if (!(s->hw->flags & MUSTEK_FLAG_SCSI_PP))
 | 
						|
    {
 | 
						|
      /* SCSI-over-parallel port doesn't seem to like being inquired here */
 | 
						|
      status = inquiry (s);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	{
 | 
						|
	  DBG (1, "sane_start: inquiry command failed: %s\n",
 | 
						|
	       sane_strstatus (status));
 | 
						|
	  goto stop_scanner_and_return;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  if ((strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0) &&
 | 
						|
      !(s->hw->flags & MUSTEK_FLAG_ADF_READY))
 | 
						|
    {
 | 
						|
      DBG (2, "sane_start: automatic document feeder is out of documents\n");
 | 
						|
      status = SANE_STATUS_NO_DOCS;
 | 
						|
      goto stop_scanner_and_return;
 | 
						|
    }
 | 
						|
 | 
						|
  if (s->hw->flags & MUSTEK_FLAG_SE)
 | 
						|
    {
 | 
						|
      status = set_window_se (s, 0);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	{
 | 
						|
	  DBG (1, "sane_start: set window command failed: %s\n",
 | 
						|
	       sane_strstatus (status));
 | 
						|
	  goto stop_scanner_and_return;
 | 
						|
	}
 | 
						|
 | 
						|
      s->scanning = SANE_TRUE;
 | 
						|
      s->cancelled = SANE_FALSE;
 | 
						|
 | 
						|
      dev_wait_ready (s);
 | 
						|
 | 
						|
      status = get_window (s, &s->params.bytes_per_line,
 | 
						|
			   &s->params.lines, &s->params.pixels_per_line);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	{
 | 
						|
	  DBG (1, "sane_start: get window command failed: %s\n",
 | 
						|
	       sane_strstatus (status));
 | 
						|
	  goto stop_scanner_and_return;
 | 
						|
	}
 | 
						|
 | 
						|
      status = calibration_se (s);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto stop_scanner_and_return;
 | 
						|
 | 
						|
      status = start_scan (s);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto stop_scanner_and_return;
 | 
						|
 | 
						|
      status = send_gamma_table_se (s);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto stop_scanner_and_return;
 | 
						|
    }
 | 
						|
 | 
						|
  else if (s->hw->flags & MUSTEK_FLAG_PRO)
 | 
						|
    {
 | 
						|
 | 
						|
      status = dev_wait_ready (s);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto stop_scanner_and_return;
 | 
						|
 | 
						|
      status = set_window_pro (s);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto stop_scanner_and_return;
 | 
						|
 | 
						|
      s->scanning = SANE_TRUE;
 | 
						|
      s->cancelled = SANE_FALSE;
 | 
						|
 | 
						|
      status = adf_and_backtrack (s);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto stop_scanner_and_return;
 | 
						|
 | 
						|
      status = mode_select_pro (s);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto stop_scanner_and_return;
 | 
						|
 | 
						|
      status = scsi_sense_wait_ready (s);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto stop_scanner_and_return;
 | 
						|
 | 
						|
      status = calibration_pro (s);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto stop_scanner_and_return;
 | 
						|
 | 
						|
      status = send_gamma_table (s);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto stop_scanner_and_return;
 | 
						|
 | 
						|
      status = start_scan (s);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto stop_scanner_and_return;
 | 
						|
 | 
						|
      status = get_image_status (s, &s->params.bytes_per_line,
 | 
						|
				 &s->params.lines);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto stop_scanner_and_return;
 | 
						|
 | 
						|
      status = scsi_sense_wait_ready (s);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto stop_scanner_and_return;
 | 
						|
    }
 | 
						|
 | 
						|
  else				/* Paragon series */
 | 
						|
    {
 | 
						|
      status = area_and_windows (s);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	{
 | 
						|
	  DBG (1, "sane_start: set scan area command failed: %s\n",
 | 
						|
	       sane_strstatus (status));
 | 
						|
	  goto stop_scanner_and_return;
 | 
						|
	}
 | 
						|
 | 
						|
      status = adf_and_backtrack (s);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto stop_scanner_and_return;
 | 
						|
 | 
						|
      if (s->one_pass_color_scan)
 | 
						|
	{
 | 
						|
	  status = mode_select_paragon (s, MUSTEK_CODE_RED);
 | 
						|
	  if (status != SANE_STATUS_GOOD)
 | 
						|
	    goto stop_scanner_and_return;
 | 
						|
 | 
						|
	  status = mode_select_paragon (s, MUSTEK_CODE_GREEN);
 | 
						|
	  if (status != SANE_STATUS_GOOD)
 | 
						|
	    goto stop_scanner_and_return;
 | 
						|
 | 
						|
	  status = mode_select_paragon (s, MUSTEK_CODE_BLUE);
 | 
						|
	}
 | 
						|
      else
 | 
						|
	status = mode_select_paragon (s, MUSTEK_CODE_GRAY);
 | 
						|
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto stop_scanner_and_return;
 | 
						|
 | 
						|
      s->scanning = SANE_TRUE;
 | 
						|
      s->cancelled = SANE_FALSE;
 | 
						|
 | 
						|
      status = send_gamma_table (s);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto stop_scanner_and_return;
 | 
						|
 | 
						|
      status = start_scan (s);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto stop_scanner_and_return;
 | 
						|
 | 
						|
      if (!(s->hw->flags & MUSTEK_FLAG_SCSI_PP))
 | 
						|
	{
 | 
						|
	  /* This second gamma table download upsets the SCSI-over-parallel models */
 | 
						|
	  status = send_gamma_table (s);
 | 
						|
	  if (status != SANE_STATUS_GOOD)
 | 
						|
	    goto stop_scanner_and_return;
 | 
						|
	}
 | 
						|
 | 
						|
      s->ld.max_value = 0;
 | 
						|
      if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS))
 | 
						|
	{
 | 
						|
	  status = line_distance (s);
 | 
						|
	  if (status != SANE_STATUS_GOOD)
 | 
						|
	    goto stop_scanner_and_return;
 | 
						|
	}
 | 
						|
 | 
						|
      status = get_image_status (s, &s->params.bytes_per_line,
 | 
						|
				 &s->params.lines);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto stop_scanner_and_return;
 | 
						|
 | 
						|
      if ((strcmp (s->val[OPT_SOURCE].s, "Automatic Document Feeder") == 0)
 | 
						|
	  && (s->hw->flags & MUSTEK_FLAG_PARAGON_2))
 | 
						|
	{
 | 
						|
	  status = paragon_2_get_adf_status (s);
 | 
						|
	  if (status != SANE_STATUS_GOOD)
 | 
						|
	    goto stop_scanner_and_return;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  s->params.pixels_per_line = s->params.bytes_per_line;
 | 
						|
  if (s->one_pass_color_scan)
 | 
						|
    {
 | 
						|
      if (strcmp (s->val[OPT_BIT_DEPTH].s, "12") == 0)
 | 
						|
	s->params.pixels_per_line /= 6;
 | 
						|
      else
 | 
						|
	s->params.pixels_per_line /= 3;
 | 
						|
    }
 | 
						|
  else if ((s->mode & MUSTEK_MODE_LINEART)
 | 
						|
	   || (s->mode & MUSTEK_MODE_HALFTONE))
 | 
						|
    s->params.pixels_per_line *= 8;
 | 
						|
 | 
						|
  s->line = 0;
 | 
						|
 | 
						|
  /* don't call any SIGTERM or SIGCHLD handlers
 | 
						|
     this is to stop xsane and other frontends from calling
 | 
						|
     its quit handlers */
 | 
						|
  memset (&act, 0, sizeof (act));
 | 
						|
  sigaction (SIGTERM, &act, 0);
 | 
						|
  sigaction (SIGCHLD, &act, 0);
 | 
						|
 | 
						|
  if (pipe (fds) < 0)
 | 
						|
    return SANE_STATUS_IO_ERROR;
 | 
						|
 | 
						|
  s->reader_fds = fds[1];
 | 
						|
 | 
						|
  /* create reader routine as new process or thread */
 | 
						|
  s->reader_pid = sanei_thread_begin (reader_process, (void *) s);
 | 
						|
 | 
						|
  if (!sanei_thread_is_valid (s->reader_pid))
 | 
						|
    {
 | 
						|
      DBG (1, "sane_start: sanei_thread_begin failed (%s)\n",
 | 
						|
	   strerror (errno));
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
 | 
						|
  if (sanei_thread_is_forked ())
 | 
						|
    {
 | 
						|
      close (s->reader_fds);
 | 
						|
      s->reader_fds = -1;
 | 
						|
    }
 | 
						|
 | 
						|
  s->pipe = fds[0];
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
stop_scanner_and_return:
 | 
						|
  do_stop (s);
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
 | 
						|
	   SANE_Int * len)
 | 
						|
{
 | 
						|
  Mustek_Scanner *s = handle;
 | 
						|
  SANE_Status status;
 | 
						|
  ssize_t nread;
 | 
						|
 | 
						|
 | 
						|
  if (!s)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_read: handle is null!\n");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  if (!buf)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_read: buf is null!\n");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  if (!len)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_read: len is null!\n");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (5, "sane_read\n");
 | 
						|
  *len = 0;
 | 
						|
 | 
						|
  if (s->cancelled)
 | 
						|
    {
 | 
						|
      DBG (4, "sane_read: scan was cancelled\n");
 | 
						|
      return SANE_STATUS_CANCELLED;
 | 
						|
    }
 | 
						|
 | 
						|
  if (!s->scanning)
 | 
						|
    {
 | 
						|
      DBG (3, "sane_read: must call sane_start before sane_read\n");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  while (*len < max_len)
 | 
						|
    {
 | 
						|
      nread = read (s->pipe, buf + *len, max_len - *len);
 | 
						|
 | 
						|
      if (s->cancelled)
 | 
						|
	{
 | 
						|
	  DBG (4, "sane_read: scan was cancelled\n");
 | 
						|
	  *len = 0;
 | 
						|
	  return SANE_STATUS_CANCELLED;
 | 
						|
	}
 | 
						|
 | 
						|
      if (nread < 0)
 | 
						|
	{
 | 
						|
	  if (errno == EAGAIN)
 | 
						|
	    {
 | 
						|
	      if (*len == 0)
 | 
						|
		DBG (5, "sane_read: no more data at the moment--try again\n");
 | 
						|
	      else
 | 
						|
		DBG (5, "sane_read: read buffer of %d bytes "
 | 
						|
		     "(%d bytes total)\n", *len, s->total_bytes);
 | 
						|
	      return SANE_STATUS_GOOD;
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      DBG (1, "sane_read: IO error\n");
 | 
						|
	      do_stop (s);
 | 
						|
	      *len = 0;
 | 
						|
	      return SANE_STATUS_IO_ERROR;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
      *len += nread;
 | 
						|
      s->total_bytes += nread;
 | 
						|
 | 
						|
      if (nread == 0)
 | 
						|
	{
 | 
						|
	  if (*len == 0)
 | 
						|
	    {
 | 
						|
	      if (!(s->hw->flags & MUSTEK_FLAG_THREE_PASS)
 | 
						|
		  || !(s->mode & MUSTEK_MODE_COLOR) || ++s->pass >= 3)
 | 
						|
		{
 | 
						|
		  DBG (5, "sane_read: pipe was closed ... calling do_stop\n");
 | 
						|
		  status = do_stop (s);
 | 
						|
		  if (status != SANE_STATUS_CANCELLED
 | 
						|
		      && status != SANE_STATUS_GOOD)
 | 
						|
		    return status;	/* something went wrong */
 | 
						|
		}
 | 
						|
	      else		/* 3pass color first or second pass */
 | 
						|
		{
 | 
						|
		  DBG (5,
 | 
						|
		       "sane_read: pipe was closed ... finishing pass %d\n",
 | 
						|
		       s->pass);
 | 
						|
		}
 | 
						|
 | 
						|
	      return do_eof (s);
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      DBG (5, "sane_read: read last buffer of %d bytes "
 | 
						|
		   "(%d bytes total)\n", *len, s->total_bytes);
 | 
						|
	      return SANE_STATUS_GOOD;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
  DBG (5, "sane_read: read full buffer of %d bytes (%d total bytes)\n",
 | 
						|
       *len, s->total_bytes);
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
sane_cancel (SANE_Handle handle)
 | 
						|
{
 | 
						|
  Mustek_Scanner *s = handle;
 | 
						|
 | 
						|
  if (!s)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_cancel: handle is null!\n");
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (4, "sane_cancel\n");
 | 
						|
  if (s->scanning)
 | 
						|
    {
 | 
						|
      s->cancelled = SANE_TRUE;
 | 
						|
      do_stop (handle);
 | 
						|
    }
 | 
						|
  DBG (5, "sane_cancel: finished\n");
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
 | 
						|
{
 | 
						|
  Mustek_Scanner *s = handle;
 | 
						|
 | 
						|
  if (!s)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_set_io_mode: handle is null!\n");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (4, "sane_set_io_mode: %s\n",
 | 
						|
       non_blocking ? "non-blocking" : "blocking");
 | 
						|
 | 
						|
  if (!s->scanning)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_set_io_mode: call sane_start before sane_set_io_mode");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  if (fcntl (s->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_set_io_mode: can't set io mode");
 | 
						|
      return SANE_STATUS_IO_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
 | 
						|
{
 | 
						|
  Mustek_Scanner *s = handle;
 | 
						|
 | 
						|
  if (!s)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_get_select_fd: handle is null!\n");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
  if (!fd)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_get_select_fd: fd is null!\n");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (4, "sane_get_select_fd\n");
 | 
						|
  if (!s->scanning)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  *fd = s->pipe;
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
#include "mustek_scsi_pp.c"
 |