kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			2974 wiersze
		
	
	
		
			74 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			2974 wiersze
		
	
	
		
			74 KiB
		
	
	
	
		
			C
		
	
	
/*
 | 
						|
  Copyright (C) 2000 by Adrian Perez Jorge
 | 
						|
 | 
						|
  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, see <https://www.gnu.org/licenses/>.
 | 
						|
 */
 | 
						|
 | 
						|
/* Developers:
 | 
						|
 | 
						|
       Adrian Perez Jorge (APJ) -
 | 
						|
            Creator of the original HP4200C backend code.
 | 
						|
	    adrianpj@easynews.com
 | 
						|
 | 
						|
       Andrew John Lewis  (AJL) -
 | 
						|
	    lewi0235@tc.umn.edu
 | 
						|
 | 
						|
       Arnar Mar Hrafnkelsson (AMH) -
 | 
						|
            addi@umich.edu
 | 
						|
 | 
						|
       Frank Zago
 | 
						|
            some cleanups and integration into SANE
 | 
						|
 | 
						|
       Henning Meier-Geinitz <henning@meier-geinitz.de>
 | 
						|
            more cleanups, bug fixes
 | 
						|
 | 
						|
TODO:
 | 
						|
 | 
						|
   - support more scanning resolutions.
 | 
						|
   - support different color depths.
 | 
						|
   - support gray and lineart.
 | 
						|
   - improve scanning speed. Compute scanning parameters based on the
 | 
						|
     image size and the scanner-to-host bandwidth.
 | 
						|
   - improve image quality.
 | 
						|
   - fix problem concerning mangled images
 | 
						|
 | 
						|
*/
 | 
						|
 | 
						|
#define BUILD 2
 | 
						|
#define BACKEND_NAME hp4200
 | 
						|
 | 
						|
#include "../include/sane/config.h"
 | 
						|
 | 
						|
#include <sys/ioctl.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <string.h>
 | 
						|
#include <math.h>
 | 
						|
#include <sys/time.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <getopt.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include <assert.h>
 | 
						|
 | 
						|
#include "../include/sane/sane.h"
 | 
						|
#include "../include/sane/sanei.h"
 | 
						|
#include "../include/sane/sanei_debug.h"
 | 
						|
#include "../include/sane/sanei_config.h"
 | 
						|
#include "../include/sane/sanei_usb.h"
 | 
						|
#include "../include/sane/sanei_pv8630.h"
 | 
						|
#include "../include/sane/saneopts.h"
 | 
						|
#include "../include/sane/sanei_backend.h"
 | 
						|
#include "hp4200.h"
 | 
						|
 | 
						|
#include "hp4200_lm9830.c"
 | 
						|
 | 
						|
#define HP4200_CONFIG_FILE "hp4200.conf"
 | 
						|
 | 
						|
/*--------------------------------------------------------------------------*/
 | 
						|
 | 
						|
#if 0
 | 
						|
/* Some of these resolution need work in color shifting. */
 | 
						|
static const SANE_Int dpi_list[] =
 | 
						|
  { 8, 50, 75, 100, 150, 200, 300, 400, 600 };
 | 
						|
#else
 | 
						|
static const SANE_Int dpi_list[] = { 4, 75, 150, 300, 600 };
 | 
						|
#endif
 | 
						|
static SANE_Range x_range = { SANE_FIX (0), SANE_FIX (8.5 * MM_PER_INCH), 0 };
 | 
						|
static SANE_Range y_range =
 | 
						|
  { SANE_FIX (0), SANE_FIX (11.75 * MM_PER_INCH), 0 };
 | 
						|
static const SANE_Range u8_range = { 0, 255, 0 };
 | 
						|
 | 
						|
struct coarse_t
 | 
						|
{
 | 
						|
  int min_red;
 | 
						|
  int min_green;
 | 
						|
  int min_blue;
 | 
						|
  int max_red;
 | 
						|
  int max_green;
 | 
						|
  int max_blue;
 | 
						|
  int red_gain;
 | 
						|
  int red_offset;
 | 
						|
  int green_gain;
 | 
						|
  int green_offset;
 | 
						|
  int blue_gain;
 | 
						|
  int blue_offset;
 | 
						|
};
 | 
						|
 | 
						|
static const double hdpi_mapping[8] = { 1, 1.5, 2, 3, 4, 6, 8, 12 };
 | 
						|
 | 
						|
static HP4200_Device *first_device = NULL;	/* device list head */
 | 
						|
static int n_devices = 0;	/* the device count */
 | 
						|
static const SANE_Device **devlist = NULL;
 | 
						|
 | 
						|
static unsigned char
 | 
						|
