kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			4195 wiersze
		
	
	
		
			116 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			4195 wiersze
		
	
	
		
			116 KiB
		
	
	
	
		
			C
		
	
	
/* ------------------------------------------------------------------------- */
 | 
						|
/* sane - Scanner Access Now Easy.
 | 
						|
   coolscan.c , version  0.4.4
 | 
						|
 | 
						|
   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 COOLSCAN flatbed scanners.  */
 | 
						|
 | 
						|
/* ------------------------------------------------------------------------- */
 | 
						|
 | 
						|
 | 
						|
/* SANE-FLOW-DIAGRAMM
 | 
						|
 | 
						|
   - sane_init() : initialize backend, attach scanners
 | 
						|
   . - sane_get_devices() : query list of scanner-devices
 | 
						|
   . - sane_open() : open a particular scanner-device
 | 
						|
   . . - sane_set_io_mode : set blocking-mode
 | 
						|
   . . - sane_get_select_fd : get scanner-fd
 | 
						|
   . . - sane_get_option_descriptor() : get option informations
 | 
						|
   . . - sane_control_option() : change option values
 | 
						|
   . .
 | 
						|
   . . - sane_start() : start image aquisition
 | 
						|
   . .   - sane_get_parameters() : returns actual scan-parameters
 | 
						|
   . .   - sane_read() : read image-data (from pipe)
 | 
						|
   . .
 | 
						|
   . . - sane_cancel() : cancel operation
 | 
						|
   . - sane_close() : close opened scanner-device
 | 
						|
   - sane_exit() : terminate use of backend
 | 
						|
 */
 | 
						|
 | 
						|
#ifdef _AIX
 | 
						|
# include "lalloca.h"		/* MUST come first for AIX! */
 | 
						|
#endif
 | 
						|
 | 
						|
#include "sane/config.h"
 | 
						|
#include "lalloca.h"
 | 
						|
 | 
						|
#include <errno.h>
 | 
						|
#include <math.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <limits.h>
 | 
						|
#include <signal.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
#include <sys/types.h>
 | 
						|
#include <unistd.h>
 | 
						|
 | 
						|
#include "sane/sane.h"
 | 
						|
#include "sane/sanei.h"
 | 
						|
#include "sane/saneopts.h"
 | 
						|
#include "sane/sanei_scsi.h"
 | 
						|
#include "sane/sanei_debug.h"
 | 
						|
#include "sane/sanei_thread.h"
 | 
						|
 | 
						|
#include "sane/sanei_config.h"
 | 
						|
#define COOLSCAN_CONFIG_FILE "coolscan.conf"
 | 
						|
#include "sane/sanei_backend.h"
 | 
						|
 | 
						|
#include "coolscan.h"
 | 
						|
#include "coolscan-scsidef.h"
 | 
						|
 | 
						|
 | 
						|
#ifndef PATH_MAX
 | 
						|
#define PATH_MAX       1024
 | 
						|
#endif
 | 
						|
 | 
						|
/* ------------------------------------------------------------------------- */
 | 
						|