getreg (HP4200_Scanner * s, unsigned char reg)
 | 
						|
{
 | 
						|
  unsigned char reg_value;
 | 
						|
 | 
						|
  if ((reg > 0x08) && (reg < 0x5b))
 | 
						|
    return (unsigned char) LOBYTE (s->regs[reg]);
 | 
						|
  else
 | 
						|
    {
 | 
						|
      lm9830_read_register (s->fd, reg, ®_value);
 | 
						|
      return reg_value;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
setreg (HP4200_Scanner * s, unsigned char reg, unsigned char reg_value)
 | 
						|
{
 | 
						|
  s->regs[reg] = reg_value;	/* dirty bit should be clear with this */
 | 
						|
  if ((reg < 0x08) || (reg > 0x5b))
 | 
						|
    {
 | 
						|
      lm9830_write_register (s->fd, reg, reg_value);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
setbits (HP4200_Scanner * s, unsigned char reg, unsigned char bitmap)
 | 
						|
{
 | 
						|
  s->regs[reg] = (s->regs[reg] & 0xff) | bitmap;
 | 
						|
  if ((reg < 0x08) || (reg > 0x5b))
 | 
						|
    {
 | 
						|
      lm9830_write_register (s->fd, reg, LOBYTE (s->regs[reg]));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
clearbits (HP4200_Scanner * s, unsigned char reg, unsigned char mask)
 | 
						|
{
 | 
						|
  s->regs[reg] = (s->regs[reg] & ~mask) & 0xff;
 | 
						|
  if ((reg < 0x08) || (reg > 0x5b))
 | 
						|
    {
 | 
						|
      lm9830_write_register (s->fd, reg, LOBYTE (s->regs[reg]));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
cache_write (HP4200_Scanner * s)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
#ifdef DEBUG_REG_CACHE
 | 
						|
  int counter = 0;
 | 
						|
#endif
 | 
						|
 | 
						|
  DBG (DBG_proc, "Writing registers\n");
 | 
						|
 | 
						|
  for (i = 0; i < 0x80; i++)
 | 
						|
    if (!(s->regs[i] & 0x100))
 | 
						|
      {				/* modified register */
 | 
						|
#ifdef DEBUG_REG_CACHE
 | 
						|
	fprintf (stderr, "%.2x", i);
 | 
						|
	if (counter == 8)
 | 
						|
	  fprintf (stderr, "\n");
 | 
						|
	else
 | 
						|
	  fprintf (stderr, ", ");
 | 
						|
	counter = (counter + 1) % 9;
 | 
						|
#endif
 | 
						|
	lm9830_write_register (s->fd, i, s->regs[i]);
 | 
						|
	s->regs[i] |= 0x100;	/* register is updated */
 | 
						|
      }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * HP4200-dependent register initialization.
 | 
						|
 */
 | 
						|
 | 
						|
static int
 | 
						|
hp4200_init_registers (HP4200_Scanner * s)
 | 
						|
{
 | 
						|
  /* set up hardware parameters */
 | 
						|
 | 
						|
  s->hw_parms.crystal_frequency = 48000000;
 | 
						|
  s->hw_parms.SRAM_size = 128;	/* Kb */
 | 
						|
  s->hw_parms.scan_area_width = 5100;	/* pixels */
 | 
						|
  s->hw_parms.scan_area_length = 11;	/* inches */
 | 
						|
  s->hw_parms.min_pixel_data_buffer_limit = 1024;	/* bytes */
 | 
						|
  s->hw_parms.sensor_line_separation = 4;	/* lines */
 | 
						|
  s->hw_parms.sensor_max_integration_time = 12;	/* milliseconds */
 | 
						|
  s->hw_parms.home_sensor = 2;
 | 
						|
  s->hw_parms.sensor_resolution = 1;	/* 600 dpi */
 | 
						|
  s->hw_parms.motor_full_steps_per_inch = 300;
 | 
						|
  s->hw_parms.motor_max_speed = 1.4;	/* inches/second */
 | 
						|
  s->hw_parms.num_tr_pulses = 1;
 | 
						|
  s->hw_parms.guard_band_duration = 1;
 | 
						|
  s->hw_parms.pulse_duration = 3;
 | 
						|
  s->hw_parms.fsteps_25_speed = 3;
 | 
						|
  s->hw_parms.fsteps_50_speed = 3;
 | 
						|
  s->hw_parms.target_value.red = 1000;
 | 
						|
  s->hw_parms.target_value.green = 1000;
 | 
						|
  s->hw_parms.target_value.blue = 1000;
 | 
						|
 | 
						|
  {
 | 
						|
    int i;
 | 
						|
 | 
						|
    /*
 | 
						|
     * we are using a cache-like data structure so registers whose
 | 
						|
     * values were written to the lm9830 and aren't volatile, have
 | 
						|
     * bit 0x100 activated.  This bit must be cleared if you want the
 | 
						|
     * value to be written to the chip once cache_write() is called.
 | 
						|
     */
 | 
						|
 | 
						|
    /* clears the registers cache */
 | 
						|
    memset (s->regs, 0, sizeof (s->regs));
 | 
						|
 | 
						|
    /*
 | 
						|
     * registers 0x00 - 0x07 are non-cacheable/volatile, so don't
 | 
						|
     * read the values using the cache.  Instead use direct functions
 | 
						|
     * to read/write registers.
 | 
						|
     */
 | 
						|
 | 
						|
    for (i = 0; i < 0x08; i++)
 | 
						|
      s->regs[i] = 0x100;
 | 
						|
  }
 | 
						|
 | 
						|
  setreg (s, 0x70, 0x70);	/* noise filter */
 | 
						|
 | 
						|
  setreg (s, 0x0b,
 | 
						|
	  INPUT_SIGNAL_POLARITY_NEGATIVE |
 | 
						|
	  CDS_ON |
 | 
						|
	  SENSOR_STANDARD |
 | 
						|
	  SENSOR_RESOLUTION_600 | LINE_SKIPPING_COLOR_PHASE_DELAY (0));
 | 
						|
 | 
						|
  setreg (s, 0x0c,
 | 
						|
	  PHI1_POLARITY_POSITIVE |
 | 
						|
	  PHI2_POLARITY_POSITIVE |
 | 
						|
	  RS_POLARITY_POSITIVE |
 | 
						|
	  CP1_POLARITY_POSITIVE |
 | 
						|
	  CP2_POLARITY_POSITIVE |
 | 
						|
	  TR1_POLARITY_NEGATIVE | TR2_POLARITY_NEGATIVE);
 | 
						|
 | 
						|
  setreg (s, 0x0d,
 | 
						|
	  PHI1_ACTIVE |
 | 
						|
	  PHI2_ACTIVE |
 | 
						|
	  RS_ACTIVE |
 | 
						|
	  CP1_ACTIVE |
 | 
						|
	  CP2_OFF |
 | 
						|
	  TR1_ACTIVE |
 | 
						|
	  TR2_OFF | NUMBER_OF_TR_PULSES (s->hw_parms.num_tr_pulses));
 | 
						|
 | 
						|
  setreg (s, 0x0e,
 | 
						|
	  TR_PULSE_DURATION (s->hw_parms.pulse_duration) |
 | 
						|
	  TR_PHI1_GUARDBAND_DURATION (s->hw_parms.guard_band_duration));
 | 
						|
 | 
						|
  /* for pixel rate timing */
 | 
						|
  setreg (s, 0x0f, 6);
 | 
						|
  setreg (s, 0x10, 23);
 | 
						|
  setreg (s, 0x11, 1);
 | 
						|
  setreg (s, 0x12, 3);
 | 
						|
  setreg (s, 0x13, 3);		/* 0 */
 | 
						|
  setreg (s, 0x14, 5);		/* 0 */
 | 
						|
  setreg (s, 0x15, 0);
 | 
						|
  setreg (s, 0x16, 0);
 | 
						|
  setreg (s, 0x17, 11);
 | 
						|
  setreg (s, 0x18, 2);		/* 1 */
 | 
						|
 | 
						|
  setreg (s, 0x19, CIS_TR1_TIMING_OFF | FAKE_OPTICAL_BLACK_PIXELS_OFF);
 | 
						|
 | 
						|
  setreg (s, 0x1a, 0);
 | 
						|
  setreg (s, 0x1b, 0);
 | 
						|
 | 
						|
  setreg (s, 0x1c, 0x0d);
 | 
						|
  setreg (s, 0x1d, 0x21);
 | 
						|
 | 
						|
  setreg (s, 0x27, TR_RED_DROP (0) | TR_GREEN_DROP (0) | TR_BLUE_DROP (0));
 | 
						|
 | 
						|
  setreg (s, 0x28, 0x00);
 | 
						|
 | 
						|
  setreg (s, 0x29, ILLUMINATION_MODE (1));
 | 
						|
  setreg (s, 0x2a, HIBYTE (0));	/* 0 */
 | 
						|
  setreg (s, 0x2b, LOBYTE (0));	/* 0 */
 | 
						|
 | 
						|
  setreg (s, 0x2c, HIBYTE (16383));
 | 
						|
  setreg (s, 0x2d, LOBYTE (16383));
 | 
						|
 | 
						|
  setreg (s, 0x2e, HIBYTE (2));	/* 2 */
 | 
						|
  setreg (s, 0x2f, LOBYTE (2));	/* 1 */
 | 
						|
 | 
						|
  setreg (s, 0x30, HIBYTE (0));
 | 
						|
  setreg (s, 0x31, LOBYTE (0));
 | 
						|
 | 
						|
  setreg (s, 0x32, HIBYTE (0));
 | 
						|
  setreg (s, 0x33, LOBYTE (0));
 | 
						|
 | 
						|
  setreg (s, 0x34, HIBYTE (32));
 | 
						|
  setreg (s, 0x35, LOBYTE (32));
 | 
						|
 | 
						|
  setreg (s, 0x36, HIBYTE (48));
 | 
						|
  setreg (s, 0x37, LOBYTE (48));
 | 
						|
 | 
						|
  setreg (s, 0x42, EPP_MODE | PPORT_DRIVE_CURRENT (3));
 | 
						|
 | 
						|
  setreg (s, 0x43,
 | 
						|
	  RAM_SIZE_128 |
 | 
						|
	  SRAM_DRIVER_CURRENT (3) | SRAM_BANDWIDTH_8 | SCANNING_FULL_DUPLEX);
 | 
						|
 | 
						|
  setreg (s, 0x45,
 | 
						|
	  MICRO_STEPPING |
 | 
						|
	  CURRENT_SENSING_PHASES (2) |
 | 
						|
	  PHASE_A_POLARITY_POSITIVE |
 | 
						|
	  PHASE_B_POLARITY_POSITIVE | STEPPER_MOTOR_OUTPUT);
 | 
						|
 | 
						|
  setreg (s, 0x4a, HIBYTE (100));
 | 
						|
  setreg (s, 0x4b, LOBYTE (100));
 | 
						|
 | 
						|
  setreg (s, 0x4c, HIBYTE (0));
 | 
						|
  setreg (s, 0x4d, LOBYTE (0));
 | 
						|
 | 
						|
  /* resume scan threshold */
 | 
						|
  setreg (s, 0x4f, 64);
 | 
						|
  /* steps to reverse */
 | 
						|
  setreg (s, 0x50, 40);
 | 
						|
  setreg (s, 0x51,
 | 
						|
	  ACCELERATION_PROFILE_STOPPED (3) |
 | 
						|
	  ACCELERATION_PROFILE_25P (s->hw_parms.fsteps_25_speed) |
 | 
						|
	  ACCELERATION_PROFILE_50P (s->hw_parms.fsteps_50_speed));
 | 
						|
  setreg (s, 0x54, NON_REVERSING_EXTRA_LINES (0) | FIRST_LINE_TO_PROCESS (0));
 | 
						|
  setreg (s, 0x55, KICKSTART_STEPS (0) | HOLD_CURRENT_TIMEOUT (2));
 | 
						|
 | 
						|
  /* stepper PWM frequency */
 | 
						|
  setreg (s, 0x56, 8);
 | 
						|
  /* stepper pwm duty cycle */
 | 
						|
  setreg (s, 0x57, 23);
 | 
						|
 | 
						|
  setreg (s, 0x58,
 | 
						|
	  PAPER_SENSOR_1_POLARITY_HIGH |
 | 
						|
	  PAPER_SENSOR_1_TRIGGER_EDGE |
 | 
						|
	  PAPER_SENSOR_1_NO_STOP_SCAN |
 | 
						|
	  PAPER_SENSOR_2_POLARITY_HIGH |
 | 
						|
	  PAPER_SENSOR_2_TRIGGER_EDGE | PAPER_SENSOR_2_STOP_SCAN);
 | 
						|
  setreg (s, 0x59,
 | 
						|
	  MISCIO_1_TYPE_OUTPUT |
 | 
						|
	  MISCIO_1_POLARITY_HIGH |
 | 
						|
	  MISCIO_1_TRIGGER_EDGE |
 | 
						|
	  MISCIO_1_OUTPUT_STATE_HIGH |
 | 
						|
	  MISCIO_2_TYPE_OUTPUT |
 | 
						|
	  MISCIO_2_POLARITY_HIGH |
 | 
						|
	  MISCIO_2_TRIGGER_EDGE | MISCIO_2_OUTPUT_STATE_HIGH);
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
static int
 | 
						|
dump_register_cache (HP4200_Scanner * s)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
 | 
						|
  for (i = 0; i < 0x80; i++)
 | 
						|
    {
 | 
						|
      fprintf (stderr, "%.2x:0x%.2x", i, s->regs[i]);
 | 
						|
      if ((i + 1) % 8)
 | 
						|
	fprintf (stderr, ", ");
 | 
						|
      else
 | 
						|
	fprintf (stderr, "\n");
 | 
						|
    }
 | 
						|
  fputs ("", stderr);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
/*
 | 
						|
 * returns the scanner head to home position
 | 
						|
 */
 | 
						|
 | 
						|
static int
 | 
						|
hp4200_goto_home (HP4200_Scanner * s)
 | 
						|
{
 | 
						|
  unsigned char cmd_reg;
 | 
						|
  unsigned char status_reg;
 | 
						|
  unsigned char old_paper_sensor_reg;
 | 
						|
 | 
						|
  cmd_reg = getreg (s, 0x07);
 | 
						|
  if (cmd_reg != 2)
 | 
						|
    {
 | 
						|
      unsigned char paper_sensor_reg;
 | 
						|
      unsigned char sensor_bit[2] = { 0x02, 0x10 };
 | 
						|
      /* sensor head is not returning */
 | 
						|
 | 
						|
      /* let's see if it's already at home */
 | 
						|
      /* first put paper (head) sensor level sensitive */
 | 
						|
      paper_sensor_reg = getreg (s, 0x58);
 | 
						|
      old_paper_sensor_reg = paper_sensor_reg;
 | 
						|
      paper_sensor_reg &= ~sensor_bit[s->hw_parms.home_sensor - 1];
 | 
						|
      setreg (s, 0x58, paper_sensor_reg);
 | 
						|
      cache_write (s);
 | 
						|
 | 
						|
      /* if the scan head is not at home then move motor backwards */
 | 
						|
      status_reg = getreg (s, 0x02);
 | 
						|
      setreg (s, 0x58, old_paper_sensor_reg);
 | 
						|
      cache_write (s);
 | 
						|
      if (!(status_reg & s->hw_parms.home_sensor))
 | 
						|
	{
 | 
						|
	  setreg (s, 0x07, 0x08);
 | 
						|
	  usleep (10 * 1000);
 | 
						|
	  setreg (s, 0x07, 0x00);
 | 
						|
	  usleep (10 * 1000);
 | 
						|
	  setreg (s, 0x07, 0x02);
 | 
						|
	}
 | 
						|
    }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
#define HP4200_CHECK_INTERVAL 1000	/* usecs between status checks */
 | 
						|
static int
 | 
						|
hp4200_wait_homed (HP4200_Scanner * s)
 | 
						|
{
 | 
						|
  unsigned char cmd_reg;
 | 
						|
 | 
						|
  cmd_reg = getreg (s, 0x07);
 | 
						|
  while (cmd_reg != 0)
 | 
						|
    {
 | 
						|
      usleep (HP4200_CHECK_INTERVAL);
 | 
						|
      cmd_reg = getreg (s, 0x07);
 | 
						|
    }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compute_fastfeed_step_size (unsigned long crystal_freq, int mclk,
 | 
						|
			    float max_speed, int steps_per_inch,
 | 
						|
			    int color_mode)
 | 
						|
{
 | 
						|
  int aux;
 | 
						|
  int r;
 | 
						|
 | 
						|
  if (color_mode == 0)
 | 
						|
    r = 24;
 | 
						|
  else
 | 
						|
    r = 8;
 | 
						|
 | 
						|
  aux = floor (crystal_freq / ((double) mclk * max_speed * 4.0 *
 | 
						|
			       steps_per_inch * r));
 | 
						|
 | 
						|
 | 
						|
  if (aux < 2)
 | 
						|
    aux = 2;
 | 
						|
  return aux;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
read_available_data (HP4200_Scanner * s, SANE_Byte * buffer,
 | 
						|
		     size_t * byte_count)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  unsigned char scankb1;
 | 
						|
  unsigned char scankb2;
 | 
						|
  size_t to_read;
 | 
						|
  size_t really_read;
 | 
						|
  size_t chunk;
 | 
						|
 | 
						|
  assert (buffer != NULL);
 | 
						|
 | 
						|
  *byte_count = 0;
 | 
						|
  do
 | 
						|
    {
 | 
						|
      scankb1 = getreg (s, 0x01);
 | 
						|
      scankb2 = getreg (s, 0x01);
 | 
						|
      if (s->aborted_by_user)
 | 
						|
	return SANE_STATUS_CANCELLED;
 | 
						|
    }
 | 
						|
  while ((scankb1 != scankb2) || (scankb1 < 12));
 | 
						|
 | 
						|
  to_read = scankb1 * 1024;
 | 
						|
 | 
						|
  while (to_read)
 | 
						|
    {
 | 
						|
      if (s->aborted_by_user)
 | 
						|
	return SANE_STATUS_CANCELLED;
 | 
						|
      chunk = (to_read > 0xffff) ? 0xffff : to_read;
 | 
						|
 | 
						|
      sanei_pv8630_write_byte (s->fd, PV8630_REPPADDRESS, 0x00);
 | 
						|
      sanei_pv8630_prep_bulkread (s->fd, chunk);
 | 
						|
      really_read = chunk;
 | 
						|
      if ((status = sanei_usb_read_bulk (s->fd, buffer, &really_read)) !=
 | 
						|
	  SANE_STATUS_GOOD)
 | 
						|
	{
 | 
						|
	  DBG (DBG_error, "sanei_usb_read_bulk failed (%s)\n",
 | 
						|
	       sane_strstatus (status));
 | 
						|
	  return status;
 | 
						|
	}
 | 
						|
      if (really_read > to_read)
 | 
						|
	{
 | 
						|
	  DBG (DBG_error, "USB stack read more bytes than requested!\n");
 | 
						|
	  return SANE_STATUS_IO_ERROR;
 | 
						|
	}
 | 
						|
      *byte_count += really_read;
 | 
						|
      buffer += really_read;
 | 
						|
      to_read -= really_read;
 | 
						|
#ifdef DEBUG
 | 
						|
      fprintf (stderr, "read %d bytes\n", really_read);
 | 
						|
#endif
 | 
						|
    }
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef unused
 | 
						|
static int
 | 
						|
compute_datalink_bandwidth (HP4200_Scanner * s)
 | 
						|
{
 | 
						|
  int line_size;
 | 
						|
  int pause_limit;
 | 
						|
  unsigned int color_mode;
 | 
						|
 | 
						|
  /*
 | 
						|
   * Line size for 8 bpp, the entire scan area width (plus the
 | 
						|
   * status byte) at optical resolution.
 | 
						|
   */
 | 
						|
 | 
						|
  if (s->user_parms.color)
 | 
						|
    {
 | 
						|
      line_size = 3 * s->hw_parms.scan_area_width + 1;
 | 
						|
      color_mode = 0;
 | 
						|
      setreg (s, 0x26, color_mode);	/* 3 channel pixel rate color */
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      line_size = s->hw_parms.scan_area_width + 1;
 | 
						|
      color_mode = 4;
 | 
						|
      setreg (s, 0x26, 0x08 | color_mode);	/* 1 channel mode A (green) */
 | 
						|
    }
 | 
						|
  setreg (s, 0x09, (3 << 3));	/* h-divider = 1, 8 bpp */
 | 
						|
 | 
						|
  {
 | 
						|
    int first_white_pixel;
 | 
						|
    unsigned int line_end;
 | 
						|
 | 
						|
    first_white_pixel = s->hw_parms.sensor_pixel_end - 10;
 | 
						|
    line_end = first_white_pixel + s->hw_parms.scan_area_width;
 | 
						|
    if (line_end > (s->hw_parms.sensor_num_pixels - 20))
 | 
						|
      line_end = s->hw_parms.sensor_num_pixels - 20;
 | 
						|
 | 
						|
    setreg (s, 0x1c, HIBYTE (s->hw_parms.sensor_pixel_start));
 | 
						|
    setreg (s, 0x1d, LOBYTE (s->hw_parms.sensor_pixel_end));
 | 
						|
    setreg (s, 0x1e, HIBYTE (first_white_pixel));
 | 
						|
    setreg (s, 0x1f, LOBYTE (first_white_pixel));
 | 
						|
    setreg (s, 0x20, HIBYTE (s->hw_parms.sensor_num_pixels));
 | 
						|
    setreg (s, 0x21, LOBYTE (s->hw_parms.sensor_num_pixels));
 | 
						|
    setreg (s, 0x22, getreg (s, 0x1e));
 | 
						|
    setreg (s, 0x23, getreg (s, 0x1f));
 | 
						|
    setreg (s, 0x24, HIBYTE (line_end));
 | 
						|
    setreg (s, 0x25, LOBYTE (line_end));
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
   * During transfer rate calculation don't forward scanner sensor.
 | 
						|
   * Stay in the calibration region.
 | 
						|
   */
 | 
						|
 | 
						|
  setreg (s, 0x4f, 0);
 | 
						|
  clearbits (s, 0x45, 0x10);
 | 
						|
 | 
						|
  /*
 | 
						|
   * Pause the scan when memory is full.
 | 
						|
   */
 | 
						|
 | 
						|
  pause_limit = s->hw_parms.SRAM_size - (line_size / 1024) - 1;
 | 
						|
  setreg (s, 0x4e, pause_limit & 0xff);
 | 
						|
 | 
						|
  s->mclk = compute_min_mclk (s->hw_parms.SRAM_bandwidth,
 | 
						|
			      s->hw_parms.crystal_frequency);
 | 
						|
 | 
						|
 | 
						|
  /*
 | 
						|
   * Set step size to fast speed.
 | 
						|
   */
 | 
						|
 | 
						|
  {
 | 
						|
    int step_size;
 | 
						|
 | 
						|
    step_size =
 | 
						|
      compute_fastfeed_step_size (s->hw_parms.crystal_frequency,
 | 
						|
				  s->mclk,
 | 
						|
				  s->hw_parms.scan_bar_max_speed,
 | 
						|
				  s->hw_parms.motor_full_steps_per_inch,
 | 
						|
				  color_mode);
 | 
						|
 | 
						|
    setreg (s, 0x46, HIBYTE (step_size));
 | 
						|
    setreg (s, 0x47, LOBYTE (step_size));
 | 
						|
    setreg (s, 0x48, HIBYTE (step_size));
 | 
						|
    setreg (s, 0x49, LOBYTE (step_size));
 | 
						|
  }
 | 
						|
 | 
						|
  cache_write (s);
 | 
						|
 | 
						|
  /*  dump_register_cache (s); */
 | 
						|
 | 
						|
  /*
 | 
						|
   * scan during 1 sec. aprox.
 | 
						|
   */
 | 
						|
 | 
						|
  setreg (s, 0x07, 0x08);
 | 
						|
  setreg (s, 0x07, 0x03);
 | 
						|
 | 
						|
  {
 | 
						|
    struct timeval tv_before;
 | 
						|
    struct timeval tv_after;
 | 
						|
    int elapsed_time_ms = 0;
 | 
						|
    long bytes_read_total;
 | 
						|
    SANE_Byte *buffer;
 | 
						|
 | 
						|
    buffer = malloc (2 * 98304);	/* check this */
 | 
						|
    if (!buffer)
 | 
						|
      {
 | 
						|
	DBG (DBG_error, "compute_datalink_bandwidth: malloc failed\n");
 | 
						|
	return 0;
 | 
						|
      }
 | 
						|
    bytes_read_total = 0;
 | 
						|
    gettimeofday (&tv_before, NULL);
 | 
						|
    do
 | 
						|
      {
 | 
						|
	size_t bytes_read;
 | 
						|
	SANE_Status status;
 | 
						|
 | 
						|
	status = read_available_data (s, buffer, &bytes_read);
 | 
						|
	if (status != SANE_STATUS_GOOD)
 | 
						|
	  {
 | 
						|
	    DBG (DBG_error, "read_available_data failed (%s)\n",
 | 
						|
		 sane_strstatus (status));
 | 
						|
	    return 0;
 | 
						|
	  }
 | 
						|
	bytes_read_total += bytes_read;
 | 
						|
	gettimeofday (&tv_after, NULL);
 | 
						|
	elapsed_time_ms = (tv_after.tv_sec - tv_before.tv_sec) * 1000;
 | 
						|
	elapsed_time_ms += (tv_after.tv_usec - tv_before.tv_usec) / 1000;
 | 
						|
      }
 | 
						|
    while (elapsed_time_ms < 1000);
 | 
						|
 | 
						|
    setreg (s, 0x07, 0x00);
 | 
						|
    free (buffer);
 | 
						|
 | 
						|
    s->msrd_parms.datalink_bandwidth = bytes_read_total /
 | 
						|
      (elapsed_time_ms / 1000);
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
    fprintf (stderr, "PC Transfer rate = %d bytes/sec. (%ld/%d)\n",
 | 
						|
	     s->msrd_parms.datalink_bandwidth, bytes_read_total,
 | 
						|
	     elapsed_time_ms);
 | 
						|
#endif
 | 
						|
  }
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
static void
 | 
						|
compute_first_gain_offset (int target, int max, int min, int *gain,
 | 
						|
			   int *offset, int *max_gain, int *min_offset)
 | 
						|
{
 | 
						|
  *gain = (int) 15.0 *(target / (max - min) - 0.933);
 | 
						|
  *offset = (int) (-1.0 * min / (512.0 * 0.0195));
 | 
						|
  if (*gain >= 32)
 | 
						|
    {
 | 
						|
      *gain = (int) 15.0 *(target / 3.0 / (max - min) - 0.933);
 | 
						|
      *offset = (int) -3.0 * min / (512.0 * 0.0195);
 | 
						|
    }
 | 
						|
  if (*gain < 0)
 | 
						|
    *gain = 0;
 | 
						|
  else if (*gain > 63)
 | 
						|
    *gain = 63;
 | 
						|
 | 
						|
  if (*offset < -31)
 | 
						|
    *offset = -31;
 | 
						|
  else if (*offset > 31)
 | 
						|
    *offset = 31;
 | 
						|
 | 
						|
  *max_gain = 63;
 | 
						|
  *min_offset = -31;
 | 
						|
}
 | 
						|
 | 
						|
#define DATA_PORT_READ (1 << 5)
 | 
						|
#define DATA_PORT_WRITE 0
 | 
						|
 | 
						|
static int
 | 
						|
write_gamma (HP4200_Scanner * s)
 | 
						|
{
 | 
						|
  int color;
 | 
						|
  int i;
 | 
						|
  unsigned char gamma[1024];
 | 
						|
  unsigned char read_gamma[1024];
 | 
						|
  int retval;
 | 
						|
  size_t to_read;
 | 
						|
  size_t to_write;
 | 
						|
 | 
						|
  for (color = 0; color < 3; color++)
 | 
						|
    {
 | 
						|
      for (i = 0; i < 1024; i++)
 | 
						|
	gamma[i] = s->user_parms.gamma[color][i];
 | 
						|
 | 
						|
      setreg (s, 0x03, color << 1);
 | 
						|
      setreg (s, 0x04, DATA_PORT_WRITE);
 | 
						|
      setreg (s, 0x05, 0x00);
 | 
						|
      sanei_pv8630_write_byte (s->fd, PV8630_REPPADDRESS, 0x06);
 | 
						|
      sanei_pv8630_prep_bulkwrite (s->fd, sizeof (gamma));
 | 
						|
      to_write = sizeof (gamma);
 | 
						|
      sanei_usb_write_bulk (s->fd, gamma, &to_write);
 | 
						|
 | 
						|
      /* check if gamma vector was correctly written */
 | 
						|
 | 
						|
      setreg (s, 0x03, color << 1);
 | 
						|
      setreg (s, 0x04, DATA_PORT_READ);
 | 
						|
      setreg (s, 0x05, 0x00);
 | 
						|
      sanei_pv8630_write_byte (s->fd, PV8630_REPPADDRESS, 0x06);
 | 
						|
      sanei_pv8630_prep_bulkread (s->fd, sizeof (read_gamma));
 | 
						|
      to_read = sizeof (read_gamma);
 | 
						|
      sanei_usb_read_bulk (s->fd, read_gamma, &to_read);
 | 
						|
      retval = memcmp (read_gamma, gamma, sizeof (read_gamma));
 | 
						|
      if (retval != 0)
 | 
						|
	{
 | 
						|
	  DBG (DBG_error, "error: color %d has bad gamma table\n", color);
 | 
						|
	}
 | 
						|
#ifdef DEBUG
 | 
						|
      else
 | 
						|
	fprintf (stderr, "color %d gamma table is good\n", color);
 | 
						|
#endif
 | 
						|
    }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
write_default_offset_gain (HP4200_Scanner * s, SANE_Byte * gain_offset,
 | 
						|
			   int size, int color)
 | 
						|
{
 | 
						|
  SANE_Byte *check_data;
 | 
						|
  int retval;
 | 
						|
  size_t to_read;
 | 
						|
  size_t to_write;
 | 
						|
 | 
						|
  setreg (s, 0x03, (color << 1) | 1);
 | 
						|
  setreg (s, 0x04, DATA_PORT_WRITE);
 | 
						|
  setreg (s, 0x05, 0x00);
 | 
						|
  sanei_pv8630_write_byte (s->fd, PV8630_REPPADDRESS, 0x06);
 | 
						|
  sanei_pv8630_prep_bulkwrite (s->fd, size);
 | 
						|
  to_write = size;
 | 
						|
  sanei_usb_write_bulk (s->fd, gain_offset, &to_write);
 | 
						|
 | 
						|
  check_data = malloc (size);
 | 
						|
  setreg (s, 0x03, (color << 1) | 1);
 | 
						|
  setreg (s, 0x04, DATA_PORT_READ);
 | 
						|
  setreg (s, 0x05, 0x00);
 | 
						|
  sanei_pv8630_write_byte (s->fd, PV8630_REPPADDRESS, 0x06);
 | 
						|
  sanei_pv8630_prep_bulkread (s->fd, size);
 | 
						|
  to_read = size;
 | 
						|
  sanei_usb_read_bulk (s->fd, check_data, &to_read);
 | 
						|
  retval = memcmp (gain_offset, check_data, size);
 | 
						|
  free (check_data);
 | 
						|
  if (retval != 0)
 | 
						|
    {
 | 
						|
      DBG (DBG_error, "error: color %d has bad gain/offset table\n", color);
 | 
						|
    }
 | 
						|
#ifdef DEBUG
 | 
						|
  else
 | 
						|
    fprintf (stderr, "color %d gain/offset table is good\n", color);
 | 
						|
#endif
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compute_gain_offset (int target, int max, int min, int *gain,
 | 
						|
		     int *offset, int *max_gain, int *min_offset)
 | 
						|
{
 | 
						|
  int gain_stable;
 | 
						|
  int is_unstable;
 | 
						|
 | 
						|
  gain_stable = 1;		/* unless the opposite is said */
 | 
						|
  is_unstable = 0;
 | 
						|
 | 
						|
  if (max > target)
 | 
						|
    {
 | 
						|
      if (*gain > 0)
 | 
						|
	{
 | 
						|
	  (*gain)--;
 | 
						|
	  *max_gain = *gain;
 | 
						|
	  gain_stable = 0;
 | 
						|
	  is_unstable |= 1;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  DBG (DBG_error, "error: integration time too long.\n");
 | 
						|
	  return -1;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      if (*gain < *max_gain)
 | 
						|
	{
 | 
						|
	  (*gain)++;
 | 
						|
	  gain_stable = 0;
 | 
						|
	  is_unstable |= 1;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  if (min == 0)
 | 
						|
    {
 | 
						|
      if (*offset < 31)
 | 
						|
	{
 | 
						|
	  (*offset)++;
 | 
						|
	  if (gain_stable)
 | 
						|
	    *min_offset = *offset;
 | 
						|
	  is_unstable |= 1;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  DBG (DBG_error, "error: max static has pixel value == 0\n");
 | 
						|
	  return -1;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      if (*offset > *min_offset)
 | 
						|
	{
 | 
						|
	  (*offset)--;
 | 
						|
	  is_unstable |= 1;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  return is_unstable;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compute_bytes_per_line (int width_in_pixels, unsigned char hdpi_code,
 | 
						|
			unsigned char pixel_packing,
 | 
						|
			unsigned char data_mode,
 | 
						|
			unsigned char AFE_operation, int m)
 | 
						|
{
 | 
						|
  const int dpi_qot_mul[] = { 1, 2, 1, 1, 1, 1, 1, 1 };
 | 
						|
  const int dpi_qot_div[] = { 1, 3, 2, 3, 4, 6, 8, 12 };
 | 
						|
  int pixels_per_line;
 | 
						|
  int bytes_per_line;
 | 
						|
  int pixels_per_byte;
 | 
						|
  int status_bytes;
 | 
						|
  const int pixels_per_byte_mapping[] = { 8, 4, 2, 1 };
 | 
						|
 | 
						|
  assert (hdpi_code <= 7);
 | 
						|
  pixels_per_line = (width_in_pixels * dpi_qot_mul[hdpi_code]) /
 | 
						|
    dpi_qot_div[hdpi_code];
 | 
						|
  if ((width_in_pixels * dpi_qot_mul[hdpi_code]) % dpi_qot_div[hdpi_code])
 | 
						|
    pixels_per_line++;
 | 
						|
 | 
						|
 | 
						|
  status_bytes = (m == 0) ? 1 : m;
 | 
						|
 | 
						|
  if (data_mode == 1)
 | 
						|
    pixels_per_byte = 1;	/* should be 0.5 but later
 | 
						|
				   bytes_per_line will be multiplied
 | 
						|
				   by 2, and also the number of status
 | 
						|
				   bytes, that in this case should be
 | 
						|
				   2.
 | 
						|
				   umm.. maybe this should be done in
 | 
						|
				   the cleaner way.
 | 
						|
				 */
 | 
						|
  else
 | 
						|
    {
 | 
						|
      assert (pixel_packing <= 3);
 | 
						|
      pixels_per_byte = pixels_per_byte_mapping[pixel_packing];
 | 
						|
    }
 | 
						|
 | 
						|
  switch (AFE_operation)
 | 
						|
    {
 | 
						|
    case PIXEL_RATE_3_CHANNELS:
 | 
						|
      bytes_per_line = ((pixels_per_line * 3) / pixels_per_byte) +
 | 
						|
	status_bytes;
 | 
						|
      break;
 | 
						|
    case MODEA_1_CHANNEL:
 | 
						|
      bytes_per_line = (pixels_per_line / pixels_per_byte) + status_bytes;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      /* Not implemented! (yet?) and not used.
 | 
						|
       * This case should not happen. */
 | 
						|
      assert (0);
 | 
						|
    }
 | 
						|
 | 
						|
  if (data_mode == 1)		/* see big note above */
 | 
						|
    bytes_per_line *= 2;
 | 
						|
 | 
						|
  return bytes_per_line;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compute_pause_limit (hardware_parameters_t * hw_parms, int bytes_per_line)
 | 
						|
{
 | 
						|
  int coef_size;
 | 
						|
  const int coef_mapping[] = { 16, 32 };
 | 
						|
  int pause_limit;
 | 
						|
 | 
						|
  coef_size = coef_mapping[hw_parms->sensor_resolution & 0x01];
 | 
						|
  pause_limit = hw_parms->SRAM_size - coef_size - (bytes_per_line / 1024) - 1;
 | 
						|
 | 
						|
  if (pause_limit > 2)
 | 
						|
    pause_limit -= 2;
 | 
						|
 | 
						|
  return pause_limit;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compute_dpd (HP4200_Scanner * s, int step_size, int line_end)
 | 
						|
{
 | 
						|
  int tr, dpd;
 | 
						|
 | 
						|
  tr = 1 /* color mode */  *
 | 
						|
    (line_end + ((s->hw_parms.num_tr_pulses + 1) *
 | 
						|
		 (2 * s->hw_parms.guard_band_duration +
 | 
						|
		  s->hw_parms.pulse_duration + 1) +
 | 
						|
		 3 - s->hw_parms.num_tr_pulses));
 | 
						|
 | 
						|
  if (tr == 0)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  dpd = (((s->hw_parms.fsteps_25_speed * 4) +
 | 
						|
	  (s->hw_parms.fsteps_50_speed * 2) +
 | 
						|
	  s->hw_parms.steps_to_reverse) * 4 * step_size) % tr;
 | 
						|
  dpd = tr - dpd;
 | 
						|
 | 
						|
  return dpd;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
read_required_bytes (HP4200_Scanner * s, int required, SANE_Byte * buffer)
 | 
						|
{
 | 
						|
  int read_count = 0;
 | 
						|
  unsigned char scankb1;
 | 
						|
  unsigned char scankb2;
 | 
						|
  size_t to_read;
 | 
						|
  size_t really_read;
 | 
						|
  size_t chunk;
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  assert (buffer != NULL);
 | 
						|
 | 
						|
  while (required)
 | 
						|
    {
 | 
						|
      do
 | 
						|
	{
 | 
						|
	  scankb1 = getreg (s, 0x01);
 | 
						|
	  scankb2 = getreg (s, 0x01);
 | 
						|
	  if (s->aborted_by_user)
 | 
						|
	    return SANE_STATUS_CANCELLED;
 | 
						|
	}
 | 
						|
      while ((scankb1 != scankb2) || (scankb1 < 12));
 | 
						|
 | 
						|
      to_read = min (required, (scankb1 * 1024));
 | 
						|
      while (to_read)
 | 
						|
	{
 | 
						|
	  if (s->aborted_by_user)
 | 
						|
	    return SANE_STATUS_CANCELLED;
 | 
						|
	  chunk = (to_read > 0xffff) ? 0xffff : to_read;
 | 
						|
 | 
						|
	  sanei_pv8630_write_byte (s->fd, PV8630_REPPADDRESS, 0x00);
 | 
						|
	  sanei_pv8630_prep_bulkread (s->fd, chunk);
 | 
						|
	  really_read = chunk;
 | 
						|
	  if ((status = sanei_usb_read_bulk (s->fd, buffer, &really_read)) !=
 | 
						|
	      SANE_STATUS_GOOD)
 | 
						|
	    {
 | 
						|
	      DBG (DBG_error, "sanei_usb_read_bulk failed (%s)\n",
 | 
						|
		   sane_strstatus (status));
 | 
						|
	      return status;
 | 
						|
	    }
 | 
						|
	  if (really_read > chunk)
 | 
						|
	    {
 | 
						|
	      DBG (DBG_error, "USB stack read more bytes than requested!\n");
 | 
						|
	      return SANE_STATUS_IO_ERROR;
 | 
						|
	    }
 | 
						|
	  buffer += really_read;
 | 
						|
	  required -= really_read;
 | 
						|
	  to_read -= really_read;
 | 
						|
	  read_count += really_read;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
scanner_buffer_init (scanner_buffer_t * sb, int size_in_kb)
 | 
						|
{
 | 
						|
 | 
						|
  sb->size = size_in_kb * 1024 + 3;
 | 
						|
  sb->buffer = malloc (sb->size);
 | 
						|
  if (!sb->buffer)
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
  sb->num_bytes = 0;
 | 
						|
  sb->data_ptr = sb->buffer;
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
scanner_buffer_read (HP4200_Scanner * s)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  size_t num_bytes_read_now;
 | 
						|
 | 
						|
  assert (s->scanner_buffer.num_bytes <= 3);
 | 
						|
 | 
						|
  memcpy (s->scanner_buffer.buffer, s->scanner_buffer.data_ptr, 3);
 | 
						|
 | 
						|
  status = read_available_data (s, s->scanner_buffer.buffer +
 | 
						|
				s->scanner_buffer.num_bytes,
 | 
						|
				&num_bytes_read_now);
 | 
						|
  s->scanner_buffer.data_ptr = s->scanner_buffer.buffer;
 | 
						|
  s->scanner_buffer.num_bytes += num_bytes_read_now;
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
#define OFFSET_CODE_SIGN(off) (((off) < 0) ? (-(off) & 0x1f) | 0x20 : (off))
 | 
						|
#define OFFSET_DECODE_SIGN(off) (((off) & 0x20) ? -(off & 0x1f) : (off))
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
do_coarse_calibration (HP4200_Scanner * s, struct coarse_t *coarse)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  unsigned char *cal_line = NULL;
 | 
						|
  unsigned char *cal_line_ptr;
 | 
						|
  int cal_line_size;
 | 
						|
  /* local scanning params */
 | 
						|
  int active_pixels_start;
 | 
						|
  int line_end;
 | 
						|
  int data_pixels_start;
 | 
						|
  int data_pixels_end;
 | 
						|
  int dpd;
 | 
						|
  int step_size;
 | 
						|
  int ff_step_size;
 | 
						|
  char steps_to_reverse;
 | 
						|
  char line_rate_color;
 | 
						|
  int vdpi;			/* vertical dots per inch */
 | 
						|
  int hdpi_code;
 | 
						|
  int calibrated;
 | 
						|
  int first_time;
 | 
						|
 | 
						|
  int red_offset = 0;
 | 
						|
  int green_offset = 0;
 | 
						|
  int blue_offset = 0;
 | 
						|
 | 
						|
  int red_gain = 1;
 | 
						|
  int green_gain = 1;
 | 
						|
  int blue_gain = 1;
 | 
						|
 | 
						|
  int min_red_offset = -31;
 | 
						|
  int min_green_offset = -31;
 | 
						|
  int min_blue_offset = -31;
 | 
						|
 | 
						|
  int max_red_gain = 63;
 | 
						|
  int max_green_gain = 63;
 | 
						|
  int max_blue_gain = 63;
 | 
						|
 | 
						|
  int max_red;
 | 
						|
  int min_red;
 | 
						|
  int max_green;
 | 
						|
  int min_green;
 | 
						|
  int max_blue;
 | 
						|
  int min_blue;
 | 
						|
  static char me[] = "do_coarse_calibration";
 | 
						|
 | 
						|
  DBG (DBG_proc, "%s\n", me);
 | 
						|
 | 
						|
  setreg (s, 0x07, 0x00);
 | 
						|
  usleep (10 * 1000);
 | 
						|
 | 
						|
  vdpi = 150;
 | 
						|
  hdpi_code = 0;
 | 
						|
  active_pixels_start = 0x40;
 | 
						|
  line_end = 0x2ee0;
 | 
						|
  s->mclk_div = 2;
 | 
						|
  data_pixels_start = 0x40;
 | 
						|
  data_pixels_end = (int) (data_pixels_start + s->hw_parms.scan_area_width);
 | 
						|
  data_pixels_end = min (data_pixels_end, line_end - 20);
 | 
						|
 | 
						|
  cal_line_size = s->hw_parms.scan_area_width * 3 * 2 + 2;
 | 
						|
 | 
						|
  setreg (s, 0x1e, HIBYTE (active_pixels_start));
 | 
						|
  setreg (s, 0x1f, LOBYTE (active_pixels_start));
 | 
						|
  setreg (s, 0x20, HIBYTE (line_end));
 | 
						|
  setreg (s, 0x21, LOBYTE (line_end));
 | 
						|
  setreg (s, 0x22, HIBYTE (data_pixels_start));
 | 
						|
  setreg (s, 0x23, LOBYTE (data_pixels_start));
 | 
						|
  setreg (s, 0x24, HIBYTE (data_pixels_end));
 | 
						|
  setreg (s, 0x25, LOBYTE (data_pixels_end));
 | 
						|
 | 
						|
  setreg (s, 0x26,
 | 
						|
	  PIXEL_RATE_3_CHANNELS |
 | 
						|
	  GRAY_CHANNEL_RED | TR_RED (0) | TR_GREEN (0) | TR_BLUE (0));
 | 
						|
 | 
						|
 | 
						|
  setreg (s, 0x08, (s->mclk_div - 1) * 2);
 | 
						|
  setreg (s, 0x09, hdpi_code | PIXEL_PACKING (3) | DATAMODE (1));
 | 
						|
  setreg (s, 0x0a, 0);		/* reserved and strange register */
 | 
						|
 | 
						|
  setreg (s, 0x38, red_offset);
 | 
						|
  setreg (s, 0x39, green_offset);
 | 
						|
  setreg (s, 0x3a, blue_offset);
 | 
						|
  setreg (s, 0x3b, red_gain);
 | 
						|
  setreg (s, 0x3c, green_gain);
 | 
						|
  setreg (s, 0x3d, blue_gain);
 | 
						|
 | 
						|
  setreg (s, 0x5e, 0x80);
 | 
						|
 | 
						|
  setreg (s, 0x3e, 0x00);	/* 1.5:1, 6/10 bits, 2*fixed */
 | 
						|
  setreg (s, 0x3f, 0x00);
 | 
						|
  setreg (s, 0x40, 0x00);
 | 
						|
  setreg (s, 0x41, 0x00);
 | 
						|
 | 
						|
  setreg (s, 0x4e, 0x5b - 0x3c);	/* max Kb to pause */
 | 
						|
  setreg (s, 0x4f, 0x02);	/* min Kb to resume */
 | 
						|
 | 
						|
  line_rate_color = 1;
 | 
						|
  step_size = (vdpi * line_end * line_rate_color) /
 | 
						|
    (4 * s->hw_parms.motor_full_steps_per_inch);
 | 
						|
 | 
						|
  dpd = compute_dpd (s, step_size, line_end);	/* 0x0ada; */
 | 
						|
#ifdef DEBUG
 | 
						|
  fprintf (stderr, "dpd = %d\n", dpd);
 | 
						|
#endif
 | 
						|
  setreg (s, 0x52, HIBYTE (dpd));
 | 
						|
  setreg (s, 0x53, LOBYTE (dpd));
 | 
						|
 | 
						|
  setreg (s, 0x46, HIBYTE (step_size));
 | 
						|
  setreg (s, 0x47, LOBYTE (step_size));
 | 
						|
 | 
						|
  ff_step_size = compute_fastfeed_step_size (s->hw_parms.crystal_frequency, s->mclk_div, s->hw_parms.motor_max_speed, s->hw_parms.motor_full_steps_per_inch, 0);	/* 0x0190; */
 | 
						|
  setreg (s, 0x48, HIBYTE (ff_step_size));
 | 
						|
  setreg (s, 0x49, LOBYTE (ff_step_size));
 | 
						|
  setreg (s, 0x4b, 0x15);
 | 
						|
  steps_to_reverse = 0x3f;
 | 
						|
  setreg (s, 0x50, steps_to_reverse);
 | 
						|
  setreg (s, 0x51, 0x15);	/* accel profile */
 | 
						|
 | 
						|
  /* this is to stay the motor stopped */
 | 
						|
  clearbits (s, 0x45, (1 << 4));
 | 
						|
 | 
						|
  cache_write (s);
 | 
						|
 | 
						|
  calibrated = 0;
 | 
						|
  first_time = 1;
 | 
						|
  cal_line = malloc (cal_line_size + 1024);
 | 
						|
 | 
						|
  do
 | 
						|
    {
 | 
						|
      unsigned char cmd_reg;
 | 
						|
 | 
						|
      /* resets the lm9830 before start scanning */
 | 
						|
      setreg (s, 0x07, 0x08);
 | 
						|
      do
 | 
						|
	{
 | 
						|
	  setreg (s, 0x07, 0x03);
 | 
						|
	  cmd_reg = getreg (s, 0x07);
 | 
						|
	}
 | 
						|
      while (cmd_reg != 0x03);
 | 
						|
 | 
						|
      cal_line_ptr = cal_line;
 | 
						|
      status = read_required_bytes (s, cal_line_size, cal_line_ptr);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto done;
 | 
						|
 | 
						|
      setreg (s, 0x07, 0x00);
 | 
						|
      {
 | 
						|
	unsigned int i;
 | 
						|
	min_red = max_red = (cal_line[0] * 256 + cal_line[1]) >> 2;
 | 
						|
	min_green = max_green = (cal_line[2] * 256 + cal_line[3]) >> 2;
 | 
						|
	min_blue = max_blue = (cal_line[4] * 256 + cal_line[5]) >> 2;
 | 
						|
	for (i = 6; i < (s->hw_parms.scan_area_width * 3 * 2); i += 6)
 | 
						|
	  {
 | 
						|
	    int value;
 | 
						|
 | 
						|
	    value = cal_line[i] * 256 + cal_line[i + 1];
 | 
						|
	    value >>= 2;
 | 
						|
	    if (value > max_red)
 | 
						|
	      max_red = value;
 | 
						|
	    value = cal_line[i + 2] * 256 + cal_line[i + 3];
 | 
						|
	    value >>= 2;
 | 
						|
	    if (value > max_green)
 | 
						|
	      max_green = value;
 | 
						|
	    value = cal_line[i + 4] * 256 + cal_line[i + 5];
 | 
						|
	    value >>= 2;
 | 
						|
	    if (value > max_blue)
 | 
						|
	      max_blue = value;
 | 
						|
	    value = cal_line[i] * 256 + cal_line[i + 1];
 | 
						|
	    value >>= 2;
 | 
						|
	    if (value < min_red)
 | 
						|
	      min_red = value;
 | 
						|
	    value = cal_line[i + 2] * 256 + cal_line[i + 3];
 | 
						|
	    value >>= 2;
 | 
						|
	    if (value < min_green)
 | 
						|
	      min_green = value;
 | 
						|
	    value = cal_line[i + 4] * 256 + cal_line[i + 5];
 | 
						|
	    value >>= 2;
 | 
						|
	    if (value < min_blue)
 | 
						|
	      min_blue = value;
 | 
						|
	  }
 | 
						|
#ifdef DEBUG
 | 
						|
	fprintf (stderr, "max_red:%d max_green:%d max_blue:%d\n",
 | 
						|
		 max_red, max_green, max_blue);
 | 
						|
	fprintf (stderr, "min_red:%d min_green:%d min_blue:%d\n",
 | 
						|
		 min_red, min_green, min_blue);
 | 
						|
#endif
 | 
						|
 | 
						|
	if (first_time)
 | 
						|
	  {
 | 
						|
	    first_time = 0;
 | 
						|
	    compute_first_gain_offset (s->hw_parms.target_value.red,
 | 
						|
				       max_red, min_red,
 | 
						|
				       &red_gain, &red_offset,
 | 
						|
				       &max_red_gain, &min_red_offset);
 | 
						|
	    compute_first_gain_offset (s->hw_parms.target_value.green,
 | 
						|
				       max_green, min_green,
 | 
						|
				       &green_gain, &green_offset,
 | 
						|
				       &max_green_gain, &min_green_offset);
 | 
						|
	    compute_first_gain_offset (s->hw_parms.target_value.blue,
 | 
						|
				       max_blue, min_blue, &blue_gain,
 | 
						|
				       &blue_offset, &max_blue_gain,
 | 
						|
				       &min_blue_offset);
 | 
						|
	  }
 | 
						|
	else
 | 
						|
	  {
 | 
						|
	    int retval;
 | 
						|
 | 
						|
	    /* this code should check return value -1 for error */
 | 
						|
 | 
						|
	    retval = compute_gain_offset (s->hw_parms.target_value.red,
 | 
						|
					  max_red, min_red,
 | 
						|
					  &red_gain, &red_offset,
 | 
						|
					  &max_red_gain, &min_red_offset);
 | 
						|
	    if (retval < 0)
 | 
						|
	      break;
 | 
						|
	    retval |= compute_gain_offset (s->hw_parms.target_value.green,
 | 
						|
					   max_green, min_green,
 | 
						|
					   &green_gain, &green_offset,
 | 
						|
					   &max_green_gain,
 | 
						|
					   &min_green_offset);
 | 
						|
	    if (retval < 0)
 | 
						|
	      break;
 | 
						|
	    retval |= compute_gain_offset (s->hw_parms.target_value.blue,
 | 
						|
					   max_blue, min_blue,
 | 
						|
					   &blue_gain, &blue_offset,
 | 
						|
					   &max_blue_gain, &min_blue_offset);
 | 
						|
	    if (retval < 0)
 | 
						|
	      break;
 | 
						|
	    calibrated = !retval;
 | 
						|
	  }
 | 
						|
 | 
						|
	setreg (s, 0x3b, red_gain);
 | 
						|
	setreg (s, 0x3c, green_gain);
 | 
						|
	setreg (s, 0x3d, blue_gain);
 | 
						|
 | 
						|
	setreg (s, 0x38, OFFSET_CODE_SIGN (red_offset));
 | 
						|
	setreg (s, 0x39, OFFSET_CODE_SIGN (green_offset));
 | 
						|
	setreg (s, 0x3a, OFFSET_CODE_SIGN (blue_offset));
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
	fprintf (stderr, "%d, %d, %d   %d, %d, %d\n", red_gain,
 | 
						|
		 green_gain, blue_gain, red_offset, green_offset,
 | 
						|
		 blue_offset);
 | 
						|
#endif
 | 
						|
	cache_write (s);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  while (!calibrated);
 | 
						|
  coarse->min_red = min_red;
 | 
						|
  coarse->min_green = min_green;
 | 
						|
  coarse->min_blue = min_blue;
 | 
						|
  coarse->max_red = max_red;
 | 
						|
  coarse->max_green = max_green;
 | 
						|
  coarse->max_blue = max_blue;
 | 
						|
  coarse->red_gain = red_gain;
 | 
						|
  coarse->green_gain = green_gain;
 | 
						|
  coarse->blue_gain = blue_gain;
 | 
						|
  coarse->red_offset = red_offset;
 | 
						|
  coarse->green_offset = green_offset;
 | 
						|
  coarse->blue_offset = blue_offset;
 | 
						|
 | 
						|
  status = SANE_STATUS_GOOD;
 | 
						|
 | 
						|
done:
 | 
						|
  if (cal_line)
 | 
						|
    free (cal_line);
 | 
						|
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compute_corr_code (int average, int min_color, int range, int target)
 | 
						|
{
 | 
						|
  int value;
 | 
						|
  int corr_code;
 | 
						|
 | 
						|
  value = average - min_color;
 | 
						|
  if (value > 0)
 | 
						|
    corr_code =
 | 
						|
      (int) (range * ((double) target / (double) value - 1.0) + 0.5);
 | 
						|
  else
 | 
						|
    corr_code = 0;
 | 
						|
  if (corr_code < 0)
 | 
						|
    corr_code = 0;
 | 
						|
  else if (corr_code > 2048)
 | 
						|
    corr_code = 0;
 | 
						|
  else if (corr_code > 1023)
 | 
						|
    corr_code = 1023;
 | 
						|
  return corr_code;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compute_hdpi_code (int hres)
 | 
						|
{
 | 
						|
  int hdpi_code;
 | 
						|
 | 
						|
  /* Calculate the horizontal DPI code based on the requested
 | 
						|
     horizontal resolution.  Defaults to 150dpi.  */
 | 
						|
  switch (hres)
 | 
						|
    {
 | 
						|
    case 600:
 | 
						|
      hdpi_code = 0;
 | 
						|
      break;
 | 
						|
    case 400:
 | 
						|
      hdpi_code = 1;
 | 
						|
      break;
 | 
						|
    case 300:
 | 
						|
      hdpi_code = 2;
 | 
						|
      break;
 | 
						|
    case 200:
 | 
						|
      hdpi_code = 3;
 | 
						|
      break;
 | 
						|
    case 150:
 | 
						|
      hdpi_code = 4;
 | 
						|
      break;
 | 
						|
    case 100:
 | 
						|
      hdpi_code = 5;
 | 
						|
      break;
 | 
						|
    case 75:
 | 
						|
      hdpi_code = 6;
 | 
						|
      break;
 | 
						|
    case 50:
 | 
						|
      hdpi_code = 7;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      hdpi_code = 4;
 | 
						|
    }
 | 
						|
  return hdpi_code;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
do_fine_calibration (HP4200_Scanner * s, struct coarse_t *coarse)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  unsigned char *cal_line;
 | 
						|
  unsigned char *cal_line_ptr;
 | 
						|
  int *average;
 | 
						|
  SANE_Byte red_gain_offset[5460 * 2];
 | 
						|
  SANE_Byte green_gain_offset[5460 * 2];
 | 
						|
  SANE_Byte blue_gain_offset[5460 * 2];
 | 
						|
  int *corr_red = NULL;
 | 
						|
  int *corr_green = NULL;
 | 
						|
  int *corr_blue = NULL;
 | 
						|
  int registro[30][5460 * 3];
 | 
						|
  int cal_line_size;
 | 
						|
  /* local scanning params */
 | 
						|
  int active_pixels_start;
 | 
						|
  int line_end;
 | 
						|
  int line_length;
 | 
						|
  int data_pixels_start;
 | 
						|
  int data_pixels_end;
 | 
						|
  int dpd;
 | 
						|
  int step_size;
 | 
						|
  int ff_step_size;
 | 
						|
  char steps_to_reverse;
 | 
						|
  char hdpi_div;
 | 
						|
  char line_rate_color;
 | 
						|
  int vdpi;			/* vertical dots per inch */
 | 
						|
  int hdpi_code;
 | 
						|
  int calibrated;
 | 
						|
  int lines_to_process;
 | 
						|
 | 
						|
  static char me[] = "do_fine_calibration";
 | 
						|
 | 
						|
  DBG (DBG_proc, "%s\n", me);
 | 
						|
 | 
						|
  setreg (s, 0x07, 0x00);
 | 
						|
  usleep (10 * 1000);
 | 
						|
 | 
						|
  vdpi = 150;
 | 
						|
  hdpi_code = compute_hdpi_code (s->user_parms.horizontal_resolution);
 | 
						|
 | 
						|
  /* figure out which horizontal divider to use based on the
 | 
						|
     calculated horizontal dpi code */
 | 
						|
  hdpi_div = hdpi_mapping[hdpi_code];
 | 
						|
  active_pixels_start = 0x40;
 | 
						|
  line_end = 0x2ee0;
 | 
						|
  line_length = s->user_parms.image_width * hdpi_div;
 | 
						|
  s->mclk_div = 2;
 | 
						|
  data_pixels_start = 0x72 + s->runtime_parms.first_pixel * hdpi_div;
 | 
						|
  data_pixels_end =
 | 
						|
    (int) (data_pixels_start + s->user_parms.image_width * hdpi_div);
 | 
						|
  data_pixels_end = min (data_pixels_end, line_end - 20);
 | 
						|
 | 
						|
  cal_line_size = line_length * 3 * 2 + 2;
 | 
						|
 | 
						|
  setreg (s, 0x1e, HIBYTE (active_pixels_start));
 | 
						|
  setreg (s, 0x1f, LOBYTE (active_pixels_start));
 | 
						|
  setreg (s, 0x20, HIBYTE (line_end));
 | 
						|
  setreg (s, 0x21, LOBYTE (line_end));
 | 
						|
  setreg (s, 0x22, HIBYTE (data_pixels_start));
 | 
						|
  setreg (s, 0x23, LOBYTE (data_pixels_start));
 | 
						|
  setreg (s, 0x24, HIBYTE (data_pixels_end));
 | 
						|
  setreg (s, 0x25, LOBYTE (data_pixels_end));
 | 
						|
 | 
						|
  setreg (s, 0x26,
 | 
						|
	  PIXEL_RATE_3_CHANNELS |
 | 
						|
	  GRAY_CHANNEL_RED | TR_RED (0) | TR_GREEN (0) | TR_BLUE (0));
 | 
						|
 | 
						|
 | 
						|
  setreg (s, 0x08, (s->mclk_div - 1) * 2);
 | 
						|
  setreg (s, 0x09, 0 | PIXEL_PACKING (3) | DATAMODE (1));
 | 
						|
  setreg (s, 0x0a, 0);		/* reserved and strange register */
 | 
						|
 | 
						|
  setreg (s, 0x38, 1);
 | 
						|
  setreg (s, 0x39, 1);
 | 
						|
  setreg (s, 0x3a, 1);
 | 
						|
  setreg (s, 0x3b, coarse->red_gain);
 | 
						|
  setreg (s, 0x3c, coarse->green_gain);
 | 
						|
  setreg (s, 0x3d, coarse->blue_gain);
 | 
						|
 | 
						|
  setreg (s, 0x5e, 0x80);
 | 
						|
 | 
						|
  setreg (s, 0x3e, 0x00);	/* 1.5:1, 6/10 bits, 2*fixed */
 | 
						|
  setreg (s, 0x3f, 0x00);
 | 
						|
  setreg (s, 0x40, 0x00);
 | 
						|
  setreg (s, 0x41, 0x00);
 | 
						|
 | 
						|
  setreg (s, 0x4e, 0x5b - 0x3c);	/* max Kb to pause */
 | 
						|
  setreg (s, 0x4f, 0x02);	/* min Kb to resume */
 | 
						|
 | 
						|
  line_rate_color = 1;
 | 
						|
  step_size = (vdpi * line_end * line_rate_color) /
 | 
						|
    (4 * s->hw_parms.motor_full_steps_per_inch);
 | 
						|
 | 
						|
  dpd = compute_dpd (s, step_size, line_end);	/* 0x0ada; */
 | 
						|
#ifdef DEBUG
 | 
						|
  fprintf (stderr, "dpd = %d\n", dpd);
 | 
						|
#endif
 | 
						|
  setreg (s, 0x52, HIBYTE (dpd));
 | 
						|
  setreg (s, 0x53, LOBYTE (dpd));
 | 
						|
 | 
						|
  setreg (s, 0x46, HIBYTE (step_size));
 | 
						|
  setreg (s, 0x47, LOBYTE (step_size));
 | 
						|
 | 
						|
  ff_step_size = compute_fastfeed_step_size (s->hw_parms.crystal_frequency, s->mclk_div, s->hw_parms.motor_max_speed, s->hw_parms.motor_full_steps_per_inch, 0);	/* 0x0190; */
 | 
						|
  setreg (s, 0x48, HIBYTE (ff_step_size));
 | 
						|
  setreg (s, 0x49, LOBYTE (ff_step_size));
 | 
						|
  setreg (s, 0x4b, 0x15);
 | 
						|
  steps_to_reverse = 0x3f;
 | 
						|
  setreg (s, 0x50, steps_to_reverse);
 | 
						|
  setreg (s, 0x51, 0x15);	/* accel profile */
 | 
						|
 | 
						|
  /* this is to activate the motor */
 | 
						|
  setbits (s, 0x45, (1 << 4));
 | 
						|
 | 
						|
  lines_to_process = 8 * step_size * 4 / line_end;
 | 
						|
  if (lines_to_process < 1)
 | 
						|
    lines_to_process = 1;
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
  fprintf (stderr, "lines to process = %d\n", lines_to_process);
 | 
						|
#endif
 | 
						|
 | 
						|
  setreg (s, 0x58, 0);
 | 
						|
 | 
						|
  cache_write (s);
 | 
						|
 | 
						|
  calibrated = 0;
 | 
						|
  cal_line = malloc (cal_line_size + 1024);
 | 
						|
  average = malloc (sizeof (int) * line_length * 3);
 | 
						|
  memset (average, 0, sizeof (int) * line_length * 3);
 | 
						|
  {
 | 
						|
    int i;
 | 
						|
    for (i = 0; i < 12; i++)
 | 
						|
      {
 | 
						|
        memset (registro[i], 0, 5460 * 3 * sizeof(int));
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /* resets the lm9830 before start scanning */
 | 
						|
  setreg (s, 0x07, 0x08);
 | 
						|
  setreg (s, 0x07, 0x03);
 | 
						|
 | 
						|
  usleep (100);
 | 
						|
 | 
						|
  do
 | 
						|
    {
 | 
						|
 | 
						|
      cal_line_ptr = cal_line;
 | 
						|
 | 
						|
      status = read_required_bytes (s, cal_line_size, cal_line_ptr);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	goto done;
 | 
						|
      {
 | 
						|
	int i, j;
 | 
						|
 | 
						|
	if (calibrated == 0)
 | 
						|
	  for (j = 0, i = 0; i < (line_length * 3); i++, j += 2)
 | 
						|
	    {
 | 
						|
	      average[i] = (cal_line[j] * 256 + cal_line[j + 1]) >> 2;
 | 
						|
	      registro[calibrated][i] = average[i];
 | 
						|
	    }
 | 
						|
	else
 | 
						|
	  for (j = 0, i = 0; i < (line_length * 3); i++, j += 2)
 | 
						|
	    {
 | 
						|
	      int value;
 | 
						|
	      value = (cal_line[j] * 256 + cal_line[j + 1]) >> 2;
 | 
						|
	      average[i] += value;
 | 
						|
	      average[i] /= 2;
 | 
						|
	      registro[calibrated][i] = value;
 | 
						|
	    }
 | 
						|
      }
 | 
						|
      calibrated++;
 | 
						|
    }
 | 
						|
  while (calibrated < lines_to_process);
 | 
						|
  lm9830_write_register (s->fd, 0x07, 0x00);
 | 
						|
  usleep (10 * 1000);
 | 
						|
 | 
						|
#if 0
 | 
						|
  {
 | 
						|
    int i;
 | 
						|
    int j = 0;
 | 
						|
    do
 | 
						|
      {
 | 
						|
	for (i = 3; (i + 6) < (line_length * 3); i += 3)
 | 
						|
	  {
 | 
						|
	    average[i] =
 | 
						|
	      (2 * average[i - 3] + average[i] + 2 * average[i + 3]) / 5;
 | 
						|
	    average[i + 1] =
 | 
						|
	      (2 * average[i - 2] + average[i + 1] + 2 * average[i + 4]) / 5;
 | 
						|
	    average[i + 2] =
 | 
						|
	      (2 * average[i - 1] + average[i + 2] + 2 * average[i + 5]) / 5;
 | 
						|
	  }
 | 
						|
	j++;
 | 
						|
      }
 | 
						|
    while (j < 3);
 | 
						|
  }
 | 
						|
#endif
 | 
						|
  {
 | 
						|
    int i;
 | 
						|
    int max_red;
 | 
						|
    int min_red;
 | 
						|
    int max_green;
 | 
						|
    int min_green;
 | 
						|
    int max_blue;
 | 
						|
    int min_blue;
 | 
						|
    min_red = max_red = average[0];
 | 
						|
    min_green = max_green = average[1];
 | 
						|
    min_blue = max_blue = average[2];
 | 
						|
    for (i = 3; i < (line_length * 3); i += 3)
 | 
						|
      {
 | 
						|
	int value;
 | 
						|
 | 
						|
	value = average[i];
 | 
						|
	if (value > max_red)
 | 
						|
	  max_red = value;
 | 
						|
	value = average[i + 1];
 | 
						|
	if (value > max_green)
 | 
						|
	  max_green = value;
 | 
						|
	value = average[i + 2];
 | 
						|
	if (value > max_blue)
 | 
						|
	  max_blue = value;
 | 
						|
	value = average[i];
 | 
						|
	if (value < min_red)
 | 
						|
	  min_red = value;
 | 
						|
	value = average[i + 1];
 | 
						|
	if (value < min_green)
 | 
						|
	  min_green = value;
 | 
						|
	value = average[i + 2];
 | 
						|
	if (value < min_blue)
 | 
						|
	  min_blue = value;
 | 
						|
      }
 | 
						|
#ifdef DEBUG
 | 
						|
    fprintf (stderr, "max_red:%d max_green:%d max_blue:%d\n",
 | 
						|
	     max_red, max_green, max_blue);
 | 
						|
    fprintf (stderr, "min_red:%d min_green:%d min_blue:%d\n",
 | 
						|
	     min_red, min_green, min_blue);
 | 
						|
#endif
 | 
						|
 | 
						|
    /* do fine calibration */
 | 
						|
    {
 | 
						|
      int min_white_red;
 | 
						|
      int min_white_green;
 | 
						|
      int min_white_blue;
 | 
						|
      double ratio;
 | 
						|
      int range;
 | 
						|
      double aux;
 | 
						|
      int min_white_err;
 | 
						|
      int j;
 | 
						|
 | 
						|
      min_white_red = min_white_green = min_white_blue = 0x3ff;
 | 
						|
      for (i = 0; i < (line_length * 3); i += 3)
 | 
						|
	{
 | 
						|
	  int value;
 | 
						|
 | 
						|
	  value = average[i] - coarse->min_red;
 | 
						|
	  if ((value > 0) && (value < min_white_red))
 | 
						|
	    min_white_red = value;
 | 
						|
	  value = average[i + 1] - coarse->min_green;
 | 
						|
	  if ((value > 0) && (value < min_white_green))
 | 
						|
	    min_white_green = value;
 | 
						|
	  value = average[i + 2] - coarse->min_blue;
 | 
						|
	  if ((value > 0) && (value < min_white_blue))
 | 
						|
	    min_white_blue = value;
 | 
						|
	}
 | 
						|
 | 
						|
      ratio = 0;
 | 
						|
      min_white_err = 0x3ff;
 | 
						|
 | 
						|
      aux = (double) s->hw_parms.target_value.red / min_white_red;
 | 
						|
      if (aux > ratio)
 | 
						|
	ratio = aux;
 | 
						|
      if (min_white_err > min_white_red)
 | 
						|
	min_white_err = min_white_red;
 | 
						|
      aux = (double) s->hw_parms.target_value.green / min_white_green;
 | 
						|
      if (aux > ratio)
 | 
						|
	ratio = aux;
 | 
						|
      if (min_white_err > min_white_green)
 | 
						|
	min_white_err = min_white_green;
 | 
						|
      aux = (double) s->hw_parms.target_value.blue / min_white_blue;
 | 
						|
      if (aux > ratio)
 | 
						|
	ratio = aux;
 | 
						|
      if (min_white_err > min_white_blue)
 | 
						|
	min_white_err = min_white_blue;
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
      fprintf (stderr, "min_white_err = %d, ratio = %f\n",
 | 
						|
	       min_white_err, ratio);
 | 
						|
#endif
 | 
						|
      if (ratio <= 1.5)
 | 
						|
	range = 2048;
 | 
						|
      else if (ratio <= 2.0)
 | 
						|
	range = 1024;
 | 
						|
      else
 | 
						|
	range = 512;
 | 
						|
 | 
						|
      corr_red = malloc (sizeof (int) * line_length);
 | 
						|
      corr_green = malloc (sizeof (int) * line_length);
 | 
						|
      corr_blue = malloc (sizeof (int) * line_length);
 | 
						|
 | 
						|
      for (i = 0, j = 0; i < (line_length * 3); i += 3, j++)
 | 
						|
	{
 | 
						|
	  corr_red[j] = compute_corr_code (average[i],
 | 
						|
					   coarse->min_red,
 | 
						|
					   range,
 | 
						|
					   s->hw_parms.target_value.red);
 | 
						|
	  corr_green[j] =
 | 
						|
	    compute_corr_code (average[i + 1], coarse->min_green,
 | 
						|
			       range, s->hw_parms.target_value.green);
 | 
						|
	  corr_blue[j] =
 | 
						|
	    compute_corr_code (average[i + 2], coarse->min_blue,
 | 
						|
			       range, s->hw_parms.target_value.blue);
 | 
						|
	}
 | 
						|
#ifdef DEBUG
 | 
						|
      {
 | 
						|
	FILE *kaka;
 | 
						|
	int i;
 | 
						|
	kaka = fopen ("corr.raw", "w");
 | 
						|
	for (i = 0; i < line_length; i++)
 | 
						|
	  {
 | 
						|
	    fprintf (kaka, "%d %d %d %d %d %d ",
 | 
						|
		     corr_red[i], corr_green[i], corr_blue[i],
 | 
						|
		     average[3 * i], average[3 * i + 1], average[3 * i + 2]);
 | 
						|
	    fprintf (kaka, "%d %d %d  %d %d %d  %d %d %d ",
 | 
						|
		     registro[0][3 * i], registro[0][3 * i + 1],
 | 
						|
		     registro[0][3 * i + 2], registro[1][3 * i],
 | 
						|
		     registro[1][3 * i + 1], registro[1][3 * i + 2],
 | 
						|
		     registro[2][3 * i], registro[2][3 * i + 1],
 | 
						|
		     registro[2][3 * i + 2]);
 | 
						|
	    fprintf (kaka, "%d %d %d  %d %d %d  %d %d %d\n",
 | 
						|
		     registro[3][3 * i], registro[3][3 * i + 1],
 | 
						|
		     registro[3][3 * i + 2], registro[4][3 * i],
 | 
						|
		     registro[4][3 * i + 1], registro[4][3 * i + 2],
 | 
						|
		     registro[5][3 * i], registro[5][3 * i + 1],
 | 
						|
		     registro[5][3 * i + 2]);
 | 
						|
	  }
 | 
						|
	fclose (kaka);
 | 
						|
      }
 | 
						|
#endif
 | 
						|
      {
 | 
						|
	int max_black;
 | 
						|
	int use_six_eight_bits;
 | 
						|
 | 
						|
	max_black = max (coarse->min_red, coarse->min_green);
 | 
						|
	max_black = max (max_black, coarse->min_blue);
 | 
						|
	use_six_eight_bits = (max_black < 64);
 | 
						|
 | 
						|
	if (use_six_eight_bits)
 | 
						|
	  {
 | 
						|
	    setreg (s, 0x3e, (1 << 4) | (1 << 3) | (1024 / range));
 | 
						|
	  }
 | 
						|
	else
 | 
						|
	  {
 | 
						|
	    setreg (s, 0x3e, (1 << 4) | (1 << 3) | (1 << 2) | (1024 / range));
 | 
						|
	  }
 | 
						|
	memset (red_gain_offset, 0, sizeof (red_gain_offset));
 | 
						|
	memset (green_gain_offset, 0, sizeof (green_gain_offset));
 | 
						|
	memset (blue_gain_offset, 0, sizeof (blue_gain_offset));
 | 
						|
	for (i = 0, j = (data_pixels_start - active_pixels_start) * 2;
 | 
						|
	     i < line_length; i++, j += 2)
 | 
						|
	  {
 | 
						|
	    if (use_six_eight_bits)
 | 
						|
	      {
 | 
						|
		red_gain_offset[j] = (coarse->min_red << 2) |
 | 
						|
		  ((corr_red[i] >> 8) & 0x03);
 | 
						|
		red_gain_offset[j + 1] = corr_red[i] & 0xff;
 | 
						|
		green_gain_offset[j] = (coarse->min_green << 2) |
 | 
						|
		  ((corr_green[i] >> 8) & 0x03);
 | 
						|
		green_gain_offset[j + 1] = corr_green[i] & 0xff;
 | 
						|
		blue_gain_offset[j] = (coarse->min_blue << 2) |
 | 
						|
		  ((corr_blue[i] >> 8) & 0x03);
 | 
						|
		blue_gain_offset[j + 1] = corr_blue[i] & 0xff;
 | 
						|
	      }
 | 
						|
	    else
 | 
						|
	      {
 | 
						|
		red_gain_offset[j] = coarse->min_red;
 | 
						|
		red_gain_offset[j + 1] = corr_red[j] >> 2;
 | 
						|
		green_gain_offset[j] = coarse->min_green;
 | 
						|
		green_gain_offset[j + 1] = corr_green[j] >> 2;
 | 
						|
		blue_gain_offset[j] = coarse->min_blue;
 | 
						|
		blue_gain_offset[j + 1] = corr_blue[j] >> 2;
 | 
						|
	      }
 | 
						|
	  }
 | 
						|
	write_default_offset_gain (s, red_gain_offset, 5460 * 2, 0);
 | 
						|
	write_default_offset_gain (s, green_gain_offset, 5460 * 2, 1);
 | 
						|
	write_default_offset_gain (s, blue_gain_offset, 5460 * 2, 2);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  status = SANE_STATUS_GOOD;
 | 
						|
 | 
						|
done:
 | 
						|
  if (corr_red)
 | 
						|
    free (corr_red);
 | 
						|
  if (corr_green)
 | 
						|
    free (corr_green);
 | 
						|
  if (corr_blue)
 | 
						|
    free (corr_blue);
 | 
						|
  if (cal_line)
 | 
						|
    free (cal_line);
 | 
						|
  if (average)
 | 
						|
    free (average);
 | 
						|
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ciclic_buffer_init_offset_correction (ciclic_buffer_t * cb, int vres)
 | 
						|
{
 | 
						|
  cb->blue_idx = 0;
 | 
						|
  switch (vres)
 | 
						|
    {
 | 
						|
    case 600:
 | 
						|
      cb->green_idx = 4;
 | 
						|
      cb->red_idx = 8;
 | 
						|
      cb->first_good_line = 8;
 | 
						|
      break;
 | 
						|
    case 400:
 | 
						|
      cb->green_idx = 3;
 | 
						|
      cb->red_idx = 6;
 | 
						|
      cb->first_good_line = 6;
 | 
						|
      break;
 | 
						|
    case 300:
 | 
						|
      cb->green_idx = 2;
 | 
						|
      cb->red_idx = 4;
 | 
						|
      cb->first_good_line = 4;
 | 
						|
      break;
 | 
						|
    case 200:
 | 
						|
      cb->blue_idx = 0;
 | 
						|
      cb->green_idx = 1;
 | 
						|
      cb->red_idx = 2;
 | 
						|
      cb->first_good_line = 4;
 | 
						|
      break;
 | 
						|
    case 150:
 | 
						|
      cb->green_idx = 1;
 | 
						|
      cb->red_idx = 2;
 | 
						|
      cb->first_good_line = 2;
 | 
						|
      break;
 | 
						|
    case 75:
 | 
						|
      cb->green_idx = 1;
 | 
						|
      cb->red_idx = 2;
 | 
						|
      cb->first_good_line = 2;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      cb->green_idx = 0;
 | 
						|
      cb->red_idx = 0;
 | 
						|
      cb->first_good_line = 0;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
  cb->buffer_position = cb->buffer_ptrs[cb->first_good_line];
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
ciclic_buffer_init (ciclic_buffer_t * cb, SANE_Int bytes_per_line,
 | 
						|
		    int vres, int status_bytes)
 | 
						|
{
 | 
						|
  cb->good_bytes = 0;
 | 
						|
  cb->num_lines = 12;
 | 
						|
  cb->size = bytes_per_line * cb->num_lines;
 | 
						|
  cb->can_consume = cb->size + cb->num_lines * status_bytes;
 | 
						|
 | 
						|
  cb->buffer = malloc (cb->size);
 | 
						|
  if (!cb->buffer)
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
 | 
						|
  {
 | 
						|
    int i;
 | 
						|
    unsigned char *buffer;
 | 
						|
    unsigned char **ptrs;
 | 
						|
 | 
						|
    ptrs = cb->buffer_ptrs = (unsigned char **)
 | 
						|
      malloc (sizeof (unsigned char *) * cb->num_lines);
 | 
						|
    if (!cb->buffer_ptrs)
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
 | 
						|
    buffer = cb->buffer;
 | 
						|
    for (i = 0; i < cb->num_lines; i++)
 | 
						|
      {
 | 
						|
	ptrs[i] = buffer;
 | 
						|
	buffer += bytes_per_line;
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  cb->current_line = 0;
 | 
						|
  cb->pixel_position = 0;
 | 
						|
  ciclic_buffer_init_offset_correction (cb, vres);
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
prepare_for_a_scan (HP4200_Scanner * s)
 | 
						|
{
 | 
						|
  /* local scanning params */
 | 
						|
  int active_pixels_start;
 | 
						|
  int line_end;
 | 
						|
  int data_pixels_start;
 | 
						|
  int data_pixels_end;
 | 
						|
  int ff_step_size;
 | 
						|
  int dpd;
 | 
						|
  int step_size;
 | 
						|
  char steps_to_reverse;
 | 
						|
  char hdpi_div;
 | 
						|
  char line_rate_color;
 | 
						|
  int hdpi_code;
 | 
						|
  unsigned char pixel_packing;
 | 
						|
  unsigned char data_mode;
 | 
						|
  unsigned char AFE_operation;
 | 
						|
  int pause_limit;
 | 
						|
  int n = 0, m = 0;
 | 
						|
 | 
						|
  setreg (s, 0x07, 0x00);
 | 
						|
  usleep (10 * 1000);
 | 
						|
 | 
						|
  hdpi_code = compute_hdpi_code (s->user_parms.horizontal_resolution);
 | 
						|
  /* figure out which horizontal divider to use based on the
 | 
						|
     calculated horizontal dpi code */
 | 
						|
  hdpi_div = hdpi_mapping[hdpi_code];
 | 
						|
 | 
						|
  /* image_width is set to the correct number of pixels by calling
 | 
						|
     fxn.  This might be the reason we can't do high res full width
 | 
						|
     scans though...not sure.  */
 | 
						|
  /*s->user_parms.image_width /= 4; */
 | 
						|
  active_pixels_start = 0x40;
 | 
						|
  line_end = 0x2ee0;		/* 2ee0 */
 | 
						|
  s->mclk_div = 2;
 | 
						|
  data_pixels_start = 0x72 + s->runtime_parms.first_pixel * hdpi_div;
 | 
						|
  data_pixels_end =
 | 
						|
    (int) (data_pixels_start + s->user_parms.image_width * hdpi_div);
 | 
						|
  data_pixels_end = min (data_pixels_end, line_end - 20);
 | 
						|
  setreg (s, 0x1e, HIBYTE (active_pixels_start));
 | 
						|
  setreg (s, 0x1f, LOBYTE (active_pixels_start));
 | 
						|
  setreg (s, 0x20, HIBYTE (line_end));
 | 
						|
  setreg (s, 0x21, LOBYTE (line_end));
 | 
						|
  setreg (s, 0x22, HIBYTE (data_pixels_start));
 | 
						|
  setreg (s, 0x23, LOBYTE (data_pixels_start));
 | 
						|
  setreg (s, 0x24, HIBYTE (data_pixels_end));
 | 
						|
  setreg (s, 0x25, LOBYTE (data_pixels_end));
 | 
						|
 | 
						|
  AFE_operation = PIXEL_RATE_3_CHANNELS;
 | 
						|
  setreg (s, 0x26,
 | 
						|
	  AFE_operation |
 | 
						|
	  GRAY_CHANNEL_RED | TR_RED (0) | TR_GREEN (0) | TR_BLUE (0));
 | 
						|
 | 
						|
  setreg (s, 0x08, (s->mclk_div - 1) * 2);
 | 
						|
  pixel_packing = 3;
 | 
						|
  data_mode = 0;
 | 
						|
  setreg (s, 0x09, hdpi_code | PIXEL_PACKING (pixel_packing) |
 | 
						|
	  DATAMODE (data_mode));
 | 
						|
  setreg (s, 0x0a, 0);		/* reserved and strange register */
 | 
						|
 | 
						|
  setreg (s, 0x5c, 0x00);
 | 
						|
  setreg (s, 0x5d, 0x00);
 | 
						|
  setreg (s, 0x5e, 0x00);
 | 
						|
 | 
						|
  if (s->user_parms.vertical_resolution == 1200)
 | 
						|
    {
 | 
						|
      /* 1 out of 2 */
 | 
						|
      n = 1;
 | 
						|
      m = 2;
 | 
						|
    }
 | 
						|
  setreg (s, 0x44, (256 - n) & 0xff);
 | 
						|
  setreg (s, 0x5a, m);
 | 
						|
  s->runtime_parms.status_bytes = (m == 0) ? 1 : m;
 | 
						|
  if (data_mode == 1)
 | 
						|
    s->runtime_parms.status_bytes *= 2;
 | 
						|
 | 
						|
  s->runtime_parms.scanner_line_size =
 | 
						|
    compute_bytes_per_line (data_pixels_end - data_pixels_start,
 | 
						|
			    hdpi_code, pixel_packing, data_mode,
 | 
						|
			    AFE_operation, m);
 | 
						|
  pause_limit = compute_pause_limit (&(s->hw_parms),
 | 
						|
				     s->runtime_parms.scanner_line_size);
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
  fprintf (stderr, "scanner_line_size = %d\npause_limit = %d\n",
 | 
						|
	   s->runtime_parms.scanner_line_size, pause_limit);
 | 
						|
#endif
 | 
						|
 | 
						|
  setreg (s, 0x4e, pause_limit);	/* max Kb to pause */
 | 
						|
  setreg (s, 0x4f, 0x02);	/* min Kb to resume */
 | 
						|
 | 
						|
  line_rate_color = 1;
 | 
						|
  step_size =
 | 
						|
    (s->user_parms.vertical_resolution * line_end * line_rate_color) /
 | 
						|
    (4 * s->hw_parms.motor_full_steps_per_inch);
 | 
						|
 | 
						|
  if (s->val[OPT_BACKTRACK].b)
 | 
						|
    {
 | 
						|
      steps_to_reverse = 0x3f;
 | 
						|
      setreg (s, 0x50, steps_to_reverse);
 | 
						|
      setreg (s, 0x51, 0x15);	/* accel profile */
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      s->hw_parms.steps_to_reverse = 0;
 | 
						|
      setreg (s, 0x50, s->hw_parms.steps_to_reverse);
 | 
						|
      setreg (s, 0x51, 0);	/* accel profile */
 | 
						|
      s->hw_parms.fsteps_25_speed = 0;
 | 
						|
      s->hw_parms.fsteps_50_speed = 0;
 | 
						|
    }
 | 
						|
 | 
						|
  dpd = compute_dpd (s, step_size, line_end);	/* 0x0ada; */
 | 
						|
#ifdef DEBUG
 | 
						|
  fprintf (stderr, "dpd = %d\n", dpd);
 | 
						|
#endif
 | 
						|
  setreg (s, 0x52, HIBYTE (dpd));
 | 
						|
  setreg (s, 0x53, LOBYTE (dpd));
 | 
						|
 | 
						|
  setreg (s, 0x46, HIBYTE (step_size));
 | 
						|
  setreg (s, 0x47, LOBYTE (step_size));
 | 
						|
 | 
						|
  ff_step_size = compute_fastfeed_step_size (s->hw_parms.crystal_frequency,
 | 
						|
					     s->mclk_div,
 | 
						|
					     s->hw_parms.motor_max_speed,
 | 
						|
					     s->hw_parms.
 | 
						|
					     motor_full_steps_per_inch, 0);
 | 
						|
  setreg (s, 0x48, HIBYTE (ff_step_size));
 | 
						|
  setreg (s, 0x49, LOBYTE (ff_step_size));
 | 
						|
  setreg (s, 0x4b, 0x15);
 | 
						|
  /* this is to stay the motor running */
 | 
						|
  setbits (s, 0x45, (1 << 4));
 | 
						|
 | 
						|
  setreg (s, 0x4a, HIBYTE (47 + s->runtime_parms.steps_to_skip));
 | 
						|
  setreg (s, 0x4b, LOBYTE (47 + s->runtime_parms.steps_to_skip));
 | 
						|
 | 
						|
  setreg (s, 0x58, 0);
 | 
						|
 | 
						|
  ciclic_buffer_init (&(s->ciclic_buffer),
 | 
						|
		      s->runtime_parms.image_line_size,
 | 
						|
		      s->user_parms.vertical_resolution,
 | 
						|
		      s->runtime_parms.status_bytes);
 | 
						|
 | 
						|
  s->runtime_parms.num_bytes_left_to_scan =
 | 
						|
    s->user_parms.lines_to_scan * s->runtime_parms.image_line_size;
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
  fprintf (stderr, "bytes to scan = %ld\n",
 | 
						|
	   s->runtime_parms.num_bytes_left_to_scan);
 | 
						|
#endif
 | 
						|
 | 
						|
  cache_write (s);
 | 
						|
 | 
						|
#ifdef DEBUG
 | 
						|
  lm9830_dump_registers (s->fd);
 | 
						|
#endif
 | 
						|
 | 
						|
  lm9830_reset (s->fd);
 | 
						|
 | 
						|
  setreg (s, 0x07, 0x03);
 | 
						|
  usleep (100);
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
end_scan (HP4200_Scanner * s)
 | 
						|
{
 | 
						|
  s->scanning = SANE_FALSE;
 | 
						|
  setreg (s, 0x07, 0x00);
 | 
						|
  lm9830_reset (s->fd);
 | 
						|
  setbits (s, 0x58, PAPER_SENSOR_2_STOP_SCAN);
 | 
						|
  cache_write (s);
 | 
						|
  setreg (s, 0x07, 0x02);
 | 
						|
 | 
						|
  /* Free some buffers */
 | 
						|
  if (s->ciclic_buffer.buffer)
 | 
						|
    {
 | 
						|
      free (s->ciclic_buffer.buffer);
 | 
						|
      s->ciclic_buffer.buffer = NULL;
 | 
						|
    }
 | 
						|
  if (s->ciclic_buffer.buffer_ptrs)
 | 
						|
    {
 | 
						|
      free (s->ciclic_buffer.buffer_ptrs);
 | 
						|
      s->ciclic_buffer.buffer_ptrs = NULL;
 | 
						|
    }
 | 
						|
  if (s->scanner_buffer.buffer)
 | 
						|
    {
 | 
						|
      free (s->scanner_buffer.buffer);
 | 
						|
      s->scanner_buffer.buffer = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
hp4200_init_scanner (HP4200_Scanner * s)
 | 
						|
{
 | 
						|
  int ff_step_size;
 | 
						|
  int mclk_div;
 | 
						|
 | 
						|
  lm9830_ini_scanner (s->fd, NULL);
 | 
						|
  hp4200_init_registers (s);
 | 
						|
  scanner_buffer_init (&(s->scanner_buffer), s->hw_parms.SRAM_size);
 | 
						|
  setreg (s, 0x07, 0x08);
 | 
						|
  usleep (10 * 1000);
 | 
						|
  setreg (s, 0x07, 0x00);
 | 
						|
  usleep (10 * 1000);
 | 
						|
  mclk_div = 2;
 | 
						|
 | 
						|
  setreg (s, 0x08, (mclk_div - 1) * 2);
 | 
						|
  ff_step_size =
 | 
						|
    compute_fastfeed_step_size (s->hw_parms.crystal_frequency,
 | 
						|
				mclk_div,
 | 
						|
				s->hw_parms.motor_max_speed,
 | 
						|
				s->hw_parms.motor_full_steps_per_inch, 0);
 | 
						|
  setreg (s, 0x48, HIBYTE (ff_step_size));
 | 
						|
  setreg (s, 0x49, LOBYTE (ff_step_size));
 | 
						|
  setbits (s, 0x45, (1 << 4));
 | 
						|
  cache_write (s);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ciclic_buffer_copy (ciclic_buffer_t * cb, SANE_Byte * buf,
 | 
						|
		    SANE_Int num_bytes, int image_line_size, int status_bytes)
 | 
						|
{
 | 
						|
  int biggest_upper_block_size;
 | 
						|
  int upper_block_size;
 | 
						|
  int lower_block_size;
 | 
						|
  int bytes_to_be_a_entire_line;
 | 
						|
 | 
						|
  /* copy the upper block */
 | 
						|
  biggest_upper_block_size = cb->size - (cb->buffer_position - cb->buffer);
 | 
						|
  upper_block_size = min (biggest_upper_block_size, num_bytes);
 | 
						|
  memcpy (buf, cb->buffer_position, upper_block_size);
 | 
						|
  cb->good_bytes -= upper_block_size;
 | 
						|
 | 
						|
  bytes_to_be_a_entire_line = (cb->buffer_position - cb->buffer) %
 | 
						|
    image_line_size;
 | 
						|
  cb->can_consume += upper_block_size +
 | 
						|
    status_bytes * (((bytes_to_be_a_entire_line + upper_block_size) /
 | 
						|
		     image_line_size) - 1);
 | 
						|
 | 
						|
  if (num_bytes < biggest_upper_block_size)
 | 
						|
    {
 | 
						|
      cb->buffer_position += num_bytes;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  /* copy the lower block */
 | 
						|
  lower_block_size = num_bytes - biggest_upper_block_size;
 | 
						|
  if (lower_block_size > 0)
 | 
						|
    {
 | 
						|
      memcpy (buf + biggest_upper_block_size, cb->buffer, lower_block_size);
 | 
						|
      cb->good_bytes -= lower_block_size;
 | 
						|
      cb->can_consume += lower_block_size + status_bytes *
 | 
						|
	(lower_block_size / image_line_size);
 | 
						|
      cb->buffer_position = cb->buffer + lower_block_size;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      cb->buffer_position = cb->buffer;
 | 
						|
    }
 | 
						|
  assert (cb->good_bytes >= 0);
 | 
						|
  assert (lower_block_size >= 0);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ciclic_buffer_consume (ciclic_buffer_t * cb,
 | 
						|
		       scanner_buffer_t * scanner_buffer,
 | 
						|
		       int image_width, int status_bytes)
 | 
						|
{
 | 
						|
  int to_consume;
 | 
						|
  int to_consume_now;
 | 
						|
  int i;
 | 
						|
  int processed;
 | 
						|
 | 
						|
  to_consume = min (cb->can_consume, scanner_buffer->num_bytes);
 | 
						|
 | 
						|
  while (to_consume)
 | 
						|
    {
 | 
						|
 | 
						|
      if (cb->pixel_position == image_width)
 | 
						|
	{
 | 
						|
	  if (scanner_buffer->num_bytes >= status_bytes)
 | 
						|
	    {
 | 
						|
	      /* forget status bytes */
 | 
						|
	      scanner_buffer->data_ptr += status_bytes;
 | 
						|
	      scanner_buffer->num_bytes -= status_bytes;
 | 
						|
	      cb->can_consume -= status_bytes;
 | 
						|
	      to_consume -= status_bytes;
 | 
						|
 | 
						|
	      cb->pixel_position = 0;	/* back to the start pixel */
 | 
						|
 | 
						|
	      cb->red_idx = (cb->red_idx + 1) % cb->num_lines;
 | 
						|
	      cb->green_idx = (cb->green_idx + 1) % cb->num_lines;
 | 
						|
	      cb->blue_idx = (cb->blue_idx + 1) % cb->num_lines;
 | 
						|
	      cb->current_line++;
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    break;
 | 
						|
	}
 | 
						|
 | 
						|
      to_consume_now = min ((image_width - cb->pixel_position) * 3,
 | 
						|
			    to_consume);
 | 
						|
 | 
						|
      if (to_consume_now < 3)
 | 
						|
	break;
 | 
						|
 | 
						|
      for (i = cb->pixel_position * 3; to_consume_now >= 3;
 | 
						|
	   i += 3, to_consume_now -= 3)
 | 
						|
	{
 | 
						|
	  cb->buffer_ptrs[cb->red_idx][i] = scanner_buffer->data_ptr[0];
 | 
						|
	  cb->buffer_ptrs[cb->green_idx][i + 1] = scanner_buffer->data_ptr[1];
 | 
						|
	  cb->buffer_ptrs[cb->blue_idx][i + 2] = scanner_buffer->data_ptr[2];
 | 
						|
	  scanner_buffer->data_ptr += 3;
 | 
						|
	}
 | 
						|
      processed = i - (cb->pixel_position * 3);
 | 
						|
      cb->pixel_position = i / 3;
 | 
						|
      to_consume -= processed;
 | 
						|
      cb->can_consume -= processed;
 | 
						|
      scanner_buffer->num_bytes -= processed;
 | 
						|
      if (cb->current_line > cb->first_good_line)
 | 
						|
	cb->good_bytes += processed;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  int to_copy_now;
 | 
						|
  int bytes_to_copy_to_frontend;
 | 
						|
  HP4200_Scanner *s = h;
 | 
						|
 | 
						|
  static char me[] = "sane_read";
 | 
						|
  DBG (DBG_proc, "%s\n", me);
 | 
						|
 | 
						|
  if (!(s->scanning))
 | 
						|
    {
 | 
						|
      /* OOPS, not scanning */
 | 
						|
      return SANE_STATUS_CANCELLED;
 | 
						|
    }
 | 
						|
 | 
						|
  if (!buf || !len)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  *len = 0;
 | 
						|
 | 
						|
  if (s->runtime_parms.num_bytes_left_to_scan == 0)
 | 
						|
    {
 | 
						|
      end_scan (s);
 | 
						|
      return SANE_STATUS_EOF;
 | 
						|
    }
 | 
						|
 | 
						|
  bytes_to_copy_to_frontend = min (s->runtime_parms.num_bytes_left_to_scan,
 | 
						|
				   maxlen);
 | 
						|
 | 
						|
  /* first copy available data from the ciclic buffer */
 | 
						|
  to_copy_now = min (s->ciclic_buffer.good_bytes, bytes_to_copy_to_frontend);
 | 
						|
 | 
						|
  if (to_copy_now > 0)
 | 
						|
    {
 | 
						|
      ciclic_buffer_copy (&(s->ciclic_buffer), buf, to_copy_now,
 | 
						|
			  s->runtime_parms.image_line_size,
 | 
						|
			  s->runtime_parms.status_bytes);
 | 
						|
      buf += to_copy_now;
 | 
						|
      bytes_to_copy_to_frontend -= to_copy_now;
 | 
						|
      *len += to_copy_now;
 | 
						|
    }
 | 
						|
 | 
						|
  /* if not enough bytes, get data from the scanner */
 | 
						|
  while (bytes_to_copy_to_frontend)
 | 
						|
    {
 | 
						|
      if (s->scanner_buffer.num_bytes < 3)
 | 
						|
	{			/* cicl buf consumes modulo 3
 | 
						|
				   bytes at least now for rgb
 | 
						|
				   color 8 bpp fixme: but this
 | 
						|
				   is ugly and not generic
 | 
						|
				 */
 | 
						|
	  status = scanner_buffer_read (s);
 | 
						|
 | 
						|
	  if (status == SANE_STATUS_CANCELLED)
 | 
						|
	    {
 | 
						|
	      end_scan (s);
 | 
						|
	      s->aborted_by_user = SANE_FALSE;
 | 
						|
	      return status;
 | 
						|
	    }
 | 
						|
	  if (status != SANE_STATUS_GOOD)
 | 
						|
	    return status;
 | 
						|
	}
 | 
						|
 | 
						|
      while ((s->scanner_buffer.num_bytes > 3) && bytes_to_copy_to_frontend)
 | 
						|
	{
 | 
						|
	  ciclic_buffer_consume (&(s->ciclic_buffer), &(s->scanner_buffer),
 | 
						|
				 s->user_parms.image_width,
 | 
						|
				 s->runtime_parms.status_bytes);
 | 
						|
	  to_copy_now = min (s->ciclic_buffer.good_bytes,
 | 
						|
			     bytes_to_copy_to_frontend);
 | 
						|
 | 
						|
	  if (to_copy_now > 0)
 | 
						|
	    {
 | 
						|
	      ciclic_buffer_copy (&(s->ciclic_buffer), buf, to_copy_now,
 | 
						|
				  s->runtime_parms.image_line_size,
 | 
						|
				  s->runtime_parms.status_bytes);
 | 
						|
	      buf += to_copy_now;
 | 
						|
	      bytes_to_copy_to_frontend -= to_copy_now;
 | 
						|
	      *len += to_copy_now;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  s->runtime_parms.num_bytes_left_to_scan -= *len;
 | 
						|
 | 
						|
  if (s->runtime_parms.num_bytes_left_to_scan < 0)
 | 
						|
    *len += s->runtime_parms.num_bytes_left_to_scan;
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static HP4200_Device *
 | 
						|
find_device (SANE_String_Const name)
 | 
						|
{
 | 
						|
  static char me[] = "find_device";
 | 
						|
  HP4200_Device *dev;
 | 
						|
 | 
						|
  DBG (DBG_proc, "%s\n", me);
 | 
						|
 | 
						|
  for (dev = first_device; dev; dev = dev->next)
 | 
						|
    {
 | 
						|
      if (strcmp (dev->dev.name, name) == 0)
 | 
						|
	{
 | 
						|
	  return dev;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
add_device (SANE_String_Const name, HP4200_Device ** argpd)
 | 
						|
{
 | 
						|
  int fd;
 | 
						|
  HP4200_Device *pd;
 | 
						|
  static const char me[] = "add_device";
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  DBG (DBG_proc, "%s(%s)\n", me, name);
 | 
						|
 | 
						|
  /* Avoid adding the same device more than once */
 | 
						|
  if ((pd = find_device (name)))
 | 
						|
    {
 | 
						|
      if (argpd)
 | 
						|
	*argpd = pd;
 | 
						|
      return SANE_STATUS_GOOD;
 | 
						|
    }
 | 
						|
 | 
						|
  /* open the device file, but read only or read/write to perform
 | 
						|
     ioctl's ? */
 | 
						|
  if ((status = sanei_usb_open (name, &fd)) != SANE_STATUS_GOOD)
 | 
						|
    {
 | 
						|
      DBG (DBG_error, "%s: open(%s) failed: %s\n", me, name,
 | 
						|
	   sane_strstatus (status));
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  /* put here some code to probe that the device attached to the
 | 
						|
     device file is a supported scanner. Maybe some ioctl */
 | 
						|
  sanei_usb_close (fd);
 | 
						|
 | 
						|
  pd = (HP4200_Device *) calloc (1, sizeof (HP4200_Device));
 | 
						|
  if (!pd)
 | 
						|
    {
 | 
						|
      DBG (DBG_error, "%s: out of memory allocating device.\n", me);
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
 | 
						|
  pd->dev.name = strdup (name);
 | 
						|
  pd->dev.vendor = "Hewlett-Packard";
 | 
						|
  pd->dev.model = "HP-4200";
 | 
						|
  pd->dev.type = "flatbed scanner";
 | 
						|
 | 
						|
  if (!pd->dev.name || !pd->dev.vendor || !pd->dev.model || !pd->dev.type)
 | 
						|
    {
 | 
						|
      DBG (DBG_error,
 | 
						|
	   "%s: out of memory allocating device descriptor strings.\n", me);
 | 
						|
      free (pd);
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
 | 
						|
  pd->handle = NULL;
 | 
						|
  pd->next = first_device;
 | 
						|
  first_device = pd;
 | 
						|
  n_devices++;
 | 
						|
  if (argpd)
 | 
						|
    *argpd = pd;
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
attach (SANE_String_Const name)
 | 
						|
{
 | 
						|
  static char me[] = "attach";
 | 
						|
  DBG (DBG_proc, "%s\n", me);
 | 
						|
  return add_device (name, NULL);
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
 | 
						|
{
 | 
						|
  static const char me[] = "sane_hp4200_init";
 | 
						|
  char dev_name[PATH_MAX];
 | 
						|
  FILE *fp;
 | 
						|
 | 
						|
  authorize = authorize;	/* keep gcc quiet */
 | 
						|
 | 
						|
  DBG_INIT ();
 | 
						|
 | 
						|
  DBG (DBG_proc, "%s\n", me);
 | 
						|
  DBG (DBG_error, "SANE hp4200 backend version %d.%d build %d from %s\n",
 | 
						|
       SANE_CURRENT_MAJOR, V_MINOR, BUILD, PACKAGE_STRING);
 | 
						|
  /* put some version_code checks here */
 | 
						|
 | 
						|
  if (NULL != version_code)
 | 
						|
    {
 | 
						|
      *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
 | 
						|
    }
 | 
						|
 | 
						|
  sanei_usb_init ();
 | 
						|
  sanei_pv8630_init ();
 | 
						|
 | 
						|
  fp = sanei_config_open (HP4200_CONFIG_FILE);
 | 
						|
  if (!fp)
 | 
						|
    {
 | 
						|
      DBG (DBG_error, "%s: configuration file not found!\n", me);
 | 
						|
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      while (sanei_config_read (dev_name, sizeof (dev_name), fp))
 | 
						|
	{
 | 
						|
	  if (dev_name[0] == '#')	/* ignore line comments */
 | 
						|
	    continue;
 | 
						|
 | 
						|
	  if (strlen (dev_name) == 0)
 | 
						|
	    continue;		/* ignore empty lines */
 | 
						|
 | 
						|
	  DBG (DBG_info, "%s: looking for devices matching %s\n",
 | 
						|
	       me, dev_name);
 | 
						|
 | 
						|
	  sanei_usb_attach_matching_devices (dev_name, attach);
 | 
						|
	}
 | 
						|
 | 
						|
      fclose (fp);
 | 
						|
    }
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
sane_exit (void)
 | 
						|
{
 | 
						|
  HP4200_Device *device, *next;
 | 
						|
 | 
						|
  DBG (DBG_proc, "sane_hp4200_exit\n");
 | 
						|
 | 
						|
  for (device = first_device; device; device = next)
 | 
						|
    {
 | 
						|
      next = device->next;
 | 
						|
      if (device->handle)
 | 
						|
	{
 | 
						|
	  sane_close (device->handle);
 | 
						|
	}
 | 
						|
      if (device->dev.name)
 | 
						|
	{
 | 
						|
	  free ((void *) device->dev.name);
 | 
						|
	}
 | 
						|
      free (device);
 | 
						|
    }
 | 
						|
  first_device = NULL;
 | 
						|
 | 
						|
  if (devlist)
 | 
						|
    {
 | 
						|
      free (devlist);
 | 
						|
      devlist = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
  n_devices = 0;
 | 
						|
 | 
						|
  DBG (DBG_proc, "sane_exit: exit\n");
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
  HP4200_Device *pdev;
 | 
						|
 | 
						|
  DBG (DBG_proc, "sane_get_devices (%p, %d)\n", (void *) device_list,
 | 
						|
       local_only);
 | 
						|
 | 
						|
  /* Waste the last list returned from this function */
 | 
						|
  if (devlist)
 | 
						|
    free (devlist);
 | 
						|
 | 
						|
  devlist = (const SANE_Device **)
 | 
						|
    malloc ((n_devices + 1) * sizeof (SANE_Device *));
 | 
						|
 | 
						|
  if (!devlist)
 | 
						|
    {
 | 
						|
      DBG (DBG_error, "sane_get_devices: out of memory\n");
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
 | 
						|
  for (i = 0, pdev = first_device; pdev; i++, pdev = pdev->next)
 | 
						|
    {
 | 
						|
      devlist[i] = &(pdev->dev);
 | 
						|
    }
 | 
						|
  devlist[i] = NULL;
 | 
						|
 | 
						|
  *device_list = devlist;
 | 
						|
 | 
						|
  DBG (DBG_proc, "sane_get_devices: exit\n");
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
init_options (HP4200_Scanner * s)
 | 
						|
{
 | 
						|
  s->opt[OPT_NUM_OPTS].name = "";
 | 
						|
  s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
 | 
						|
  s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
 | 
						|
  s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
 | 
						|
  s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_NUM_OPTS].unit = SANE_UNIT_NONE;
 | 
						|
  s->opt[OPT_NUM_OPTS].size = sizeof (SANE_Word);
 | 
						|
  s->opt[OPT_NUM_OPTS].constraint_type = SANE_CONSTRAINT_NONE;
 | 
						|
  s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
 | 
						|
 | 
						|
  s->opt[OPT_RES].name = SANE_NAME_SCAN_RESOLUTION;
 | 
						|
  s->opt[OPT_RES].title = SANE_TITLE_SCAN_RESOLUTION;
 | 
						|
  s->opt[OPT_RES].desc = SANE_DESC_SCAN_RESOLUTION;
 | 
						|
  s->opt[OPT_RES].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
 | 
						|
  s->opt[OPT_RES].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_RES].size = sizeof (SANE_Word);
 | 
						|
  s->opt[OPT_RES].unit = SANE_UNIT_DPI;
 | 
						|
  s->opt[OPT_RES].constraint_type = SANE_CONSTRAINT_WORD_LIST;
 | 
						|
  s->opt[OPT_RES].constraint.word_list = dpi_list;
 | 
						|
  s->val[OPT_RES].w = 150;
 | 
						|
 | 
						|
  s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
 | 
						|
  s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
 | 
						|
  s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
 | 
						|
  s->opt[OPT_TL_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
 | 
						|
  s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_TL_X].size = sizeof (SANE_Fixed);
 | 
						|
  s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
 | 
						|
  s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_TL_X].constraint.range = &x_range;
 | 
						|
  s->val[OPT_TL_X].w = x_range.min;
 | 
						|
 | 
						|
  s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
 | 
						|
  s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
 | 
						|
  s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
 | 
						|
  s->opt[OPT_TL_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
 | 
						|
  s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_TL_Y].size = sizeof (SANE_Fixed);
 | 
						|
  s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
 | 
						|
  s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_TL_Y].constraint.range = &y_range;
 | 
						|
  s->val[OPT_TL_Y].w = y_range.min;
 | 
						|
 | 
						|
  s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
 | 
						|
  s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
 | 
						|
  s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
 | 
						|
  s->opt[OPT_BR_X].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
 | 
						|
  s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_BR_X].size = sizeof (SANE_Fixed);
 | 
						|
  s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
 | 
						|
  s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_BR_X].constraint.range = &x_range;
 | 
						|
  s->val[OPT_BR_X].w = x_range.max;
 | 
						|
 | 
						|
  s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
 | 
						|
  s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
 | 
						|
  s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
 | 
						|
  s->opt[OPT_BR_Y].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
 | 
						|
  s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
 | 
						|
  s->opt[OPT_BR_Y].size = sizeof (SANE_Fixed);
 | 
						|
  s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
 | 
						|
  s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_BR_Y].constraint.range = &y_range;
 | 
						|
  s->val[OPT_BR_Y].w = y_range.max;
 | 
						|
 | 
						|
  s->opt[OPT_BACKTRACK].name = SANE_NAME_BACKTRACK;
 | 
						|
  s->opt[OPT_BACKTRACK].title = SANE_TITLE_BACKTRACK;
 | 
						|
  s->opt[OPT_BACKTRACK].desc = SANE_DESC_BACKTRACK;
 | 
						|
  s->opt[OPT_BACKTRACK].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
 | 
						|
  s->opt[OPT_BACKTRACK].type = SANE_TYPE_BOOL;
 | 
						|
  s->opt[OPT_BACKTRACK].size = sizeof (SANE_Bool);
 | 
						|
  s->opt[OPT_BACKTRACK].unit = SANE_UNIT_NONE;
 | 
						|
  s->opt[OPT_BACKTRACK].constraint_type = SANE_CONSTRAINT_NONE;
 | 
						|
  s->val[OPT_BACKTRACK].b = SANE_TRUE;
 | 
						|
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_R].cap =
 | 
						|
    SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_R].size = 1024 * sizeof (SANE_Word);
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
 | 
						|
  s->val[OPT_GAMMA_VECTOR_R].wa = s->user_parms.gamma[0];
 | 
						|
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_G].cap =
 | 
						|
    SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_G].size = 1024 * sizeof (SANE_Word);
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
 | 
						|
  s->val[OPT_GAMMA_VECTOR_G].wa = s->user_parms.gamma[1];
 | 
						|
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_B].cap =
 | 
						|
    SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_B].size = 1024 * sizeof (SANE_Word);
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
 | 
						|
  s->val[OPT_GAMMA_VECTOR_B].wa = s->user_parms.gamma[2];
 | 
						|
 | 
						|
  {
 | 
						|
    int i;
 | 
						|
    double gamma = 2.0;
 | 
						|
    for (i = 0; i < 1024; i++)
 | 
						|
      {
 | 
						|
	s->user_parms.gamma[0][i] =
 | 
						|
	  255 * pow (((double) i + 1) / 1024, 1.0 / gamma);
 | 
						|
	s->user_parms.gamma[1][i] =	s->user_parms.gamma[0][i];
 | 
						|
	s->user_parms.gamma[2][i] =	s->user_parms.gamma[0][i];
 | 
						|
#ifdef DEBUG
 | 
						|
	printf ("%d %d\n", i, s->user_parms.gamma[0][i]);
 | 
						|
#endif
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /* preview */
 | 
						|
  s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
 | 
						|
  s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
 | 
						|
  s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
 | 
						|
  s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
 | 
						|
  s->opt[OPT_PREVIEW].size = sizeof (SANE_Word);
 | 
						|
  s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
 | 
						|
  s->val[OPT_PREVIEW].w = SANE_FALSE;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_open (SANE_String_Const name, SANE_Handle * h)
 | 
						|
{
 | 
						|
  static const char me[] = "sane_hp4200_open";
 | 
						|
  SANE_Status status;
 | 
						|
  HP4200_Device *dev;
 | 
						|
  HP4200_Scanner *s;
 | 
						|
 | 
						|
  DBG (DBG_proc, "%s (%s, %p)\n", me, name, (void *) h);
 | 
						|
 | 
						|
  if (name && name[0])
 | 
						|
    {
 | 
						|
      dev = find_device (name);
 | 
						|
      if (!dev)
 | 
						|
	{
 | 
						|
	  status = add_device (name, &dev);
 | 
						|
	  if (status != SANE_STATUS_GOOD)
 | 
						|
	    return status;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      dev = first_device;
 | 
						|
    }
 | 
						|
  if (!dev)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  if (!h)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  s = *h = (HP4200_Scanner *) calloc (1, sizeof (HP4200_Scanner));
 | 
						|
  if (!s)
 | 
						|
    {
 | 
						|
      DBG (DBG_error, "%s: out of memory creating scanner structure.\n", me);
 | 
						|
      return SANE_STATUS_NO_MEM;
 | 
						|
    }
 | 
						|
 | 
						|
  dev->handle = s;
 | 
						|
  s->aborted_by_user = SANE_FALSE;
 | 
						|
  s->ciclic_buffer.buffer = NULL;
 | 
						|
  s->scanner_buffer.buffer = NULL;
 | 
						|
  s->dev = dev;
 | 
						|
  s->user_parms.image_width = 0;
 | 
						|
  s->user_parms.lines_to_scan = 0;
 | 
						|
  s->user_parms.vertical_resolution = 0;
 | 
						|
  s->scanning = SANE_FALSE;
 | 
						|
  s->fd = -1;
 | 
						|
 | 
						|
  init_options (s);
 | 
						|
 | 
						|
  if ((sanei_usb_open (dev->dev.name, &s->fd) != SANE_STATUS_GOOD))
 | 
						|
    {
 | 
						|
      DBG (DBG_error, "%s: Can't open %s.\n", me, dev->dev.name);
 | 
						|
      return SANE_STATUS_IO_ERROR;	/* fixme: return busy when file is
 | 
						|
					   being accessed already */
 | 
						|
    }
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
sane_close (SANE_Handle h)
 | 
						|
{
 | 
						|
  HP4200_Scanner *s = (HP4200_Scanner *) h;
 | 
						|
  DBG (DBG_proc, "sane_hp4200_close (%p)\n", (void *) h);
 | 
						|
 | 
						|
  if (s)
 | 
						|
    {
 | 
						|
      s->dev->handle = NULL;
 | 
						|
      if (s->fd != -1)
 | 
						|
	{
 | 
						|
	  sanei_usb_close (s->fd);
 | 
						|
	}
 | 
						|
      free (s);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
const SANE_Option_Descriptor *
 | 
						|
sane_get_option_descriptor (SANE_Handle h, SANE_Int n)
 | 
						|
{
 | 
						|
  static char me[] = "sane_get_option_descriptor";
 | 
						|
  HP4200_Scanner *s = (HP4200_Scanner *) h;
 | 
						|
 | 
						|
  DBG (DBG_proc, "%s\n", me);
 | 
						|
 | 
						|
  if ((n < 0) || (n >= NUM_OPTIONS))
 | 
						|
    return NULL;
 | 
						|
 | 
						|
  return s->opt + n;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_control_option (SANE_Handle handle, SANE_Int option,
 | 
						|
		     SANE_Action action, void *val, SANE_Int * info)
 | 
						|
{
 | 
						|
  HP4200_Scanner *s = (HP4200_Scanner *) handle;
 | 
						|
  SANE_Status status;
 | 
						|
  SANE_Int myinfo = 0;
 | 
						|
  SANE_Word cap;
 | 
						|
 | 
						|
  DBG (DBG_proc, "sane_control_option\n");
 | 
						|
 | 
						|
  if (info)
 | 
						|
    *info = 0;
 | 
						|
 | 
						|
  if (s->scanning)
 | 
						|
    {
 | 
						|
      return SANE_STATUS_DEVICE_BUSY;
 | 
						|
    }
 | 
						|
 | 
						|
  if (option < 0 || option >= NUM_OPTIONS)
 | 
						|
    {
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  cap = s->opt[option].cap;
 | 
						|
  if (!SANE_OPTION_IS_ACTIVE (cap))
 | 
						|
    {
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  if (action == SANE_ACTION_GET_VALUE)
 | 
						|
    {
 | 
						|
 | 
						|
      switch (option)
 | 
						|
	{
 | 
						|
	case OPT_NUM_OPTS:
 | 
						|
	case OPT_RES:
 | 
						|
	case OPT_TL_X:
 | 
						|
	case OPT_TL_Y:
 | 
						|
	case OPT_BR_X:
 | 
						|
	case OPT_BR_Y:
 | 
						|
	case OPT_PREVIEW:
 | 
						|
	  *(SANE_Word *) val = s->val[option].w;
 | 
						|
	  break;
 | 
						|
 | 
						|
	case OPT_BACKTRACK:
 | 
						|
	  *(SANE_Bool *) val = s->val[option].b;
 | 
						|
	  break;
 | 
						|
 | 
						|
	case OPT_GAMMA_VECTOR_R:
 | 
						|
	case OPT_GAMMA_VECTOR_G:
 | 
						|
	case OPT_GAMMA_VECTOR_B:
 | 
						|
	  memcpy (val, s->val[option].wa, s->opt[option].size);
 | 
						|
	  break;
 | 
						|
	default:
 | 
						|
	  return SANE_STATUS_UNSUPPORTED;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else if (action == SANE_ACTION_SET_VALUE)
 | 
						|
    {
 | 
						|
 | 
						|
      if (!SANE_OPTION_IS_SETTABLE (cap))
 | 
						|
	{
 | 
						|
	  DBG (DBG_error, "could not set option, not settable\n");
 | 
						|
	  return SANE_STATUS_INVAL;
 | 
						|
	}
 | 
						|
 | 
						|
      status = sanei_constrain_value (s->opt + option, val, &myinfo);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	return status;
 | 
						|
 | 
						|
      switch (option)
 | 
						|
	{
 | 
						|
 | 
						|
	  /* Numeric side-effect free options */
 | 
						|
	case OPT_PREVIEW:
 | 
						|
	  s->val[option].w = *(SANE_Word *) val;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	  /* Numeric side-effect options */
 | 
						|
	case OPT_RES:
 | 
						|
	case OPT_TL_X:
 | 
						|
	case OPT_TL_Y:
 | 
						|
	case OPT_BR_X:
 | 
						|
	case OPT_BR_Y:
 | 
						|
	  myinfo |= SANE_INFO_RELOAD_PARAMS;
 | 
						|
	  s->val[option].w = *(SANE_Word *) val;
 | 
						|
	  break;
 | 
						|
 | 
						|
	case OPT_BACKTRACK:
 | 
						|
	  s->val[option].b = *(SANE_Bool *) val;
 | 
						|
	  break;
 | 
						|
 | 
						|
	case OPT_GAMMA_VECTOR_R:
 | 
						|
	case OPT_GAMMA_VECTOR_G:
 | 
						|
	case OPT_GAMMA_VECTOR_B:
 | 
						|
	  memcpy (s->val[option].wa, val, s->opt[option].size);
 | 
						|
	  break;
 | 
						|
	default:
 | 
						|
	  return SANE_STATUS_UNSUPPORTED;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      return SANE_STATUS_UNSUPPORTED;
 | 
						|
    }
 | 
						|
 | 
						|
  if (info)
 | 
						|
    *info = myinfo;
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
compute_parameters (HP4200_Scanner * s)
 | 
						|
{
 | 
						|
  int resolution;
 | 
						|
  int opt_tl_x;
 | 
						|
  int opt_br_x;
 | 
						|
  int opt_tl_y;
 | 
						|
  int opt_br_y;
 | 
						|
 | 
						|
  if (s->val[OPT_PREVIEW].w == SANE_TRUE)
 | 
						|
    {
 | 
						|
      resolution = 50;
 | 
						|
      opt_tl_x = SANE_UNFIX (x_range.min);
 | 
						|
      opt_tl_y = SANE_UNFIX (y_range.min);
 | 
						|
      opt_br_x = SANE_UNFIX (x_range.max);
 | 
						|
      opt_br_y = SANE_UNFIX (y_range.max);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      resolution = s->val[OPT_RES].w;
 | 
						|
      opt_tl_x = SANE_UNFIX (s->val[OPT_TL_X].w);
 | 
						|
      opt_tl_y = SANE_UNFIX (s->val[OPT_TL_Y].w);
 | 
						|
      opt_br_x = SANE_UNFIX (s->val[OPT_BR_X].w);
 | 
						|
      opt_br_y = SANE_UNFIX (s->val[OPT_BR_Y].w);
 | 
						|
    }
 | 
						|
 | 
						|
  s->user_parms.horizontal_resolution = resolution;
 | 
						|
  s->user_parms.vertical_resolution = resolution;
 | 
						|
 | 
						|
  s->runtime_parms.steps_to_skip = floor (300.0 / MM_PER_INCH * opt_tl_y);
 | 
						|
  s->user_parms.lines_to_scan =
 | 
						|
    floor ((opt_br_y - opt_tl_y) / MM_PER_INCH * resolution);
 | 
						|
  s->user_parms.image_width =
 | 
						|
    floor ((opt_br_x - opt_tl_x) / MM_PER_INCH * resolution);
 | 
						|
  s->runtime_parms.first_pixel = floor (opt_tl_x / MM_PER_INCH * resolution);
 | 
						|
 | 
						|
  /* fixme: add support for more depth's and bpp's. */
 | 
						|
  s->runtime_parms.image_line_size = s->user_parms.image_width * 3;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_get_parameters (SANE_Handle h, SANE_Parameters * p)
 | 
						|
{
 | 
						|
  static char me[] = "sane_get_parameters";
 | 
						|
  HP4200_Scanner *s = (HP4200_Scanner *) h;
 | 
						|
 | 
						|
  DBG (DBG_proc, "%s\n", me);
 | 
						|
  if (!p)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  p->format = SANE_FRAME_RGB;
 | 
						|
  p->last_frame = SANE_TRUE;
 | 
						|
  p->depth = 8;
 | 
						|
  if (!s->scanning)
 | 
						|
    {
 | 
						|
      compute_parameters (s);
 | 
						|
    }
 | 
						|
 | 
						|
  p->lines = s->user_parms.lines_to_scan;
 | 
						|
  p->pixels_per_line = s->user_parms.image_width;
 | 
						|
  p->bytes_per_line = s->runtime_parms.image_line_size;
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_start (SANE_Handle h)
 | 
						|
{
 | 
						|
  HP4200_Scanner *s = (HP4200_Scanner *) h;
 | 
						|
  struct coarse_t coarse;
 | 
						|
 | 
						|
  static char me[] = "sane_start";
 | 
						|
  DBG (DBG_proc, "%s\n", me);
 | 
						|
 | 
						|
  s->scanning = SANE_TRUE;
 | 
						|
  s->aborted_by_user = SANE_FALSE;
 | 
						|
  s->user_parms.color = SANE_TRUE;
 | 
						|
 | 
						|
  compute_parameters (s);
 | 
						|
 | 
						|
  hp4200_init_scanner (s);
 | 
						|
  hp4200_goto_home (s);
 | 
						|
  hp4200_wait_homed (s);
 | 
						|
  /* restore default register values here... */
 | 
						|
  write_gamma (s);
 | 
						|
  hp4200_init_registers (s);
 | 
						|
  lm9830_ini_scanner (s->fd, NULL);
 | 
						|
  /* um... do not call cache_write() here, don't know why :( */
 | 
						|
  do_coarse_calibration (s, &coarse);
 | 
						|
  do_fine_calibration (s, &coarse);
 | 
						|
  prepare_for_a_scan (s);
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
sane_cancel (SANE_Handle h)
 | 
						|
{
 | 
						|
  static char me[] = "sane_cancel";
 | 
						|
  HP4200_Scanner *s = (HP4200_Scanner *) h;
 | 
						|
  DBG (DBG_proc, "%s\n", me);
 | 
						|
 | 
						|
  s->aborted_by_user = SANE_TRUE;
 | 
						|
 | 
						|
  end_scan (s);
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
 | 
						|
{
 | 
						|
  HP4200_Scanner *dev = handle;
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  non_blocking = non_blocking;	/* silence gcc */
 | 
						|
 | 
						|
  if (dev->scanning == SANE_FALSE)
 | 
						|
    {
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  if (non_blocking == SANE_FALSE)
 | 
						|
    {
 | 
						|
      status = SANE_STATUS_GOOD;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      status = SANE_STATUS_UNSUPPORTED;
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (DBG_proc, "sane_set_io_mode: exit\n");
 | 
						|
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_get_select_fd (SANE_Handle h, SANE_Int * fd)
 | 
						|
{
 | 
						|
  static char me[] = "sane_get_select_fd";
 | 
						|
 | 
						|
  h = h;			/* keep gcc quiet */
 | 
						|
  fd = fd;			/* keep gcc quiet */
 | 
						|
 | 
						|
  DBG (DBG_proc, "%s\n", me);
 | 
						|
  return SANE_STATUS_UNSUPPORTED;
 | 
						|
}
 |