static const SANE_Int resolution_list[] =
 | 
						|
{
 | 
						|
  25,
 | 
						|
  2700, 1350, 900, 675, 540, 450, 385, 337, 300, 270, 245, 225, 207,
 | 
						|
  192, 180, 168, 158, 150, 142, 135, 128, 122, 117, 112, 108
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
#define coolscan_do_scsi_open(dev, fd, handler) sanei_scsi_open(dev, fd, handler)
 | 
						|
#define coolscan_do_scsi_close(fd)              sanei_scsi_close(fd)
 | 
						|
 | 
						|
#define	COOLSCAN_MAX_RETRY	25
 | 
						|
 | 
						|
 | 
						|
static SANE_Status sense_handler (int scsi_fd, unsigned char * result, void *arg);
 | 
						|
static int coolscan_check_values (Coolscan_t * s);
 | 
						|
static int get_internal_info (Coolscan_t *);
 | 
						|
static void coolscan_get_inquiry_values (Coolscan_t *);
 | 
						|
static void hexdump (int level, char *comment, unsigned char *p, int l);
 | 
						|
static int swap_res (Coolscan_t * s);
 | 
						|
/* --------------------------- COOLSCAN_DO_SCSI_CMD  ----------------------- */
 | 
						|
static int
 | 
						|
do_scsi_cmd (int fd, unsigned char *cmd, int cmd_len, unsigned char *out, size_t out_len)
 | 
						|
{
 | 
						|
  int ret;
 | 
						|
  size_t ol = out_len;
 | 
						|
 | 
						|
  hexdump (20, "", cmd, cmd_len);
 | 
						|
 | 
						|
  ret = sanei_scsi_cmd (fd, cmd, cmd_len, out, &ol);
 | 
						|
  if ((out_len != 0) && (out_len != ol))
 | 
						|
    {
 | 
						|
      DBG (1, "sanei_scsi_cmd: asked %lu bytes, got %lu\n",
 | 
						|
	   (u_long) out_len, (u_long) ol);
 | 
						|
    }
 | 
						|
  if (ret)
 | 
						|
    {
 | 
						|
      DBG (1, "sanei_scsi_cmd: returning 0x%08x\n", ret);
 | 
						|
    }
 | 
						|
  DBG (10, "sanei_scsi_cmd: returning %lu bytes:\n", (u_long) ol);
 | 
						|
  if (out != NULL && out_len != 0)
 | 
						|
    hexdump (15, "", out, (out_len > 0x60) ? 0x60 : out_len);
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
request_sense_parse (unsigned char *sensed_data)
 | 
						|
{
 | 
						|
  int ret, sense, asc, ascq;
 | 
						|
  sense = get_RS_sense_key (sensed_data);
 | 
						|
  asc = get_RS_ASC (sensed_data);
 | 
						|
  ascq = get_RS_ASCQ (sensed_data);
 | 
						|
 | 
						|
  ret = SANE_STATUS_IO_ERROR;
 | 
						|
 | 
						|
  switch (sense)
 | 
						|
    {
 | 
						|
    case 0x0:
 | 
						|
      DBG (5, "\t%d/%d/%d: Scanner ready\n", sense, asc, ascq);
 | 
						|
      return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
    case 0x1:
 | 
						|
      if ((0x37 == asc) && (0x00 == ascq)) {
 | 
						|
	DBG (1, "\t%d/%d/%d: Rounded Parameter\n", sense, asc, ascq);
 | 
						|
        ret = SANE_STATUS_GOOD;
 | 
						|
      }
 | 
						|
      else if ((0x61 == asc) && (0x02 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: Out Of Focus\n", sense, asc, ascq);
 | 
						|
      else
 | 
						|
	DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
 | 
						|
      break;
 | 
						|
 | 
						|
    case 0x2:
 | 
						|
      if ((0x4 == asc) && (0x1 == ascq)) {
 | 
						|
	DBG (10, "\t%d/%d/%d: Logical unit is in process of becomming ready\n", 
 | 
						|
	     sense, asc, ascq);
 | 
						|
	ret = SANE_STATUS_DEVICE_BUSY;
 | 
						|
      }
 | 
						|
      else if ((0x3A == asc) && (0x00 == ascq))
 | 
						|
	{
 | 
						|
	  DBG (1, "\t%d/%d/%d: No Diapo inserted\n", sense, asc, ascq);
 | 
						|
          ret = SANE_STATUS_GOOD;
 | 
						|
	}
 | 
						|
      else if ((0x60 == asc) && (0x00 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: Lamp Failure\n", sense, asc, ascq);
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
 | 
						|
          ret = SANE_STATUS_GOOD;
 | 
						|
	}
 | 
						|
      break;
 | 
						|
 | 
						|
    case 0x3:
 | 
						|
      if ((0x3b == asc) && (0xe == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: Medium source element empty\n", sense, asc, ascq);
 | 
						|
      else if ((0x53 == asc) && (0x00 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: Media Load of Eject Failed\n", sense, asc, ascq);
 | 
						|
      else
 | 
						|
	DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
 | 
						|
      break;
 | 
						|
 | 
						|
    case 0x4:
 | 
						|
      if ((0x15 == asc) && (0x1 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: Mechanical Positioning Error\n", sense, asc, ascq);
 | 
						|
      else
 | 
						|
	DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
 | 
						|
      break;
 | 
						|
 | 
						|
    case 0x5:
 | 
						|
      if ((0x00 == asc) && (0x5 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: End-Of-Data Detected\n", sense, asc, ascq);
 | 
						|
      else if ((0x1a == asc) && (0x00 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: Parameter List Length Error\n", sense, asc, ascq);
 | 
						|
      else if ((0x20 == asc) && (0x00 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: Invalid Command Operation Code\n", sense, asc, ascq);
 | 
						|
      else if ((0x24 == asc) && (0x00 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: Invalid Field In CDB\n", sense, asc, ascq);
 | 
						|
      else if ((0x25 == asc) && (0x00 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: Logical Unit Not Supported\n", sense, asc, ascq);
 | 
						|
      else if ((0x26 == asc) && (0x00 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: Invalid Field in Parameter List\n", sense, asc, ascq);
 | 
						|
      else if ((0x2c == asc) && (0x00 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: Command Sequence Error\n", sense, asc, ascq);
 | 
						|
      else if ((0x39 == asc) && (0x00 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: Saving Parameters Not Supported\n", sense, asc, ascq);
 | 
						|
      else if ((0x3d == asc) && (0x00 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: Invalid Bits In Identify Message\n", sense, asc, ascq);
 | 
						|
      else
 | 
						|
	DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
 | 
						|
      break;
 | 
						|
 | 
						|
    case 0x6:
 | 
						|
      if ((0x29 == asc) && (0x0 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: Power On, Reset, or Bus Device Reset Occured\n", sense, asc, ascq);
 | 
						|
      else if ((0x2a == asc) && (0x1 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: Mode Parameters Changed\n", sense, asc, ascq);
 | 
						|
      else
 | 
						|
	DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
 | 
						|
      break;
 | 
						|
 | 
						|
    case 0xb:
 | 
						|
      if ((0x43 == asc) && (0x0 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: Message Error\n", sense, asc, ascq);
 | 
						|
      else if ((0x47 == asc) && (0x0 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: SCSI Parity Error\n", sense, asc, ascq);
 | 
						|
      else if ((0x48 == asc) && (0x0 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: Initiator Detected Error Message Received\n", sense, asc, ascq);
 | 
						|
      else if ((0x49 == asc) && (0x0 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: Invalid Message Error\n", sense, asc, ascq);
 | 
						|
      else if ((0x4e == asc) && (0x0 == ascq))
 | 
						|
	DBG (1, "\t%d/%d/%d: Overlapped Commands Attempted\n", sense, asc, ascq);
 | 
						|
      else
 | 
						|
	DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      DBG (1, "\tUnknown - Sense=%d, ASC=%d, ASCQ=%d\n", sense, asc, ascq);
 | 
						|
      break;
 | 
						|
    }				/* switch */
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
/* 
 | 
						|
 *  wait_scanner should spin until TEST_UNIT_READY returns 0 (GOOD)
 | 
						|
 *  returns 0 on success,
 | 
						|
 *  returns -1 on error.  
 | 
						|
 */
 | 
						|
static int
 | 
						|
wait_scanner (Coolscan_t * s)
 | 
						|
{
 | 
						|
  int ret = -1;
 | 
						|
  int cnt = 0;
 | 
						|
  DBG (10, "wait_scanner: Testing if scanner is ready\n");
 | 
						|
 | 
						|
  while (ret != 0)
 | 
						|
    {
 | 
						|
      ret = do_scsi_cmd (s->sfd, test_unit_ready.cmd,
 | 
						|
			 test_unit_ready.size, 0, 0);
 | 
						|
 | 
						|
      if (ret == SANE_STATUS_DEVICE_BUSY)
 | 
						|
	{
 | 
						|
	  usleep (500000);	/* wait 0.5 seconds */
 | 
						|
	  if (cnt++ > 40)
 | 
						|
	    {			/* 20 sec. max (prescan takes up to 15 sec. */
 | 
						|
	      DBG (1, "wait_scanner: scanner does NOT get ready\n");
 | 
						|
	      return -1;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      else if (ret == SANE_STATUS_GOOD)
 | 
						|
	{
 | 
						|
	  DBG (10, "wait_scanner: scanner is ready\n");
 | 
						|
	  return ret;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  DBG (1, "wait_scanner: test unit ready failed (%s)\n",
 | 
						|
	       sane_strstatus (ret));
 | 
						|
	}
 | 
						|
    }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* ------------------------- COOLSCAN GRAB SCANNER ----------------------------- */
 | 
						|
 | 
						|
 | 
						|
/* coolscan_grab_scanner should go through the following command sequence:
 | 
						|
 * TEST UNIT READY
 | 
						|
 *     CHECK CONDITION  \
 | 
						|
 * REQUEST SENSE         > These should be handled automagically by
 | 
						|
 *     UNIT ATTENTION   /  the kernel if they happen (powerup/reset)
 | 
						|
 * TEST UNIT READY
 | 
						|
 *     GOOD
 | 
						|
 * RESERVE UNIT
 | 
						|
 *     GOOD
 | 
						|
 * 
 | 
						|
 * It is then responsible for installing appropriate signal handlers
 | 
						|
 * to call emergency_give_scanner() if user aborts.
 | 
						|
 */
 | 
						|
 | 
						|
static int
 | 
						|
coolscan_grab_scanner (Coolscan_t * s)
 | 
						|
{
 | 
						|
  int ret;
 | 
						|
 | 
						|
  DBG (10, "grabbing scanner\n");
 | 
						|
 | 
						|
  wait_scanner (s);		/* wait for scanner ready, if not print 
 | 
						|
				   sense and return 1 */
 | 
						|
  ret = do_scsi_cmd (s->sfd, reserve_unit.cmd, reserve_unit.size, NULL, 0);
 | 
						|
  if (ret)
 | 
						|
    return ret;
 | 
						|
 | 
						|
  DBG (10, "scanner reserved\n");
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* 
 | 
						|
 * Convert a size in ilu to the units expected by the scanner
 | 
						|
 */
 | 
						|
 | 
						|
static int
 | 
						|
resDivToVal (int res_div)
 | 
						|
{ 
 | 
						|
  if (res_div < 1 || res_div > resolution_list[0])
 | 
						|
    { 
 | 
						|
      DBG (1, "Invalid resolution divisor %d \n", res_div);
 | 
						|
      return 2700;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      return resolution_list[res_div];
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
resValToDiv (int res_val)
 | 
						|
{
 | 
						|
  int res_div;
 | 
						|
  int max_res = resolution_list[0];
 | 
						|
  for (res_div = 1; res_div <= max_res; res_div++)
 | 
						|
    {
 | 
						|
      if (resolution_list[res_div] == res_val)
 | 
						|
	break;
 | 
						|
    }
 | 
						|
  if (res_div > max_res)
 | 
						|
    {
 | 
						|
      DBG (1, "Invalid resolution value\n");
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      return res_div;
 | 
						|
    }
 | 
						|
}
 | 
						|
/* 
 | 
						|
 * use mode select to force a mesurement divisor of 2700
 | 
						|
 */
 | 
						|
static unsigned char mode_select[] =
 | 
						|
{
 | 
						|
  MODE_SELECT, 0x10, 0, 0, 20, 0,
 | 
						|
  0, 0, 0, 8,
 | 
						|
  0, 0, 0, 0, 0, 0, 0, 1,
 | 
						|
  3, 6, 0, 0, 0xA, 0x8C, 0, 0};
 | 
						|
 | 
						|
static int
 | 
						|
select_MUD (Coolscan_t * s)
 | 
						|
{
 | 
						|
  return do_scsi_cmd (s->sfd, mode_select, 26, NULL, 0);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
coolscan_autofocus_LS30 (Coolscan_t * s)
 | 
						|
{
 | 
						|
  int x, y;
 | 
						|
 | 
						|
  wait_scanner(s);
 | 
						|
  memcpy(s->buffer, autofocusLS30.cmd, autofocusLS30.size);
 | 
						|
  memcpy(s->buffer+ autofocusLS30.size, autofocuspos, 9);
 | 
						|
 | 
						|
  x = s->xmaxpix - (s->brx + s->tlx) / 2;
 | 
						|
  y = (s->bry + s->tly) / 2;
 | 
						|
 | 
						|
  DBG (10, "Attempting AutoFocus at x=%d, y=%d\n", x, y);
 | 
						|
 | 
						|
  do_scsi_cmd (s->sfd, s->buffer,
 | 
						|
	       autofocusLS30.size  + 9, NULL, 0);
 | 
						|
  /* Trashes when used in combination with scsi-driver AM53C974.o  */
 | 
						|
  do_scsi_cmd (s->sfd, command_c1.cmd,
 | 
						|
	       command_c1.size, NULL, 0);
 | 
						|
  
 | 
						|
  DBG (10, "\tWaiting end of Autofocus\n");
 | 
						|
  wait_scanner (s);
 | 
						|
  DBG (10, "AutoFocused.\n");
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
coolscan_autofocus (Coolscan_t * s)
 | 
						|
{
 | 
						|
  int x, y;
 | 
						|
 | 
						|
  if(s->LS>=2)
 | 
						|
    { return coolscan_autofocus_LS30(s);
 | 
						|
    }
 | 
						|
 | 
						|
  wait_scanner(s);
 | 
						|
  memcpy(s->buffer, autofocus.cmd, autofocus.size);
 | 
						|
 | 
						|
  x = s->xmaxpix - (s->brx + s->tlx) / 2;
 | 
						|
  y = (s->bry + s->tly) / 2;
 | 
						|
 | 
						|
  DBG (10, "Attempting AutoFocus at x=%d, y=%d\n", x, y);
 | 
						|
 | 
						|
  set_AF_XPoint (s->buffer, x);
 | 
						|
  set_AF_YPoint (s->buffer, y);
 | 
						|
 | 
						|
  set_AF_transferlength (s->buffer, 0); /* should be 8 !*/
 | 
						|
  do_scsi_cmd (s->sfd, s->buffer,
 | 
						|
	       autofocus.size  + AF_Point_length, NULL, 0);
 | 
						|
 | 
						|
  sleep(5);		 	/* autofocus takes a minimum of 5 sec. */
 | 
						|
 | 
						|
  DBG (10, "\tWaiting end of Autofocus\n");
 | 
						|
  wait_scanner (s);
 | 
						|
  DBG (10, "AutoFocused.\n");
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
   static int
 | 
						|
   coolscan_abort_scan (Coolscan_t * s)
 | 
						|
   {
 | 
						|
   int ret;
 | 
						|
 | 
						|
   DBG (5, "Aborting scan...\n");
 | 
						|
   ret = do_scsi_cmd (s->sfd, sabort.cmd, sabort.size, NULL, 0);
 | 
						|
   if (ret)
 | 
						|
   DBG (5, "Scan Aborted\n");
 | 
						|
   else
 | 
						|
   DBG (5, "Not scanning\n");
 | 
						|
   return 0;
 | 
						|
   }
 | 
						|
 */
 | 
						|
static int
 | 
						|
coolscan_mode_sense (Coolscan_t * s)
 | 
						|
{
 | 
						|
  int ret, len;
 | 
						|
 | 
						|
  DBG (10, "Mode Sense...\n");
 | 
						|
  len = 12;
 | 
						|
  set_MS_DBD (mode_sense.cmd, 1);
 | 
						|
  set_MS_len (mode_sense.cmd, len);
 | 
						|
  ret = do_scsi_cmd (s->sfd, mode_sense.cmd, mode_sense.size,
 | 
						|
		     s->buffer, len);
 | 
						|
 | 
						|
  if (ret == 0)
 | 
						|
    {
 | 
						|
      s->MUD = get_MS_MUD (s->buffer);
 | 
						|
      DBG (10, "\tMode Sensed (MUD is %d)\n", s->MUD);
 | 
						|
    }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
coolscan_object_discharge (Coolscan_t * s)
 | 
						|
{
 | 
						|
  int ret;
 | 
						|
 | 
						|
  DBG (10, "Trying to discharge object...\n");
 | 
						|
 | 
						|
  memcpy (s->buffer, object_position.cmd, object_position.size);
 | 
						|
  set_OP_autofeed (s->buffer, OP_Discharge);
 | 
						|
  ret = do_scsi_cmd (s->sfd, s->buffer,
 | 
						|
		     object_position.size, NULL, 0);
 | 
						|
  wait_scanner (s);
 | 
						|
  DBG (10, "Object discharged.\n");
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
coolscan_object_feed (Coolscan_t * s)
 | 
						|
{
 | 
						|
  int ret;
 | 
						|
  DBG (10, "Trying to feed object...\n");
 | 
						|
  if (!s->asf)
 | 
						|
    {
 | 
						|
      DBG (10, "\tAutofeeder not present.\n");
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
  memcpy (s->buffer, object_position.cmd, object_position.size);
 | 
						|
  set_OP_autofeed (s->buffer, OP_Feed);
 | 
						|
  ret = do_scsi_cmd (s->sfd, s->buffer,
 | 
						|
		     object_position.size, NULL, 0);
 | 
						|
  wait_scanner (s);
 | 
						|
  DBG (10, "Object fed.\n");
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
/* coolscan_give_scanner should go through the following sequence:
 | 
						|
 * OBJECT POSITION DISCHARGE
 | 
						|
 *     GOOD
 | 
						|
 * RELEASE UNIT
 | 
						|
 *     GOOD
 | 
						|
 */
 | 
						|
static int
 | 
						|
coolscan_give_scanner (Coolscan_t * s)
 | 
						|
{
 | 
						|
  DBG (10, "trying to release scanner ...\n");
 | 
						|
  coolscan_object_discharge (s);
 | 
						|
  wait_scanner (s);
 | 
						|
  do_scsi_cmd (s->sfd, release_unit.cmd, release_unit.size, NULL, 0);
 | 
						|
  DBG (10, "scanner released\n");
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
coolscan_set_window_param_LS20 (Coolscan_t * s, int prescan)
 | 
						|
{
 | 
						|
  unsigned char buffer_r[max_WDB_size];
 | 
						|
  int ret;
 | 
						|
 | 
						|
  wait_scanner (s);
 | 
						|
  memset (buffer_r, '\0', max_WDB_size);	/* clear buffer */
 | 
						|
  memcpy (buffer_r, window_descriptor_block.cmd,
 | 
						|
	  window_descriptor_block.size);	/* copy preset data */
 | 
						|
 | 
						|
  set_WD_wid (buffer_r, WD_wid_all);	/* window identifier */
 | 
						|
  set_WD_auto (buffer_r, s->set_auto);	/* 0 or 1: don't know what it is */
 | 
						|
 | 
						|
  set_WD_negative (buffer_r, s->negative);	/* Negative/positive slide */
 | 
						|
 | 
						|
  if (prescan)
 | 
						|
    {
 | 
						|
      set_WD_scanmode (buffer_r, WD_Prescan);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      set_WD_scanmode (buffer_r, WD_Scan);
 | 
						|
 | 
						|
      /* geometry */
 | 
						|
      set_WD_Xres (buffer_r, resDivToVal (s->x_nres));	/* x resolution in dpi */
 | 
						|
      set_WD_Yres (buffer_r, resDivToVal (s->y_nres));	/* y resolution in dpi */
 | 
						|
 | 
						|
      /* the coolscan  uses the upper right corner as the origin of coordinates */
 | 
						|
      /* xmax and ymax are given in 1200 dpi */
 | 
						|
      set_WD_ULX (buffer_r, (s->xmaxpix - s->brx));
 | 
						|
      set_WD_ULY (buffer_r, s->tly);	/* upper_edge y */
 | 
						|
      set_WD_width (buffer_r, (s->brx - s->tlx + 1));
 | 
						|
      set_WD_length (buffer_r, (s->bry - s->tly + 1));
 | 
						|
 | 
						|
      /* BTC */
 | 
						|
      if (s->brightness == 128)
 | 
						|
	{
 | 
						|
	  set_WD_brightness (buffer_r, 0);
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  set_WD_brightness (buffer_r, s->brightness);	/* brightness */
 | 
						|
	}
 | 
						|
 | 
						|
      if (s->contrast == 128)
 | 
						|
	{
 | 
						|
	  set_WD_contrast (buffer_r, 0);
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  set_WD_contrast (buffer_r, s->contrast);	/* contrast */
 | 
						|
	}
 | 
						|
 | 
						|
      /* scanmode */
 | 
						|
      if (s->colormode == GREYSCALE)
 | 
						|
	set_WD_composition (buffer_r, WD_comp_grey);	/* GRAY composition */
 | 
						|
      else
 | 
						|
	set_WD_composition (buffer_r, WD_comp_rgb_full);	/* RGB composition */
 | 
						|
 | 
						|
      set_WD_dropoutcolor (buffer_r, s->dropoutcolor);	/* Which color to scan with when grayscale scan */
 | 
						|
      set_WD_transfermode (buffer_r, WD_LineSequence);
 | 
						|
      set_WD_gammaselection (buffer_r, s->gammaselection);	/* monitor/linear */
 | 
						|
 | 
						|
      set_WD_shading (buffer_r, WD_Shading_ON);		/* default for non-manufacturing */
 | 
						|
 | 
						|
      if (1 == s->LS)
 | 
						|
	{			/* Analog gamma reserved on LS-1000 */
 | 
						|
	  set_WD_analog_gamma_R (buffer_r, 0);
 | 
						|
	  set_WD_analog_gamma_G (buffer_r, 0);
 | 
						|
	  set_WD_analog_gamma_R (buffer_r, 0);
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  /* Quote spec: "It is recomended that analog gamma bits 5, 4 and 3 be
 | 
						|
	   * set to 1 (OFF) when the object type of byte 48 is positive and the
 | 
						|
	   * gamma specificateion of byte 51 is linear, and to 0 (ON) in all
 | 
						|
	   * other cases." */
 | 
						|
	  /*
 | 
						|
	  int foo;
 | 
						|
	  if ((buffer_r[48] == WD_Positive) && (buffer_r[51] == WD_Linear))
 | 
						|
	    foo = WD_Analog_Gamma_OFF;
 | 
						|
	  else
 | 
						|
	    foo = WD_Analog_Gamma_ON;
 | 
						|
	  set_WD_analog_gamma_R (buffer_r, foo);
 | 
						|
	  set_WD_analog_gamma_G (buffer_r, foo);
 | 
						|
	  set_WD_analog_gamma_B (buffer_r, foo);
 | 
						|
	  */
 | 
						|
	  set_WD_analog_gamma_R (buffer_r, s->analog_gamma_r);
 | 
						|
	  set_WD_analog_gamma_G (buffer_r, s->analog_gamma_g);
 | 
						|
	  set_WD_analog_gamma_B (buffer_r, s->analog_gamma_b);
 | 
						|
	  if (s->gamma_bind)
 | 
						|
	    {
 | 
						|
	      set_WD_LUT_R (buffer_r, 1);
 | 
						|
	      set_WD_LUT_G (buffer_r, 1);
 | 
						|
	      set_WD_LUT_B (buffer_r, 1);
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      set_WD_LUT_R (buffer_r, 1);
 | 
						|
	      set_WD_LUT_G (buffer_r, 2);
 | 
						|
	      set_WD_LUT_B (buffer_r, 3);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      set_WD_averaging (buffer_r, s->averaging);
 | 
						|
 | 
						|
      set_WD_brightness_R (buffer_r, s->brightness_R);
 | 
						|
      set_WD_brightness_G (buffer_r, s->brightness_G);
 | 
						|
      set_WD_brightness_B (buffer_r, s->brightness_B);
 | 
						|
 | 
						|
      set_WD_contrast_R (buffer_r, s->contrast_R);
 | 
						|
      set_WD_contrast_G (buffer_r, s->contrast_G);
 | 
						|
      set_WD_contrast_B (buffer_r, s->contrast_B);
 | 
						|
 | 
						|
      set_WD_exposure_R (buffer_r, s->exposure_R);
 | 
						|
      set_WD_exposure_G (buffer_r, s->exposure_G);
 | 
						|
      set_WD_exposure_B (buffer_r, s->exposure_B);
 | 
						|
      set_WD_shift_R (buffer_r, s->shift_R);
 | 
						|
      set_WD_shift_G (buffer_r, s->shift_G);
 | 
						|
      set_WD_shift_B (buffer_r, s->shift_B);
 | 
						|
 | 
						|
 
 | 
						|
      /* FIXME: LUT-[RGB] */
 | 
						|
      /* FIXME: stop on/off */
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (10, "\tx_nres=%d, y_nres=%d, upper left-x=%d, upper left-y=%d\n",
 | 
						|
       s->x_nres, s->y_nres, s->tlx, s->tly);
 | 
						|
  DBG (10, "\twindow width=%d, MUD=%d, brx=%d\n",
 | 
						|
       s->brx - s->tlx, s->MUD, s->brx);
 | 
						|
  DBG (10, "\tcolormode=%d, bits per pixel=%d\n",
 | 
						|
       s->colormode, s->bits_per_color);
 | 
						|
  DBG (10, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n",
 | 
						|
       s->negative, s->dropoutcolor, s->preview, s->transfermode,
 | 
						|
       s->gammaselection);
 | 
						|
 | 
						|
  /* prepare SCSI-BUFFER */
 | 
						|
  memcpy (s->buffer, set_window.cmd, set_window.size);	/* SET-WINDOW cmd */
 | 
						|
  memcpy ((s->buffer + set_window.size),	/* add WPDB */
 | 
						|
	  window_parameter_data_block.cmd,
 | 
						|
	  window_parameter_data_block.size);
 | 
						|
  set_WPDB_wdblen ((s->buffer + set_window.size), used_WDB_size);	/* set WD_len */
 | 
						|
  memcpy (s->buffer + set_window.size + window_parameter_data_block.size,
 | 
						|
	  buffer_r, window_descriptor_block.size);
 | 
						|
 | 
						|
  hexdump (15, "Window set", buffer_r, s->wdb_len);
 | 
						|
 | 
						|
  set_SW_xferlen (s->buffer, (window_parameter_data_block.size +
 | 
						|
			      window_descriptor_block.size));
 | 
						|
 | 
						|
  ret = do_scsi_cmd (s->sfd, s->buffer, set_window.size +
 | 
						|
		     window_parameter_data_block.size +
 | 
						|
		     window_descriptor_block.size,
 | 
						|
		     NULL, 0);
 | 
						|
  DBG (10, "window set.\n");
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
coolscan_set_window_param_LS30 (Coolscan_t * s, int wid, int prescan)
 | 
						|
{
 | 
						|
  unsigned char buffer_r[max_WDB_size];
 | 
						|
  int ret;
 | 
						|
 | 
						|
  wait_scanner (s);
 | 
						|
  memset (buffer_r, '\0', max_WDB_size);	/* clear buffer */
 | 
						|
  memcpy (buffer_r, window_descriptor_block_LS30.cmd,
 | 
						|
	  window_descriptor_block_LS30.size);	/* copy preset data */
 | 
						|
 | 
						|
  set_WD_wid (buffer_r, wid);          	/* window identifier */
 | 
						|
  set_WD_auto (buffer_r, s->set_auto);	/* 0 or 1: don't know what it is */
 | 
						|
 | 
						|
  /* geometry */
 | 
						|
  set_WD_Xres (buffer_r, resDivToVal (s->x_nres));	/* x resolution in dpi */
 | 
						|
  set_WD_Yres (buffer_r, resDivToVal (s->y_nres));	/* y resolution in dpi */
 | 
						|
 | 
						|
  if (prescan)
 | 
						|
    {
 | 
						|
      set_WD_scanmode_LS30 (buffer_r, WD_Prescan);
 | 
						|
      set_WD_Xres (buffer_r, resDivToVal (1));	/* x res. in dpi */
 | 
						|
      set_WD_Yres (buffer_r, resDivToVal (1));	/* y res. in dpi */
 | 
						|
      buffer_r[0x29]=0x81;
 | 
						|
      buffer_r[0x2a]=0x04;
 | 
						|
      buffer_r[0x2b]=0x02;
 | 
						|
      buffer_r[0x2c]=0x01;
 | 
						|
      buffer_r[0x2d]=0xff;
 | 
						|
      buffer_r[0x30]=0x00;
 | 
						|
      buffer_r[0x31]=0x00;
 | 
						|
      buffer_r[0x32]=0x00;
 | 
						|
      buffer_r[0x33]=0x00;
 | 
						|
      set_WD_width (buffer_r,(2592));
 | 
						|
      set_WD_length (buffer_r,(3894));
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      set_WD_scanmode_LS30 (buffer_r, WD_Scan);
 | 
						|
 | 
						|
      /* the coolscan LS-30 uses the upper left corner 
 | 
						|
	 as the origin of coordinates */
 | 
						|
      /* xmax and ymax are given in 1200 dpi */
 | 
						|
      set_WD_ULX (buffer_r, s->tlx);
 | 
						|
      set_WD_ULY (buffer_r, s->tly);	/* upper_edge y */
 | 
						|
      set_WD_width (buffer_r, (s->brx - s->tlx+1));
 | 
						|
      set_WD_length (buffer_r, (s->bry - s->tly+1));
 | 
						|
 | 
						|
      /* BTC */
 | 
						|
      if (s->brightness == 128)
 | 
						|
	{
 | 
						|
	  buffer_r[0x32]=0x00;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  buffer_r[0x32]=s->brightness;	/* brightness */
 | 
						|
	}
 | 
						|
 | 
						|
      if (s->contrast == 128)
 | 
						|
	{
 | 
						|
	  buffer_r[0x33]=0x00;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  buffer_r[0x33]=s->contrast;	/* contrast */
 | 
						|
	}
 | 
						|
 | 
						|
      /* scanmode */
 | 
						|
      if (s->colormode == GREYSCALE)
 | 
						|
	set_WD_composition (buffer_r, WD_comp_grey);	/* GRAY composition */
 | 
						|
      else
 | 
						|
	set_WD_composition (buffer_r, WD_comp_rgb_full);	/* RGB composition */
 | 
						|
 | 
						|
      set_WD_composition (buffer_r, WD_comp_rgb_full);  /* allways RGB composition */
 | 
						|
 | 
						|
      /* Bits per pixel */
 | 
						|
      set_WD_bitsperpixel(buffer_r, s->bits_per_color);
 | 
						|
 | 
						|
      buffer_r[0x29]=0x81;
 | 
						|
      buffer_r[0x2a]=0x01;
 | 
						|
      buffer_r[0x2b]=0x02;
 | 
						|
      buffer_r[0x2c]=0x01;
 | 
						|
      buffer_r[0x2d]=0xff;
 | 
						|
      buffer_r[0x30]=0x00;
 | 
						|
 | 
						|
    }
 | 
						|
    set_WD_negative_LS30(buffer_r, s->negative);	/* Negative/positive slide */
 | 
						|
 | 
						|
    switch(wid)
 | 
						|
    { case 1:  set_gain_LS30(buffer_r,(s->exposure_R*s->pretv_r)/50);	
 | 
						|
                 break;
 | 
						|
      case 2:  set_gain_LS30(buffer_r,(s->exposure_G*s->pretv_g)/50);	
 | 
						|
                 break;
 | 
						|
      case 3:  set_gain_LS30(buffer_r,(s->exposure_B*s->pretv_b)/50);	
 | 
						|
                 break;
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (10, "\texpo_r=%d, expo_g=%d, expob=%d\n",
 | 
						|
       s->exposure_R, s->exposure_G, s->exposure_B);
 | 
						|
  DBG (10, "\tpre_r=%d, pre_g=%d, preb=%d\n",
 | 
						|
       s->pretv_r, s->pretv_g, s->pretv_b);
 | 
						|
  DBG (10, "\tx_nres=%d, y_nres=%d, upper left-x=%d, upper left-y=%d\n",
 | 
						|
       s->x_nres, s->y_nres, s->tlx, s->tly);
 | 
						|
  DBG (10, "\twindow width=%d, MUD=%d, brx=%d\n",
 | 
						|
       s->brx - s->tlx, s->MUD, s->brx);
 | 
						|
  DBG (10, "\tcolormode=%d, bits per pixel=%d\n",
 | 
						|
       s->colormode, s->bits_per_color);
 | 
						|
  DBG (10, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n",
 | 
						|
       s->negative, s->dropoutcolor, s->preview, s->transfermode,
 | 
						|
       s->gammaselection);
 | 
						|
 | 
						|
  /* prepare SCSI-BUFFER */
 | 
						|
  memcpy (s->buffer, set_window.cmd, set_window.size);	/* SET-WINDOW cmd */
 | 
						|
  memcpy ((s->buffer + set_window.size),	/* add WPDB */
 | 
						|
	  window_parameter_data_block.cmd,
 | 
						|
	  window_parameter_data_block.size);
 | 
						|
  set_WPDB_wdblen ((s->buffer + set_window.size), used_WDB_size_LS30);	/* set WD_len */
 | 
						|
  memcpy (s->buffer + set_window.size + window_parameter_data_block.size,
 | 
						|
	  buffer_r, window_descriptor_block_LS30.size);
 | 
						|
 | 
						|
  hexdump (15, "Window set", buffer_r, s->wdb_len);
 | 
						|
 | 
						|
  set_SW_xferlen (s->buffer, (window_parameter_data_block.size +
 | 
						|
			      window_descriptor_block_LS30.size));
 | 
						|
 | 
						|
  ret = do_scsi_cmd (s->sfd, s->buffer, set_window.size +
 | 
						|
		     window_parameter_data_block.size +
 | 
						|
		     window_descriptor_block_LS30.size,
 | 
						|
		     NULL, 0);
 | 
						|
  DBG (10, "window set.\n");
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
coolscan_set_window_param (Coolscan_t * s, int prescan)
 | 
						|
{
 | 
						|
  int ret;
 | 
						|
  ret=0;
 | 
						|
  DBG (10, "set_window_param\n");
 | 
						|
  
 | 
						|
  if(s->LS<2)                   /* distinquish between old and new scanners */
 | 
						|
  { ret=coolscan_set_window_param_LS20 (s,prescan);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {  do_scsi_cmd (s->sfd,commande1.cmd,commande1.size,s->buffer,0x0d);
 | 
						|
     wait_scanner (s);
 | 
						|
     wait_scanner (s);
 | 
						|
     coolscan_set_window_param_LS30(s,1,prescan);
 | 
						|
     ret=coolscan_set_window_param_LS30(s,2,prescan);
 | 
						|
     ret=coolscan_set_window_param_LS30(s,3,prescan);     
 | 
						|
     if(s->colormode&0x08)
 | 
						|
     { ret=coolscan_set_window_param_LS30(s,9,prescan);
 | 
						|
     }
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* 
 | 
						|
 * The only purpose of get_window is debugging. None of the return parameters
 | 
						|
 * is currently used. 
 | 
						|
 */
 | 
						|
static int
 | 
						|
coolscan_get_window_param_LS30 (Coolscan_t * s, int wid,int prescanok)
 | 
						|
{
 | 
						|
  int translen;
 | 
						|
  unsigned char *buf;
 | 
						|
 | 
						|
  DBG (10, "GET_WINDOW_PARAM\n");
 | 
						|
  /*  wait_scanner (s); */
 | 
						|
 | 
						|
  translen = window_parameter_data_block.size + window_descriptor_block_LS30.size;
 | 
						|
 | 
						|
  /* prepare SCSI-BUFFER */
 | 
						|
  memset (s->buffer, '\0', max_WDB_size);	/* clear buffer */
 | 
						|
 | 
						|
  set_SW_xferlen (get_window.cmd, translen);	/* Transfer length */
 | 
						|
  get_window.cmd[5]= wid;                     	/* window identifier */
 | 
						|
 | 
						|
  hexdump (15, "Get window cmd", get_window.cmd, get_window.size);
 | 
						|
  do_scsi_cmd (s->sfd, get_window.cmd, get_window.size,
 | 
						|
		     s->buffer, translen);
 | 
						|
 | 
						|
  buf = s->buffer + window_parameter_data_block.size;
 | 
						|
  hexdump (10, "Window get", buf, 117);
 | 
						|
 | 
						|
  s->brightness = buf[0x32];	/* brightness */
 | 
						|
  s->contrast = buf[0x33];	/* contrast */
 | 
						|
  DBG (10, "\tbrightness=%d, contrast=%d\n", s->brightness, s->contrast);
 | 
						|
 | 
						|
  /* Useful? */
 | 
						|
  s->bits_per_color = get_WD_bitsperpixel (buf);	/* bits/pixel (8) */
 | 
						|
 | 
						|
  DBG (10, "\tcolormode=%d, bits per pixel=%d\n",
 | 
						|
       s->colormode, s->bits_per_color);
 | 
						|
 | 
						|
  if(prescanok)
 | 
						|
  { switch(wid)
 | 
						|
    { case 1: s->pretv_r = get_gain_LS30(buf);	
 | 
						|
              break;
 | 
						|
      case 2: s->pretv_g = get_gain_LS30(buf);	
 | 
						|
              break;
 | 
						|
      case 3: s->pretv_b = get_gain_LS30(buf);	
 | 
						|
            break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* Should this one be set at all, here? */
 | 
						|
  s->transfermode = get_WD_transfermode (buf);
 | 
						|
 | 
						|
  s->gammaselection = get_WD_gammaselection (buf);	/* monitor/linear */
 | 
						|
  DBG (10, "\tpre_r=%d, pre_g=%d, preb=%d\n",
 | 
						|
       s->pretv_r, s->pretv_g, s->pretv_b);
 | 
						|
 | 
						|
  DBG (5, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n",
 | 
						|
       s->negative, s->dropoutcolor, s->preview, s->transfermode,
 | 
						|
       s->gammaselection);
 | 
						|
 | 
						|
  DBG (10, "get_window_param - return\n");
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* 
 | 
						|
 * The only purpose of get_window is debugging. None of the return parameters
 | 
						|
 * is currently used. 
 | 
						|
 */
 | 
						|
static int
 | 
						|
coolscan_get_window_param_LS20 (Coolscan_t * s)
 | 
						|
{
 | 
						|
  int translen;
 | 
						|
  unsigned char *buf;
 | 
						|
 | 
						|
  DBG (10, "GET_WINDOW_PARAM\n");
 | 
						|
  wait_scanner (s);
 | 
						|
 | 
						|
  translen = window_parameter_data_block.size + window_descriptor_block.size;
 | 
						|
 | 
						|
  /* prepare SCSI-BUFFER */
 | 
						|
  memset (s->buffer, '\0', max_WDB_size);	/* clear buffer */
 | 
						|
 | 
						|
  set_SW_xferlen (get_window.cmd, translen);	/* Transfer length */
 | 
						|
 | 
						|
  hexdump (15, "Get window cmd", get_window.cmd, get_window.size);
 | 
						|
  do_scsi_cmd (s->sfd, get_window.cmd, get_window.size,
 | 
						|
		     s->buffer, translen);
 | 
						|
 | 
						|
  buf = s->buffer + window_parameter_data_block.size;
 | 
						|
  hexdump (10, "Window get", buf, 117);
 | 
						|
 | 
						|
  /* BTC */
 | 
						|
  s->brightness = get_WD_brightness (buf);	/* brightness */
 | 
						|
  s->contrast = get_WD_contrast (buf);	/* contrast */
 | 
						|
  DBG (10, "\tbrightness=%d, contrast=%d\n", s->brightness, s->contrast);
 | 
						|
 | 
						|
  if (WD_comp_gray == get_WD_composition (buf))
 | 
						|
    s->colormode = GREYSCALE;
 | 
						|
  else
 | 
						|
    s->colormode = RGB;
 | 
						|
 | 
						|
  /* Useful? */
 | 
						|
  s->bits_per_color = get_WD_bitsperpixel (buf);	/* bits/pixel (8) */
 | 
						|
 | 
						|
  DBG (10, "\tcolormode=%d, bits per pixel=%d\n",
 | 
						|
       s->colormode, s->bits_per_color);
 | 
						|
 | 
						|
 | 
						|
  s->dropoutcolor = get_WD_dropoutcolor (buf);	/* Which color to scan with when grayscale scan */
 | 
						|
 | 
						|
  /* Should this one be set at all, here? */
 | 
						|
  s->transfermode = get_WD_transfermode (buf);
 | 
						|
 | 
						|
  s->gammaselection = get_WD_gammaselection (buf);	/* monitor/linear */
 | 
						|
 | 
						|
  DBG (5, "\tnegative=%d, dropoutcolor=%d, preview=%d, transfermode=%d, gammasel=%d\n",
 | 
						|
       s->negative, s->dropoutcolor, s->preview, s->transfermode,
 | 
						|
       s->gammaselection);
 | 
						|
 | 
						|
  /* Should this one be set at all? */
 | 
						|
  s->shading = get_WD_shading (buf);
 | 
						|
  s->averaging = get_WD_averaging (buf);
 | 
						|
  DBG (10, "get_window_param - return\n");
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* 
 | 
						|
 * The only purpose of get_window is debugging. None of the return parameters
 | 
						|
 * is currently used. 
 | 
						|
 */
 | 
						|
static int
 | 
						|
coolscan_get_window_param (Coolscan_t * s, int prescanok)
 | 
						|
{
 | 
						|
  int ret;
 | 
						|
  DBG (10, "get_window_param\n");
 | 
						|
 | 
						|
  ret=0;  
 | 
						|
  if(s->LS<2)                   /* distinquish between old and new scanners */
 | 
						|
  { ret=coolscan_get_window_param_LS20 (s);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {  
 | 
						|
     ret=coolscan_get_window_param_LS30(s,1,prescanok);
 | 
						|
     ret=coolscan_get_window_param_LS30(s,2,prescanok);
 | 
						|
     ret=coolscan_get_window_param_LS30(s,3,prescanok);
 | 
						|
     if(s->colormode&0x08)
 | 
						|
     { ret=coolscan_get_window_param_LS30(s,9,prescanok);
 | 
						|
     }
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
coolscan_start_scanLS30 (Coolscan_t * s)
 | 
						|
{ int channels;
 | 
						|
  DBG (10, "starting scan\n");
 | 
						|
 | 
						|
  channels=1;
 | 
						|
  memcpy (s->buffer, scan.cmd, scan.size);
 | 
						|
  switch(s->colormode)
 | 
						|
    {  case RGB:
 | 
						|
       case GREYSCALE:	 
 | 
						|
	       channels=s->buffer[4]=0x03; /* window 1 */
 | 
						|
               s->buffer[6]=0x01; /* window 1 */
 | 
						|
	       s->buffer[7]=0x02; /* window 2 */
 | 
						|
	       s->buffer[8]=0x03; /* window 3 */  
 | 
						|
	       
 | 
						|
              break;      
 | 
						|
       case RGBI:
 | 
						|
	       channels=s->buffer[4]=0x04; /* window 1 */
 | 
						|
               s->buffer[6]=0x01; /* window 1 */
 | 
						|
	       s->buffer[7]=0x02; /* window 2 */
 | 
						|
	       s->buffer[8]=0x03; /* window 3 */  
 | 
						|
	       s->buffer[9]=0x09; /* window 3 */  
 | 
						|
              break; 
 | 
						|
       case IRED:
 | 
						|
	       channels=s->buffer[4]=0x01; /* window 1 */
 | 
						|
	       s->buffer[8]=0x09; /* window 3 */  
 | 
						|
              break; 
 | 
						|
    }
 | 
						|
 | 
						|
  return do_scsi_cmd (s->sfd, s->buffer, scan.size+channels, NULL, 0);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
coolscan_start_scan (Coolscan_t * s)
 | 
						|
{
 | 
						|
  DBG (10, "starting scan\n");
 | 
						|
  if(s->LS>=2)
 | 
						|
    { return coolscan_start_scanLS30(s);
 | 
						|
    }
 | 
						|
  return do_scsi_cmd (s->sfd, scan.cmd, scan.size, NULL, 0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
prescan (Coolscan_t * s)
 | 
						|
{
 | 
						|
  int ret;
 | 
						|
 | 
						|
  DBG (10, "Starting prescan...\n");
 | 
						|
  if(s->LS<2)
 | 
						|
  {  coolscan_set_window_param (s, 1);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  { 
 | 
						|
     do_scsi_cmd (s->sfd,commande1.cmd,commande1.size,s->buffer,0x0d);
 | 
						|
     wait_scanner (s);
 | 
						|
     wait_scanner (s);
 | 
						|
     coolscan_set_window_param_LS30 (s,1,1);
 | 
						|
     coolscan_set_window_param_LS30 (s,2,1);
 | 
						|
     coolscan_set_window_param_LS30 (s,3,1);
 | 
						|
  
 | 
						|
  }
 | 
						|
  ret = coolscan_start_scan(s);
 | 
						|
 | 
						|
  sleep(8);			/* prescan takes a minimum of 10 sec. */
 | 
						|
  wait_scanner (s);
 | 
						|
  DBG (10, "Prescan done\n");
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
do_prescan_now (Coolscan_t * scanner)
 | 
						|
{
 | 
						|
 | 
						|
  DBG (10, "do_prescan_now \n");
 | 
						|
  if (scanner->scanning == SANE_TRUE)
 | 
						|
    return SANE_STATUS_DEVICE_BUSY;
 | 
						|
 | 
						|
  if (scanner->sfd < 0)
 | 
						|
    {				/* first call */
 | 
						|
      if (sanei_scsi_open (scanner->sane.name,
 | 
						|
			   &(scanner->sfd),
 | 
						|
			   sense_handler, 0) != SANE_STATUS_GOOD)
 | 
						|
	{
 | 
						|
	  DBG (1, "do_prescan_now: open of %s failed:\n",
 | 
						|
	       scanner->sane.name);
 | 
						|
	  return SANE_STATUS_INVAL;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  scanner->scanning = SANE_TRUE;
 | 
						|
 | 
						|
 | 
						|
  if (coolscan_check_values (scanner) != 0)
 | 
						|
    {				/* Verify values */
 | 
						|
      DBG (1, "ERROR: invalid scan-values\n");
 | 
						|
      scanner->scanning = SANE_FALSE;
 | 
						|
      coolscan_give_scanner (scanner);
 | 
						|
      sanei_scsi_close (scanner->sfd);
 | 
						|
      scanner->sfd = -1;
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  if (coolscan_grab_scanner (scanner))
 | 
						|
    {
 | 
						|
      sanei_scsi_close (scanner->sfd);
 | 
						|
      scanner->sfd = -1;
 | 
						|
      DBG (5, "WARNING: unable to reserve scanner: device busy\n");
 | 
						|
      scanner->scanning = SANE_FALSE;
 | 
						|
      return SANE_STATUS_DEVICE_BUSY;
 | 
						|
    }
 | 
						|
 | 
						|
  prescan (scanner);  
 | 
						|
  if(scanner->LS<2)
 | 
						|
    {	get_internal_info(scanner);
 | 
						|
    }
 | 
						|
  coolscan_get_window_param (scanner,1);
 | 
						|
  scanner->scanning = SANE_FALSE;
 | 
						|
  coolscan_give_scanner (scanner);
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
send_one_LUT (Coolscan_t * s, SANE_Word * LUT, int reg)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  short lutval;
 | 
						|
  short bytesperval;
 | 
						|
  unsigned char *gamma, *gamma_p;
 | 
						|
  unsigned short *gamma_s;
 | 
						|
 | 
						|
  DBG (10, "send LUT\n");
 | 
						|
 | 
						|
  if(s->LS<2)
 | 
						|
  { set_S_datatype_code (send.cmd, R_user_reg_gamma);
 | 
						|
    bytesperval=1;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    send.cmd[0x02]=3;
 | 
						|
    send.cmd[0x05]=1;
 | 
						|
    bytesperval=2;
 | 
						|
  }
 | 
						|
 | 
						|
  set_S_xfer_length (send.cmd, s->lutlength*bytesperval);
 | 
						|
  set_S_datatype_qual_upper (send.cmd, reg);
 | 
						|
 | 
						|
  gamma = alloca (send.size + s->lutlength*2);
 | 
						|
  memcpy (gamma, send.cmd, send.size);
 | 
						|
  if(s->LS<2)  
 | 
						|
  { gamma_p = &gamma[send.size];
 | 
						|
    for (i = 0; i < s->lutlength; i++)
 | 
						|
    {
 | 
						|
      if (LUT[i] > 255)
 | 
						|
	LUT[i] = 255;		/* broken gtk */
 | 
						|
      *gamma_p++ = (unsigned char) LUT[i];
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if(s->LS==2)  
 | 
						|
  { gamma_s = (unsigned short*)( &gamma[send.size]);
 | 
						|
    for (i = 0; i < s->lutlength; i++)
 | 
						|
    {
 | 
						|
       if(s->negative)
 | 
						|
       {
 | 
						|
         lutval=(unsigned short)(LUT[(s->lutlength-i)]);  
 | 
						|
       }
 | 
						|
       else    
 | 
						|
       {
 | 
						|
	 lutval=(unsigned short)(LUT[i]);      
 | 
						|
       }     
 | 
						|
       if (LUT[i] >= s->max_lut_val)
 | 
						|
       LUT[i] = s->max_lut_val-1;	          	/* broken gtk */
 | 
						|
       if(s->low_byte_first)                                /* if on little endian machine: */
 | 
						|
       {
 | 
						|
         lutval=((lutval&0x00ff)<<8)+((lutval&0xff00)>>8); /* inverse byteorder */       
 | 
						|
       }
 | 
						|
       *gamma_s++ = lutval;   
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else if(s->LS==3)  
 | 
						|
  { gamma_s = (unsigned short*)( &gamma[send.size]);
 | 
						|
    for (i = 0; i < s->lutlength; i++)
 | 
						|
    {
 | 
						|
       if(s->negative)
 | 
						|
       {
 | 
						|
         lutval=(unsigned short)(LUT[s->lutlength-i]);  
 | 
						|
       }
 | 
						|
       else    
 | 
						|
       {
 | 
						|
	 lutval=(unsigned short)(LUT[i]);      
 | 
						|
       }     
 | 
						|
       if (LUT[i] >= s->max_lut_val)
 | 
						|
       LUT[i] = s->max_lut_val-1;	          	    /* broken gtk */
 | 
						|
       if(s->low_byte_first)                                /* if on little endian machine: */
 | 
						|
       {  lutval=((lutval&0x00ff)<<8)+((lutval&0xff00)>>8); /* inverse byteorder */ 
 | 
						|
       }
 | 
						|
       *gamma_s++ = lutval;   
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return do_scsi_cmd (s->sfd, gamma, send.size + s->lutlength*bytesperval, NULL, 0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
send_LUT (Coolscan_t * s)
 | 
						|
{
 | 
						|
  wait_scanner (s);
 | 
						|
  if (s->gamma_bind)
 | 
						|
    {
 | 
						|
      send_one_LUT (s, s->gamma, S_DQ_Reg1);
 | 
						|
      if(s->LS>=2)
 | 
						|
	{      send_one_LUT (s, s->gamma, S_DQ_Reg2);
 | 
						|
	       send_one_LUT (s, s->gamma, S_DQ_Reg3);
 | 
						|
               if(s->colormode&0x08)
 | 
						|
	       { send_one_LUT (s, s->gamma, S_DQ_Reg9);
 | 
						|
	       }   
 | 
						|
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      send_one_LUT (s, s->gamma_r, S_DQ_Reg1);
 | 
						|
      send_one_LUT (s, s->gamma_g, S_DQ_Reg2);
 | 
						|
      send_one_LUT (s, s->gamma_b, S_DQ_Reg3);
 | 
						|
      if(s->colormode&0x08)
 | 
						|
      { send_one_LUT (s, s->gamma_r, S_DQ_Reg9);
 | 
						|
      }   
 | 
						|
    }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
coolscan_read_data_block (Coolscan_t * s, unsigned int datatype, unsigned int length)
 | 
						|
{
 | 
						|
  int r;
 | 
						|
 | 
						|
  DBG (10, "read_data_block (type= %x length = %d)\n",datatype,length);
 | 
						|
  /*wait_scanner(s); */
 | 
						|
 | 
						|
  set_R_datatype_code (sread.cmd, datatype);
 | 
						|
  sread.cmd[4]=00;
 | 
						|
  sread.cmd[5]=00;
 | 
						|
  set_R_xfer_length (sread.cmd, length);
 | 
						|
 | 
						|
  r = do_scsi_cmd (s->sfd, sread.cmd, sread.size, s->buffer, length);
 | 
						|
  return ((r != 0) ? -1 : (int) length);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
coolscan_do_inquiry (Coolscan_t * s)
 | 
						|
{
 | 
						|
  int size;
 | 
						|
 | 
						|
  DBG (10, "do_inquiry\n");
 | 
						|
  memset (s->buffer, '\0', 256);	/* clear buffer */
 | 
						|
  size = 36;			/* Hardcoded, and as specified by Nikon */
 | 
						|
  /* then get inquiry with actual size */
 | 
						|
  set_inquiry_return_size (inquiry.cmd, size);
 | 
						|
  do_scsi_cmd (s->sfd, inquiry.cmd, inquiry.size, s->buffer, size);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
coolscan_identify_scanner (Coolscan_t * s)
 | 
						|
{
 | 
						|
  unsigned char vendor[9];
 | 
						|
  unsigned char product[0x11];
 | 
						|
  unsigned char version[5];
 | 
						|
  unsigned char *pp;
 | 
						|
  int i;
 | 
						|
 | 
						|
  vendor[8] = product[0x10] = version[4] = 0;
 | 
						|
  DBG (10, "identify_scanner\n");
 | 
						|
  coolscan_do_inquiry (s);	/* get inquiry */
 | 
						|
  if (get_inquiry_periph_devtype (s->buffer) != IN_periph_devtype_scanner)
 | 
						|
    {
 | 
						|
      DBG (5, "identify_scanner: not a scanner\n");
 | 
						|
      return 1;
 | 
						|
    }				/* no, continue searching */
 | 
						|
 | 
						|
  coolscan_get_inquiry_values (s);
 | 
						|
 | 
						|
  get_inquiry_vendor (s->buffer, vendor);
 | 
						|
  get_inquiry_product (s->buffer, product);
 | 
						|
  get_inquiry_version (s->buffer, version);
 | 
						|
 | 
						|
  if (strncmp ("Nikon   ", vendor, 8))
 | 
						|
    {
 | 
						|
      DBG (5, "identify_scanner: \"%s\" isn't a Nikon product\n", vendor);
 | 
						|
      return 1;
 | 
						|
    }				/* Not a Nikon product */
 | 
						|
 | 
						|
  pp = &vendor[8];
 | 
						|
  vendor[8] = ' ';
 | 
						|
  while (*pp == ' ')
 | 
						|
    {
 | 
						|
      *pp-- = '\0';
 | 
						|
    }
 | 
						|
 | 
						|
  pp = &product[0x10];
 | 
						|
  product[0x10] = ' ';
 | 
						|
  while (*(pp - 1) == ' ')
 | 
						|
    {
 | 
						|
      *pp-- = '\0';
 | 
						|
    }				/* leave one blank at the end! */
 | 
						|
 | 
						|
  pp = &version[4];
 | 
						|
  version[4] = ' ';
 | 
						|
  while (*pp == ' ')
 | 
						|
    {
 | 
						|
      *pp-- = '\0';
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (10, "Found Nikon scanner %sversion %s on device %s\n",
 | 
						|
       product, version, s->devicename);
 | 
						|
 | 
						|
  /* look for scanners that do not give all inquiry-informations */
 | 
						|
  /* and if possible use driver-known inquiry-data  */
 | 
						|
  if (get_inquiry_additional_length (s->buffer) >= 0x1f)
 | 
						|
    {
 | 
						|
      /* Now identify full supported scanners */
 | 
						|
      for (i = 0; i < known_scanners; i++)
 | 
						|
	{
 | 
						|
	  if (!strncmp (product, scanner_str[i], strlen (scanner_str[i])))
 | 
						|
	    {
 | 
						|
	      s->LS = i;
 | 
						|
	      return 0;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      if (s->cont)
 | 
						|
	return 0;
 | 
						|
      else
 | 
						|
	return 1;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
pixels_per_line (Coolscan_t * s)
 | 
						|
{
 | 
						|
  int pic_dot;
 | 
						|
  if(s->LS<2)
 | 
						|
  {  pic_dot = (s->brx - s->tlx + s->x_nres) / s->x_nres; 
 | 
						|
  }
 | 
						|
  else
 | 
						|
  { pic_dot = (s->brx - s->tlx + 1) / s->x_nres; 
 | 
						|
  }
 | 
						|
  DBG (10, "pic_dot=%d\n", pic_dot);
 | 
						|
  return pic_dot;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
lines_per_scan (Coolscan_t * s)
 | 
						|
{
 | 
						|
  int pic_line;
 | 
						|
  if(s->LS<2)
 | 
						|
  { pic_line = (s->bry - s->tly + s->y_nres) / s->y_nres; 
 | 
						|
  }
 | 
						|
  else
 | 
						|
  { pic_line = (( s->bry - s->tly + 1.0 )  / s->y_nres); 
 | 
						|
  }
 | 
						|
  DBG (10, "pic_line=%d\n", pic_line);
 | 
						|
  return pic_line;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
scan_bytes_per_line (Coolscan_t * s)
 | 
						|
{ int bpl;
 | 
						|
  switch(s->colormode)
 | 
						|
    {  case RGB:
 | 
						|
       case GREYSCALE:
 | 
						|
              bpl=pixels_per_line (s) * 3;
 | 
						|
              if(s->bits_per_color>8) bpl=bpl*2;
 | 
						|
              return bpl;
 | 
						|
              break;      
 | 
						|
       case RGBI:
 | 
						|
       case IRED:
 | 
						|
              bpl=pixels_per_line (s) * 4;
 | 
						|
              if(s->bits_per_color>8) bpl=bpl*2;
 | 
						|
              return bpl;
 | 
						|
              break; 
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
write_bytes_per_line (Coolscan_t * s)
 | 
						|
{ int bpl;
 | 
						|
  switch(s->colormode)
 | 
						|
    {  case RGB:
 | 
						|
              bpl=pixels_per_line (s) * 3;
 | 
						|
              if(s->bits_per_color>8) bpl=bpl*2;
 | 
						|
              return bpl;
 | 
						|
              break;      
 | 
						|
       case RGBI:
 | 
						|
              bpl=pixels_per_line (s) * 4;
 | 
						|
              if(s->bits_per_color>8) bpl=bpl*2;
 | 
						|
              return bpl;
 | 
						|
              break; 
 | 
						|
       case IRED:
 | 
						|
       case GREYSCALE:
 | 
						|
              bpl= pixels_per_line (s) ;
 | 
						|
              if(s->bits_per_color>8) bpl=bpl*2;
 | 
						|
              return bpl;
 | 
						|
              break; 
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
coolscan_trim_rowbufsize (Coolscan_t * s)
 | 
						|
{
 | 
						|
  unsigned int row_len;
 | 
						|
  row_len = scan_bytes_per_line (s);
 | 
						|
  s->row_bufsize = (s->row_bufsize < row_len) ? s->row_bufsize
 | 
						|
    : s->row_bufsize - (s->row_bufsize % row_len);
 | 
						|
  DBG (10, "trim_bufsize to %d\n", s->row_bufsize);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
coolscan_check_values (Coolscan_t * s)
 | 
						|
{
 | 
						|
  DBG (10, "check_values\n");
 | 
						|
  /* -------------------------- asf --------------------------------- */
 | 
						|
  if (s->asf != 0)
 | 
						|
    {
 | 
						|
      if (s->autofeeder == 0)
 | 
						|
	{
 | 
						|
	  DBG (1, "ERROR: ASF-MODE NOT SUPPORTED BY SCANNER, ABORTING\n");
 | 
						|
	  return (1);
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  return (0);
 | 
						|
}
 | 
						|
 | 
						|
/* test_little_endian */
 | 
						|
 | 
						|
static SANE_Bool 
 | 
						|
coolscan_test_little_endian(void)
 | 
						|
{
 | 
						|
  SANE_Int testvalue = 255;
 | 
						|
  unsigned char *firstbyte = (unsigned char *) &testvalue;
 | 
						|
 | 
						|
  if (*firstbyte == 255)
 | 
						|
  { return SANE_TRUE;
 | 
						|
  }
 | 
						|
  return SANE_FALSE;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
get_inquiery_part_LS30 (Coolscan_t * s, unsigned char part)
 | 
						|
{ 
 | 
						|
  int size;
 | 
						|
  int ret;
 | 
						|
 | 
						|
  /* Get length of reponse */
 | 
						|
  inquiry.cmd[1]=0x01;
 | 
						|
  inquiry.cmd[2]=part;
 | 
						|
  size=4;
 | 
						|
  set_inquiry_return_size (inquiry.cmd, size);
 | 
						|
  ret = do_scsi_cmd (s->sfd, inquiry.cmd, inquiry.size,
 | 
						|
		     s->buffer, size);
 | 
						|
  size=get_inquiry_length(s->buffer); 
 | 
						|
  size+=4;
 | 
						|
  /* then get inquiry with actual size */
 | 
						|
  set_inquiry_return_size (inquiry.cmd, size);
 | 
						|
  ret = do_scsi_cmd (s->sfd, inquiry.cmd, inquiry.size,
 | 
						|
		     s->buffer, size);
 | 
						|
  return size;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
coolscan_read_var_data_block (Coolscan_t * s,int datatype)
 | 
						|
{
 | 
						|
  int r;
 | 
						|
  int size;
 | 
						|
 | 
						|
  DBG (10, "read_data_block (type= %x)\n",datatype);
 | 
						|
  /*wait_scanner(s); */
 | 
						|
 | 
						|
  sread.cmd[2]=datatype;
 | 
						|
  sread.cmd[4]=00;
 | 
						|
  sread.cmd[5]=03;
 | 
						|
  size=6;
 | 
						|
  set_R_xfer_length (sread.cmd, size);
 | 
						|
  r = do_scsi_cmd (s->sfd, sread.cmd, sread.size,
 | 
						|
		     s->buffer, size);
 | 
						|
  size=s->buffer[5]; 
 | 
						|
  set_R_xfer_length (sread.cmd, size);
 | 
						|
  r = do_scsi_cmd (s->sfd, sread.cmd, sread.size,
 | 
						|
		     s->buffer, size);
 | 
						|
  return ((r != 0) ? -1 : size);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
get_inquiery_LS30 (Coolscan_t * s)
 | 
						|
{ 
 | 
						|
  unsigned char part;
 | 
						|
  unsigned char parts[5];
 | 
						|
  int size;
 | 
						|
  int i;
 | 
						|
 | 
						|
  /* Get vector of inquiery parts */
 | 
						|
  size=get_inquiery_part_LS30(s, (unsigned char) 0);
 | 
						|
  /* Get the parts of inquiery */  
 | 
						|
  for(i=0;i<5;i++)
 | 
						|
  { parts[i]=((unsigned char *)s->buffer)[4+11+i];
 | 
						|
  }
 | 
						|
  for(i=0;i<5;i++)
 | 
						|
  { part=parts[i];
 | 
						|
    size=get_inquiery_part_LS30 (s, part);
 | 
						|
    switch(part)
 | 
						|
    {  case 0x0c1:/* max size and resolution */                   
 | 
						|
                    s->adbits = 8;
 | 
						|
                    s->outputbits = 8;
 | 
						|
		    s->maxres = getnbyte(s->buffer+0x12,2)-1;
 | 
						|
		    s->xmaxpix = getnbyte(s->buffer+0x53,2)-1;
 | 
						|
		    s->ymaxpix = getnbyte(s->buffer+0x3c,2)-1;
 | 
						|
                  break;
 | 
						|
       case 0x0d1:
 | 
						|
                  break;
 | 
						|
       case 0x0e1:
 | 
						|
                  break;
 | 
						|
       case 0x0f0:
 | 
						|
                  break;
 | 
						|
       case 0x0f8:
 | 
						|
                  break;
 | 
						|
    }  
 | 
						|
  }
 | 
						|
 | 
						|
  /* get windows */
 | 
						|
  coolscan_get_window_param_LS30 (s,0,0);
 | 
						|
   s->xmax = get_WD_width(s->buffer);
 | 
						|
   s->ymax = get_WD_length(s->buffer);
 | 
						|
  coolscan_get_window_param_LS30 (s,1,0);
 | 
						|
  coolscan_get_window_param_LS30 (s,2,0);
 | 
						|
  coolscan_get_window_param_LS30 (s,3,0);
 | 
						|
  coolscan_get_window_param_LS30 (s,4,0);
 | 
						|
  coolscan_get_window_param_LS30 (s,9,0);
 | 
						|
 | 
						|
  s->analoggamma = 0;
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
get_feeder_type_LS30 (Coolscan_t * s)
 | 
						|
{ 
 | 
						|
  int size;
 | 
						|
  unsigned char *ptr;
 | 
						|
  int ima;
 | 
						|
 | 
						|
  /* find out about Film-strip-feeder or Mount-Feeder */
 | 
						|
  size=get_inquiery_part_LS30(s, (unsigned char) 1);
 | 
						|
  if(strncmp(s->buffer+5,"Strip",5)==0)
 | 
						|
  { s->feeder=STRIP_FEEDER;
 | 
						|
    s->autofeeder = 1;
 | 
						|
  }
 | 
						|
  if(strncmp(s->buffer+5,"Mount",5)==0)
 | 
						|
  { s->feeder=MOUNT_FEEDER;
 | 
						|
  }
 | 
						|
  /* find out about Film-strip-feeder positions*/
 | 
						|
  if(s->feeder==STRIP_FEEDER)
 | 
						|
  { size=coolscan_read_var_data_block (s,(int)0x88);
 | 
						|
    if(size>=4)
 | 
						|
    { s->numima=s->buffer[3];
 | 
						|
      if(s->numima>6) s->numima=6; /* limit to 6 images for now */
 | 
						|
      if(s->numima>(size-4)/16) s->numima=(size-4)/16;
 | 
						|
      ptr=s->buffer+4;
 | 
						|
      for(ima=0;ima<s->numima;ima++)
 | 
						|
      {  s->ipos[ima].start=getnbyte(ptr,4);
 | 
						|
         s->ipos[ima].offset=getnbyte(ptr+4,4);
 | 
						|
         s->ipos[ima].end=getnbyte(ptr+8,4);
 | 
						|
         s->ipos[ima].height=getnbyte(ptr+12,4);
 | 
						|
	 ptr+=16;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    s->posima=0;
 | 
						|
  }
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
get_internal_info_LS20 (Coolscan_t * s)
 | 
						|
{
 | 
						|
  int ret;
 | 
						|
 | 
						|
  DBG (10, "get_internal_info\n");
 | 
						|
  wait_scanner (s);
 | 
						|
  memset (s->buffer, '\0', DI_length);	/* clear buffer */
 | 
						|
 | 
						|
  set_R_datatype_code (sread.cmd, R_device_internal_info);
 | 
						|
  set_R_datatype_qual_upper (sread.cmd, R_DQ_none);
 | 
						|
  set_R_xfer_length (sread.cmd, DI_length);
 | 
						|
  /* then get inquiry with actual size */
 | 
						|
  ret = do_scsi_cmd (s->sfd, sread.cmd, sread.size,
 | 
						|
		     s->buffer, DI_length);
 | 
						|
 | 
						|
  s->adbits = get_DI_ADbits (s->buffer);
 | 
						|
  s->outputbits = get_DI_Outputbits (s->buffer);
 | 
						|
  s->maxres = get_DI_MaxResolution (s->buffer);
 | 
						|
  s->xmax = get_DI_Xmax (s->buffer);
 | 
						|
  s->ymax = get_DI_Ymax (s->buffer);
 | 
						|
  s->xmaxpix = get_DI_Xmaxpixel (s->buffer);
 | 
						|
  s->ymaxpix = get_DI_Ymaxpixel (s->buffer);
 | 
						|
  s->ycurrent = get_DI_currentY (s->buffer);
 | 
						|
  s->currentfocus = get_DI_currentFocus (s->buffer);
 | 
						|
  s->currentscanpitch = get_DI_currentscanpitch (s->buffer);
 | 
						|
  s->autofeeder = get_DI_autofeeder (s->buffer);
 | 
						|
  s->analoggamma = get_DI_analoggamma (s->buffer);
 | 
						|
  s->derr[0] = get_DI_deviceerror0 (s->buffer);
 | 
						|
  s->derr[1] = get_DI_deviceerror1 (s->buffer);
 | 
						|
  s->derr[2] = get_DI_deviceerror2 (s->buffer);
 | 
						|
  s->derr[3] = get_DI_deviceerror3 (s->buffer);
 | 
						|
  s->derr[4] = get_DI_deviceerror4 (s->buffer);
 | 
						|
  s->derr[5] = get_DI_deviceerror5 (s->buffer);
 | 
						|
  s->derr[6] = get_DI_deviceerror6 (s->buffer);
 | 
						|
  s->derr[7] = get_DI_deviceerror7 (s->buffer);
 | 
						|
  s->wbetr_r = get_DI_WBETR_R (s->buffer);
 | 
						|
  s->webtr_g = get_DI_WBETR_G (s->buffer);
 | 
						|
  s->webtr_b = get_DI_WBETR_B (s->buffer);
 | 
						|
  s->pretv_r = get_DI_PRETV_R (s->buffer);
 | 
						|
  s->pretv_g = get_DI_PRETV_G (s->buffer);
 | 
						|
  s->pretv_r = get_DI_PRETV_R (s->buffer);
 | 
						|
  s->cetv_r = get_DI_CETV_R (s->buffer);
 | 
						|
  s->cetv_g = get_DI_CETV_G (s->buffer);
 | 
						|
  s->cetv_b = get_DI_CETV_B (s->buffer);
 | 
						|
  s->ietu_r = get_DI_IETU_R (s->buffer);
 | 
						|
  s->ietu_g = get_DI_IETU_G (s->buffer);
 | 
						|
  s->ietu_b = get_DI_IETU_B (s->buffer);
 | 
						|
  s->limitcondition = get_DI_limitcondition (s->buffer);
 | 
						|
  s->offsetdata_r = get_DI_offsetdata_R (s->buffer);
 | 
						|
  s->offsetdata_g = get_DI_offsetdata_G (s->buffer);
 | 
						|
  s->offsetdata_b = get_DI_offsetdata_B (s->buffer);
 | 
						|
  get_DI_poweron_errors (s->buffer, s->power_on_errors);
 | 
						|
 | 
						|
  DBG (10,
 | 
						|
       "\tadbits=%d\toutputbits=%d\tmaxres=%d\txmax=%d\tymax=%d\n"
 | 
						|
       "\txmaxpix=%d\tymaxpix=%d\tycurrent=%d\tcurrentfocus=%d\n"
 | 
						|
       "\tautofeeder=%s\tanaloggamma=%s\tcurrentscanpitch=%d\n"
 | 
						|
       "\tWhite balance exposure time var [RGB]=\t%d %d %d\n"
 | 
						|
       "\tPrescan result exposure time var [RGB]=\t%d %d %d\n"
 | 
						|
       "\tCurrent exposure time var.[RGB]=\t%d %d %d\n"
 | 
						|
       "\tInternal exposure time unit[RGB]=\t%d %d %d\n"
 | 
						|
       "\toffsetdata_[rgb]=\t0x%x 0x%x 0x%x\n"
 | 
						|
       "\tlimitcondition=0x%x\n"
 | 
						|
       "\tdevice error code = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n"
 | 
						|
       "\tpower-on errors = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
 | 
						|
       s->adbits, s->outputbits, s->maxres, s->xmax, s->ymax,
 | 
						|
       s->xmaxpix, s->ymaxpix, s->ycurrent, s->currentfocus,
 | 
						|
       s->autofeeder ? "Yes" : "No", s->analoggamma ? "Yes" : "No",
 | 
						|
       s->currentscanpitch,
 | 
						|
       s->wbetr_r, s->webtr_g, s->webtr_b, s->pretv_r, s->pretv_g,
 | 
						|
       s->pretv_r, s->cetv_r, s->cetv_g, s->cetv_b, s->ietu_r,
 | 
						|
       s->ietu_g, s->ietu_b,
 | 
						|
       s->offsetdata_r, s->offsetdata_g, s->offsetdata_b,
 | 
						|
       s->limitcondition, 
 | 
						|
       s->derr[0], s->derr[1], s->derr[2], s->derr[3], s->derr[4],
 | 
						|
       s->derr[5], s->derr[6], s->derr[7],
 | 
						|
       s->power_on_errors[0], s->power_on_errors[1],
 | 
						|
       s->power_on_errors[2], s->power_on_errors[3],
 | 
						|
       s->power_on_errors[4], s->power_on_errors[5],
 | 
						|
       s->power_on_errors[6], s->power_on_errors[7]);
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
get_internal_info (Coolscan_t * s)
 | 
						|
{
 | 
						|
  int ret;
 | 
						|
 | 
						|
  DBG (10, "get_internal_info\n");
 | 
						|
  
 | 
						|
  if(s->LS<2)                   /* distinquish between old and new scanners */
 | 
						|
  { ret=get_internal_info_LS20 (s);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  { ret=get_inquiery_LS30 (s);
 | 
						|
  }
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
coolscan_get_inquiry_values (Coolscan_t * s)
 | 
						|
{
 | 
						|
  unsigned char *inquiry_block;
 | 
						|
 | 
						|
  DBG (10, "get_inquiry_values\n");
 | 
						|
 | 
						|
  inquiry_block = (unsigned char *) s->buffer;
 | 
						|
  s->inquiry_len = 36;
 | 
						|
 | 
						|
  get_inquiry_vendor (inquiry_block, s->vendor);
 | 
						|
  s->vendor[8] = '\0';
 | 
						|
  get_inquiry_product (inquiry_block, s->product);
 | 
						|
  s->product[16] = '\0';
 | 
						|
  get_inquiry_version (inquiry_block, s->version);
 | 
						|
  s->version[4] = '\0';
 | 
						|
 | 
						|
  if (s->inquiry_len < 36)
 | 
						|
    {
 | 
						|
      DBG (1, "WARNING: inquiry return block is unexpected short (%d instead of 36).\n", s->inquiry_len);
 | 
						|
    }
 | 
						|
  s->inquiry_wdb_len = 117;
 | 
						|
  return;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
coolscan_initialize_values (Coolscan_t * s)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  DBG (10, "initialize_values\n");
 | 
						|
  /* Initialize us structure */
 | 
						|
  if(s->LS<2)                   /* LS-20 or LS-10000 */
 | 
						|
  {  select_MUD (s);		/* must be before mode_sense - not for LS-30*/
 | 
						|
     coolscan_mode_sense (s);	/* Obtain MUD (Measurement Unit Divisor) */
 | 
						|
     get_internal_info (s);	/* MUST be called first. */
 | 
						|
     s->wdb_len = 117;
 | 
						|
  }
 | 
						|
  if(s->LS>=2)                  /* LS-30 */
 | 
						|
  {
 | 
						|
    get_inquiery_LS30(s);	/* Info about scanner*/ 
 | 
						|
    select_MUD (s);		/* must be before mode_sense */
 | 
						|
    get_feeder_type_LS30(s);
 | 
						|
    s->wdb_len = 117;
 | 
						|
  }
 | 
						|
 | 
						|
  s->cont = 0;			/* do not continue if scanner is unknown */
 | 
						|
  s->verbose = 2;		/* 1=verbose,2=very verbose */
 | 
						|
 | 
						|
 | 
						|
  s->x_nres = s->y_nres = 2;	/* 2 => 1350 dpi */
 | 
						|
  s->x_p_nres = s->y_p_nres = 9;	/* 9 => 300 dpi */
 | 
						|
  s->tlx = 0;
 | 
						|
  s->tly = 0;
 | 
						|
  s->brx = s->xmaxpix;		/* 2700 / 1200; */
 | 
						|
  s->bry = s->ymaxpix;		/* 2700 / 1200; */
 | 
						|
 | 
						|
 | 
						|
  s->set_auto = 0;		/* Always 0 on Nikon LS-{100|2}0 */
 | 
						|
  s->preview = 0;		/* 1 for preview */
 | 
						|
  s->colormode = RGB;		/* GREYSCALE or RGB */
 | 
						|
  s->colormode_p = RGB;		/* GREYSCALE or RGB for preview*/
 | 
						|
  s->asf = 0;			/* 1 if asf shall be used */
 | 
						|
  s->gammaselection = WD_Linear;
 | 
						|
 | 
						|
  s->brightness = 128;
 | 
						|
  s->brightness_R = 128;
 | 
						|
  s->brightness_G = 128;
 | 
						|
  s->brightness_B = 128;
 | 
						|
  s->contrast = 128;
 | 
						|
  s->contrast_R = 128;
 | 
						|
  s->contrast_G = 128;
 | 
						|
  s->contrast_B = 128;
 | 
						|
 | 
						|
  s->exposure_R = 50;
 | 
						|
  s->exposure_G = 50;
 | 
						|
  s->exposure_B = 50;
 | 
						|
 | 
						|
  s->pretv_r=40000;
 | 
						|
  s->pretv_g=40000;  
 | 
						|
  s->pretv_b=40000;
 | 
						|
 | 
						|
  s->shift_R = 128;
 | 
						|
  s->shift_G = 128;
 | 
						|
  s->shift_B = 128;
 | 
						|
 | 
						|
  s->ired_red=60;
 | 
						|
  s->ired_green=1;
 | 
						|
  s->ired_blue=1;
 | 
						|
 | 
						|
  s->prescan = 1;
 | 
						|
  s->bits_per_color = 8;
 | 
						|
  s->rgb_control = 0;
 | 
						|
  s->gamma_bind = 1;
 | 
						|
  switch(s->LS)
 | 
						|
  {  case 0:s->lutlength=2048;
 | 
						|
            s->max_lut_val=256;
 | 
						|
            break;
 | 
						|
     case 1:s->lutlength=512;
 | 
						|
            s->max_lut_val=512;
 | 
						|
            break;
 | 
						|
     case 2:s->lutlength=1024;
 | 
						|
            s->max_lut_val=1024;
 | 
						|
            break;
 | 
						|
     case 3:s->lutlength=4096;
 | 
						|
            s->max_lut_val=4096;
 | 
						|
            break;
 | 
						|
  }
 | 
						|
  for (i = 0; i < s->lutlength; i++)
 | 
						|
  {
 | 
						|
     s->gamma[i] =((short)((((double)i)/s->lutlength)*s->max_lut_val));
 | 
						|
     s->gamma_r[i] = s->gamma[i];
 | 
						|
     s->gamma_g[i] = s->gamma[i];
 | 
						|
     s->gamma_b[i] = s->gamma[i];
 | 
						|
  } 
 | 
						|
 | 
						|
  if (coolscan_test_little_endian() == SANE_TRUE)
 | 
						|
  {
 | 
						|
    s->low_byte_first = 1;					        /* in 2 byte mode send lowbyte first */
 | 
						|
    DBG(10,"backend runs on little endian machine\n");
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    s->low_byte_first = 0;					       /* in 2 byte mode send highbyte first */
 | 
						|
    DBG(10,"backend runs on big endian machine\n");
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
hexdump (int level, char *comment, unsigned char *p, int l)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  char line[128];
 | 
						|
  char *ptr;
 | 
						|
 | 
						|
  DBG (level, "%s\n", comment);
 | 
						|
  ptr = line;
 | 
						|
  for (i = 0; i < l; i++, p++)
 | 
						|
    {
 | 
						|
      if ((i % 16) == 0)
 | 
						|
	{
 | 
						|
	  if (ptr != line)
 | 
						|
	    {
 | 
						|
	      *ptr = '\0';
 | 
						|
	      DBG (level, "%s\n", line);
 | 
						|
	      ptr = line;
 | 
						|
	    }
 | 
						|
	  sprintf (ptr, "%3.3d:", i);
 | 
						|
	  ptr += 4;
 | 
						|
	}
 | 
						|
      sprintf (ptr, " %2.2x", *p);
 | 
						|
      ptr += 3;
 | 
						|
    }
 | 
						|
  *ptr = '\0';
 | 
						|
  DBG (level, "%s\n", line);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
sense_handler (int scsi_fd, unsigned char * result, void *arg)
 | 
						|
{
 | 
						|
  scsi_fd = scsi_fd;
 | 
						|
  arg = arg;
 | 
						|
 | 
						|
  if (result[0] != 0x70)
 | 
						|
    {
 | 
						|
      return SANE_STATUS_IO_ERROR;	/* we only know about this one  */
 | 
						|
    }
 | 
						|
  return request_sense_parse(result);
 | 
						|
 
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ------------------------------------------------------------------------- */
 | 
						|
 | 
						|
 | 
						|
#define MM_PER_INCH	25.4
 | 
						|
/* ilu per mm */
 | 
						|
 | 
						|
#define length_quant SANE_UNFIX(SANE_FIX(MM_PER_INCH / 2700.0))
 | 
						|
#define mmToIlu(mm) ((mm) / length_quant)
 | 
						|
#define iluToMm(ilu) ((ilu) * length_quant)
 | 
						|
 | 
						|
#define P_200_TO_255(per) SANE_UNFIX((per + 100) * 255/200 )
 | 
						|
#define P_100_TO_255(per) SANE_UNFIX(per * 255/100 )
 | 
						|
 | 
						|
static const char negativeStr[] = "Negative";
 | 
						|
static const char positiveStr[] = "Positive";
 | 
						|
static SANE_String_Const type_list[] =
 | 
						|
{
 | 
						|
  positiveStr,
 | 
						|
  negativeStr,
 | 
						|
  0
 | 
						|
};
 | 
						|
 | 
						|
static const char colorStr[] = "Color";
 | 
						|
static const char grayStr[] = "Gray";
 | 
						|
static const char rgbiStr[] = "RGBI";
 | 
						|
static const char iredStr[] = "Infrared";
 | 
						|
 | 
						|
static SANE_String_Const scan_mode_list_LS20[] =
 | 
						|
{
 | 
						|
  colorStr,
 | 
						|
  grayStr,
 | 
						|
  NULL
 | 
						|
};
 | 
						|
 | 
						|
static SANE_String_Const scan_mode_list_LS30[] =
 | 
						|
{
 | 
						|
  colorStr,
 | 
						|
  grayStr,
 | 
						|
#ifdef HAS_IRED	     
 | 
						|
  rgbiStr,
 | 
						|
#endif /* HAS_IRED */
 | 
						|
  NULL
 | 
						|
};
 | 
						|
 | 
						|
static SANE_Int bit_depth_list[9];
 | 
						|
 | 
						|
static const char neverStr[] = "never";
 | 
						|
static const char previewStr[] = "before preview";
 | 
						|
static const char scanStr[] = "before scan";
 | 
						|
static const char preandscanStr[] = "before preview and scan";
 | 
						|
static SANE_String_Const autofocus_mode_list[] =
 | 
						|
{
 | 
						|
  neverStr,
 | 
						|
  previewStr,
 | 
						|
  scanStr,
 | 
						|
  preandscanStr,
 | 
						|
  NULL
 | 
						|
};
 | 
						|
 | 
						|
static SANE_String_Const source_list[4] =
 | 
						|
{NULL, NULL, NULL, NULL};
 | 
						|
 | 
						|
static const SANE_Range gamma_range_8 = 
 | 
						|
{
 | 
						|
  0,				/* minimum */
 | 
						|
  255,				/* maximum */
 | 
						|
  1				/* quantization */
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
static const SANE_Range gamma_range_9 = 
 | 
						|
{
 | 
						|
  0,				/* minimum */
 | 
						|
  511,				/* maximum */
 | 
						|
  1				/* quantization */
 | 
						|
};
 | 
						|
 | 
						|
static const SANE_Range gamma_range_10 = 
 | 
						|
{
 | 
						|
  0,				/* minimum */
 | 
						|
  1023,				/* maximum */
 | 
						|
  1				/* quantization */
 | 
						|
};
 | 
						|
 | 
						|
static const SANE_Range gamma_range_12 = 
 | 
						|
{
 | 
						|
  0,				/* minimum */
 | 
						|
  4096,				/* maximum */
 | 
						|
  1				/* quantization */
 | 
						|
};
 | 
						|
 | 
						|
static const SANE_Range brightness_range =
 | 
						|
{
 | 
						|
  -5,
 | 
						|
  +5,
 | 
						|
  1
 | 
						|
};
 | 
						|
 | 
						|
static const SANE_Range contrast_range =
 | 
						|
{
 | 
						|
  -5,
 | 
						|
  +5,
 | 
						|
  0
 | 
						|
};
 | 
						|
 | 
						|
static const SANE_Range exposure_range =
 | 
						|
{
 | 
						|
  24,
 | 
						|
  400,
 | 
						|
  2
 | 
						|
};
 | 
						|
 | 
						|
static const SANE_Range shift_range =
 | 
						|
{
 | 
						|
  -15,
 | 
						|
  +15,
 | 
						|
  0
 | 
						|
};
 | 
						|
 | 
						|
static int num_devices;
 | 
						|
static Coolscan_t *first_dev;
 | 
						|
 | 
						|
 | 
						|
static size_t
 | 
						|
max_string_size (const SANE_String_Const strings[])
 | 
						|
{
 | 
						|
  size_t size, max_size = 0;
 | 
						|
  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
 | 
						|
do_eof (Coolscan_t * scanner)
 | 
						|
{
 | 
						|
  DBG (10, "do_eof\n");
 | 
						|
 | 
						|
  if (scanner->pipe >= 0)
 | 
						|
    {
 | 
						|
      close (scanner->pipe);
 | 
						|
      scanner->pipe = -1;
 | 
						|
    }
 | 
						|
  return SANE_STATUS_EOF;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
do_cancel (Coolscan_t * scanner)
 | 
						|
{
 | 
						|
  DBG (10, "do_cancel\n");
 | 
						|
  swap_res (scanner);
 | 
						|
  scanner->scanning = SANE_FALSE;
 | 
						|
 | 
						|
  do_eof (scanner);		/* close pipe and reposition scanner */
 | 
						|
 | 
						|
  if (scanner->reader_pid > 0)
 | 
						|
    {
 | 
						|
      int exit_status;
 | 
						|
 | 
						|
      DBG (10, "do_cancel: kill reader_process\n");
 | 
						|
 | 
						|
      /* ensure child knows it's time to stop: */
 | 
						|
      sanei_thread_kill (scanner->reader_pid);
 | 
						|
      while (sanei_thread_waitpid(scanner->reader_pid, &exit_status) !=
 | 
						|
                                                        scanner->reader_pid );
 | 
						|
      scanner->reader_pid = 0;
 | 
						|
    }
 | 
						|
 | 
						|
  if (scanner->sfd >= 0)
 | 
						|
    {
 | 
						|
      coolscan_give_scanner (scanner);
 | 
						|
      DBG (10, "do_cancel: close filedescriptor\n");
 | 
						|
      sanei_scsi_close (scanner->sfd);
 | 
						|
      scanner->sfd = -1;
 | 
						|
    }
 | 
						|
 | 
						|
  return SANE_STATUS_CANCELLED;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
attach_scanner (const char *devicename, Coolscan_t ** devp)
 | 
						|
{
 | 
						|
  Coolscan_t *dev;
 | 
						|
  int sfd;
 | 
						|
 | 
						|
  DBG (10, "attach_scanner: %s\n", devicename);
 | 
						|
 | 
						|
  for (dev = first_dev; dev; dev = dev->next)
 | 
						|
    {
 | 
						|
      if (strcmp (dev->sane.name, devicename) == 0)
 | 
						|
	{
 | 
						|
	  if (devp)
 | 
						|
	    {
 | 
						|
	      *devp = dev;
 | 
						|
	    }
 | 
						|
	  DBG (5, "attach_scanner: scanner already attached (is ok)!\n");
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (10, "attach_scanner: opening %s\n", devicename);
 | 
						|
  if (sanei_scsi_open (devicename, &sfd, sense_handler, 0) != 0)
 | 
						|
    {
 | 
						|
      DBG (1, "attach_scanner: open failed\n");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  if (NULL == (dev = malloc (sizeof (*dev))))
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
 | 
						|
	
 | 
						|
  dev->row_bufsize = (sanei_scsi_max_request_size < (64 * 1024)) ?
 | 
						|
  	sanei_scsi_max_request_size : 64 * 1024;
 | 
						|
 
 | 
						|
  if ((dev->buffer = malloc (dev->row_bufsize)) == NULL)
 | 
						|
/*  if ((dev->buffer = malloc (sanei_scsi_max_request_size)) == NULL)*/
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
 | 
						|
  if ((dev->obuffer = malloc (dev->row_bufsize)) == NULL)
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
 | 
						|
  dev->devicename = strdup (devicename);
 | 
						|
  dev->sfd = sfd;
 | 
						|
 | 
						|
  /* Nikon manual: Step 1 */
 | 
						|
  if (coolscan_identify_scanner (dev) != 0)
 | 
						|
    {
 | 
						|
      DBG (1, "attach_scanner: scanner-identification failed\n");
 | 
						|
      sanei_scsi_close (dev->sfd);
 | 
						|
      free (dev->buffer);
 | 
						|
      free (dev);
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Get MUD (via mode_sense), internal info (via get_internal_info), and
 | 
						|
   * initialize values */
 | 
						|
  coolscan_initialize_values (dev);
 | 
						|
 | 
						|
  /* Why? */
 | 
						|
  sanei_scsi_close (dev->sfd);
 | 
						|
  dev->sfd = -1;
 | 
						|
 | 
						|
  dev->sane.name = dev->devicename;
 | 
						|
  dev->sane.vendor = dev->vendor;
 | 
						|
  dev->sane.model = dev->product;
 | 
						|
  dev->sane.type = "slide scanner";
 | 
						|
 | 
						|
  dev->x_range.min = SANE_FIX (0);
 | 
						|
  dev->x_range.quant = SANE_FIX (length_quant);
 | 
						|
  dev->x_range.max = SANE_FIX ((double) ((dev->xmaxpix) * length_quant));
 | 
						|
 | 
						|
  dev->y_range.min = SANE_FIX (0.0);
 | 
						|
  dev->y_range.quant = SANE_FIX (length_quant);
 | 
						|
  dev->y_range.max = SANE_FIX ((double) ((dev->ymaxpix) * length_quant));
 | 
						|
 | 
						|
  /* ...and this?? */
 | 
						|
  dev->dpi_range.min = SANE_FIX (108);
 | 
						|
  dev->dpi_range.quant = SANE_FIX (0);
 | 
						|
  dev->dpi_range.max = SANE_FIX (dev->maxres);
 | 
						|
  DBG (10, "attach: dev->dpi_range.max = %f\n",
 | 
						|
       SANE_UNFIX (dev->dpi_range.max));
 | 
						|
 | 
						|
  ++num_devices;
 | 
						|
  dev->next = first_dev;
 | 
						|
  first_dev = dev;
 | 
						|
 | 
						|
  if (devp)
 | 
						|
    {
 | 
						|
      *devp = dev;
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (10, "attach_scanner done\n");
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
attach_one (const char *devName)
 | 
						|
{
 | 
						|
  return attach_scanner(devName, 0);
 | 
						|
}
 | 
						|
 | 
						|
static RETSIGTYPE
 | 
						|
sigterm_handler (int signal)
 | 
						|
{
 | 
						|
  signal = signal;
 | 
						|
  sanei_scsi_req_flush_all ();	/* flush SCSI queue */
 | 
						|
  _exit (SANE_STATUS_GOOD);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
typedef struct Color_correct_s
 | 
						|
{ int sum;           /* number of pixels summed so far */
 | 
						|
  double sumr;          /* sum of red pixel values*/
 | 
						|
  double sumi;          /* sum of infrared pixel values*/
 | 
						|
  double sumri;         /* sum of red*ired pixel values*/
 | 
						|
  double sumii;         /* sum of ired*ired pixel values*/
 | 
						|
  double sumrr;         /* sum of ired*ired pixel values*/
 | 
						|
  int  mr;         /* factor between red and ired values (*256) */ 
 | 
						|
  int  br;         /* offset of ired values */ 
 | 
						|
} ColorCorrect;
 | 
						|
 | 
						|
/* --------------------------------------------------------------- 
 | 
						|
 | 
						|
  function:   RGBIfix
 | 
						|
 | 
						|
  taks:       Correct the infrared channel
 | 
						|
      
 | 
						|
  import:     unsigned char * rgbimat - RGBI - matrix from scanner
 | 
						|
              int size - number of pixels to correct
 | 
						|
	      int *lutr - lookup table for red correction
 | 
						|
	      int *lutg - lookup table for red correction
 | 
						|
	      int *lutb - lookup table for red correction
 | 
						|
	      int *lutr - lookup table for red correction
 | 
						|
 | 
						|
  export:     unsigned char * orgbimat - RGBI - corrected matrix
 | 
						|
                  
 | 
						|
  written by: Andreas RICK   19.6.1999
 | 
						|
                           
 | 
						|
  ----------------------------------------------------------------*/
 | 
						|
 | 
						|
static int Calc_fix_LUT(Coolscan_t * s)
 | 
						|
{ int uselutr,uselutg,uselutb,useluti;    
 | 
						|
/*  static int irmulr= -34*25; */
 | 
						|
   int irmulr= -64*25;
 | 
						|
   int irmulg= -1*25;
 | 
						|
   int irmulb= -0*25;
 | 
						|
   int irmuli= 256*25;
 | 
						|
   int div;
 | 
						|
   int i;
 | 
						|
 | 
						|
    irmulr=s->ired_red*(25);
 | 
						|
    irmulg=s->ired_green*(25);
 | 
						|
    irmulb=s->ired_blue*(25);
 | 
						|
    irmuli=25*256;
 | 
						|
  
 | 
						|
  if(s->LS==2) /* TODO: right conversion factors for 10 and 12 bit */
 | 
						|
    { div=4;
 | 
						|
    }
 | 
						|
  else  if(s->LS==3) 
 | 
						|
    { div=16;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    { return 0;
 | 
						|
    }
 | 
						|
 | 
						|
  memset(s->lutr, 0,256*4);
 | 
						|
  memset(s->lutg, 0,256*4);
 | 
						|
  memset(s->lutb, 0,256*4);
 | 
						|
  memset(s->luti, 0,256*4);
 | 
						|
 | 
						|
  for(i=0;i<s->lutlength;i++)
 | 
						|
  {  if(s->gamma_bind)
 | 
						|
     { uselutr=uselutg=uselutb=useluti=s->gamma[i]/div;
 | 
						|
     }
 | 
						|
     else
 | 
						|
     { uselutr=s->gamma_r[i]/div;
 | 
						|
       uselutg=s->gamma_g[i]/div;
 | 
						|
       uselutb=s->gamma_b[i]/div;
 | 
						|
       useluti=s->gamma_r[i]/div;
 | 
						|
     }
 | 
						|
     s->lutr[uselutr]=(int)(irmulr*pow((double)i,(double)0.333333)); 
 | 
						|
     s->lutg[uselutg]=(int)(irmulg*pow((double)i,(double)0.333333)); 
 | 
						|
     s->lutb[uselutb]=(int)(irmulb*pow((double)i,(double)0.333333)); 
 | 
						|
     s->luti[useluti]=(int)(irmuli*pow((double)i,(double)0.333333)); 
 | 
						|
     if(uselutr<255)
 | 
						|
     { if(s->lutr[uselutr+1]==0) s->lutr[uselutr+1]=s->lutr[uselutr];
 | 
						|
     }
 | 
						|
     if(uselutg<255)
 | 
						|
     { if(s->lutg[uselutg+1]==0) s->lutg[uselutg+1]=s->lutg[uselutg];
 | 
						|
     }
 | 
						|
     if(uselutb<255)
 | 
						|
     { if(s->lutb[uselutb+1]==0) s->lutb[uselutb+1]=s->lutb[uselutb];
 | 
						|
     }
 | 
						|
     if(useluti<255)
 | 
						|
     { if(s->luti[useluti+1]==0) s->luti[useluti+1]=s->luti[useluti];
 | 
						|
     }
 | 
						|
  }
 | 
						|
  /* DEBUG
 | 
						|
  for(i=0;i<255;i++)
 | 
						|
  { fprintf(stderr,"%d %d %d %d\n"
 | 
						|
	    ,s->lutr[i],s->lutg[i],s->lutb[i],s->luti[i]);
 | 
						|
  }
 | 
						|
  */
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/* --------------------------------------------------------------- 
 | 
						|
 | 
						|
  function:   RGBIfix
 | 
						|
 | 
						|
  taks:       Correct the infrared channel
 | 
						|
      
 | 
						|
  import:     unsigned char * rgbimat - RGBI - matrix from scanner
 | 
						|
              int size - number of pixels to correct
 | 
						|
	      int *lutr - lookup table for red correction
 | 
						|
	      int *lutg - lookup table for red correction
 | 
						|
	      int *lutb - lookup table for red correction
 | 
						|
	      int *lutr - lookup table for red correction
 | 
						|
 | 
						|
  export:     unsigned char * orgbimat - RGBI - corrected matrix
 | 
						|
                  
 | 
						|
  written by: Andreas RICK   19.6.1999
 | 
						|
                           
 | 
						|
  ----------------------------------------------------------------*/
 | 
						|
 | 
						|
static int RGBIfix(Coolscan_t * scanner,
 | 
						|
	   unsigned char* rgbimat,
 | 
						|
	   unsigned char* orgbimat, 	   
 | 
						|
	    int size,
 | 
						|
	    int *lutr,
 | 
						|
	    int *lutg,
 | 
						|
	    int *lutb,
 | 
						|
	    int *luti)
 | 
						|
	    
 | 
						|
{
 | 
						|
  unsigned char *pr,*pg,*pb,*pi;
 | 
						|
  unsigned char *opr,*opg,*opb,*opi;
 | 
						|
 | 
						|
   int r,g,b,i;
 | 
						|
   int ii;
 | 
						|
   int x;
 | 
						|
   for(x=0;x<size;x++)
 | 
						|
   {
 | 
						|
        pr=rgbimat+x*4;
 | 
						|
	pg=pr+1; 
 | 
						|
	pb=pg+1;
 | 
						|
	pi=pb+1;	
 | 
						|
        opr=orgbimat+x*4;
 | 
						|
	opg=opr+1; 
 | 
						|
	opb=opg+1;
 | 
						|
	opi=opb+1;
 | 
						|
	r=lutr[(*pr)];
 | 
						|
	g=lutg[(*pg)];
 | 
						|
	b=lutb[(*pb)];	
 | 
						|
	i=luti[(*pi)];	
 | 
						|
	ii= i-r-g-b;
 | 
						|
	(*opr)=(*pr);
 | 
						|
	(*opg)=(*pg);
 | 
						|
	(*opb)=(*pb);	
 | 
						|
	if(ii<0)ii=0;
 | 
						|
	if(ii>255*256)ii=255*256;
 | 
						|
	if(scanner->negative)
 | 
						|
	{
 | 
						|
	    (*opi)=(unsigned char)(255-(ii>>8));	
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
	  (*opi)=(unsigned char)(ii>>8);	
 | 
						|
	}
 | 
						|
   }
 | 
						|
   return 1;
 | 
						|
}
 | 
						|
 | 
						|
/* --------------------------------------------------------------- 
 | 
						|
 | 
						|
  function:   RGBIfix16
 | 
						|
 | 
						|
  taks:       Correct the infrared channel for 16 bit images
 | 
						|
              (doesn't do anything for now)
 | 
						|
      
 | 
						|
  import:     unsigned char * rgbimat - RGBI - matrix from scanner
 | 
						|
              int size - number of pixels to correct
 | 
						|
	      int *lutr - lookup table for red correction
 | 
						|
	      int *lutg - lookup table for red correction
 | 
						|
	      int *lutb - lookup table for red correction
 | 
						|
	      int *lutr - lookup table for red correction
 | 
						|
 | 
						|
  export:     unsigned char * orgbimat - RGBI - corrected matrix
 | 
						|
                  
 | 
						|
  written by: Andreas RICK   19.6.1999
 | 
						|
                           
 | 
						|
  ----------------------------------------------------------------*/
 | 
						|
 | 
						|
static int RGBIfix16(Coolscan_t * scanner,
 | 
						|
	      unsigned short* rgbimat,
 | 
						|
	      unsigned short* orgbimat, 	   
 | 
						|
	      int size,
 | 
						|
	      int *lutr,
 | 
						|
	      int *lutg,
 | 
						|
	      int *lutb,
 | 
						|
	      int *luti)
 | 
						|
	    
 | 
						|
{  
 | 
						|
  unsigned short *pr,*pg,*pb,*pi;
 | 
						|
  unsigned short *opr,*opg,*opb,*opi;
 | 
						|
  int x;
 | 
						|
 | 
						|
  scanner = scanner; lutr = lutr; lutg = lutg; lutb = lutb; luti = luti;
 | 
						|
 | 
						|
   for(x=0;x<size;x++)
 | 
						|
   {
 | 
						|
        pr=rgbimat+x*4;
 | 
						|
	pg=pr+1; 
 | 
						|
	pb=pg+1;
 | 
						|
	pi=pb+1;	
 | 
						|
        opr=orgbimat+x*4;
 | 
						|
	opg=opr+1; 
 | 
						|
	opb=opg+1;
 | 
						|
	opi=opb+1;
 | 
						|
	(*opr)=(((*pr)&0x00ff)<<8)+(((*pr)&0xff00)>>8); 
 | 
						|
      	(*opg)=(((*pg)&0x00ff)<<8)+(((*pg)&0xff00)>>8); 
 | 
						|
	(*opb)=(((*pb)&0x00ff)<<8)+(((*pb)&0xff00)>>8); 	
 | 
						|
	(*opi)=(((*pi)&0x00ff)<<8)+(((*pi)&0xff00)>>8); 
 | 
						|
   }
 | 
						|
   return 1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* --------------------------------------------------------------- 
 | 
						|
 | 
						|
  function:   rgb2g
 | 
						|
 | 
						|
  taks:       Convert RGB data to grey
 | 
						|
      
 | 
						|
  import:     unsigned char * rgbimat - RGB - matrix from scanner
 | 
						|
              int size - size of input data (num pixel)
 | 
						|
 | 
						|
  export:     unsigned char * gomat - Grey matrix
 | 
						|
                  
 | 
						|
  written by: Andreas RICK   13.7.1999
 | 
						|
                           
 | 
						|
  ----------------------------------------------------------------*/
 | 
						|
#define RtoG ((int)(0.27*256))
 | 
						|
#define GtoG ((int)(0.54*256))
 | 
						|
#define BtoG ((int)(0.19*256))
 | 
						|
 | 
						|
static int rgb2g(unsigned char* rgbimat,unsigned char* gomat, 	   
 | 
						|
	  int size)
 | 
						|
	    
 | 
						|
{  unsigned char *pr,*pg,*pb;
 | 
						|
   unsigned char *opg;
 | 
						|
 | 
						|
   int g;
 | 
						|
   int x;
 | 
						|
   for(x=0;x<size;x++)
 | 
						|
   {
 | 
						|
        pr=rgbimat+x*3;
 | 
						|
	pg=pr+1; 
 | 
						|
	pb=pg+1;
 | 
						|
        opg=gomat+x;
 | 
						|
	g= RtoG*(*pr) + GtoG*(*pg) + BtoG*(*pb);
 | 
						|
	(*opg)=(unsigned char)(g>>8);	
 | 
						|
   }
 | 
						|
   return 1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* --------------------------------------------------------------- 
 | 
						|
 | 
						|
  function:   RGBIfix1
 | 
						|
 | 
						|
  taks:       Correct the infrared channel.
 | 
						|
              The input image data is the output of scaning
 | 
						|
	      with LUT. To calculate the original values
 | 
						|
	      the lutr and luti is applied.
 | 
						|
	      The infrared values is corrected by:
 | 
						|
 | 
						|
	      Ir=mr*lutr(r)+luti(i)
 | 
						|
      
 | 
						|
  import:     unsigned char * rgbimat - RGBI - matrix from scanner
 | 
						|
              int size - number of pixels to correct
 | 
						|
 	      ColorCorrect *cc,
 | 
						|
	      int *lutr - lookup table for red correction
 | 
						|
	      int *luti - lookup table for ired correction
 | 
						|
 | 
						|
  export:     unsigned char * orgbimat - RGBI - corrected matrix
 | 
						|
                  
 | 
						|
  written by: Andreas RICK   3.7.1999
 | 
						|
                           
 | 
						|
  ----------------------------------------------------------------*/
 | 
						|
#if 0
 | 
						|
static int RGBIfix1(unsigned char* rgbimat,unsigned char* orgbimat, 	   
 | 
						|
	    int size,
 | 
						|
	    int *lutr,
 | 
						|
	    int *lutg,
 | 
						|
	    int *lutb,
 | 
						|
	    int *luti)
 | 
						|
	    
 | 
						|
{  unsigned char *pr,*pg,*pb,*pi;
 | 
						|
   unsigned char *opr,*opg,*opb,*opi;
 | 
						|
   ColorCorrect cc;
 | 
						|
   int r,i;
 | 
						|
   static int thresi=100;
 | 
						|
   int ii;
 | 
						|
   int x;
 | 
						|
 | 
						|
   lutg = lutg; lutb = lutb;
 | 
						|
 | 
						|
   /* calculate regression between r and ir */
 | 
						|
   cc.sum=0;
 | 
						|
   cc.sumr=cc.sumii=cc.sumrr=cc.sumi=cc.sumri=0.0;
 | 
						|
   for(x=0;x<size;x++)
 | 
						|
   {    pr=rgbimat+x*4;
 | 
						|
	pi=pr+3;	
 | 
						|
	r=lutr[(*pr)];
 | 
						|
	i=luti[(*pi)];
 | 
						|
	/*	r=(*pr);
 | 
						|
		i=(*pi); */
 | 
						|
	if((*pi)>thresi)
 | 
						|
        { cc.sum++;
 | 
						|
          cc.sumr+=r;
 | 
						|
          cc.sumii+=(i*i);
 | 
						|
          cc.sumrr+=(r*r);
 | 
						|
          cc.sumi+=i;
 | 
						|
          cc.sumri+=(i*r);
 | 
						|
	}
 | 
						|
   }
 | 
						|
   if((cc.sumii!=0)&&(cc.sum!=0))
 | 
						|
   { double dn,dz,dm;
 | 
						|
     dz=(cc.sumri-cc.sumr*cc.sumi/cc.sum);
 | 
						|
     dn=(cc.sumrr-cc.sumr*cc.sumr/cc.sum);
 | 
						|
     DBG (2, "Reg:dz:%e dn:%e\n",dz,dn);
 | 
						|
     if(dn!=0)
 | 
						|
     {  dm=(dz/dn);
 | 
						|
        cc.mr=(int)(dm*1024);
 | 
						|
     }
 | 
						|
     else
 | 
						|
     { cc.mr=0;
 | 
						|
       dm=0;
 | 
						|
     }
 | 
						|
     cc.br=(int)((cc.sumi-dm*cc.sumr)/cc.sum);
 | 
						|
   }
 | 
						|
   else
 | 
						|
   { cc.mr=0;
 | 
						|
   }
 | 
						|
   DBG (2, "Regression: size:%d I=%d/1024*R b:%d s:%d sr:%e si:%e sii:%e sri:%e  srr:%e\n",
 | 
						|
	size,cc.mr,cc.br,cc.sum,cc.sumr,cc.sumi,cc.sumii,cc.sumri,cc.sumrr);
 | 
						|
   for(x=0;x<size;x++)
 | 
						|
   {
 | 
						|
 | 
						|
        pr=rgbimat+x*4;
 | 
						|
	pg=pr+1; 
 | 
						|
	pb=pg+1;
 | 
						|
	pi=pb+1;	
 | 
						|
        opr=orgbimat+x*4;
 | 
						|
	opg=opr+1; 
 | 
						|
	opb=opg+1;
 | 
						|
	opi=opb+1;
 | 
						|
	r=lutr[(*pr)];
 | 
						|
	i=luti[(*pi)];
 | 
						|
	/*	r=(*pr);
 | 
						|
		i=(*pi); */
 | 
						|
	ii= ((i-((r*cc.mr)>>10)-cc.br)>>2) +128;
 | 
						|
	(*opr)=(*pr);
 | 
						|
	(*opg)=(*pg);
 | 
						|
	(*opb)=(*pb);	
 | 
						|
	if(ii<0) ii=0;
 | 
						|
	if(ii>255) ii=255;
 | 
						|
	(*opi)=(unsigned char)(ii);	
 | 
						|
   }
 | 
						|
   return 1;
 | 
						|
}
 | 
						|
 | 
						|
#endif
 | 
						|
/* This function is executed as a child process. */
 | 
						|
static int
 | 
						|
reader_process (void *data )
 | 
						|
{
 | 
						|
  int status;
 | 
						|
  unsigned int i;
 | 
						|
  unsigned char h;
 | 
						|
  unsigned int data_left;
 | 
						|
  unsigned int data_to_read;
 | 
						|
  unsigned int data_to_write;
 | 
						|
  FILE *fp;
 | 
						|
  sigset_t sigterm_set, ignore_set;
 | 
						|
  struct SIGACTION act;
 | 
						|
  unsigned int bpl, linesPerBuf, lineOffset;
 | 
						|
  unsigned char r_data, g_data, b_data;
 | 
						|
  unsigned int j, line;
 | 
						|
  Coolscan_t * scanner = (Coolscan_t*)data;
 | 
						|
 | 
						|
  if (sanei_thread_is_forked ())
 | 
						|
    {
 | 
						|
      DBG (10, "reader_process started (forked)\n");
 | 
						|
      close (scanner->pipe);
 | 
						|
      scanner->pipe = -1;
 | 
						|
 | 
						|
      sigfillset ( &ignore_set );
 | 
						|
      sigdelset  ( &ignore_set, SIGTERM );
 | 
						|
#if defined (__APPLE__) && defined (__MACH__)
 | 
						|
      sigdelset  ( &ignore_set, SIGUSR2 );
 | 
						|
#endif
 | 
						|
      sigprocmask( SIG_SETMASK, &ignore_set, 0 );
 | 
						|
 | 
						|
      memset (&act, 0, sizeof (act));
 | 
						|
      sigaction (SIGTERM, &act, 0);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      DBG (10, "reader_process started (as thread)\n");
 | 
						|
    }
 | 
						|
 | 
						|
  sigemptyset (&sigterm_set);
 | 
						|
  sigaddset (&sigterm_set, SIGTERM);
 | 
						|
 | 
						|
  fp = fdopen ( scanner->reader_fds, "w");
 | 
						|
  if (!fp)
 | 
						|
    {
 | 
						|
      DBG (1, "reader_process: couldn't open pipe!\n");
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (10, "reader_process: starting to READ data\n");
 | 
						|
 | 
						|
  data_left = scan_bytes_per_line (scanner) *
 | 
						|
    lines_per_scan (scanner);
 | 
						|
  
 | 
						|
  /*scanner->row_bufsize = sanei_scsi_max_request_size;*/
 | 
						|
  coolscan_trim_rowbufsize (scanner);	/* trim bufsize */
 | 
						|
 | 
						|
  DBG (10, "reader_process: reading %u bytes in blocks of %u bytes\n",
 | 
						|
       data_left, scanner->row_bufsize);
 | 
						|
 | 
						|
  memset (&act, 0, sizeof (act));
 | 
						|
  act.sa_handler = sigterm_handler;
 | 
						|
  sigaction (SIGTERM, &act, 0);
 | 
						|
  /* wait_scanner(scanner); */
 | 
						|
  do
 | 
						|
    {
 | 
						|
      data_to_read = (data_left < scanner->row_bufsize) ?
 | 
						|
	data_left : scanner->row_bufsize;
 | 
						|
 | 
						|
      data_to_write=data_to_read;
 | 
						|
 | 
						|
      status = coolscan_read_data_block (scanner
 | 
						|
					 ,R_datatype_imagedata,data_to_read);
 | 
						|
      if (status == 0)
 | 
						|
	{
 | 
						|
	  continue;
 | 
						|
	}
 | 
						|
      if (status == -1)
 | 
						|
	{
 | 
						|
	  DBG (1, "reader_process: unable to get image data from scanner!\n");
 | 
						|
	  fclose (fp);
 | 
						|
	  return (-1);
 | 
						|
	}
 | 
						|
    
 | 
						|
      if (scanner->LS == 1) {	/* mirror image for LS-1000 */
 | 
						|
	  bpl = scan_bytes_per_line(scanner);
 | 
						|
	  linesPerBuf = data_to_read / bpl;
 | 
						|
	  
 | 
						|
	  for (line = 0, lineOffset = 0; line < linesPerBuf; 
 | 
						|
	       line++, lineOffset += bpl ) {
 | 
						|
	      
 | 
						|
	      if (scanner->colormode == RGB) {
 | 
						|
		  for (j = 0; j < bpl/2 ; j += 3) {
 | 
						|
		      r_data=scanner->buffer[lineOffset + j];
 | 
						|
		      g_data=scanner->buffer[lineOffset + j + 1];
 | 
						|
		      b_data=scanner->buffer[lineOffset + j + 2];
 | 
						|
		      
 | 
						|
		      scanner->buffer[lineOffset + j] = 
 | 
						|
			  scanner->buffer[lineOffset + bpl -1 - j - 2 ];
 | 
						|
		      scanner->buffer[lineOffset + j + 1] = 
 | 
						|
			  scanner->buffer[lineOffset + bpl -1 - j - 1 ];
 | 
						|
		      scanner->buffer[lineOffset + j + 2] = 
 | 
						|
			  scanner->buffer[lineOffset + bpl -1 - j ];
 | 
						|
		  
 | 
						|
		      scanner->buffer[lineOffset + bpl -1 - j - 2 ] = r_data;
 | 
						|
		      scanner->buffer[lineOffset + bpl -1 - j - 1] = g_data;
 | 
						|
		      scanner->buffer[lineOffset + bpl -1 - j] = b_data;
 | 
						|
		  }
 | 
						|
	      }
 | 
						|
	      else {
 | 
						|
		  for (j = 0; j < bpl/2; j++) {
 | 
						|
		      r_data=scanner->buffer[lineOffset + j];
 | 
						|
		      scanner->buffer[lineOffset + j] = 
 | 
						|
			  scanner->buffer[lineOffset + bpl - 1 - j];
 | 
						|
		      scanner->buffer[lineOffset + bpl - 1 - j] = r_data;
 | 
						|
		  }
 | 
						|
	      }
 | 
						|
	  }	  
 | 
						|
      }    
 | 
						|
      if(scanner->colormode==RGBI) 
 | 
						|
      {  /* Correct Infrared Channel */
 | 
						|
	if(scanner->bits_per_color>8)
 | 
						|
	{
 | 
						|
	  RGBIfix16(scanner, (unsigned short * ) scanner->buffer, 
 | 
						|
		    (unsigned short * )scanner->obuffer,
 | 
						|
		 data_to_read/8,scanner->lutr,
 | 
						|
		 scanner->lutg,scanner->lutb,scanner->luti);
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
	  RGBIfix(scanner,scanner->buffer,scanner->obuffer,
 | 
						|
		 data_to_read/4,scanner->lutr,
 | 
						|
		 scanner->lutg,scanner->lutb,scanner->luti);
 | 
						|
	}
 | 
						|
      }
 | 
						|
      else if((scanner->colormode==GREYSCALE)&&(scanner->LS>=2))
 | 
						|
      {  /* Convert to Grey */
 | 
						|
	 data_to_write/=3;
 | 
						|
	 rgb2g(scanner->buffer,scanner->obuffer,data_to_write);
 | 
						|
      }
 | 
						|
      else
 | 
						|
      { /* or just copy */
 | 
						|
	memcpy (scanner->obuffer, scanner->buffer,data_to_read);
 | 
						|
      }     
 | 
						|
      if((!scanner->low_byte_first)&&(scanner->bits_per_color>8))
 | 
						|
	{  for(i=0;i<data_to_write;i++) /* inverse byteorder */        
 | 
						|
           { h=scanner->obuffer[i];
 | 
						|
             scanner->obuffer[i]=scanner->obuffer[i+1];
 | 
						|
	     i++;
 | 
						|
  	     scanner->obuffer[i]=h;
 | 
						|
	   }
 | 
						|
	}
 | 
						|
      fwrite (scanner->obuffer, 1, data_to_write, fp);
 | 
						|
      fflush (fp);      
 | 
						|
      data_left -= data_to_read;
 | 
						|
      DBG (10, "reader_process: buffer of %d bytes read; %d bytes to go\n",
 | 
						|
	   data_to_read, data_left);
 | 
						|
    }
 | 
						|
  while (data_left);
 | 
						|
 | 
						|
  fclose (fp);
 | 
						|
 | 
						|
  DBG (10, "reader_process: finished reading data\n");
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
init_options (Coolscan_t * scanner)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  int bit_depths;
 | 
						|
 | 
						|
  DBG (10, "init_options\n");
 | 
						|
 | 
						|
  memset (scanner->opt, 0, sizeof (scanner->opt));
 | 
						|
 | 
						|
  for (i = 0; i < NUM_OPTIONS; ++i)
 | 
						|
    {
 | 
						|
      scanner->opt[i].size = sizeof (SANE_Word);
 | 
						|
      scanner->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
 | 
						|
    }
 | 
						|
 | 
						|
  scanner->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
 | 
						|
  scanner->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
 | 
						|
  scanner->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
 | 
						|
 | 
						|
  /* "Mode" group: */
 | 
						|
  scanner->opt[OPT_MODE_GROUP].title = "Scan Mode";
 | 
						|
  scanner->opt[OPT_MODE_GROUP].desc = "";
 | 
						|
  scanner->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
 | 
						|
  scanner->opt[OPT_MODE_GROUP].cap = 0;
 | 
						|
  scanner->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | 
						|
 | 
						|
  /* scan mode */
 | 
						|
  scanner->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
 | 
						|
  scanner->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
 | 
						|
  scanner->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
 | 
						|
  scanner->opt[OPT_MODE].type = SANE_TYPE_STRING;
 | 
						|
  if(scanner->LS<2)
 | 
						|
  { scanner->opt[OPT_MODE].size = max_string_size (scan_mode_list_LS20);
 | 
						|
    scanner->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | 
						|
    scanner->opt[OPT_MODE].constraint.string_list = scan_mode_list_LS20;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  { scanner->opt[OPT_MODE].size = max_string_size (scan_mode_list_LS30);
 | 
						|
    scanner->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | 
						|
    scanner->opt[OPT_MODE].constraint.string_list = scan_mode_list_LS30;
 | 
						|
  }
 | 
						|
 | 
						|
  /* source */
 | 
						|
  source_list[0] = "Slide";
 | 
						|
  source_list[1] = "Automatic Slide Feeder";
 | 
						|
  source_list[2] = NULL;
 | 
						|
  if (!scanner->autofeeder)
 | 
						|
    {
 | 
						|
      scanner->opt[OPT_SOURCE].cap = SANE_CAP_INACTIVE;
 | 
						|
    }
 | 
						|
 | 
						|
  scanner->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
 | 
						|
  scanner->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
 | 
						|
  scanner->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
 | 
						|
  scanner->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
 | 
						|
  scanner->opt[OPT_SOURCE].size = max_string_size (source_list);
 | 
						|
  scanner->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | 
						|
  scanner->opt[OPT_SOURCE].constraint.string_list = source_list;
 | 
						|
 | 
						|
  /* negative */
 | 
						|
  scanner->opt[OPT_TYPE].name = "type";
 | 
						|
  scanner->opt[OPT_TYPE].title = "Film type";
 | 
						|
  scanner->opt[OPT_TYPE].desc =
 | 
						|
    "Select the film type (positive (slide) or negative)";
 | 
						|
  scanner->opt[OPT_TYPE].type = SANE_TYPE_STRING;
 | 
						|
  scanner->opt[OPT_TYPE].size = max_string_size (type_list);
 | 
						|
  scanner->opt[OPT_TYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | 
						|
  scanner->opt[OPT_TYPE].constraint.string_list = type_list;
 | 
						|
 | 
						|
  scanner->opt[OPT_PRESCAN].name = "prescan";
 | 
						|
  scanner->opt[OPT_PRESCAN].title = "Prescan";
 | 
						|
  scanner->opt[OPT_PRESCAN].desc =
 | 
						|
    "Perform a prescan during preview";
 | 
						|
  scanner->opt[OPT_PRESCAN].type = SANE_TYPE_BOOL;
 | 
						|
  scanner->opt[OPT_PRESCAN].unit = SANE_UNIT_NONE;
 | 
						|
 | 
						|
  scanner->opt[OPT_PRESCAN_NOW].name = "prescan now";
 | 
						|
  scanner->opt[OPT_PRESCAN_NOW].title = "Prescan now";
 | 
						|
  scanner->opt[OPT_PRESCAN_NOW].desc =
 | 
						|
    "Perform a prescan now";
 | 
						|
  scanner->opt[OPT_PRESCAN_NOW].type = SANE_TYPE_BUTTON;
 | 
						|
  scanner->opt[OPT_PRESCAN_NOW].unit = SANE_UNIT_NONE;
 | 
						|
  scanner->opt[OPT_PRESCAN_NOW].size = 0;
 | 
						|
  scanner->opt[OPT_PRESCAN_NOW].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
 | 
						|
  scanner->opt[OPT_PRESCAN_NOW].constraint_type = SANE_CONSTRAINT_NONE;
 | 
						|
  scanner->opt[OPT_PRESCAN_NOW].constraint.string_list = 0;
 | 
						|
 | 
						|
 /* bit depth */
 | 
						|
  
 | 
						|
  bit_depths=0;
 | 
						|
  bit_depth_list[++bit_depths] = 8;
 | 
						|
  if (scanner->LS==2)
 | 
						|
  {
 | 
						|
    bit_depth_list[++bit_depths] = 10;
 | 
						|
  }
 | 
						|
  if (scanner->LS==3)
 | 
						|
  {
 | 
						|
    bit_depth_list[++bit_depths] = 12;
 | 
						|
  }
 | 
						|
 | 
						|
  bit_depth_list[0] = bit_depths;
 | 
						|
 | 
						|
  scanner->opt[OPT_BIT_DEPTH].name  = SANE_NAME_BIT_DEPTH;
 | 
						|
  scanner->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
 | 
						|
  scanner->opt[OPT_BIT_DEPTH].desc  = SANE_DESC_BIT_DEPTH;
 | 
						|
  scanner->opt[OPT_BIT_DEPTH].type  = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_BIT_DEPTH].unit  = SANE_UNIT_BIT;
 | 
						|
  scanner->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
 | 
						|
  scanner->opt[OPT_BIT_DEPTH].constraint.word_list = bit_depth_list;
 | 
						|
 | 
						|
  /* resolution */
 | 
						|
  scanner->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
 | 
						|
  scanner->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
 | 
						|
  scanner->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
 | 
						|
  scanner->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
 | 
						|
  scanner->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
 | 
						|
  scanner->opt[OPT_RESOLUTION].constraint.word_list = resolution_list;
 | 
						|
 | 
						|
  scanner->opt[OPT_PREVIEW_RESOLUTION].name = "preview-resolution";
 | 
						|
  scanner->opt[OPT_PREVIEW_RESOLUTION].title = "Preview resolution";
 | 
						|
  scanner->opt[OPT_PREVIEW_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
 | 
						|
  scanner->opt[OPT_PREVIEW_RESOLUTION].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_PREVIEW_RESOLUTION].unit = SANE_UNIT_DPI;
 | 
						|
  scanner->opt[OPT_PREVIEW_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
 | 
						|
  scanner->opt[OPT_PREVIEW_RESOLUTION].constraint.word_list = resolution_list;
 | 
						|
 | 
						|
  /* "Geometry" group: */
 | 
						|
  scanner->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
 | 
						|
  scanner->opt[OPT_GEOMETRY_GROUP].desc = "";
 | 
						|
  scanner->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
 | 
						|
  scanner->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
 | 
						|
  scanner->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | 
						|
 | 
						|
  /* top-left x */
 | 
						|
  scanner->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
 | 
						|
  scanner->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
 | 
						|
  scanner->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
 | 
						|
  scanner->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
 | 
						|
  scanner->opt[OPT_TL_X].unit = SANE_UNIT_MM;
 | 
						|
  scanner->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_TL_X].constraint.range = &(scanner->x_range);
 | 
						|
 | 
						|
  /* top-left y */
 | 
						|
  scanner->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
 | 
						|
  scanner->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
 | 
						|
  scanner->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
 | 
						|
  scanner->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
 | 
						|
  scanner->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
 | 
						|
  scanner->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_TL_Y].constraint.range = &(scanner->y_range);
 | 
						|
 | 
						|
  /* bottom-right x */
 | 
						|
  scanner->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
 | 
						|
  scanner->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
 | 
						|
  scanner->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
 | 
						|
  scanner->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
 | 
						|
  scanner->opt[OPT_BR_X].unit = SANE_UNIT_MM;
 | 
						|
  scanner->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_BR_X].constraint.range = &(scanner->x_range);
 | 
						|
 | 
						|
  /* bottom-right y */
 | 
						|
  scanner->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
 | 
						|
  scanner->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
 | 
						|
  scanner->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
 | 
						|
  scanner->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
 | 
						|
  scanner->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
 | 
						|
  scanner->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_BR_Y].constraint.range = &(scanner->y_range);
 | 
						|
 | 
						|
 | 
						|
  /* ------------------------------ */
 | 
						|
 | 
						|
  /* "Enhancement" group: */
 | 
						|
  scanner->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
 | 
						|
  scanner->opt[OPT_ENHANCEMENT_GROUP].desc = "";
 | 
						|
  scanner->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
 | 
						|
  scanner->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
 | 
						|
  scanner->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | 
						|
 | 
						|
 | 
						|
  scanner->opt[OPT_GAMMA_BIND].name = "gamma-bind";
 | 
						|
  scanner->opt[OPT_GAMMA_BIND].title = "Gamma bind";
 | 
						|
  scanner->opt[OPT_GAMMA_BIND].desc =
 | 
						|
    "Use same gamma correction for all colours";
 | 
						|
  scanner->opt[OPT_GAMMA_BIND].type = SANE_TYPE_BOOL;
 | 
						|
  scanner->opt[OPT_GAMMA_BIND].unit = SANE_UNIT_NONE;
 | 
						|
 | 
						|
  
 | 
						|
  scanner->opt[OPT_ANALOG_GAMMA].name = "analog_gamma";
 | 
						|
  scanner->opt[OPT_ANALOG_GAMMA].title = "Analog Gamma";
 | 
						|
  scanner->opt[OPT_ANALOG_GAMMA].desc = "Analog Gamma";
 | 
						|
  scanner->opt[OPT_ANALOG_GAMMA].type = SANE_TYPE_BOOL;
 | 
						|
  scanner->opt[OPT_ANALOG_GAMMA].unit = SANE_UNIT_NONE;
 | 
						|
  if (!scanner->analoggamma)
 | 
						|
    {
 | 
						|
      scanner->opt[OPT_ANALOG_GAMMA].cap = SANE_CAP_INACTIVE;
 | 
						|
    }
 | 
						|
 | 
						|
  scanner->opt[OPT_AVERAGING].name = "averaging";
 | 
						|
  scanner->opt[OPT_AVERAGING].title = "Averaging";
 | 
						|
  scanner->opt[OPT_AVERAGING].desc = "Averaging";
 | 
						|
  scanner->opt[OPT_AVERAGING].type = SANE_TYPE_BOOL;
 | 
						|
  scanner->opt[OPT_AVERAGING].unit = SANE_UNIT_NONE;
 | 
						|
 | 
						|
 | 
						|
  scanner->opt[OPT_RGB_CONTROL].name = "rgb-control";
 | 
						|
  scanner->opt[OPT_RGB_CONTROL].title = "RGB control";
 | 
						|
  scanner->opt[OPT_RGB_CONTROL].desc =
 | 
						|
    "toggles brightness/contrast control over individual colours";
 | 
						|
  scanner->opt[OPT_RGB_CONTROL].type = SANE_TYPE_BOOL;
 | 
						|
  scanner->opt[OPT_RGB_CONTROL].unit = SANE_UNIT_NONE;
 | 
						|
  if(scanner->LS>=2)
 | 
						|
  {  scanner->opt[OPT_RGB_CONTROL].cap |= SANE_CAP_INACTIVE;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
  /* brightness */
 | 
						|
  scanner->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
 | 
						|
  scanner->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
 | 
						|
  scanner->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
 | 
						|
  scanner->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
 | 
						|
  scanner->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_BRIGHTNESS].constraint.range = &brightness_range;
 | 
						|
  if(scanner->LS>=2)
 | 
						|
  {  scanner->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  scanner->opt[OPT_R_BRIGHTNESS].name = "red-brightness";
 | 
						|
  scanner->opt[OPT_R_BRIGHTNESS].title = "Red brightness";
 | 
						|
  scanner->opt[OPT_R_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
 | 
						|
  scanner->opt[OPT_R_BRIGHTNESS].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_R_BRIGHTNESS].unit = SANE_UNIT_NONE;
 | 
						|
  scanner->opt[OPT_R_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_R_BRIGHTNESS].constraint.range = &brightness_range;
 | 
						|
  scanner->opt[OPT_R_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
  scanner->opt[OPT_G_BRIGHTNESS].name = "green-brightness";
 | 
						|
  scanner->opt[OPT_G_BRIGHTNESS].title = "Green brightness";
 | 
						|
  scanner->opt[OPT_G_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
 | 
						|
  scanner->opt[OPT_G_BRIGHTNESS].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_G_BRIGHTNESS].unit = SANE_UNIT_NONE;
 | 
						|
  scanner->opt[OPT_G_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_G_BRIGHTNESS].constraint.range = &brightness_range;
 | 
						|
  scanner->opt[OPT_G_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
  scanner->opt[OPT_B_BRIGHTNESS].name = "blue-brightness";
 | 
						|
  scanner->opt[OPT_B_BRIGHTNESS].title = "Blue brightness";
 | 
						|
  scanner->opt[OPT_B_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
 | 
						|
  scanner->opt[OPT_B_BRIGHTNESS].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_B_BRIGHTNESS].unit = SANE_UNIT_NONE;
 | 
						|
  scanner->opt[OPT_B_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_B_BRIGHTNESS].constraint.range = &brightness_range;
 | 
						|
  scanner->opt[OPT_B_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
  /* contrast */
 | 
						|
  scanner->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
 | 
						|
  scanner->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
 | 
						|
  scanner->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
 | 
						|
  scanner->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
 | 
						|
  scanner->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_CONTRAST].constraint.range = &contrast_range;
 | 
						|
  if(scanner->LS>=2)
 | 
						|
  {  scanner->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  scanner->opt[OPT_R_CONTRAST].name = "red-contrast";
 | 
						|
  scanner->opt[OPT_R_CONTRAST].title = "Red contrast";
 | 
						|
  scanner->opt[OPT_R_CONTRAST].desc = SANE_DESC_CONTRAST;
 | 
						|
  scanner->opt[OPT_R_CONTRAST].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_R_CONTRAST].unit = SANE_UNIT_NONE;
 | 
						|
  scanner->opt[OPT_R_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_R_CONTRAST].constraint.range = &contrast_range;
 | 
						|
  scanner->opt[OPT_R_CONTRAST].cap |= SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
  scanner->opt[OPT_G_CONTRAST].name = "green-contrast";
 | 
						|
  scanner->opt[OPT_G_CONTRAST].title = "Green contrast";
 | 
						|
  scanner->opt[OPT_G_CONTRAST].desc = SANE_DESC_CONTRAST;
 | 
						|
  scanner->opt[OPT_G_CONTRAST].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_G_CONTRAST].unit = SANE_UNIT_NONE;
 | 
						|
  scanner->opt[OPT_G_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_G_CONTRAST].constraint.range = &contrast_range;
 | 
						|
  scanner->opt[OPT_G_CONTRAST].cap |= SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
  scanner->opt[OPT_B_CONTRAST].name = "blue-contrast";
 | 
						|
  scanner->opt[OPT_B_CONTRAST].title = "Blue contrast";
 | 
						|
  scanner->opt[OPT_B_CONTRAST].desc = SANE_DESC_CONTRAST;
 | 
						|
  scanner->opt[OPT_B_CONTRAST].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_B_CONTRAST].unit = SANE_UNIT_NONE;
 | 
						|
  scanner->opt[OPT_B_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_B_CONTRAST].constraint.range = &contrast_range;
 | 
						|
  scanner->opt[OPT_B_CONTRAST].cap |= SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
  scanner->opt[OPT_EXPOSURE].name = "exposure";
 | 
						|
  scanner->opt[OPT_EXPOSURE].title = "Exposure";
 | 
						|
  scanner->opt[OPT_EXPOSURE].desc = "";
 | 
						|
  scanner->opt[OPT_EXPOSURE].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
  scanner->opt[OPT_EXPOSURE].unit = SANE_UNIT_PERCENT;
 | 
						|
  scanner->opt[OPT_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_EXPOSURE].constraint.range = &exposure_range;
 | 
						|
 | 
						|
  scanner->opt[OPT_R_EXPOSURE].name = "red-exposure";
 | 
						|
  scanner->opt[OPT_R_EXPOSURE].title = "Red exposure";
 | 
						|
  scanner->opt[OPT_R_EXPOSURE].desc = "";
 | 
						|
  scanner->opt[OPT_R_EXPOSURE].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_R_EXPOSURE].cap |= SANE_CAP_INACTIVE;
 | 
						|
  scanner->opt[OPT_R_EXPOSURE].unit = SANE_UNIT_PERCENT;
 | 
						|
  scanner->opt[OPT_R_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_R_EXPOSURE].constraint.range = &exposure_range;
 | 
						|
 | 
						|
  scanner->opt[OPT_G_EXPOSURE].name = "green-exposure";
 | 
						|
  scanner->opt[OPT_G_EXPOSURE].title = "Green exposure";
 | 
						|
  scanner->opt[OPT_G_EXPOSURE].desc = "";
 | 
						|
  scanner->opt[OPT_G_EXPOSURE].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_G_EXPOSURE].unit = SANE_UNIT_PERCENT;
 | 
						|
  scanner->opt[OPT_G_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_G_EXPOSURE].constraint.range = &exposure_range;
 | 
						|
  scanner->opt[OPT_G_EXPOSURE].cap |= SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
  scanner->opt[OPT_B_EXPOSURE].name = "blue-exposure";
 | 
						|
  scanner->opt[OPT_B_EXPOSURE].title = "Blue exposre";
 | 
						|
  scanner->opt[OPT_B_EXPOSURE].desc = "";
 | 
						|
  scanner->opt[OPT_B_EXPOSURE].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_B_EXPOSURE].unit = SANE_UNIT_PERCENT;
 | 
						|
  scanner->opt[OPT_B_EXPOSURE].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_B_EXPOSURE].constraint.range = &exposure_range;
 | 
						|
  scanner->opt[OPT_B_EXPOSURE].cap |= SANE_CAP_INACTIVE;
 | 
						|
  if(scanner->LS>=2)
 | 
						|
  {  scanner->opt[OPT_R_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
     scanner->opt[OPT_G_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
     scanner->opt[OPT_B_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
     scanner->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
 | 
						|
  }
 | 
						|
 | 
						|
  scanner->opt[OPT_R_SHIFT].name = "red-shift";
 | 
						|
  scanner->opt[OPT_R_SHIFT].title = "Red shift";
 | 
						|
  scanner->opt[OPT_R_SHIFT].desc = "";
 | 
						|
  scanner->opt[OPT_R_SHIFT].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_R_SHIFT].unit = SANE_UNIT_NONE;
 | 
						|
  scanner->opt[OPT_R_SHIFT].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_R_SHIFT].constraint.range = &shift_range;
 | 
						|
  if(scanner->LS>=2)
 | 
						|
  {  scanner->opt[OPT_R_SHIFT].cap |= SANE_CAP_INACTIVE;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  scanner->opt[OPT_G_SHIFT].name = "green-shift";
 | 
						|
  scanner->opt[OPT_G_SHIFT].title = "Green shift";
 | 
						|
  scanner->opt[OPT_G_SHIFT].desc = "";
 | 
						|
  scanner->opt[OPT_G_SHIFT].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_G_SHIFT].unit = SANE_UNIT_NONE;
 | 
						|
  scanner->opt[OPT_G_SHIFT].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_G_SHIFT].constraint.range = &shift_range;
 | 
						|
  if(scanner->LS>=2)
 | 
						|
  {  scanner->opt[OPT_G_SHIFT].cap |= SANE_CAP_INACTIVE;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  scanner->opt[OPT_B_SHIFT].name = "blue-shift";
 | 
						|
  scanner->opt[OPT_B_SHIFT].title = "Blue shift";
 | 
						|
  scanner->opt[OPT_B_SHIFT].desc = "";
 | 
						|
  scanner->opt[OPT_B_SHIFT].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_B_SHIFT].unit = SANE_UNIT_NONE;
 | 
						|
  scanner->opt[OPT_B_SHIFT].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_B_SHIFT].constraint.range = &shift_range;
 | 
						|
  if(scanner->LS>=2)
 | 
						|
  {  scanner->opt[OPT_B_SHIFT].cap |= SANE_CAP_INACTIVE;
 | 
						|
  }
 | 
						|
 | 
						|
  /* R+G+B gamma vector */
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
 | 
						|
  if (scanner->LS == 1)
 | 
						|
    {
 | 
						|
      scanner->opt[OPT_GAMMA_VECTOR].cap = SANE_CAP_INACTIVE;
 | 
						|
    }
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
 | 
						|
  switch(scanner->LS)
 | 
						|
  {  case 0:
 | 
						|
           scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_8;
 | 
						|
           scanner->lutlength=2048;
 | 
						|
           break;
 | 
						|
     case 1:
 | 
						|
           scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_9;
 | 
						|
           scanner->lutlength=512;
 | 
						|
           break;
 | 
						|
     case 2:
 | 
						|
           scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_10;
 | 
						|
           scanner->lutlength=1024;
 | 
						|
           break;
 | 
						|
     case 3:
 | 
						|
           scanner->opt[OPT_GAMMA_VECTOR].constraint.range = &gamma_range_12;
 | 
						|
           scanner->lutlength=4096;
 | 
						|
           break;
 | 
						|
  }
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR].size = scanner->lutlength * sizeof (SANE_Word);
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
 | 
						|
  /* red gamma vector */
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
 | 
						|
  switch(scanner->LS)
 | 
						|
  {  case 0:
 | 
						|
           scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_8;
 | 
						|
           scanner->lutlength=2048;
 | 
						|
           break;
 | 
						|
     case 1:
 | 
						|
           scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_9;
 | 
						|
           scanner->lutlength=512;
 | 
						|
           break;
 | 
						|
     case 2:
 | 
						|
           scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_10;
 | 
						|
           scanner->lutlength=1024;
 | 
						|
           break;
 | 
						|
     case 3:
 | 
						|
           scanner->opt[OPT_GAMMA_VECTOR_R].constraint.range = &gamma_range_12;
 | 
						|
           scanner->lutlength=4096;
 | 
						|
           break;
 | 
						|
  }
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_R].size = scanner->lutlength * sizeof (SANE_Word);
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
 | 
						|
  /* green gamma vector */
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
 | 
						|
  switch(scanner->LS)
 | 
						|
  {  case 0:
 | 
						|
           scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_8;
 | 
						|
           scanner->lutlength=2048;
 | 
						|
           break;
 | 
						|
     case 1:
 | 
						|
           scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_9;
 | 
						|
           scanner->lutlength=512;
 | 
						|
           break;
 | 
						|
     case 2:
 | 
						|
           scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_10;
 | 
						|
           scanner->lutlength=1024;
 | 
						|
           break;
 | 
						|
     case 3:
 | 
						|
           scanner->opt[OPT_GAMMA_VECTOR_G].constraint.range = &gamma_range_12;
 | 
						|
           scanner->lutlength=4096;
 | 
						|
           break;
 | 
						|
  }
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_G].size = scanner->lutlength * sizeof (SANE_Word);
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
 | 
						|
  /* blue gamma vector */
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
 | 
						|
  switch(scanner->LS)
 | 
						|
  {  case 0:
 | 
						|
           scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_8;
 | 
						|
           scanner->lutlength=2048;
 | 
						|
           break;
 | 
						|
     case 1:
 | 
						|
           scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_9;
 | 
						|
           scanner->lutlength=512;
 | 
						|
           break;
 | 
						|
     case 2:
 | 
						|
           scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_10;
 | 
						|
           scanner->lutlength=1024;
 | 
						|
           break;
 | 
						|
     case 3:
 | 
						|
           scanner->opt[OPT_GAMMA_VECTOR_B].constraint.range = &gamma_range_12;
 | 
						|
           scanner->lutlength=4096;
 | 
						|
           break;
 | 
						|
  }
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_B].size = scanner->lutlength * sizeof (SANE_Word);
 | 
						|
  scanner->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
 | 
						|
 | 
						|
  /* ------------------------------ */
 | 
						|
 | 
						|
  /* "Advanced" group: */
 | 
						|
  scanner->opt[OPT_ADVANCED_GROUP].title = "Advanced";
 | 
						|
  scanner->opt[OPT_ADVANCED_GROUP].desc = "";
 | 
						|
  scanner->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP;
 | 
						|
  scanner->opt[OPT_ADVANCED_GROUP].cap = SANE_CAP_ADVANCED;
 | 
						|
  scanner->opt[OPT_ADVANCED_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | 
						|
 | 
						|
  /* preview */
 | 
						|
  scanner->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
 | 
						|
  scanner->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
 | 
						|
  scanner->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
 | 
						|
  scanner->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
 | 
						|
 | 
						|
  /* Autofocus */
 | 
						|
  scanner->opt[OPT_AUTOFOCUS].name = "Autofocus";
 | 
						|
  scanner->opt[OPT_AUTOFOCUS].title ="Autofocus";
 | 
						|
  scanner->opt[OPT_AUTOFOCUS].desc = "When to do autofocussing";
 | 
						|
  scanner->opt[OPT_AUTOFOCUS].type = SANE_TYPE_STRING;
 | 
						|
  scanner->opt[OPT_AUTOFOCUS].size = max_string_size (autofocus_mode_list);
 | 
						|
  scanner->opt[OPT_AUTOFOCUS].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | 
						|
  scanner->opt[OPT_AUTOFOCUS].constraint.string_list = autofocus_mode_list;
 | 
						|
 | 
						|
  scanner->opt[OPT_IRED_RED].name = "IRED cor. red";
 | 
						|
  scanner->opt[OPT_IRED_RED].title ="IRED cor. red";
 | 
						|
  scanner->opt[OPT_IRED_RED].desc = "Correction of infrared from red";
 | 
						|
  scanner->opt[OPT_IRED_RED].type = SANE_TYPE_INT;
 | 
						|
  scanner->opt[OPT_IRED_RED].unit = SANE_UNIT_NONE;
 | 
						|
  scanner->opt[OPT_IRED_RED].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  scanner->opt[OPT_IRED_RED].constraint.range = &gamma_range_8;
 | 
						|
  scanner->opt[OPT_IRED_RED].cap |= SANE_CAP_ADVANCED;
 | 
						|
  if(scanner->LS<2)
 | 
						|
  {  scanner->opt[OPT_IRED_RED].cap |= SANE_CAP_INACTIVE;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
  /*  scanner->opt[OPT_PREVIEW].cap   = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT; */
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
 | 
						|
{
 | 
						|
  char dev_name[PATH_MAX];
 | 
						|
  size_t len;
 | 
						|
  FILE *fp;
 | 
						|
 | 
						|
  authorize = authorize;
 | 
						|
 | 
						|
  DBG_INIT ();
 | 
						|
  sanei_thread_init ();
 | 
						|
  
 | 
						|
  DBG (10, "sane_init\n");
 | 
						|
  if (version_code)
 | 
						|
      *version_code = SANE_VERSION_CODE (V_MAJOR, V_MINOR, 0);
 | 
						|
 | 
						|
  fp = sanei_config_open (COOLSCAN_CONFIG_FILE);
 | 
						|
  if (!fp)
 | 
						|
    {
 | 
						|
      attach_scanner ("/dev/scanner", 0); /* no config-file: /dev/scanner */
 | 
						|
      return SANE_STATUS_GOOD;
 | 
						|
    }
 | 
						|
 | 
						|
  while (sanei_config_read (dev_name, sizeof (dev_name), fp))
 | 
						|
    {
 | 
						|
      if (dev_name[0] == '#')
 | 
						|
	continue;		/* ignore line comments */
 | 
						|
      len = strlen (dev_name);
 | 
						|
 | 
						|
      if (!len)
 | 
						|
	continue;		/* ignore empty lines */
 | 
						|
 | 
						|
      sanei_config_attach_matching_devices (dev_name, attach_one);
 | 
						|
      /*attach_scanner (dev_name, 0);*/
 | 
						|
    }
 | 
						|
  fclose (fp);
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
sane_exit (void)
 | 
						|
{
 | 
						|
  Coolscan_t *dev, *next;
 | 
						|
 | 
						|
  DBG (10, "sane_exit\n");
 | 
						|
 | 
						|
  for (dev = first_dev; dev; dev = next)
 | 
						|
    {
 | 
						|
      next = dev->next;
 | 
						|
      free (dev->devicename);
 | 
						|
      free (dev->buffer);
 | 
						|
      free (dev->obuffer);
 | 
						|
      free (dev);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* ----------------------------- SANE GET DEVICES -------------------------- */
 | 
						|
SANE_Status
 | 
						|
sane_get_devices (const SANE_Device *** device_list,
 | 
						|
		  SANE_Bool local_only)
 | 
						|
{
 | 
						|
 | 
						|
  static const SANE_Device **devlist = 0;
 | 
						|
  Coolscan_t *dev;
 | 
						|
  int i;
 | 
						|
 | 
						|
  local_only = local_only;
 | 
						|
 | 
						|
  DBG (10, "sane_get_devices\n");
 | 
						|
 | 
						|
  if (devlist)
 | 
						|
    free (devlist);
 | 
						|
 | 
						|
  devlist = calloc (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;
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_open (SANE_String_Const devicename, SANE_Handle * handle)
 | 
						|
{
 | 
						|
  Coolscan_t *dev;
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  DBG (10, "sane_open\n");
 | 
						|
 | 
						|
  if (devicename[0])
 | 
						|
    {				/* search for devicename */
 | 
						|
      for (dev = first_dev; dev; dev = dev->next)
 | 
						|
	{
 | 
						|
	  if (strcmp (dev->sane.name, devicename) == 0)
 | 
						|
	    {
 | 
						|
	      break;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
      if (!dev)
 | 
						|
	{
 | 
						|
	  status = attach_scanner (devicename, &dev);
 | 
						|
	  if (status != SANE_STATUS_GOOD)
 | 
						|
	    {
 | 
						|
	      return status;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      dev = first_dev;		/* empty devicname -> use first device */
 | 
						|
    }
 | 
						|
 | 
						|
  if (!dev)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  dev->sfd = -1;
 | 
						|
  dev->pipe = -1;
 | 
						|
  dev->scanning = SANE_FALSE;
 | 
						|
 | 
						|
  init_options (dev);
 | 
						|
  *handle = dev;
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
sane_close (SANE_Handle handle)
 | 
						|
{
 | 
						|
  DBG (10, "sane_close\n");
 | 
						|
  if (((Coolscan_t *) handle)->scanning)
 | 
						|
    do_cancel (handle);
 | 
						|
}
 | 
						|
 | 
						|
const SANE_Option_Descriptor *
 | 
						|
sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
 | 
						|
{
 | 
						|
  Coolscan_t *scanner = handle;
 | 
						|
 | 
						|
  DBG (10, "sane_get_option_descriptor %d\n", option);
 | 
						|
 | 
						|
  if ((unsigned) option >= NUM_OPTIONS)
 | 
						|
    return 0;
 | 
						|
  return &scanner->opt[option];
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
   static void
 | 
						|
   worddump(char *comment, SANE_Word * p, int l)
 | 
						|
   {
 | 
						|
   int i;
 | 
						|
   char line[128];
 | 
						|
   char *ptr;
 | 
						|
 | 
						|
   DBG (5, "%s\n", comment);
 | 
						|
   ptr = line;
 | 
						|
   for (i = 0; i < l; i++, p++)
 | 
						|
   {
 | 
						|
   if ((i % 8) == 0)
 | 
						|
   {
 | 
						|
   if (ptr != line)
 | 
						|
   {
 | 
						|
   *ptr = '\0';
 | 
						|
   DBG (5, "%s\n", line);
 | 
						|
   ptr = line;
 | 
						|
   }
 | 
						|
   sprintf (ptr, "%3.3d:", i);
 | 
						|
   ptr += 4;
 | 
						|
   }
 | 
						|
   sprintf (ptr, " %4.4d", *p);
 | 
						|
   ptr += 5;
 | 
						|
   }
 | 
						|
   *ptr = '\0';
 | 
						|
   DBG (5, "%s\n", line);
 | 
						|
   }
 | 
						|
 */
 | 
						|
SANE_Status
 | 
						|
sane_control_option (SANE_Handle handle, SANE_Int option,
 | 
						|
		     SANE_Action action, void *val,
 | 
						|
		     SANE_Int * info)
 | 
						|
{
 | 
						|
  Coolscan_t *scanner = handle;
 | 
						|
  SANE_Status status;
 | 
						|
  SANE_Word cap;
 | 
						|
 | 
						|
  if (info)
 | 
						|
    *info = 0;
 | 
						|
 | 
						|
  if (scanner->scanning)
 | 
						|
    return SANE_STATUS_DEVICE_BUSY;
 | 
						|
 | 
						|
  if (option >= NUM_OPTIONS)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  cap = scanner->opt[option].cap;
 | 
						|
 | 
						|
 | 
						|
  if (action == SANE_ACTION_GET_VALUE)
 | 
						|
    {
 | 
						|
      DBG (10, "sane_control_option %d, get value\n", option);
 | 
						|
      switch (option)
 | 
						|
	{
 | 
						|
	  /* word options: */
 | 
						|
	case OPT_TL_X:
 | 
						|
	  *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->tlx));
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_TL_Y:
 | 
						|
	  *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->tly));
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_BR_X:
 | 
						|
	  *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->brx));
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_BR_Y:
 | 
						|
	  *(SANE_Word *) val = SANE_FIX (iluToMm (scanner->bry));
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_PREVIEW:
 | 
						|
	  *(SANE_Word *) val = scanner->preview;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_AUTOFOCUS:
 | 
						|
	  switch(scanner->autofocus)
 | 
						|
	    {  case AF_NEVER: strcpy (val,neverStr);
 | 
						|
  	                 break;
 | 
						|
	       case AF_PREVIEW:strcpy (val,previewStr);
 | 
						|
  	                 break;             
 | 
						|
               case AF_SCAN:if(scanner->LS>=2) strcpy (val,scanStr);
 | 
						|
  	                 break;             
 | 
						|
	       case AF_PREANDSCAN:if(scanner->LS>=2) strcpy (val,preandscanStr);
 | 
						|
  	                 break;             
 | 
						|
	    }
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_NUM_OPTS:
 | 
						|
	  *(SANE_Word *) val = NUM_OPTIONS;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_RESOLUTION:
 | 
						|
	  *(SANE_Word *) val = resDivToVal (scanner->x_nres);
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_PREVIEW_RESOLUTION:
 | 
						|
	  *(SANE_Word *) val = resDivToVal (scanner->x_p_nres);
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_BIT_DEPTH:
 | 
						|
	  *(SANE_Word *) val = scanner->bits_per_color;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_CONTRAST:
 | 
						|
	  *(SANE_Word *) val = scanner->contrast - 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_R_CONTRAST:
 | 
						|
	  *(SANE_Word *) val = scanner->contrast_R - 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_G_CONTRAST:
 | 
						|
	  *(SANE_Word *) val = scanner->contrast_G - 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_B_CONTRAST:
 | 
						|
	  *(SANE_Word *) val = scanner->contrast_B - 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_BRIGHTNESS:
 | 
						|
	  *(SANE_Word *) val = scanner->brightness - 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_R_BRIGHTNESS:
 | 
						|
	  *(SANE_Word *) val = scanner->brightness_R - 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_G_BRIGHTNESS:
 | 
						|
	  *(SANE_Word *) val = scanner->brightness_G - 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_B_BRIGHTNESS:
 | 
						|
	  *(SANE_Word *) val = scanner->brightness_B - 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_EXPOSURE:
 | 
						|
	  *(SANE_Word *) val = scanner->exposure_R * 2;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_R_EXPOSURE:
 | 
						|
	  *(SANE_Word *) val = scanner->exposure_R * 2;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_G_EXPOSURE:
 | 
						|
	  *(SANE_Word *) val = scanner->exposure_G * 2;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_B_EXPOSURE:
 | 
						|
	  *(SANE_Word *) val = scanner->exposure_B * 2;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_R_SHIFT:
 | 
						|
	  *(SANE_Word *) val = scanner->shift_R - 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_G_SHIFT:
 | 
						|
	  *(SANE_Word *) val = scanner->shift_G - 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_B_SHIFT:
 | 
						|
	  *(SANE_Word *) val = scanner->shift_B - 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
 | 
						|
	case OPT_IRED_RED:
 | 
						|
	  *(SANE_Word *) val = scanner->ired_red;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	  /* string options: */
 | 
						|
	case OPT_TYPE:
 | 
						|
	  strcpy (val, ((scanner->negative) ? negativeStr : positiveStr));
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_MODE:
 | 
						|
	  switch(scanner->colormode)
 | 
						|
	    {  case RGB: strcpy (val,colorStr);
 | 
						|
  	                 break;
 | 
						|
	       case GREYSCALE:strcpy (val,grayStr);
 | 
						|
  	                 break;             
 | 
						|
               case RGBI:if(scanner->LS>=2) strcpy (val,rgbiStr);
 | 
						|
		         else strcpy (val,colorStr);
 | 
						|
  	                 break;             
 | 
						|
	       case IRED:if(scanner->LS>=2) strcpy (val,iredStr); 
 | 
						|
	                 else strcpy (val,grayStr);
 | 
						|
  	                 break;             
 | 
						|
	    }
 | 
						|
 	    if (info)
 | 
						|
	    {
 | 
						|
	      *info |= SANE_INFO_RELOAD_PARAMS;
 | 
						|
	    }
 | 
						|
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_PRESCAN:
 | 
						|
	  *(SANE_Word *) val = (scanner->prescan) ? SANE_TRUE : SANE_FALSE;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_PRESCAN_NOW:	 
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_RGB_CONTROL:
 | 
						|
	  *(SANE_Word *) val = (scanner->rgb_control) ? SANE_TRUE : SANE_FALSE;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_GAMMA_BIND:
 | 
						|
	  *(SANE_Word *) val = (scanner->gamma_bind) ? SANE_TRUE : SANE_FALSE;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_ANALOG_GAMMA:
 | 
						|
	  *(SANE_Word *) val = 
 | 
						|
	    (scanner->analog_gamma_r) ? SANE_TRUE : SANE_FALSE;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_AVERAGING:
 | 
						|
	  *(SANE_Word *) val = (scanner->averaging) ? SANE_TRUE : SANE_FALSE;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_GAMMA_VECTOR:
 | 
						|
	  memcpy (val, scanner->gamma, scanner->opt[option].size);
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	case OPT_GAMMA_VECTOR_R:
 | 
						|
	  memcpy (val, scanner->gamma_r, scanner->opt[option].size);
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	case OPT_GAMMA_VECTOR_G:
 | 
						|
	  memcpy (val, scanner->gamma_g, scanner->opt[option].size);
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	case OPT_GAMMA_VECTOR_B:
 | 
						|
	  memcpy (val, scanner->gamma_b, scanner->opt[option].size);
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_SOURCE:
 | 
						|
	  if (strcmp (val, "Automatic Slide Feeder") == 0)
 | 
						|
	    {
 | 
						|
	      /* Feed/Discharge/update filename/etc */
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      /* Reset above */
 | 
						|
	    }
 | 
						|
	  if (info)
 | 
						|
	    {
 | 
						|
	      *info |= SANE_INFO_RELOAD_PARAMS;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
    }
 | 
						|
  else if (action == SANE_ACTION_SET_VALUE)
 | 
						|
    {
 | 
						|
      DBG (10, "sane_control_option %d, set value\n", option);
 | 
						|
 | 
						|
      if (!SANE_OPTION_IS_ACTIVE (cap))
 | 
						|
	return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
      if (!SANE_OPTION_IS_SETTABLE (cap))
 | 
						|
	return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
      status = sanei_constrain_value (scanner->opt + option, val, info);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	return status;
 | 
						|
 | 
						|
      switch (option)
 | 
						|
	{
 | 
						|
	case OPT_GAMMA_BIND:
 | 
						|
	  scanner->gamma_bind = (*(SANE_Word *) val == SANE_TRUE);
 | 
						|
	  if (scanner->LS != 1)
 | 
						|
	    {
 | 
						|
	      if (scanner->gamma_bind)
 | 
						|
		{
 | 
						|
		  scanner->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		  scanner->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
 | 
						|
		  scanner->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
 | 
						|
		  scanner->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
		}
 | 
						|
	      else
 | 
						|
		{
 | 
						|
		  scanner->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
 | 
						|
		  scanner->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		  scanner->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
		  scanner->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	  if (info)
 | 
						|
	    {
 | 
						|
	      *info |= SANE_INFO_RELOAD_OPTIONS;
 | 
						|
	    }
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_ANALOG_GAMMA:
 | 
						|
	  scanner->analog_gamma_r = scanner->analog_gamma_g =
 | 
						|
	    scanner->analog_gamma_b = (*(SANE_Word *) val == SANE_TRUE);
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_AVERAGING:
 | 
						|
	  scanner->averaging = (*(SANE_Word *) val == SANE_TRUE);
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_PRESCAN:
 | 
						|
	  scanner->prescan = (*(SANE_Word *) val == SANE_TRUE);
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_PRESCAN_NOW:
 | 
						|
	   do_prescan_now(scanner);  
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_BIT_DEPTH:
 | 
						|
	   scanner->bits_per_color=(*(SANE_Word *)val);
 | 
						|
 	  if (info)
 | 
						|
	    *info |= SANE_INFO_RELOAD_PARAMS;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
 | 
						|
	case OPT_RGB_CONTROL:
 | 
						|
	  scanner->rgb_control = (*(SANE_Word *) val == SANE_TRUE);
 | 
						|
	  if (scanner->rgb_control)
 | 
						|
	    {
 | 
						|
	      scanner->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
 | 
						|
	      scanner->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
 | 
						|
	      scanner->opt[OPT_EXPOSURE].cap |= SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
	      scanner->opt[OPT_R_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
	      scanner->opt[OPT_G_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
	      scanner->opt[OPT_B_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
	      scanner->opt[OPT_R_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
	      scanner->opt[OPT_G_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
	      scanner->opt[OPT_B_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
	      scanner->opt[OPT_R_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
	      scanner->opt[OPT_G_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
	      scanner->opt[OPT_B_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
	      scanner->contrast_R = 128;
 | 
						|
	      scanner->contrast_G = 128;
 | 
						|
	      scanner->contrast_B = 128;
 | 
						|
	      scanner->brightness_R = 128;
 | 
						|
	      scanner->brightness_G = 128;
 | 
						|
	      scanner->brightness_B = 128;
 | 
						|
	      scanner->exposure_R = 50;
 | 
						|
	      scanner->exposure_G = 50;
 | 
						|
	      scanner->exposure_B = 50;
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      scanner->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
	      scanner->opt[OPT_CONTRAST].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
	      scanner->opt[OPT_EXPOSURE].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
	      scanner->contrast = 128;
 | 
						|
	      scanner->brightness = 128;
 | 
						|
	      scanner->exposure_R = 50;
 | 
						|
	      scanner->exposure_G = 50;
 | 
						|
	      scanner->exposure_B = 50;
 | 
						|
 | 
						|
	      scanner->opt[OPT_R_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
 | 
						|
	      scanner->opt[OPT_G_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
 | 
						|
	      scanner->opt[OPT_B_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
	      scanner->opt[OPT_R_CONTRAST].cap |= SANE_CAP_INACTIVE;
 | 
						|
	      scanner->opt[OPT_G_CONTRAST].cap |= SANE_CAP_INACTIVE;
 | 
						|
	      scanner->opt[OPT_B_CONTRAST].cap |= SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
	      scanner->opt[OPT_R_EXPOSURE].cap |= SANE_CAP_INACTIVE;
 | 
						|
	      scanner->opt[OPT_G_EXPOSURE].cap |= SANE_CAP_INACTIVE;
 | 
						|
	      scanner->opt[OPT_B_EXPOSURE].cap |= SANE_CAP_INACTIVE;
 | 
						|
	    }
 | 
						|
	  if (info)
 | 
						|
	    {
 | 
						|
	      *info |= SANE_INFO_RELOAD_OPTIONS;
 | 
						|
	    }
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_RESOLUTION:
 | 
						|
	  scanner->y_nres = scanner->x_nres =
 | 
						|
	    resValToDiv (*(SANE_Word *) val);
 | 
						|
 | 
						|
	  if (info)
 | 
						|
	    *info |= SANE_INFO_RELOAD_PARAMS;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_PREVIEW_RESOLUTION:
 | 
						|
	  scanner->y_p_nres = scanner->x_p_nres =
 | 
						|
	    resValToDiv (*(SANE_Word *) val);
 | 
						|
 | 
						|
	  if (info)
 | 
						|
	    *info |= SANE_INFO_RELOAD_PARAMS;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_TL_X:
 | 
						|
	  scanner->tlx = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
 | 
						|
	  if (info)
 | 
						|
	    *info |= SANE_INFO_RELOAD_PARAMS;
 | 
						|
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_TL_Y:
 | 
						|
	  scanner->tly = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
 | 
						|
	  if (info)
 | 
						|
	    *info |= SANE_INFO_RELOAD_PARAMS;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_BR_X:
 | 
						|
	  scanner->brx = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
 | 
						|
	  if (info)
 | 
						|
	    *info |= SANE_INFO_RELOAD_PARAMS;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_BR_Y:
 | 
						|
	  scanner->bry = mmToIlu (SANE_UNFIX (*(SANE_Word *) val));
 | 
						|
	  if (info)
 | 
						|
	    *info |= SANE_INFO_RELOAD_PARAMS;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_NUM_OPTS:
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_PREVIEW:
 | 
						|
	  scanner->preview = *(SANE_Word *) val;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_AUTOFOCUS:
 | 
						|
	  if(strcmp(val,neverStr)==0) 
 | 
						|
	    {    scanner->autofocus=AF_NEVER;
 | 
						|
	    }
 | 
						|
	  if(strcmp(val,previewStr)==0)  
 | 
						|
	    {    scanner->autofocus=AF_PREVIEW;
 | 
						|
	    }
 | 
						|
	  if(strcmp(val,scanStr)==0)  
 | 
						|
	    {    scanner->autofocus=AF_SCAN;
 | 
						|
	    }
 | 
						|
	  if(strcmp(val,preandscanStr)==0)  
 | 
						|
	    {    scanner->autofocus=AF_PREANDSCAN;;
 | 
						|
	    }
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_CONTRAST:
 | 
						|
	  scanner->contrast = *(SANE_Word *) val + 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	case OPT_R_CONTRAST:
 | 
						|
	  scanner->contrast_R = *(SANE_Word *) val + 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	case OPT_G_CONTRAST:
 | 
						|
	  scanner->contrast_G = *(SANE_Word *) val + 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	case OPT_B_CONTRAST:
 | 
						|
	  scanner->contrast_B = *(SANE_Word *) val + 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_BRIGHTNESS:
 | 
						|
	  scanner->brightness = *(SANE_Word *) val + 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	case OPT_R_BRIGHTNESS:
 | 
						|
	  scanner->brightness_R = *(SANE_Word *) val + 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	case OPT_G_BRIGHTNESS:
 | 
						|
	  scanner->brightness_G = *(SANE_Word *) val + 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	case OPT_B_BRIGHTNESS:
 | 
						|
	  scanner->brightness_B = *(SANE_Word *) val + 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_EXPOSURE:
 | 
						|
	  scanner->exposure_R = *(SANE_Word *) val / 2;
 | 
						|
	  scanner->exposure_G = *(SANE_Word *) val / 2;
 | 
						|
	  scanner->exposure_B = *(SANE_Word *) val / 2;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	case OPT_R_EXPOSURE:
 | 
						|
	  scanner->exposure_R = *(SANE_Word *) val / 2;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	case OPT_G_EXPOSURE:
 | 
						|
	  scanner->exposure_G = *(SANE_Word *) val / 2;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	case OPT_B_EXPOSURE:
 | 
						|
	  scanner->exposure_B = *(SANE_Word *) val / 2;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_R_SHIFT:
 | 
						|
	  scanner->shift_R = *(SANE_Word *) val + 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	case OPT_G_SHIFT:
 | 
						|
	  scanner->shift_G = *(SANE_Word *) val + 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	case OPT_B_SHIFT:
 | 
						|
	  scanner->shift_B = *(SANE_Word *) val + 128;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_IRED_RED:
 | 
						|
	  scanner->ired_red= *(SANE_Word *) val; 
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_SOURCE:
 | 
						|
	  scanner->asf = (strcmp (val, "Automatic...") == 0);
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_TYPE:
 | 
						|
	  scanner->negative = (strcmp (val, negativeStr) == 0);
 | 
						|
	  if (info)
 | 
						|
	    {
 | 
						|
	      *info |= SANE_INFO_RELOAD_PARAMS;
 | 
						|
	    }
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	case OPT_MODE:
 | 
						|
	  if(strcmp(val,colorStr)==0) 
 | 
						|
	    {    scanner->colormode=RGB;
 | 
						|
	         scanner->colormode_p=RGB;
 | 
						|
	    }
 | 
						|
	  if(strcmp(val,grayStr)==0)  
 | 
						|
	    {    scanner->colormode=GREYSCALE;
 | 
						|
	         scanner->colormode_p=GREYSCALE;
 | 
						|
	    }
 | 
						|
	  if(strcmp(val,rgbiStr)==0)
 | 
						|
	    {    scanner->colormode=RGBI;
 | 
						|
	         scanner->colormode_p=RGB;
 | 
						|
	    }
 | 
						|
	  if(strcmp(val,iredStr)==0) 
 | 
						|
	    {    scanner->colormode=IRED;
 | 
						|
	         scanner->colormode_p=GREYSCALE;
 | 
						|
	    }
 | 
						|
	  if (info)
 | 
						|
	    {
 | 
						|
	      *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
 | 
						|
	    }
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_GAMMA_VECTOR:
 | 
						|
	  memcpy (scanner->gamma, val, scanner->opt[option].size);
 | 
						|
	  if(scanner->LS>2)  Calc_fix_LUT(scanner);
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_GAMMA_VECTOR_R:
 | 
						|
	  memcpy (scanner->gamma_r, val, scanner->opt[option].size);
 | 
						|
	  if(scanner->LS>2)  Calc_fix_LUT(scanner);
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_GAMMA_VECTOR_G:
 | 
						|
	  memcpy (scanner->gamma_g, val, scanner->opt[option].size);
 | 
						|
	  if(scanner->LS>2)  Calc_fix_LUT(scanner);
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	case OPT_GAMMA_VECTOR_B:
 | 
						|
	  memcpy (scanner->gamma_b, val, scanner->opt[option].size);
 | 
						|
	  if(scanner->LS>2)  Calc_fix_LUT(scanner);
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	}			/* switch */
 | 
						|
    }				/* else */
 | 
						|
  return SANE_STATUS_INVAL;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
 | 
						|
{
 | 
						|
  Coolscan_t *scanner = handle;
 | 
						|
 | 
						|
  DBG (10, "sane_get_parameters");
 | 
						|
  switch(scanner->colormode)
 | 
						|
    {  case RGB:
 | 
						|
              params->format =  SANE_FRAME_RGB;
 | 
						|
              break;
 | 
						|
#ifdef HAS_IRED
 | 
						|
       case RGBI:
 | 
						|
              params->format =  SANE_FRAME_RGBA; 
 | 
						|
              break; 
 | 
						|
#endif /* HAS_RGBI */
 | 
						|
       case GREYSCALE:
 | 
						|
              params->format =  SANE_FRAME_GRAY;
 | 
						|
              break;
 | 
						|
    }
 | 
						|
 | 
						|
  params->depth = scanner->bits_per_color>8?16:8;
 | 
						|
  params->pixels_per_line = pixels_per_line (scanner);
 | 
						|
  params->lines = lines_per_scan (scanner);
 | 
						|
  params->bytes_per_line = write_bytes_per_line (scanner);
 | 
						|
  params->last_frame = 1;
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
static int
 | 
						|
swap_res (Coolscan_t * s)
 | 
						|
{
 | 
						|
  if (s->preview)
 | 
						|
    {
 | 
						|
      /* swap preview/scan resolutions */
 | 
						|
      int xres, yres, cmode;
 | 
						|
      xres = s->x_nres;
 | 
						|
      yres = s->y_nres;
 | 
						|
      s->x_nres = s->x_p_nres;
 | 
						|
      s->y_nres = s->y_p_nres;
 | 
						|
 | 
						|
      s->x_p_nres = xres;
 | 
						|
      s->y_p_nres = yres;
 | 
						|
      cmode=s->colormode;
 | 
						|
      s->colormode=s->colormode_p;
 | 
						|
      s->colormode_p=cmode;
 | 
						|
    }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
SANE_Status
 | 
						|
sane_start (SANE_Handle handle)
 | 
						|
{
 | 
						|
  Coolscan_t *scanner = handle;
 | 
						|
  int fds[2];
 | 
						|
 | 
						|
  DBG (10, "sane_start\n");
 | 
						|
  if (scanner->scanning == SANE_TRUE)
 | 
						|
    return SANE_STATUS_DEVICE_BUSY;
 | 
						|
 | 
						|
  if (scanner->sfd < 0)
 | 
						|
    {				/* first call */
 | 
						|
      if (sanei_scsi_open (scanner->sane.name,
 | 
						|
			   &(scanner->sfd),
 | 
						|
			   sense_handler, 0) != SANE_STATUS_GOOD)
 | 
						|
	{
 | 
						|
	  DBG (1, "sane_start: open of %s failed:\n",
 | 
						|
	       scanner->sane.name);
 | 
						|
	  return SANE_STATUS_INVAL;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  scanner->scanning = SANE_TRUE;
 | 
						|
 | 
						|
 | 
						|
  if (coolscan_check_values (scanner) != 0)
 | 
						|
    {				/* Verify values */
 | 
						|
      DBG (1, "ERROR: invalid scan-values\n");
 | 
						|
      scanner->scanning = SANE_FALSE;
 | 
						|
      coolscan_give_scanner (scanner);
 | 
						|
      sanei_scsi_close (scanner->sfd);
 | 
						|
      scanner->sfd = -1;
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  if (coolscan_grab_scanner (scanner))
 | 
						|
    {
 | 
						|
      sanei_scsi_close (scanner->sfd);
 | 
						|
      scanner->sfd = -1;
 | 
						|
      DBG (5, "WARNING: unable to reserve scanner: device busy\n");
 | 
						|
      scanner->scanning = SANE_FALSE;
 | 
						|
      return SANE_STATUS_DEVICE_BUSY;
 | 
						|
    }
 | 
						|
 | 
						|
  /* hoho, step 2c, -perm */
 | 
						|
  coolscan_object_feed (scanner);
 | 
						|
 | 
						|
  swap_res (scanner);
 | 
						|
  if (!scanner->preview)
 | 
						|
  { if(scanner->autofocus & 0x02)
 | 
						|
    {  coolscan_autofocus (scanner);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
    { 
 | 
						|
      if(scanner->autofocus & 0x01)
 | 
						|
      {  coolscan_autofocus (scanner);
 | 
						|
      }
 | 
						|
      if (scanner->prescan) {
 | 
						|
	prescan (scanner);  
 | 
						|
	if(scanner->LS<2)
 | 
						|
        {	get_internal_info(scanner);
 | 
						|
	}
 | 
						|
        coolscan_get_window_param (scanner,1);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  /*read_LUT(scanner); */
 | 
						|
  if(scanner->LS<2)
 | 
						|
  {  send_LUT (scanner);
 | 
						|
     coolscan_set_window_param (scanner, 0);
 | 
						|
     coolscan_get_window_param (scanner,0);
 | 
						|
     coolscan_start_scan (scanner);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {  coolscan_set_window_param (scanner, 0);
 | 
						|
     send_LUT (scanner);
 | 
						|
     Calc_fix_LUT(scanner);
 | 
						|
     coolscan_start_scan (scanner);
 | 
						|
     wait_scanner (scanner);
 | 
						|
     coolscan_get_window_param (scanner,0);
 | 
						|
  }
 | 
						|
 | 
						|
  DBG (10, "bytes per line        = %d\n", scan_bytes_per_line (scanner));
 | 
						|
  DBG (10, "pixels_per_line       = %d\n", pixels_per_line (scanner));
 | 
						|
  DBG (10, "lines                 = %d\n", lines_per_scan (scanner));
 | 
						|
  DBG (10, "negative              = %d\n", scanner->negative);
 | 
						|
  DBG (10, "brightness (halftone) = %d\n", scanner->brightness);
 | 
						|
  DBG (10, "contrast   (halftone) = %d\n", scanner->contrast);
 | 
						|
  DBG (10, "fast preview function = %d\n", scanner->preview);
 | 
						|
 | 
						|
  /* create a pipe, fds[0]=read-fd, fds[1]=write-fd */
 | 
						|
  if (pipe (fds) < 0)
 | 
						|
    {
 | 
						|
      DBG (1, "ERROR: could not create pipe\n");
 | 
						|
 | 
						|
      swap_res (scanner);
 | 
						|
      scanner->scanning = SANE_FALSE;
 | 
						|
      coolscan_give_scanner (scanner);
 | 
						|
      sanei_scsi_close (scanner->sfd);
 | 
						|
      scanner->sfd = -1;
 | 
						|
      return SANE_STATUS_IO_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
  scanner->pipe       = fds[0];
 | 
						|
  scanner->reader_fds = fds[1];
 | 
						|
  scanner->reader_pid = sanei_thread_begin( reader_process, (void*)scanner );
 | 
						|
  if (scanner->reader_pid < 0)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_start: sanei_thread_begin failed (%s)\n",
 | 
						|
             strerror (errno));
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
 | 
						|
  if (sanei_thread_is_forked ())
 | 
						|
    {
 | 
						|
      close (scanner->reader_fds);
 | 
						|
      scanner->reader_fds = -1;
 | 
						|
    }
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_read (SANE_Handle handle, SANE_Byte * buf,
 | 
						|
	   SANE_Int max_len, SANE_Int * len)
 | 
						|
{
 | 
						|
  Coolscan_t *scanner = handle;
 | 
						|
  ssize_t nread;
 | 
						|
 | 
						|
  *len = 0;
 | 
						|
 | 
						|
  nread = read (scanner->pipe, buf, max_len);
 | 
						|
  DBG (10, "sane_read: read %ld bytes\n", (long) nread);
 | 
						|
 | 
						|
  if (!(scanner->scanning))
 | 
						|
    {
 | 
						|
      return do_cancel (scanner);
 | 
						|
    }
 | 
						|
 | 
						|
  if (nread < 0)
 | 
						|
    {
 | 
						|
      if (errno == EAGAIN)
 | 
						|
	{
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  do_cancel (scanner);
 | 
						|
	  return SANE_STATUS_IO_ERROR;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  *len = nread;
 | 
						|
 | 
						|
  if (nread == 0)
 | 
						|
    return do_eof (scanner);	/* close pipe */
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
sane_cancel (SANE_Handle handle)
 | 
						|
{
 | 
						|
  Coolscan_t *s = handle;
 | 
						|
 | 
						|
  if (s->reader_pid > 0)
 | 
						|
    {
 | 
						|
      sanei_thread_kill   ( s->reader_pid );
 | 
						|
      sanei_thread_waitpid( s->reader_pid, NULL );
 | 
						|
      s->reader_pid = 0;
 | 
						|
    }
 | 
						|
  swap_res (s);
 | 
						|
  s->scanning = SANE_FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
 | 
						|
{
 | 
						|
  Coolscan_t *scanner = handle;
 | 
						|
 | 
						|
  DBG (10, "sane_set_io_mode: non_blocking=%d\n", non_blocking);
 | 
						|
 | 
						|
  if (!scanner->scanning)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  if (fcntl (scanner->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
 | 
						|
    return SANE_STATUS_IO_ERROR;
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
 | 
						|
{
 | 
						|
  Coolscan_t *scanner = handle;
 | 
						|
 | 
						|
  DBG (10, "sane_get_select_fd\n");
 | 
						|
 | 
						|
  if (!scanner->scanning)
 | 
						|
    {
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
  *fd = scanner->pipe;
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 |