kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			2253 wiersze
		
	
	
		
			57 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			2253 wiersze
		
	
	
		
			57 KiB
		
	
	
	
		
			C
		
	
	
/* sane - Scanner Access Now Easy.
 | 
						|
   Copyright (C) 1997 David Mosberger-Tang
 | 
						|
   This file is part of the SANE package.
 | 
						|
 | 
						|
   This program is free software; you can redistribute it and/or
 | 
						|
   modify it under the terms of the GNU General Public License as
 | 
						|
   published by the Free Software Foundation; either version 2 of the
 | 
						|
   License, or (at your option) any later version.
 | 
						|
 | 
						|
   This program is distributed in the hope that it will be useful, but
 | 
						|
   WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
   General Public License for more details.
 | 
						|
 | 
						|
   You should have received a copy of the GNU General Public License
 | 
						|
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
						|
 | 
						|
   As a special exception, the authors of SANE give permission for
 | 
						|
   additional uses of the libraries contained in this release of SANE.
 | 
						|
 | 
						|
   The exception is that, if you link a SANE library with other files
 | 
						|
   to produce an executable, this does not by itself cause the
 | 
						|
   resulting executable to be covered by the GNU General Public
 | 
						|
   License.  Your use of that executable is in no way restricted on
 | 
						|
   account of linking the SANE library code into it.
 | 
						|
 | 
						|
   This exception does not, however, invalidate any other reasons why
 | 
						|
   the executable file might be covered by the GNU General Public
 | 
						|
   License.
 | 
						|
 | 
						|
   If you submit changes to SANE to the maintainers to be included in
 | 
						|
   a subsequent release, you agree by submitting the changes that
 | 
						|
   those changes may be distributed with this exception intact.
 | 
						|
 | 
						|
   If you write modifications of your own for SANE, it is your choice
 | 
						|
   whether to permit this exception to apply to your modifications.
 | 
						|
   If you do not wish that, delete this exception notice.
 | 
						|
 | 
						|
   This file implements a SANE backend for the Connectix QuickCam.  At
 | 
						|
   present, only the color camera is supported though the driver
 | 
						|
   should be able to easily accommodate black and white cameras.
 | 
						|
 | 
						|
   Portions of this code are derived from Scott Laird's qcam driver.
 | 
						|
   It's copyright notice is reproduced here:
 | 
						|
 | 
						|
   Copyright (C) 1996 by Scott Laird
 | 
						|
 | 
						|
   Permission is hereby granted, free of charge, to any person
 | 
						|
   obtaining a copy of this software and associated documentation
 | 
						|
   files (the "Software"), to deal in the Software without
 | 
						|
   restriction, including without limitation the rights to use, copy,
 | 
						|
   modify, merge, publish, distribute, sublicense, and/or sell copies
 | 
						|
   of the Software, and to permit persons to whom the Software is
 | 
						|
   furnished to do so, subject to the following conditions:
 | 
						|
 | 
						|
   The above copyright notice and this permission notice shall be
 | 
						|
   included in all copies or substantial portions of the Software.
 | 
						|
 | 
						|
   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | 
						|
   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | 
						|
   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 | 
						|
   NONINFRINGEMENT.  IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY
 | 
						|
   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 | 
						|
   CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 | 
						|
   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
 | 
						|
 | 
						|
#ifdef _AIX
 | 
						|
# include "lalloca.h"		/* MUST come first for AIX! */
 | 
						|
#endif
 | 
						|
 | 
						|
#include "../include/sane/config.h"
 | 
						|
#include "lalloca.h"
 | 
						|
 | 
						|
#include <assert.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <limits.h>
 | 
						|
#include <math.h>
 | 
						|
#include <setjmp.h>
 | 
						|
#include <signal.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/wait.h>
 | 
						|
#include <unistd.h>
 | 
						|
 | 
						|
#include "../include/sane/sane.h"
 | 
						|
#include "../include/sane/sanei.h"
 | 
						|
#include "../include/sane/saneopts.h"
 | 
						|
 | 
						|
 | 
						|
#define BACKEND_NAME qcam
 | 
						|
#include "../include/sane/sanei_backend.h"
 | 
						|
 | 
						|
#ifndef PATH_MAX
 | 
						|
# define PATH_MAX	1024
 | 
						|
#endif
 | 
						|
 | 
						|
#include "../include/sane/sanei_config.h"
 | 
						|
#define QCAM_CONFIG_FILE "qcam.conf"
 | 
						|
 | 
						|
#include "qcam.h"
 | 
						|
 | 
						|
/* status bits */
 | 
						|
#define NeedRamTable		(1 << 1)
 | 
						|
#define BlackBalanceInProgress	(1 << 6)
 | 
						|
#define CameraNotReady		(1 << 7)
 | 
						|
 | 
						|
/* lpdata bits: */
 | 
						|
#define Cmd0_7		0xff
 | 
						|
#define CamRdy2		(   1 << 0)	/* byte mode */
 | 
						|
#define Data0_6		(0x7f << 1)	/* byte mode */
 | 
						|
 | 
						|
/* lpstatus bits: */
 | 
						|
#define CamRdy1		(   1 << 3)	/* nibble mode */
 | 
						|
#define Nibble0_3	(0x0f << 4)	/* nibble mode */
 | 
						|
#define Data7_11	(0x1f << 3)	/* byte mode */
 | 
						|
 | 
						|
/* lpcontrol bits: */
 | 
						|
#define Strobe		(   1 << 0)	/* unused */
 | 
						|
#define Autofeed	(   1 << 1)
 | 
						|
#define Reset_N		(   1 << 2)
 | 
						|
#define PCAck		(   1 << 3)
 | 
						|
#define BiDir		(   1 << 5)
 | 
						|
 | 
						|
static int num_devices;
 | 
						|
static QC_Device *first_dev;
 | 
						|
static QC_Scanner *first_handle;
 | 
						|
 | 
						|
static const SANE_String_Const resolution_list[] = {
 | 
						|
  "Low",			/* million-mode */
 | 
						|
  "High",			/* billion-mode */
 | 
						|
  0
 | 
						|
};
 | 
						|
 | 
						|
static const SANE_Int mono_depth_list[] = {
 | 
						|
  2,				/* # of elements */
 | 
						|
  4, 6
 | 
						|
};
 | 
						|
 | 
						|
static const SANE_Int color_depth_list[] = {
 | 
						|
  /*2 */ 1,
 | 
						|
  /* # of elements */
 | 
						|
  /*16, */ 24
 | 
						|
    /* "thousand" mode not implemented yet */
 | 
						|
};
 | 
						|
 | 
						|
static const SANE_Int xfer_scale_list[] = {
 | 
						|
  3,				/* # of elements */
 | 
						|
  1, 2, 4
 | 
						|
};
 | 
						|
 | 
						|
static const SANE_Range u8_range = {
 | 
						|
  /* min, max, quantization */
 | 
						|
  0, 255, 0
 | 
						|
};
 | 
						|
 | 
						|
static const SANE_Range brightness_range = {
 | 
						|
  /* min, max, quantization */
 | 
						|
  0, 254, 0			/* 255 is bulb mode! */
 | 
						|
};
 | 
						|
 | 
						|
static const SANE_Range x_range[] = {
 | 
						|
  /* min, max, quantization */
 | 
						|
  {0, 338, 2},			/* million mode */
 | 
						|
  {0, 676, 4},			/* billion mode */
 | 
						|
};
 | 
						|
 | 
						|
static const SANE_Range odd_x_range[] = {
 | 
						|
  /* min, max, quantization */
 | 
						|
  {1, 339, 2},			/* million mode */
 | 
						|
  {3, 683, 4},			/* billion mode */
 | 
						|
};
 | 
						|
 | 
						|
static const SANE_Range y_range[] = {
 | 
						|
  /* min, max, quantization */
 | 
						|
  {0, 249, 1},			/* million mode */
 | 
						|
  {0, 498, 2},			/* billion mode */
 | 
						|
};
 | 
						|
 | 
						|
static const SANE_Range odd_y_range[] = {
 | 
						|
  /* min, max, quantization */
 | 
						|
  {0, 249, 1},			/* million mode */
 | 
						|
  {1, 499, 2},			/* billion mode */
 | 
						|
};
 | 
						|
 | 
						|
static const SANE_Range bw_x_range = { 0, 334, 2 };
 | 
						|
static const SANE_Range odd_bw_x_range = { 1, 335, 2 };
 | 
						|
static const SANE_Range bw_y_range = { 0, 241, 1 };
 | 
						|
static const SANE_Range odd_bw_y_range = { 1, 242, 1 };
 | 
						|
 | 
						|
#include "../include/sane/sanei_directio.h"
 | 
						|
 | 
						|
#define read_lpdata(d)		sanei_inb ((d)->port)
 | 
						|
#define read_lpstatus(d)	sanei_inb ((d)->port + 1)
 | 
						|
#define read_lpcontrol(d)	sanei_inb ((d)->port + 2)
 | 
						|
#define write_lpdata(d,v)	sanei_outb ((d)->port, (v))
 | 
						|
#define write_lpcontrol(d,v)	sanei_outb ((d)->port + 2, (v))
 | 
						|
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
enable_ports (QC_Device * q)
 | 
						|
{
 | 
						|
  /* better safe than sorry */
 | 
						|
  if (q->port < 0x278 || q->port > 0x3bc)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  if (sanei_ioperm (q->port, 3, 1) < 0)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
disable_ports (QC_Device * q)
 | 
						|
{
 | 
						|
  if (sanei_ioperm (q->port, 3, 0) < 0)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
/* We need a short delay loop -- something well under a millisecond.
 | 
						|
   Unfortunately, adding 2 usleep(1)'s to qc_command slowed it down by
 | 
						|
   a factor of over 1000 over the same loop with 2 usleep(0)'s, and
 | 
						|
   that's too slow -- qc_start was taking over a second to run.  This
 | 
						|
   seems to help, but if anyone has a good speed-independent pause
 | 
						|
   routine, please tell me. -- Scott
 | 
						|
 | 
						|
   If you're worried about hogging the CPU: don't worry, the qcam
 | 
						|
   interface leaves you no choice, so this doesn't make the situation
 | 
						|
   any worse... */
 | 
						|
 | 
						|
static int
 | 
						|
qc_wait (QC_Device * q)
 | 
						|
{
 | 
						|
  return read_lpstatus (q);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* This function uses POSIX fcntl-style locking on a file created in
 | 
						|
   the /tmp directory.  Because it uses the Unix record locking
 | 
						|
   facility, locks are relinquished automatically on process
 | 
						|
   termination, so "dead locks" are not a problem.  (FYI, the lock
 | 
						|
   file will remain after process termination, but this is actually
 | 
						|
   desired so that the next process need not re-creat(2)e it... just
 | 
						|
   lock it.)  The wait argument indicates whether or not this function
 | 
						|
   should "block" waiting for the previous lock to be relinquished.
 | 
						|
   This is ideal so that multiple processes (eg. qcam) taking
 | 
						|
   "snapshots" can peacefully coexist.
 | 
						|
 | 
						|
   -- Dave Plonka (plonka@carroll1.cc.edu) */
 | 
						|
static SANE_Status
 | 
						|
qc_lock_wait (QC_Device * q, int wait)
 | 
						|
{
 | 
						|
#ifdef F_SETLK
 | 
						|
 | 
						|
#ifndef HAVE_STRUCT_FLOCK
 | 
						|
  struct flock
 | 
						|
  {
 | 
						|
    off_t l_start;
 | 
						|
    off_t l_len;
 | 
						|
    pid_t l_pid;
 | 
						|
    short l_type;
 | 
						|
    short l_whence;
 | 
						|
  };
 | 
						|
#endif /* !HAVE_STRUCT_FLOCK */
 | 
						|
  struct flock sfl;
 | 
						|
#endif
 | 
						|
 | 
						|
  DBG (3, "qc_lock_wait: acquiring lock for 0x%x\n", q->port);
 | 
						|
 | 
						|
#ifdef F_SETLK
 | 
						|
  memset (&sfl, 0, sizeof (sfl));
 | 
						|
#endif
 | 
						|
 | 
						|
  if (q->lock_fd < 0)
 | 
						|
    {
 | 
						|
      char lockfile[128];
 | 
						|
 | 
						|
      sprintf (lockfile, "/tmp/LOCK.qcam.0x%x", q->port);
 | 
						|
      q->lock_fd = open (lockfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
 | 
						|
      if (q->lock_fd < 0)
 | 
						|
	{
 | 
						|
	  DBG (1, "qc_lock_wait: failed to open %s (%s)\n",
 | 
						|
	       lockfile, strerror (errno));
 | 
						|
	  return SANE_STATUS_INVAL;
 | 
						|
	}
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
#ifdef F_SETLK
 | 
						|
  sfl.l_type = F_WRLCK;
 | 
						|
  if (fcntl (q->lock_fd, wait ? F_SETLKW : F_SETLK, &sfl) != 0)
 | 
						|
    {
 | 
						|
      DBG (1, "qc_lock_wait: failed to acquire lock (%s)\n",
 | 
						|
	   strerror (errno));
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
  DBG (3, "qc_lock_wait: got lock for 0x%x\n", q->port);
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
qc_unlock (QC_Device * q)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  char lockfile[128];
 | 
						|
#ifdef F_SETLK
 | 
						|
#ifndef HAVE_STRUCT_FLOCK
 | 
						|
  struct flock
 | 
						|
  {
 | 
						|
    off_t l_start;
 | 
						|
    off_t l_len;
 | 
						|
    pid_t l_pid;
 | 
						|
    short l_type;
 | 
						|
    short l_whence;
 | 
						|
  };
 | 
						|
#endif /* !HAVE_STRUCT_FLOCK */
 | 
						|
  struct flock sfl;
 | 
						|
#endif
 | 
						|
 | 
						|
  DBG (3, "qc_unlock: releasing lock for 0x%x\n", q->port);
 | 
						|
 | 
						|
#ifdef F_SETLK
 | 
						|
  memset (&sfl, 0, sizeof (sfl));
 | 
						|
#endif
 | 
						|
  if (q->lock_fd < 0)
 | 
						|
    {
 | 
						|
      DBG (3, "qc_unlock; port was not locked\n");
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
  /* clear the exclusive lock */
 | 
						|
 | 
						|
#ifdef F_SETLK
 | 
						|
  sfl.l_type = F_UNLCK;
 | 
						|
 | 
						|
  if (fcntl (q->lock_fd, F_SETLK, &sfl) != 0)
 | 
						|
    {
 | 
						|
      DBG (3, "qc_unlock: failed to release lock (%s)\n", strerror (errno));
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
#endif
 | 
						|
  sprintf (lockfile, "/tmp/LOCK.qcam.0x%x", q->port);
 | 
						|
  DBG (1, "qc_unlock: /tmp/LOCK.qcam.0x%x\n", q->port);
 | 
						|
  unlink (lockfile);
 | 
						|
  close (q->lock_fd);
 | 
						|
  q->lock_fd = -1;
 | 
						|
  DBG (1, "qc_unlock: exit\n");
 | 
						|
  status = SANE_STATUS_GOOD;
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
qc_lock (QC_Device * q)
 | 
						|
{
 | 
						|
  return qc_lock_wait (q, 1);
 | 
						|
}
 | 
						|
 | 
						|
/* Busy-waits for a handshake signal from the QuickCam.  Almost all
 | 
						|
   communication with the camera requires handshaking.  This is why
 | 
						|
   qcam is a CPU hog.  */
 | 
						|
static int
 | 
						|
qc_waithand (QC_Device * q, int val)
 | 
						|
{
 | 
						|
  int status;
 | 
						|
 | 
						|
  while (((status = read_lpstatus (q)) & CamRdy1) != val);
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
/* This is used when the qcam is in bidirectional ("byte") mode, and
 | 
						|
   the handshaking signal is CamRdy2 (bit 0 of data reg) instead of
 | 
						|
   CamRdy1 (bit 3 of status register).  It also returns the last value
 | 
						|
   read, since this data is useful.  */
 | 
						|
static unsigned int
 | 
						|
qc_waithand2 (QC_Device * q, int val)
 | 
						|
{
 | 
						|
  unsigned int status;
 | 
						|
 | 
						|
  do
 | 
						|
    {
 | 
						|
      status = read_lpdata (q);
 | 
						|
    }
 | 
						|
  while ((status & CamRdy2) != (unsigned int) val);
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
static unsigned int
 | 
						|
qc_send (QC_Device * q, unsigned int byte)
 | 
						|
{
 | 
						|
  unsigned int echo;
 | 
						|
  int n1, n2;
 | 
						|
 | 
						|
  write_lpdata (q, byte);
 | 
						|
  qc_wait (q);
 | 
						|
  write_lpcontrol (q, Autofeed | Reset_N);
 | 
						|
  qc_wait (q);
 | 
						|
 | 
						|
  n1 = qc_waithand (q, CamRdy1);
 | 
						|
 | 
						|
  write_lpcontrol (q, Autofeed | Reset_N | PCAck);
 | 
						|
  qc_wait (q);
 | 
						|
  n2 = qc_waithand (q, 0);
 | 
						|
 | 
						|
  echo = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
 | 
						|
#ifndef NDEBUG
 | 
						|
  if (echo != byte)
 | 
						|
    {
 | 
						|
      DBG (1, "qc_send: sent 0x%02x, camera echoed 0x%02x\n", byte, echo);
 | 
						|
      n2 = read_lpstatus (q);
 | 
						|
      echo = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
 | 
						|
      if (echo != byte)
 | 
						|
	DBG (1, "qc_send: (re-read does not help)\n");
 | 
						|
      else
 | 
						|
	DBG (1, "qc_send: (fixed on re-read)\n");
 | 
						|
    }
 | 
						|
#endif
 | 
						|
  return echo;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
qc_readparam (QC_Device * q)
 | 
						|
{
 | 
						|
  int n1, n2;
 | 
						|
  int cmd;
 | 
						|
 | 
						|
  write_lpcontrol (q, Autofeed | Reset_N);	/* clear PCAck */
 | 
						|
  n1 = qc_waithand (q, CamRdy1);
 | 
						|
 | 
						|
  write_lpcontrol (q, Autofeed | Reset_N | PCAck);	/* set PCAck */
 | 
						|
  n2 = qc_waithand (q, 0);
 | 
						|
 | 
						|
  cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
 | 
						|
  return cmd;
 | 
						|
}
 | 
						|
 | 
						|
static unsigned int
 | 
						|
qc_getstatus (QC_Device * q)
 | 
						|
{
 | 
						|
  unsigned int status;
 | 
						|
 | 
						|
  qc_send (q, QC_SEND_STATUS);
 | 
						|
  status = qc_readparam (q);
 | 
						|
  DBG (3, "qc_getstatus: status=0x%02x\n", status);
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
qc_setscanmode (QC_Scanner * s, u_int * modep)
 | 
						|
{
 | 
						|
  QC_Device *q = s->hw;
 | 
						|
  u_int mode = 0;
 | 
						|
 | 
						|
  if (q->version != QC_COLOR)
 | 
						|
    {
 | 
						|
      switch (s->val[OPT_XFER_SCALE].w)
 | 
						|
	{
 | 
						|
	case 1:
 | 
						|
	  mode = 0;
 | 
						|
	  break;
 | 
						|
	case 2:
 | 
						|
	  mode = 4;
 | 
						|
	  break;
 | 
						|
	case 4:
 | 
						|
	  mode = 8;
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
      switch (s->val[OPT_DEPTH].w)
 | 
						|
	{
 | 
						|
	case 4:
 | 
						|
	  break;
 | 
						|
	case 6:
 | 
						|
	  mode += 2;
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      switch (s->val[OPT_XFER_SCALE].w)
 | 
						|
	{
 | 
						|
	case 1:
 | 
						|
	  mode = 0;
 | 
						|
	  break;
 | 
						|
	case 2:
 | 
						|
	  mode = 2;
 | 
						|
	  break;
 | 
						|
	case 4:
 | 
						|
	  mode = 4;
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
      if (s->resolution == QC_RES_LOW)
 | 
						|
	mode |= 0x18;		/* millions mode */
 | 
						|
      else
 | 
						|
	mode |= 0x10;		/* billions mode */
 | 
						|
    }
 | 
						|
  if (s->val[OPT_TEST].w)
 | 
						|
    mode |= 0x40;		/* test mode */
 | 
						|
 | 
						|
  if (q->port_mode == QC_BIDIR)
 | 
						|
    mode |= 1;
 | 
						|
 | 
						|
  DBG (2, "scanmode (before increment): 0x%x\n", mode);
 | 
						|
 | 
						|
  if (q->version == QC_COLOR)
 | 
						|
    ++mode;
 | 
						|
 | 
						|
  *modep = mode;
 | 
						|
}
 | 
						|
 | 
						|
/* Read data bytes from the camera.  The number of bytes read is
 | 
						|
   returned as the function result.  Depending on the mode, it may be
 | 
						|
   either 1, 3 or 6.  On failure, 0 is returned.  If buffer is 0, the
 | 
						|
   internal state-machine is reset.  */
 | 
						|
static size_t
 | 
						|
qc_readbytes (QC_Scanner * s, unsigned char buffer[])
 | 
						|
{
 | 
						|
  QC_Device *q = s->hw;
 | 
						|
  unsigned int hi, lo;
 | 
						|
  unsigned int hi2, lo2;
 | 
						|
  size_t bytes = 0;
 | 
						|
 | 
						|
  if (!buffer)
 | 
						|
    {
 | 
						|
      s->readbytes_state = 0;
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
  switch (q->port_mode)
 | 
						|
    {
 | 
						|
    case QC_BIDIR:
 | 
						|
      /* bi-directional port */
 | 
						|
 | 
						|
      /* read off 24 bits: */
 | 
						|
      write_lpcontrol (q, Autofeed | Reset_N | BiDir);
 | 
						|
      lo = qc_waithand2 (q, 1) >> 1;
 | 
						|
      hi = (read_lpstatus (q) >> 3) & 0x1f;
 | 
						|
      write_lpcontrol (q, Autofeed | Reset_N | PCAck | BiDir);
 | 
						|
      lo2 = qc_waithand2 (q, 0) >> 1;
 | 
						|
      hi2 = (read_lpstatus (q) >> 3) & 0x1f;
 | 
						|
      if (q->version == QC_COLOR)
 | 
						|
	{
 | 
						|
	  /* is Nibble3 inverted for color quickcams only? */
 | 
						|
	  hi ^= 0x10;
 | 
						|
	  hi2 ^= 0x10;
 | 
						|
	}
 | 
						|
      switch (s->val[OPT_DEPTH].w)
 | 
						|
	{
 | 
						|
	case 4:
 | 
						|
	  buffer[0] = lo & 0xf;
 | 
						|
	  buffer[1] = ((lo & 0x70) >> 4) | ((hi & 1) << 3);
 | 
						|
	  buffer[2] = (hi & 0x1e) >> 1;
 | 
						|
	  buffer[3] = lo2 & 0xf;
 | 
						|
	  buffer[4] = ((lo2 & 0x70) >> 4) | ((hi2 & 1) << 3);
 | 
						|
	  buffer[5] = (hi2 & 0x1e) >> 1;
 | 
						|
	  bytes = 6;
 | 
						|
	  break;
 | 
						|
 | 
						|
	case 6:
 | 
						|
	  buffer[0] = lo & 0x3f;
 | 
						|
	  buffer[1] = ((lo & 0x40) >> 6) | (hi << 1);
 | 
						|
	  buffer[2] = lo2 & 0x3f;
 | 
						|
	  buffer[3] = ((lo2 & 0x40) >> 6) | (hi2 << 1);
 | 
						|
	  bytes = 4;
 | 
						|
	  break;
 | 
						|
 | 
						|
	case 24:
 | 
						|
	  buffer[0] = lo | ((hi & 0x1) << 7);
 | 
						|
	  buffer[1] = ((hi2 & 0x1e) >> 1) | ((hi & 0x1e) << 3);
 | 
						|
	  buffer[2] = lo2 | ((hi2 & 0x1) << 7);
 | 
						|
	  bytes = 3;
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
      break;
 | 
						|
 | 
						|
    case QC_UNIDIR:		/* Unidirectional Port */
 | 
						|
      write_lpcontrol (q, Autofeed | Reset_N);
 | 
						|
      lo = (qc_waithand (q, CamRdy1) & 0xf0) >> 4;
 | 
						|
      write_lpcontrol (q, Autofeed | Reset_N | PCAck);
 | 
						|
      hi = (qc_waithand (q, 0) & 0xf0) >> 4;
 | 
						|
 | 
						|
      if (q->version == QC_COLOR)
 | 
						|
	{
 | 
						|
	  /* invert Nibble3 */
 | 
						|
	  hi ^= 8;
 | 
						|
	  lo ^= 8;
 | 
						|
	}
 | 
						|
 | 
						|
      switch (s->val[OPT_DEPTH].w)
 | 
						|
	{
 | 
						|
	case 4:
 | 
						|
	  buffer[0] = lo;
 | 
						|
	  buffer[1] = hi;
 | 
						|
	  bytes = 2;
 | 
						|
	  break;
 | 
						|
 | 
						|
	case 6:
 | 
						|
	  switch (s->readbytes_state)
 | 
						|
	    {
 | 
						|
	    case 0:
 | 
						|
	      buffer[0] = (lo << 2) | ((hi & 0xc) >> 2);
 | 
						|
	      s->saved_bits = (hi & 3) << 4;
 | 
						|
	      s->readbytes_state = 1;
 | 
						|
	      bytes = 1;
 | 
						|
	      break;
 | 
						|
 | 
						|
	    case 1:
 | 
						|
	      buffer[0] = lo | s->saved_bits;
 | 
						|
	      s->saved_bits = hi << 2;
 | 
						|
	      s->readbytes_state = 2;
 | 
						|
	      bytes = 1;
 | 
						|
	      break;
 | 
						|
 | 
						|
	    case 2:
 | 
						|
	      buffer[0] = ((lo & 0xc) >> 2) | s->saved_bits;
 | 
						|
	      buffer[1] = ((lo & 3) << 4) | hi;
 | 
						|
	      s->readbytes_state = 0;
 | 
						|
	      bytes = 2;
 | 
						|
	      break;
 | 
						|
 | 
						|
	    default:
 | 
						|
	      DBG (1, "qc_readbytes: bad unidir 6-bit stat %d\n",
 | 
						|
		   s->readbytes_state);
 | 
						|
	      break;
 | 
						|
	    }
 | 
						|
	  break;
 | 
						|
 | 
						|
	case 24:
 | 
						|
	  buffer[0] = (lo << 4) | hi;
 | 
						|
	  bytes = 1;
 | 
						|
	  break;
 | 
						|
 | 
						|
	default:
 | 
						|
	  DBG (1, "qc_readbytes: bad unidir bit depth %d\n",
 | 
						|
	       s->val[OPT_DEPTH].w);
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      DBG (1, "qc_readbytes: bad port_mode %d\n", q->port_mode);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  return bytes;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
qc_reset (QC_Device * q)
 | 
						|
{
 | 
						|
  write_lpcontrol (q, Strobe | Autofeed | Reset_N | PCAck);
 | 
						|
  qc_wait (q);
 | 
						|
  write_lpcontrol (q, Strobe | Autofeed | PCAck);
 | 
						|
  qc_wait (q);
 | 
						|
  write_lpcontrol (q, Strobe | Autofeed | Reset_N | PCAck);
 | 
						|
}
 | 
						|
 | 
						|
/* This function is executed as a child process.  The reason this is
 | 
						|
   executed as a subprocess is because the qcam interface directly reads
 | 
						|
   off of a I/O port (rather than a filedescriptor).  Thus, to have
 | 
						|
   something to select() on, we transfer the data through a pipe.
 | 
						|
 | 
						|
   WARNING: Since this is executed as a subprocess, it's NOT possible
 | 
						|
   to update any of the variables in the main process (in particular
 | 
						|
   the scanner state cannot be updated).  */
 | 
						|
 | 
						|
static jmp_buf env;
 | 
						|
 | 
						|
static void
 | 
						|
sighandler (int signal)
 | 
						|
{
 | 
						|
  DBG (3, "sighandler: got signal %d\n", signal);
 | 
						|
  longjmp (env, 1);
 | 
						|
}
 | 
						|
 | 
						|
/* Original despeckling code by Patrick Reynolds <patrickr@virginia.edu> */
 | 
						|
 | 
						|
static void
 | 
						|
despeckle (int width, int height, SANE_Byte * in, SANE_Byte * out)
 | 
						|
{
 | 
						|
  long x, i;
 | 
						|
  /* The light-check threshold.  Higher numbers remove more lights but
 | 
						|
     blur the image more.  30 is good for indoor lighting.  */
 | 
						|
# define NO_LIGHTS 30
 | 
						|
 | 
						|
  /* macros to make the code a little more readable, p=previous, n=next */
 | 
						|
# define R	in[i*3]
 | 
						|
# define G	in[i*3+1]
 | 
						|
# define B	in[i*3+2]
 | 
						|
# define pR	in[i*3-3]
 | 
						|
# define pG	in[i*3-2]
 | 
						|
# define pB	in[i*3-1]
 | 
						|
# define nR	in[i*3+3]
 | 
						|
# define nG	in[i*3+4]
 | 
						|
# define nB	in[i*3+5]
 | 
						|
 | 
						|
  DBG (1, "despeckle: width=%d, height=%d\n", width, height);
 | 
						|
 | 
						|
  for (x = i = 0; i < width * height; ++i)
 | 
						|
    {
 | 
						|
      if (x == 0 || x == width - 1)
 | 
						|
	memcpy (&out[i * 3], &in[i * 3], 3);
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  if (R - (G + B) / 2 >
 | 
						|
	      NO_LIGHTS + ((pR - (pG + pB) / 2) + (nR - (nG + nB) / 2)))
 | 
						|
	    out[i * 3] = (pR + nR) / 2;
 | 
						|
	  else
 | 
						|
	    out[i * 3] = R;
 | 
						|
 | 
						|
	  if (G - (R + B) / 2 >
 | 
						|
	      NO_LIGHTS + ((pG - (pR + pB) / 2) + (nG - (nR + nB) / 2)))
 | 
						|
	    out[i * 3 + 1] = (pG + nG) / 2;
 | 
						|
	  else
 | 
						|
	    out[i * 3 + 1] = G;
 | 
						|
 | 
						|
	  if (B - (G + R) / 2 >
 | 
						|
	      NO_LIGHTS + ((pB - (pG + pR) / 2) + (nB - (nG + nR) / 2)))
 | 
						|
	    out[i * 3 + 2] = (pB + nB) / 2;
 | 
						|
	  else
 | 
						|
	    out[i * 3 + 2] = B;
 | 
						|
	}
 | 
						|
      if (++x >= width)
 | 
						|
	x = 0;
 | 
						|
    }
 | 
						|
# undef R
 | 
						|
# undef G
 | 
						|
# undef B
 | 
						|
# undef pR
 | 
						|
# undef pG
 | 
						|
# undef pB
 | 
						|
# undef nR
 | 
						|
# undef nG
 | 
						|
# undef nB
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
despeckle32 (int width, int height, SANE_Byte * in, SANE_Byte * out)
 | 
						|
{
 | 
						|
  long x, i;
 | 
						|
  /* macros to make the code a little more readable, p=previous, n=next */
 | 
						|
# define B	in[i*4]
 | 
						|
# define Ga	in[i*4 + 1]
 | 
						|
# define Gb	in[i*4 + 1]	/* ignore Gb and use Ga instead---Gb is weird */
 | 
						|
# define R	in[i*4 + 3]
 | 
						|
# define pB	in[i*4 - 4]
 | 
						|
# define pGa	in[i*4 - 3]
 | 
						|
# define pGb	in[i*4 - 1]	/* ignore Gb and use Ga instead---Gb is weird */
 | 
						|
# define pR	in[i*4 - 1]
 | 
						|
# define nB	in[i*4 + 4]
 | 
						|
# define nGa	in[i*4 + 5]
 | 
						|
# define nGb	in[i*4 + 5]	/* ignore Gb and use Ga instead---Gb is weird */
 | 
						|
# define nR	in[i*4 + 7]
 | 
						|
 | 
						|
  DBG (1, "despeckle32: width=%d, height=%d\n", width, height);
 | 
						|
 | 
						|
  for (x = i = 0; i < width * height; ++i)
 | 
						|
    {
 | 
						|
      if (x == 0 || x == width - 1)
 | 
						|
	memcpy (&out[i * 4], &in[i * 4], 4);
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  if (x >= width - 2)
 | 
						|
	    /* the last red pixel seems to be black at all times, use
 | 
						|
	       R instead: */
 | 
						|
	    nR = R;
 | 
						|
 | 
						|
	  if (R - ((Ga + Gb) / 2 + B) / 2 >
 | 
						|
	      NO_LIGHTS + ((pR - ((pGa + pGb) / 2 + pB) / 2) +
 | 
						|
			   (nR - ((nGa + nGb) / 2 + nB) / 2)))
 | 
						|
	    out[i * 4 + 3] = (pR + nR) / 2;
 | 
						|
	  else
 | 
						|
	    out[i * 4 + 3] = R;
 | 
						|
 | 
						|
	  if (Ga - (R + B) / 2 > NO_LIGHTS + ((pGa - (pR + pB) / 2) +
 | 
						|
					      (nGa - (nR + nB) / 2)))
 | 
						|
	    out[i * 4 + 1] = (pGa + nGa) / 2;
 | 
						|
	  else
 | 
						|
	    out[i * 4 + 1] = Ga;
 | 
						|
 | 
						|
	  if (Gb - (R + B) / 2 > NO_LIGHTS + ((pGb - (pR + pB) / 2) +
 | 
						|
					      (nGb - (nR + nB) / 2)))
 | 
						|
	    out[i * 4 + 2] = (pGb + nGb) / 2;
 | 
						|
	  else
 | 
						|
	    out[i * 4 + 2] = Gb;
 | 
						|
 | 
						|
	  if (B - ((Ga + Gb) / 2 + R) / 2 >
 | 
						|
	      NO_LIGHTS + ((pB - ((pGa + pGb) / 2 + pR) / 2) +
 | 
						|
			   (nB - ((nGa + nGb) / 2 + nR) / 2)))
 | 
						|
	    out[i * 4 + 0] = (pB + nB) / 2;
 | 
						|
	  else
 | 
						|
	    out[i * 4 + 0] = B;
 | 
						|
	}
 | 
						|
      if (++x >= width)
 | 
						|
	x = 0;
 | 
						|
    }
 | 
						|
# undef R
 | 
						|
# undef Ga
 | 
						|
# undef Gb
 | 
						|
# undef B
 | 
						|
# undef pR
 | 
						|
# undef pGa
 | 
						|
# undef pGb
 | 
						|
# undef pB
 | 
						|
# undef nR
 | 
						|
# undef nGa
 | 
						|
# undef nGb
 | 
						|
# undef nB
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
reader_process (QC_Scanner * s, int in_fd, int out_fd)
 | 
						|
{
 | 
						|
  static SANE_Byte *buffer = 0, *extra = 0;
 | 
						|
  static size_t buffer_size = 0;
 | 
						|
  size_t count, len, num_bytes;
 | 
						|
  QC_Device *q = s->hw;
 | 
						|
  QC_Scan_Request req;
 | 
						|
  int width, height;
 | 
						|
  SANE_Byte *src;
 | 
						|
  FILE *ofp;
 | 
						|
 | 
						|
  DBG (5, "reader_process: enter\n");
 | 
						|
 | 
						|
  enable_ports (q);
 | 
						|
 | 
						|
  ofp = fdopen (out_fd, "w");
 | 
						|
  if (!ofp)
 | 
						|
    return 1;
 | 
						|
 | 
						|
  while (1)
 | 
						|
    {
 | 
						|
      if (setjmp (env))
 | 
						|
	{
 | 
						|
	  char ch;
 | 
						|
 | 
						|
	  /* acknowledge the signal: */
 | 
						|
	  DBG (1, "reader_process: sending signal ACK\n");
 | 
						|
	  fwrite (&ch, 1, 1, ofp);
 | 
						|
	  fflush (ofp);		/* force everything out the pipe */
 | 
						|
	  continue;
 | 
						|
	}
 | 
						|
      signal (SIGINT, sighandler);
 | 
						|
 | 
						|
      /* the main process gets us started by writing a size_t giving
 | 
						|
         the number of bytes we should expect: */
 | 
						|
      if (read (in_fd, &req, sizeof (req)) != sizeof (req))
 | 
						|
	{
 | 
						|
	  perror ("read");
 | 
						|
	  return 1;
 | 
						|
	}
 | 
						|
      num_bytes = req.num_bytes;
 | 
						|
 | 
						|
      DBG (3, "reader_process: got request for %lu bytes\n",
 | 
						|
	   (u_long) num_bytes);
 | 
						|
 | 
						|
      /* Don't do this in sane_start() since there may be a long
 | 
						|
         timespan between it and the first sane_read(), which would
 | 
						|
         result in poor images.  */
 | 
						|
      qc_send (q, QC_SEND_VIDEO_FRAME);
 | 
						|
      qc_send (q, req.mode);
 | 
						|
 | 
						|
      if (req.despeckle
 | 
						|
	  && (!extra || buffer_size < num_bytes
 | 
						|
	      || buffer_size >= 2 * num_bytes))
 | 
						|
	{
 | 
						|
	  if (extra)
 | 
						|
	    extra = realloc (extra, num_bytes);
 | 
						|
	  else
 | 
						|
	    extra = malloc (num_bytes);
 | 
						|
	  if (!extra)
 | 
						|
	    {
 | 
						|
	      DBG (1, "reader_process: malloc(%ld) failed\n",
 | 
						|
		   (long) num_bytes);
 | 
						|
	      exit (1);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
      if (buffer_size < num_bytes || buffer_size >= 2 * num_bytes)
 | 
						|
	{
 | 
						|
	  if (buffer)
 | 
						|
	    buffer = realloc (buffer, num_bytes);
 | 
						|
	  else
 | 
						|
	    buffer = malloc (num_bytes);
 | 
						|
	  if (!buffer)
 | 
						|
	    {
 | 
						|
	      DBG (1, "reader_process: malloc(%ld) failed\n",
 | 
						|
		   (long) num_bytes);
 | 
						|
	      exit (1);
 | 
						|
	    }
 | 
						|
	  buffer_size = num_bytes;
 | 
						|
	}
 | 
						|
 | 
						|
      if (q->port_mode == QC_BIDIR)
 | 
						|
	{
 | 
						|
	  /* turn port into input port */
 | 
						|
	  write_lpcontrol (q, Autofeed | Reset_N | PCAck | BiDir);
 | 
						|
	  usleep (3);
 | 
						|
	  write_lpcontrol (q, Autofeed | Reset_N | BiDir);
 | 
						|
	  qc_waithand (q, CamRdy1);
 | 
						|
	  write_lpcontrol (q, Autofeed | Reset_N | PCAck | BiDir);
 | 
						|
	  qc_waithand (q, 0);
 | 
						|
	}
 | 
						|
 | 
						|
      if (q->version == QC_COLOR)
 | 
						|
	for (len = 0; len < num_bytes; len += count)
 | 
						|
	  count = qc_readbytes (s, buffer + len);
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  /* strange -- should be 15:63 below, but 4bpp is odd */
 | 
						|
	  int shift, invert;
 | 
						|
	  unsigned int i;
 | 
						|
	  u_char val;
 | 
						|
 | 
						|
	  switch (s->val[OPT_DEPTH].w)
 | 
						|
	    {
 | 
						|
	    case 4:
 | 
						|
	      invert = 16;
 | 
						|
	      shift = 4;
 | 
						|
	      break;
 | 
						|
 | 
						|
	    case 6:
 | 
						|
	      invert = 63;
 | 
						|
	      shift = 2;
 | 
						|
	      break;
 | 
						|
 | 
						|
	    default:
 | 
						|
	      DBG (1, "reader_process: unexpected depth %d\n",
 | 
						|
		   s->val[OPT_DEPTH].w);
 | 
						|
	      return 1;
 | 
						|
	    }
 | 
						|
 | 
						|
	  for (len = 0; len < num_bytes; len += count)
 | 
						|
	    {
 | 
						|
	      count = qc_readbytes (s, buffer + len);
 | 
						|
	      for (i = 0; i < count; ++i)
 | 
						|
		{
 | 
						|
		  /* 4bpp is odd (again) -- inverter is 16, not 15,
 | 
						|
		     but output must be 0-15 */
 | 
						|
		  val = buffer[len + i];
 | 
						|
		  if (val > 0 || invert != 16)
 | 
						|
		    val = invert - val;
 | 
						|
		  buffer[len + i] = (val << shift) | (val >> (8 - 2 * shift));
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	  qc_readbytes (s, 0);	/* reset state machine */
 | 
						|
	}
 | 
						|
      /* we're done reading this frame: */
 | 
						|
      DBG (2, "reader_process: frame complete\n");
 | 
						|
 | 
						|
      if (q->port_mode == QC_BIDIR)
 | 
						|
	{
 | 
						|
	  /* return port to output mode */
 | 
						|
	  write_lpcontrol (q, Autofeed);
 | 
						|
	  usleep (3);
 | 
						|
	  write_lpcontrol (q, Autofeed | Reset_N);
 | 
						|
	  usleep (3);
 | 
						|
	  write_lpcontrol (q, Autofeed | Reset_N | PCAck);
 | 
						|
	}
 | 
						|
 | 
						|
      if (req.resolution == QC_RES_HIGH)
 | 
						|
	{
 | 
						|
	  SANE_Byte buf[6];
 | 
						|
	  int x, y;
 | 
						|
 | 
						|
	  /* in billions mode, we need to oversample the data: */
 | 
						|
	  src = buffer;
 | 
						|
	  width = req.params.pixels_per_line;
 | 
						|
	  height = req.params.lines;
 | 
						|
 | 
						|
	  if (req.despeckle)
 | 
						|
	    {
 | 
						|
	      despeckle32 (width / 2, req.params.lines / 2, buffer, extra);
 | 
						|
	      src = extra;
 | 
						|
	    }
 | 
						|
 | 
						|
	  assert (!(width & 1));	/* width must be even */
 | 
						|
 | 
						|
	  for (y = 0; y < height; ++y)
 | 
						|
	    {
 | 
						|
	      /* even line */
 | 
						|
 | 
						|
	      for (x = 0; x < width; x += 2)
 | 
						|
		{
 | 
						|
		  int red1, green1, blue1, green2, blue2;
 | 
						|
 | 
						|
		  blue1 = src[0];
 | 
						|
		  green1 = src[1];
 | 
						|
		  red1 = src[3];
 | 
						|
		  if (x >= width - 2)
 | 
						|
		    {
 | 
						|
		      red1 = src[-1];	/* last red seems to be missing */
 | 
						|
		      blue2 = blue1;
 | 
						|
		      green2 = green1;
 | 
						|
		    }
 | 
						|
		  else
 | 
						|
		    {
 | 
						|
		      blue2 = src[4];
 | 
						|
		      green2 = src[5];
 | 
						|
		    }
 | 
						|
		  src += 4;
 | 
						|
 | 
						|
		  buf[0] = red1;
 | 
						|
		  buf[1] = green1;
 | 
						|
		  buf[2] = blue1;
 | 
						|
		  buf[3] = red1;
 | 
						|
		  buf[4] = green2;
 | 
						|
		  buf[5] = blue2;
 | 
						|
		  if (fwrite (buf, 1, 6, ofp) != 6)
 | 
						|
		    {
 | 
						|
		      perror ("fwrite: short write");
 | 
						|
		      return 1;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
	      if (++y >= height)
 | 
						|
		break;
 | 
						|
 | 
						|
	      src -= 2 * width;	/* 4 bytes/pixel -> 2 pixels of 3 bytes each */
 | 
						|
 | 
						|
	      /* odd line */
 | 
						|
	      for (x = 0; x < width; x += 2)
 | 
						|
		{
 | 
						|
		  int red1, green3, blue3, green4, blue4;
 | 
						|
		  int yoff;
 | 
						|
 | 
						|
		  if (x >= width - 2)
 | 
						|
		    red1 = src[-1];	/* last red seems to be missing */
 | 
						|
		  else
 | 
						|
		    red1 = src[3];
 | 
						|
		  yoff = 2 * width;
 | 
						|
		  if (y >= height - 1)
 | 
						|
		    yoff = 0;
 | 
						|
		  green3 = src[yoff + 1];
 | 
						|
		  blue3 = src[yoff + 0];
 | 
						|
		  if (x >= width - 2)
 | 
						|
		    {
 | 
						|
		      blue4 = blue3;
 | 
						|
		      green4 = green3;
 | 
						|
		    }
 | 
						|
		  else
 | 
						|
		    {
 | 
						|
		      blue4 = src[yoff + 4];
 | 
						|
		      green4 = src[yoff + 5];
 | 
						|
		    }
 | 
						|
		  src += 4;
 | 
						|
 | 
						|
		  buf[0] = red1;
 | 
						|
		  buf[1] = green3;
 | 
						|
		  buf[2] = blue3;
 | 
						|
		  buf[3] = red1;
 | 
						|
		  buf[4] = green4;
 | 
						|
		  buf[5] = blue4;
 | 
						|
		  if (fwrite (buf, 1, 6, ofp) != 6)
 | 
						|
		    {
 | 
						|
		      perror ("fwrite: short write");
 | 
						|
		      return 1;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  src = buffer;
 | 
						|
	  if (req.despeckle)
 | 
						|
	    {
 | 
						|
	      despeckle (req.params.pixels_per_line, req.params.lines,
 | 
						|
			 buffer, extra);
 | 
						|
	      src = extra;
 | 
						|
	    }
 | 
						|
 | 
						|
	  /* now write the whole thing to the main process: */
 | 
						|
	  if (fwrite (src, 1, num_bytes, ofp) != num_bytes)
 | 
						|
	    {
 | 
						|
	      perror ("fwrite: short write");
 | 
						|
	      return 1;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
      fflush (ofp);
 | 
						|
    }
 | 
						|
  assert (SANE_FALSE);		/* not reached */
 | 
						|
  DBG (5, "reader_process: exit\n");
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
attach (const char *devname, QC_Device ** devp)
 | 
						|
{
 | 
						|
  int i, n1, n2, s1, s2, cmd, port, force_unidir;
 | 
						|
  SANE_Status result, status;
 | 
						|
  QC_Device *q;
 | 
						|
  char *endp;
 | 
						|
 | 
						|
  DBG (3, "attach: enter\n");
 | 
						|
  errno = 0;
 | 
						|
  force_unidir = 0;
 | 
						|
  if (devname[0] == 'u')
 | 
						|
    {
 | 
						|
      force_unidir = 1;
 | 
						|
      ++devname;
 | 
						|
    }
 | 
						|
  port = strtol (devname, &endp, 0);
 | 
						|
  if (endp == devname || errno == ERANGE)
 | 
						|
    {
 | 
						|
      DBG (1, "attach: invalid port address `%s'\n", devname);
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  for (q = first_dev; q; q = q->next)
 | 
						|
    if (port == q->port)
 | 
						|
      {
 | 
						|
	if (devp)
 | 
						|
	  *devp = q;
 | 
						|
	return SANE_STATUS_GOOD;
 | 
						|
      }
 | 
						|
 | 
						|
  q = malloc (sizeof (*q));
 | 
						|
  if (!q)
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
 | 
						|
  memset (q, 0, sizeof (*q));
 | 
						|
  q->port = port;
 | 
						|
  q->lock_fd = -1;
 | 
						|
 | 
						|
  result = enable_ports (q);
 | 
						|
  if (result != SANE_STATUS_GOOD)
 | 
						|
    {
 | 
						|
      DBG (1, "attach: cannot enable ports (%s)\n", strerror (errno));
 | 
						|
      free (q);
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  /* lock camera while we determine its version: */
 | 
						|
  qc_lock (q);
 | 
						|
 | 
						|
  qc_reset (q);
 | 
						|
 | 
						|
  write_lpdata (q, QC_SEND_VERSION);
 | 
						|
  qc_wait (q);
 | 
						|
  write_lpcontrol (q, Autofeed | Reset_N);	/* make PCAck inactive */
 | 
						|
  qc_wait (q);
 | 
						|
 | 
						|
  for (i = 0; (i < 1000) && !(s1 = (n1 = read_lpstatus (q)) & CamRdy1); i++);
 | 
						|
  if (!s1)
 | 
						|
    {
 | 
						|
      DBG (2, "attach: failed to get CamRdy1 at port 0x%x\n", q->port);
 | 
						|
      goto unlock_and_fail;
 | 
						|
    }
 | 
						|
 | 
						|
  write_lpcontrol (q, Autofeed | Reset_N | PCAck);
 | 
						|
  qc_wait (q);
 | 
						|
 | 
						|
  for (i = 0; (i < 1000) && (s2 = (n2 = read_lpstatus (q)) & CamRdy1); i++);
 | 
						|
  if (s2)
 | 
						|
    {
 | 
						|
      DBG (2, "attach: CamRdy1 failed to clear at port 0x%x\n", q->port);
 | 
						|
      goto unlock_and_fail;
 | 
						|
    }
 | 
						|
 | 
						|
  cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
 | 
						|
 | 
						|
  if (cmd != QC_SEND_VERSION)
 | 
						|
    {
 | 
						|
      DBG (2, "attach: got 0x%02x instead of 0x%02x\n", cmd, QC_SEND_VERSION);
 | 
						|
      goto unlock_and_fail;
 | 
						|
    }
 | 
						|
 | 
						|
  q->version = qc_readparam (q);
 | 
						|
  DBG (1, "attach: found QuickCam version 0x%02x\n", q->version);
 | 
						|
 | 
						|
  q->port_mode = QC_UNIDIR;
 | 
						|
  if (!force_unidir)
 | 
						|
    {
 | 
						|
      write_lpcontrol (q, BiDir);
 | 
						|
      write_lpdata (q, 0x75);
 | 
						|
      if (read_lpdata (q) != 0x75)
 | 
						|
	q->port_mode = QC_BIDIR;
 | 
						|
    }
 | 
						|
 | 
						|
  /* For some reason the color quickcam needs two set-black commands
 | 
						|
     after a reset.  Thus, we now set the black-level to some
 | 
						|
     reasonable value (0) so that the next set-black level command
 | 
						|
     will really go through.  */
 | 
						|
  if (q->version == QC_COLOR)
 | 
						|
    {
 | 
						|
      qc_send (q, QC_SET_BLACK);
 | 
						|
      qc_send (q, 0);
 | 
						|
 | 
						|
      DBG (3, "attach: resetting black_level\n");
 | 
						|
 | 
						|
      /* wait for set black level command to finish: */
 | 
						|
      while (qc_getstatus (q) & (CameraNotReady | BlackBalanceInProgress))
 | 
						|
	usleep (10000);
 | 
						|
    }
 | 
						|
 | 
						|
  status = qc_unlock (q);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    {
 | 
						|
      DBG (1, "attach: status qc_unlock NOK\n");
 | 
						|
      /* status = SANE_STATUS_GOOD;  */
 | 
						|
    }
 | 
						|
  q->sane.name = strdup (devname);
 | 
						|
  q->sane.vendor = "Connectix";
 | 
						|
  q->sane.model =
 | 
						|
    (q->version == QC_COLOR) ? "Color QuickCam" : "B&W QuickCam";
 | 
						|
  q->sane.type = "video camera";
 | 
						|
 | 
						|
  ++num_devices;
 | 
						|
  q->next = first_dev;
 | 
						|
  first_dev = q;
 | 
						|
 | 
						|
  if (devp)
 | 
						|
    *devp = q;
 | 
						|
  DBG (3, "attach: exit status OK\n");
 | 
						|
  status = SANE_STATUS_GOOD;
 | 
						|
  return status;
 | 
						|
 | 
						|
 | 
						|
unlock_and_fail:
 | 
						|
  status = qc_unlock (q);
 | 
						|
  if (status != SANE_STATUS_GOOD)
 | 
						|
    {
 | 
						|
      DBG (1, "attach: unlock_and_fail status qc_unlock NOK\n");
 | 
						|
    }
 | 
						|
  free (q);
 | 
						|
  DBG (3, "attach: exit status NOK\n");
 | 
						|
  status = SANE_STATUS_INVAL;
 | 
						|
  return status;
 | 
						|
}
 | 
						|
 | 
						|
static SANE_Status
 | 
						|
init_options (QC_Scanner * s)
 | 
						|
{
 | 
						|
  int i;
 | 
						|
 | 
						|
  DBG (3, "init_options: enter\n");
 | 
						|
 | 
						|
  memset (s->opt, 0, sizeof (s->opt));
 | 
						|
  memset (s->val, 0, sizeof (s->val));
 | 
						|
 | 
						|
  for (i = 0; i < NUM_OPTIONS; ++i)
 | 
						|
    {
 | 
						|
      s->opt[i].size = sizeof (SANE_Word);
 | 
						|
      s->opt[i].cap = (SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT);
 | 
						|
    }
 | 
						|
 | 
						|
  s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
 | 
						|
  s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
 | 
						|
  s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
 | 
						|
  s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
 | 
						|
 | 
						|
  /* "Mode" group: */
 | 
						|
 | 
						|
  s->opt[OPT_MODE_GROUP].title = "Scan Mode";
 | 
						|
  s->opt[OPT_MODE_GROUP].desc = "";
 | 
						|
  s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
 | 
						|
  s->opt[OPT_MODE_GROUP].cap = 0;
 | 
						|
  s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | 
						|
 | 
						|
  /* resolution */
 | 
						|
  s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
 | 
						|
  s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
 | 
						|
  s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
 | 
						|
  s->opt[OPT_RESOLUTION].type = SANE_TYPE_STRING;
 | 
						|
  s->opt[OPT_RESOLUTION].size = 5;	/* sizeof("High") */
 | 
						|
  s->opt[OPT_RESOLUTION].unit = SANE_UNIT_NONE;
 | 
						|
  s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
 | 
						|
  s->opt[OPT_RESOLUTION].constraint.string_list = resolution_list;
 | 
						|
  s->val[OPT_RESOLUTION].s = strdup (resolution_list[QC_RES_LOW]);
 | 
						|
 | 
						|
  /* bit-depth */
 | 
						|
  s->opt[OPT_DEPTH].name = SANE_NAME_BIT_DEPTH;
 | 
						|
  s->opt[OPT_DEPTH].title = "Pixel depth";
 | 
						|
  s->opt[OPT_DEPTH].desc = "Number of bits per pixel.";
 | 
						|
  s->opt[OPT_DEPTH].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_DEPTH].unit = SANE_UNIT_BIT;
 | 
						|
  s->opt[OPT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
 | 
						|
  s->opt[OPT_DEPTH].constraint.word_list = color_depth_list;
 | 
						|
  s->val[OPT_DEPTH].w = color_depth_list[NELEMS (color_depth_list) - 1];
 | 
						|
 | 
						|
  /* test */
 | 
						|
  s->opt[OPT_TEST].name = "test-image";
 | 
						|
  s->opt[OPT_TEST].title = "Force test image";
 | 
						|
  s->opt[OPT_TEST].desc =
 | 
						|
    "Acquire a test-image instead of the image seen by the camera. "
 | 
						|
    "The test image consists of red, green, and blue squares of "
 | 
						|
    "32x32 pixels each.  Use this to find out whether the "
 | 
						|
    "camera is connected properly.";
 | 
						|
  s->opt[OPT_TEST].type = SANE_TYPE_BOOL;
 | 
						|
  s->val[OPT_TEST].w = SANE_FALSE;
 | 
						|
 | 
						|
  /* "Geometry" group: */
 | 
						|
  s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
 | 
						|
  s->opt[OPT_GEOMETRY_GROUP].desc = "";
 | 
						|
  s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
 | 
						|
  s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
 | 
						|
  s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | 
						|
 | 
						|
  /* top-left x */
 | 
						|
  s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
 | 
						|
  s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
 | 
						|
  s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
 | 
						|
  s->opt[OPT_TL_X].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_TL_X].unit = SANE_UNIT_PIXEL;
 | 
						|
  s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_TL_X].constraint.range = &x_range[QC_RES_LOW];
 | 
						|
  s->val[OPT_TL_X].w = 10;
 | 
						|
 | 
						|
  /* top-left y */
 | 
						|
  s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
 | 
						|
  s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
 | 
						|
  s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
 | 
						|
  s->opt[OPT_TL_Y].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_TL_Y].unit = SANE_UNIT_PIXEL;
 | 
						|
  s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_TL_Y].constraint.range = &y_range[QC_RES_LOW];
 | 
						|
  s->val[OPT_TL_Y].w = 0;
 | 
						|
 | 
						|
  /* bottom-right x */
 | 
						|
  s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
 | 
						|
  s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
 | 
						|
  s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
 | 
						|
  s->opt[OPT_BR_X].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_BR_X].unit = SANE_UNIT_PIXEL;
 | 
						|
  s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_BR_X].constraint.range = &odd_x_range[QC_RES_LOW];
 | 
						|
  s->val[OPT_BR_X].w = 339;
 | 
						|
 | 
						|
  /* bottom-right y */
 | 
						|
  s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
 | 
						|
  s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
 | 
						|
  s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
 | 
						|
  s->opt[OPT_BR_Y].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_BR_Y].unit = SANE_UNIT_PIXEL;
 | 
						|
  s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_BR_Y].constraint.range = &odd_y_range[QC_RES_LOW];
 | 
						|
  s->val[OPT_BR_Y].w = 245;
 | 
						|
 | 
						|
  /* xfer-scale */
 | 
						|
  s->opt[OPT_XFER_SCALE].name = "transfer-scale";
 | 
						|
  s->opt[OPT_XFER_SCALE].title = "Transfer scale";
 | 
						|
  s->opt[OPT_XFER_SCALE].desc =
 | 
						|
    "The transferscale determines how many of the acquired pixels actually "
 | 
						|
    "get sent to the computer.  For example, a transfer scale of 2 would "
 | 
						|
    "request that every other acquired pixel would be omitted.  That is, "
 | 
						|
    "when scanning a 200 pixel wide and 100 pixel tall area, the resulting "
 | 
						|
    "image would be only 100x50 pixels large.  Using a large transfer scale "
 | 
						|
    "improves acquisition speed, but reduces resolution.";
 | 
						|
  s->opt[OPT_XFER_SCALE].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_XFER_SCALE].constraint_type = SANE_CONSTRAINT_WORD_LIST;
 | 
						|
  s->opt[OPT_XFER_SCALE].constraint.word_list = xfer_scale_list;
 | 
						|
  s->val[OPT_XFER_SCALE].w = xfer_scale_list[1];
 | 
						|
 | 
						|
  /* despeckle */
 | 
						|
  s->opt[OPT_DESPECKLE].name = "despeckle";
 | 
						|
  s->opt[OPT_DESPECKLE].title = "Speckle filter";
 | 
						|
  s->opt[OPT_DESPECKLE].desc = "Turning on this filter will remove the "
 | 
						|
    "christmas lights that are typically present in dark images.";
 | 
						|
  s->opt[OPT_DESPECKLE].type = SANE_TYPE_BOOL;
 | 
						|
  s->opt[OPT_DESPECKLE].constraint_type = SANE_CONSTRAINT_NONE;
 | 
						|
  s->val[OPT_DESPECKLE].w = 0;
 | 
						|
 | 
						|
 | 
						|
  /* "Enhancement" group: */
 | 
						|
 | 
						|
  s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
 | 
						|
  s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
 | 
						|
  s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
 | 
						|
  s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
 | 
						|
  s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
 | 
						|
 | 
						|
  /* brightness */
 | 
						|
  s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
 | 
						|
  s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
 | 
						|
  s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS
 | 
						|
    "  In a conventional camera, this control corresponds to the "
 | 
						|
    "exposure time.";
 | 
						|
  s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_AUTOMATIC;
 | 
						|
  s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_BRIGHTNESS].constraint.range = &brightness_range;
 | 
						|
  s->val[OPT_BRIGHTNESS].w = 135;
 | 
						|
 | 
						|
  /* contrast */
 | 
						|
  s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
 | 
						|
  s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
 | 
						|
  s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
 | 
						|
  s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_CONTRAST].constraint.range = &u8_range;
 | 
						|
  s->val[OPT_CONTRAST].w = 104;
 | 
						|
 | 
						|
  /* black-level */
 | 
						|
  s->opt[OPT_BLACK_LEVEL].name = SANE_NAME_BLACK_LEVEL;
 | 
						|
  s->opt[OPT_BLACK_LEVEL].title = SANE_TITLE_BLACK_LEVEL;
 | 
						|
  s->opt[OPT_BLACK_LEVEL].desc = SANE_DESC_BLACK_LEVEL
 | 
						|
    " This value should be selected so that black areas just start "
 | 
						|
    "to look really black (not gray).";
 | 
						|
  s->opt[OPT_BLACK_LEVEL].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_BLACK_LEVEL].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_BLACK_LEVEL].constraint.range = &u8_range;
 | 
						|
  s->val[OPT_BLACK_LEVEL].w = 0;
 | 
						|
 | 
						|
  /* white-level */
 | 
						|
  s->opt[OPT_WHITE_LEVEL].name = SANE_NAME_WHITE_LEVEL;
 | 
						|
  s->opt[OPT_WHITE_LEVEL].title = SANE_TITLE_WHITE_LEVEL;
 | 
						|
  s->opt[OPT_WHITE_LEVEL].desc = SANE_DESC_WHITE_LEVEL
 | 
						|
    " This value should be selected so that white areas just start "
 | 
						|
    "to look really white (not gray).";
 | 
						|
  s->opt[OPT_WHITE_LEVEL].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_WHITE_LEVEL].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_WHITE_LEVEL].constraint.range = &u8_range;
 | 
						|
  s->val[OPT_WHITE_LEVEL].w = 150;
 | 
						|
 | 
						|
  /* hue */
 | 
						|
  s->opt[OPT_HUE].name = SANE_NAME_HUE;
 | 
						|
  s->opt[OPT_HUE].title = SANE_TITLE_HUE;
 | 
						|
  s->opt[OPT_HUE].desc = SANE_DESC_HUE;
 | 
						|
  s->opt[OPT_HUE].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_HUE].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_HUE].constraint.range = &u8_range;
 | 
						|
  s->val[OPT_HUE].w = 128;
 | 
						|
 | 
						|
  /* saturation */
 | 
						|
  s->opt[OPT_SATURATION].name = SANE_NAME_SATURATION;
 | 
						|
  s->opt[OPT_SATURATION].title = SANE_TITLE_SATURATION;
 | 
						|
  s->opt[OPT_SATURATION].desc = SANE_DESC_SATURATION;
 | 
						|
  s->opt[OPT_SATURATION].type = SANE_TYPE_INT;
 | 
						|
  s->opt[OPT_SATURATION].constraint_type = SANE_CONSTRAINT_RANGE;
 | 
						|
  s->opt[OPT_SATURATION].constraint.range = &u8_range;
 | 
						|
  s->val[OPT_SATURATION].w = 100;
 | 
						|
 | 
						|
  DBG (3, "init_options: exit\n");
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
 | 
						|
{
 | 
						|
  char dev_name[PATH_MAX], *str;
 | 
						|
  size_t len;
 | 
						|
  FILE *fp;
 | 
						|
  (void) authorize;		/* silence compilation warnings */
 | 
						|
 | 
						|
  DBG_INIT ();
 | 
						|
 | 
						|
  DBG (1, "sane_init: enter\n");
 | 
						|
 | 
						|
  if (version_code)
 | 
						|
    *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0);
 | 
						|
 | 
						|
  fp = sanei_config_open (QCAM_CONFIG_FILE);
 | 
						|
  if (!fp)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_init: file `%s' not accessible\n", QCAM_CONFIG_FILE);
 | 
						|
      return SANE_STATUS_INVAL;
 | 
						|
    }
 | 
						|
 | 
						|
  while (sanei_config_read (dev_name, sizeof (dev_name), fp))
 | 
						|
    {
 | 
						|
      if (dev_name[0] == '#')	/* ignore line comments */
 | 
						|
	continue;
 | 
						|
      len = strlen (dev_name);
 | 
						|
 | 
						|
      if (!len)
 | 
						|
	continue;		/* ignore empty lines */
 | 
						|
 | 
						|
      for (str = dev_name; *str && !isspace (*str) && *str != '#'; ++str);
 | 
						|
      *str = '\0';
 | 
						|
 | 
						|
      attach (dev_name, 0);
 | 
						|
    }
 | 
						|
  fclose (fp);
 | 
						|
 | 
						|
  DBG (1, "sane_init: exit\n");
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
sane_exit (void)
 | 
						|
{
 | 
						|
  QC_Device *dev, *next;
 | 
						|
  static const SANE_Device **devlist;
 | 
						|
 | 
						|
  DBG (5, "sane_exit: enter\n");
 | 
						|
 | 
						|
  for (dev = first_dev; dev; dev = next)
 | 
						|
    {
 | 
						|
      next = dev->next;
 | 
						|
      free ((void *) dev->sane.name);
 | 
						|
      disable_ports (dev);
 | 
						|
      free (dev);
 | 
						|
    }
 | 
						|
  if (devlist) {
 | 
						|
	  free (devlist);
 | 
						|
          devlist = NULL;
 | 
						|
  }
 | 
						|
  DBG (5, "sane_exit: exit\n");
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
 | 
						|
{
 | 
						|
  static const SANE_Device **devlist = 0;
 | 
						|
  QC_Device *dev;
 | 
						|
  int i;
 | 
						|
 | 
						|
  DBG (5, "sane_get_devices: enter\n");
 | 
						|
 | 
						|
  (void) local_only;		/* silence compilation warnings */
 | 
						|
 | 
						|
  if (devlist)
 | 
						|
    free (devlist);
 | 
						|
 | 
						|
  devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
 | 
						|
  if (!devlist)
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
 | 
						|
  i = 0;
 | 
						|
  for (dev = first_dev; i < num_devices; dev = dev->next)
 | 
						|
    devlist[i++] = &dev->sane;
 | 
						|
  devlist[i++] = 0;
 | 
						|
 | 
						|
  *device_list = devlist;
 | 
						|
 | 
						|
  DBG (5, "sane_get_devices: exit\n");
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_open (SANE_String_Const devicename, SANE_Handle * handle)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  QC_Device *dev;
 | 
						|
  QC_Scanner *s;
 | 
						|
 | 
						|
  DBG (5, "sane_open: enter: (devicename = %s)\n", devicename);
 | 
						|
  if (devicename[0])
 | 
						|
    {
 | 
						|
      status = attach (devicename, &dev);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	return status;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    /* empty devicname -> use first device */
 | 
						|
    dev = first_dev;
 | 
						|
 | 
						|
  if (!dev)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  s = malloc (sizeof (*s));
 | 
						|
  if (!s)
 | 
						|
    return SANE_STATUS_NO_MEM;
 | 
						|
  memset (s, 0, sizeof (*s));
 | 
						|
  s->user_corner = 0;
 | 
						|
  s->hw = dev;
 | 
						|
  s->value_changed = ~0;	/* ensure all options get updated */
 | 
						|
  s->reader_pid = -1;
 | 
						|
  s->to_child = -1;
 | 
						|
  s->from_child = -1;
 | 
						|
  s->read_fd = -1;
 | 
						|
 | 
						|
  init_options (s);
 | 
						|
 | 
						|
  /* The contrast option seems to have an effect for b&w cameras only,
 | 
						|
     so don't give the user the impression that this is a useful thing
 | 
						|
     to set... */
 | 
						|
  if (s->hw->version == QC_COLOR)
 | 
						|
    s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
 | 
						|
  else
 | 
						|
    {
 | 
						|
      /* Black level, Hue and Saturation are things the b&w cameras
 | 
						|
         know nothing about.  Despeckle might be useful, but this code
 | 
						|
         seems to work for color cameras only right now. The framesize
 | 
						|
         seems to work better in these ranges.  */
 | 
						|
      s->opt[OPT_DESPECKLE].cap |= SANE_CAP_INACTIVE;
 | 
						|
      s->opt[OPT_BLACK_LEVEL].cap |= SANE_CAP_INACTIVE;
 | 
						|
      s->opt[OPT_HUE].cap |= SANE_CAP_INACTIVE;
 | 
						|
      s->opt[OPT_SATURATION].cap |= SANE_CAP_INACTIVE;
 | 
						|
      s->opt[OPT_RESOLUTION].cap |= SANE_CAP_INACTIVE;
 | 
						|
      s->opt[OPT_TEST].cap |= SANE_CAP_INACTIVE;
 | 
						|
 | 
						|
      s->opt[OPT_DEPTH].constraint.word_list = mono_depth_list;
 | 
						|
      s->val[OPT_DEPTH].w = mono_depth_list[NELEMS (mono_depth_list) - 1];
 | 
						|
      s->opt[OPT_TL_X].constraint.range = &bw_x_range;
 | 
						|
      s->val[OPT_TL_X].w = 14;
 | 
						|
      s->opt[OPT_TL_Y].constraint.range = &bw_y_range;
 | 
						|
      s->val[OPT_TL_Y].w = 0;
 | 
						|
      s->opt[OPT_BR_X].constraint.range = &odd_bw_x_range;
 | 
						|
      s->val[OPT_BR_X].w = 333;
 | 
						|
      s->opt[OPT_BR_Y].constraint.range = &odd_bw_y_range;
 | 
						|
      s->val[OPT_BR_Y].w = 239;
 | 
						|
 | 
						|
      s->val[OPT_BRIGHTNESS].w = 170;
 | 
						|
      s->val[OPT_CONTRAST].w = 150;
 | 
						|
      s->val[OPT_WHITE_LEVEL].w = 150;
 | 
						|
    }
 | 
						|
 | 
						|
  /* insert newly opened handle into list of open handles: */
 | 
						|
  s->next = first_handle;
 | 
						|
  first_handle = s;
 | 
						|
 | 
						|
  *handle = s;
 | 
						|
 | 
						|
  DBG (5, "sane_open: exit\n");
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
sane_close (SANE_Handle handle)
 | 
						|
{
 | 
						|
  QC_Scanner *prev, *s;
 | 
						|
 | 
						|
  DBG (5, "sane_close: enter\n");
 | 
						|
 | 
						|
  /* remove handle from list of open handles: */
 | 
						|
  prev = 0;
 | 
						|
  for (s = first_handle; s; s = s->next)
 | 
						|
    {
 | 
						|
      if (s == handle)
 | 
						|
	break;
 | 
						|
      prev = s;
 | 
						|
    }
 | 
						|
  if (!s)
 | 
						|
    {
 | 
						|
      DBG (1, "sane_close: bad handle %p\n", handle);
 | 
						|
      return;			/* oops, not a handle we know about */
 | 
						|
    }
 | 
						|
  if (prev)
 | 
						|
    prev->next = s->next;
 | 
						|
  else
 | 
						|
    first_handle = s->next;
 | 
						|
 | 
						|
  if (s->scanning)
 | 
						|
    sane_cancel (handle);
 | 
						|
 | 
						|
  if (s->reader_pid >= 0)
 | 
						|
    {
 | 
						|
      kill (s->reader_pid, SIGTERM);
 | 
						|
      waitpid (s->reader_pid, 0, 0);
 | 
						|
      s->reader_pid = 0;
 | 
						|
    }
 | 
						|
  if (s->to_child >= 0)
 | 
						|
    close (s->to_child);
 | 
						|
  if (s->from_child >= 0)
 | 
						|
    close (s->from_child);
 | 
						|
  if (s->read_fd >= 0)
 | 
						|
    close (s->read_fd);
 | 
						|
 | 
						|
  free (s);
 | 
						|
 | 
						|
  DBG (5, "sane_close: exit\n");
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
const SANE_Option_Descriptor *
 | 
						|
sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
 | 
						|
{
 | 
						|
  QC_Scanner *s = handle;
 | 
						|
 | 
						|
  DBG (5, "sane_get_option_descriptor: enter\n");
 | 
						|
 | 
						|
  if ((unsigned) option >= NUM_OPTIONS)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  DBG (5, "sane_get_option_descriptor: exit\n");
 | 
						|
 | 
						|
  return s->opt + option;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_control_option (SANE_Handle handle, SANE_Int option,
 | 
						|
		     SANE_Action action, void *val, SANE_Int * info)
 | 
						|
{
 | 
						|
  QC_Scanner *s = handle;
 | 
						|
  QC_Resolution old_res;
 | 
						|
  SANE_Status status;
 | 
						|
  SANE_Word cap;
 | 
						|
  char *old_val;
 | 
						|
  int i;
 | 
						|
 | 
						|
  DBG (5, "sane_control_option: enter\n");
 | 
						|
 | 
						|
  if (info)
 | 
						|
    *info = 0;
 | 
						|
 | 
						|
  if (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)
 | 
						|
	{
 | 
						|
	  /* word options: */
 | 
						|
	case OPT_NUM_OPTS:
 | 
						|
	case OPT_DEPTH:
 | 
						|
	case OPT_DESPECKLE:
 | 
						|
	case OPT_TEST:
 | 
						|
	case OPT_TL_X:
 | 
						|
	case OPT_TL_Y:
 | 
						|
	case OPT_BR_X:
 | 
						|
	case OPT_BR_Y:
 | 
						|
	case OPT_XFER_SCALE:
 | 
						|
	case OPT_BRIGHTNESS:
 | 
						|
	case OPT_CONTRAST:
 | 
						|
	case OPT_BLACK_LEVEL:
 | 
						|
	case OPT_WHITE_LEVEL:
 | 
						|
	case OPT_HUE:
 | 
						|
	case OPT_SATURATION:
 | 
						|
	  *(SANE_Word *) val = s->val[option].w;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	  /* string options: */
 | 
						|
	case OPT_RESOLUTION:
 | 
						|
	  strcpy (val, s->val[option].s);
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	default:
 | 
						|
	  DBG (1, "control_option: option %d unknown\n", option);
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else if (action == SANE_ACTION_SET_VALUE)
 | 
						|
    {
 | 
						|
      if (!SANE_OPTION_IS_SETTABLE (cap))
 | 
						|
	return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
      status = sanei_constrain_value (s->opt + option, val, info);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	return status;
 | 
						|
 | 
						|
      if (option >= OPT_TL_X && option <= OPT_BR_Y)
 | 
						|
	s->user_corner |= 1 << (option - OPT_TL_X);
 | 
						|
 | 
						|
      assert (option <= 31);
 | 
						|
      s->value_changed |= 1 << option;
 | 
						|
 | 
						|
      switch (option)
 | 
						|
	{
 | 
						|
	  /* (mostly) side-effect-free word options: */
 | 
						|
	case OPT_TL_X:
 | 
						|
	case OPT_TL_Y:
 | 
						|
	case OPT_BR_X:
 | 
						|
	case OPT_BR_Y:
 | 
						|
	case OPT_XFER_SCALE:
 | 
						|
	case OPT_DEPTH:
 | 
						|
	  if (!s->scanning && info && s->val[option].w != *(SANE_Word *) val)
 | 
						|
	    /* only signal the reload params if we're not scanning---no point
 | 
						|
	       in creating the frontend useless work */
 | 
						|
	    *info |= SANE_INFO_RELOAD_PARAMS;
 | 
						|
	  /* fall through */
 | 
						|
	case OPT_NUM_OPTS:
 | 
						|
	case OPT_TEST:
 | 
						|
	case OPT_DESPECKLE:
 | 
						|
	case OPT_BRIGHTNESS:
 | 
						|
	case OPT_CONTRAST:
 | 
						|
	case OPT_BLACK_LEVEL:
 | 
						|
	case OPT_WHITE_LEVEL:
 | 
						|
	case OPT_HUE:
 | 
						|
	case OPT_SATURATION:
 | 
						|
	  s->val[option].w = *(SANE_Word *) val;
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	  /* options with side-effects: */
 | 
						|
	case OPT_RESOLUTION:
 | 
						|
	  old_val = s->val[OPT_RESOLUTION].s;
 | 
						|
 | 
						|
	  if (strcmp (old_val, val) != 0)
 | 
						|
	    return SANE_STATUS_GOOD;	/* no change */
 | 
						|
 | 
						|
	  if (info)
 | 
						|
	    {
 | 
						|
	      *info |= SANE_INFO_RELOAD_OPTIONS;
 | 
						|
	      if (!s->scanning)
 | 
						|
		*info |= SANE_INFO_RELOAD_PARAMS;
 | 
						|
	    }
 | 
						|
	  free (old_val);
 | 
						|
	  s->val[OPT_RESOLUTION].s = strdup (val);
 | 
						|
 | 
						|
	  /* low-resolution mode: */
 | 
						|
	  old_res = s->resolution;
 | 
						|
	  s->resolution = QC_RES_LOW;
 | 
						|
	  if (strcmp (val, resolution_list[QC_RES_HIGH]) == 0)
 | 
						|
	    /* high-resolution mode: */
 | 
						|
	    s->resolution = QC_RES_HIGH;
 | 
						|
	  s->opt[OPT_TL_X].constraint.range = &x_range[s->resolution];
 | 
						|
	  s->opt[OPT_BR_X].constraint.range = &odd_x_range[s->resolution];
 | 
						|
	  s->opt[OPT_TL_Y].constraint.range = &y_range[s->resolution];
 | 
						|
	  s->opt[OPT_BR_Y].constraint.range = &odd_y_range[s->resolution];
 | 
						|
 | 
						|
	  if (old_res == QC_RES_LOW && s->resolution == QC_RES_HIGH)
 | 
						|
	    {
 | 
						|
	      for (i = OPT_TL_X; i <= OPT_BR_Y; ++i)
 | 
						|
		s->val[i].w *= 2;
 | 
						|
	      s->val[OPT_BR_X].w += 1;
 | 
						|
	      s->val[OPT_BR_Y].w += 1;
 | 
						|
	      s->opt[OPT_TEST].cap |= SANE_CAP_INACTIVE;
 | 
						|
	    }
 | 
						|
	  else if (old_res == QC_RES_HIGH && s->resolution == QC_RES_LOW)
 | 
						|
	    {
 | 
						|
	      for (i = OPT_TL_X; i <= OPT_BR_Y; ++i)
 | 
						|
		s->val[i].w /= 2;
 | 
						|
	      s->opt[OPT_TEST].cap &= ~SANE_CAP_INACTIVE;
 | 
						|
	    }
 | 
						|
 | 
						|
	  if (!(s->user_corner & 0x4))
 | 
						|
	    s->val[OPT_BR_X].w = odd_x_range[s->resolution].max;
 | 
						|
	  if (!(s->user_corner & 0x8))
 | 
						|
	    s->val[OPT_BR_Y].w = odd_y_range[s->resolution].max - 4;
 | 
						|
 | 
						|
	  /* make sure the affected options have valid values: */
 | 
						|
	  for (i = OPT_TL_X; i <= OPT_BR_Y; ++i)
 | 
						|
	    if (s->val[i].w > s->opt[i].constraint.range->max)
 | 
						|
	      s->val[i].w = s->opt[i].constraint.range->max;
 | 
						|
 | 
						|
          DBG (5, "sane_control_option: exit\n");
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  else if (action == SANE_ACTION_SET_AUTO)
 | 
						|
    {
 | 
						|
      switch (option)
 | 
						|
	{
 | 
						|
	case OPT_BRIGHTNESS:
 | 
						|
	  /* not implemented yet */
 | 
						|
          DBG (5, "sane_control_option: exit\n");
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
 | 
						|
	default:
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (5, "sane_control_option: NOK exit\n");
 | 
						|
  return SANE_STATUS_INVAL;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
 | 
						|
{
 | 
						|
  QC_Scanner *s = handle;
 | 
						|
  QC_Device *q = s->hw;
 | 
						|
  int xfer_scale;
 | 
						|
  size_t Bpp = 3;		/* # of bytes per pixel */
 | 
						|
 | 
						|
  DBG (5, "sane_get_parameters: enter\n");
 | 
						|
 | 
						|
  if (!s->scanning)
 | 
						|
    {
 | 
						|
      /* Only compute new parameters when not scanning---allows
 | 
						|
         changing width/height etc while scan is in progress.  */
 | 
						|
      xfer_scale = s->val[OPT_XFER_SCALE].w;
 | 
						|
 | 
						|
      s->params.format = SANE_FRAME_RGB;
 | 
						|
      if (q->version != QC_COLOR)
 | 
						|
	{
 | 
						|
	  s->params.format = SANE_FRAME_GRAY;
 | 
						|
	  Bpp = 1;
 | 
						|
	}
 | 
						|
      s->params.last_frame = SANE_TRUE;
 | 
						|
 | 
						|
      s->params.pixels_per_line = s->val[OPT_BR_X].w - s->val[OPT_TL_X].w + 1;
 | 
						|
      s->params.pixels_per_line /= xfer_scale;
 | 
						|
      s->params.pixels_per_line &= ~1UL;	/* ensure it's even */
 | 
						|
      if (s->params.pixels_per_line < 2)
 | 
						|
	s->params.pixels_per_line = 2;
 | 
						|
 | 
						|
      s->params.lines = s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w + 1;
 | 
						|
      s->params.lines /= xfer_scale;
 | 
						|
      if (s->params.lines < 1)
 | 
						|
	s->params.lines = 1;
 | 
						|
 | 
						|
      s->params.bytes_per_line = Bpp * s->params.pixels_per_line;
 | 
						|
      s->params.depth = 8;
 | 
						|
    }
 | 
						|
  if (params)
 | 
						|
    *params = s->params;
 | 
						|
 | 
						|
  DBG (5, "sane_get_parameters: exit\n");
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_start (SANE_Handle handle)
 | 
						|
{
 | 
						|
  int top, left, width, height, undecimated_width, undecimated_height;
 | 
						|
  QC_Scanner *s = handle;
 | 
						|
  QC_Device *q = s->hw;
 | 
						|
  QC_Scan_Request req;
 | 
						|
 | 
						|
  DBG (5, "sane_start: enter\n");
 | 
						|
 | 
						|
  if (s->scanning)
 | 
						|
    return SANE_STATUS_DEVICE_BUSY;
 | 
						|
 | 
						|
  if (s->reader_pid < 0)
 | 
						|
    {
 | 
						|
      int p2c_pipe[2];		/* parent->child pipe */
 | 
						|
      int c2p_pipe[2];		/* child->parent pipe */
 | 
						|
 | 
						|
      if (pipe (p2c_pipe) < 0 || pipe (c2p_pipe) < 0)
 | 
						|
	{
 | 
						|
	  DBG (3, "start: failed to create pipes\n");
 | 
						|
	  return SANE_STATUS_IO_ERROR;
 | 
						|
	}
 | 
						|
 | 
						|
      s->reader_pid = fork ();
 | 
						|
      if (s->reader_pid == 0)
 | 
						|
	{
 | 
						|
	  /* this is the child */
 | 
						|
	  signal (SIGHUP, SIG_DFL);
 | 
						|
	  signal (SIGINT, SIG_DFL);
 | 
						|
	  signal (SIGPIPE, SIG_DFL);
 | 
						|
	  signal (SIGTERM, SIG_DFL);
 | 
						|
	  _exit (reader_process (s, p2c_pipe[0], c2p_pipe[1]));
 | 
						|
	}
 | 
						|
      close (p2c_pipe[0]);
 | 
						|
      close (c2p_pipe[1]);
 | 
						|
      s->to_child = p2c_pipe[1];
 | 
						|
      s->from_child = c2p_pipe[0];
 | 
						|
    }
 | 
						|
 | 
						|
  s->read_fd = dup (s->from_child);
 | 
						|
  sane_get_parameters (s, 0);	/* ensure up-to-date parameters */
 | 
						|
 | 
						|
  qc_lock (q);
 | 
						|
  s->holding_lock = SANE_TRUE;
 | 
						|
 | 
						|
  if (q->version == QC_COLOR)
 | 
						|
    {
 | 
						|
      qc_send (q, QC_SET_SPEED);
 | 
						|
      qc_send (q, 2);
 | 
						|
 | 
						|
      /* wait for camera to become ready: */
 | 
						|
      while (qc_getstatus (q) & CameraNotReady)
 | 
						|
	usleep (10000);
 | 
						|
 | 
						|
      /* Only send black_level if necessary; this optimization may
 | 
						|
         fail if two applications access the camera in an interleaved
 | 
						|
         fashion; but the black-level command is slow enough that it
 | 
						|
         cannot be issued for every image acquisition.  */
 | 
						|
      if (s->value_changed & (1 << OPT_BLACK_LEVEL))
 | 
						|
	{
 | 
						|
	  s->value_changed &= ~(1 << OPT_BLACK_LEVEL);
 | 
						|
 | 
						|
	  qc_send (q, QC_SET_BLACK);
 | 
						|
	  qc_send (q, s->val[OPT_BLACK_LEVEL].w);
 | 
						|
 | 
						|
	  DBG (3, "start: black_level=%d\n", s->val[OPT_BLACK_LEVEL].w);
 | 
						|
 | 
						|
	  /* wait for set black level command to finish: */
 | 
						|
	  while (qc_getstatus (q) & (CameraNotReady | BlackBalanceInProgress))
 | 
						|
	    usleep (10000);
 | 
						|
	}
 | 
						|
 | 
						|
      if (s->value_changed & (1 << OPT_HUE))
 | 
						|
	{
 | 
						|
	  s->value_changed &= ~(1 << OPT_HUE);
 | 
						|
	  qc_send (q, QC_COL_SET_HUE);
 | 
						|
	  qc_send (q, s->val[OPT_HUE].w);
 | 
						|
	}
 | 
						|
 | 
						|
      if (s->value_changed & (1 << OPT_SATURATION))
 | 
						|
	{
 | 
						|
	  s->value_changed &= ~(1 << OPT_SATURATION);
 | 
						|
	  qc_send (q, QC_SET_SATURATION);
 | 
						|
	  qc_send (q, s->val[OPT_SATURATION].w);
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  if (q->version != QC_COLOR)
 | 
						|
    qc_reset (q);
 | 
						|
 | 
						|
  if (s->value_changed & (1 << OPT_CONTRAST))
 | 
						|
    {
 | 
						|
      s->value_changed &= ~(1 << OPT_CONTRAST);
 | 
						|
      qc_send (q, ((q->version == QC_COLOR)
 | 
						|
		   ? QC_COL_SET_CONTRAST : QC_MONO_SET_CONTRAST));
 | 
						|
      qc_send (q, s->val[OPT_CONTRAST].w);
 | 
						|
    }
 | 
						|
 | 
						|
  if (s->value_changed & (1 << OPT_BRIGHTNESS))
 | 
						|
    {
 | 
						|
      s->value_changed &= ~(1 << OPT_BRIGHTNESS);
 | 
						|
      qc_send (q, QC_SET_BRIGHTNESS);
 | 
						|
      qc_send (q, s->val[OPT_BRIGHTNESS].w);
 | 
						|
    }
 | 
						|
 | 
						|
  width = s->params.pixels_per_line;
 | 
						|
  height = s->params.lines;
 | 
						|
  if (s->resolution == QC_RES_HIGH)
 | 
						|
    {
 | 
						|
      width /= 2;		/* the expansion occurs through oversampling */
 | 
						|
      height /= 2;		/* we acquire only half the lines that we generate */
 | 
						|
    }
 | 
						|
  undecimated_width = width * s->val[OPT_XFER_SCALE].w;
 | 
						|
  undecimated_height = height * s->val[OPT_XFER_SCALE].w;
 | 
						|
 | 
						|
  s->num_bytes = 0;
 | 
						|
  s->bytes_per_frame = s->params.lines * s->params.bytes_per_line;
 | 
						|
 | 
						|
  qc_send (q, QC_SET_NUM_V);
 | 
						|
  qc_send (q, undecimated_height);
 | 
						|
 | 
						|
  if (q->version == QC_COLOR)
 | 
						|
    {
 | 
						|
      qc_send (q, QC_SET_NUM_H);
 | 
						|
      qc_send (q, undecimated_width / 2);
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      int val, val2;
 | 
						|
 | 
						|
      if (q->port_mode == QC_UNIDIR && s->val[OPT_DEPTH].w == 6)
 | 
						|
	{
 | 
						|
	  val = undecimated_width;
 | 
						|
	  val2 = s->val[OPT_XFER_SCALE].w * 4;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  val = undecimated_width * s->val[OPT_DEPTH].w;
 | 
						|
	  val2 =
 | 
						|
	    ((q->port_mode == QC_BIDIR) ? 24 : 8) * s->val[OPT_XFER_SCALE].w;
 | 
						|
	}
 | 
						|
      val = (val + val2 - 1) / val2;
 | 
						|
      qc_send (q, QC_SET_NUM_H);
 | 
						|
      qc_send (q, val);
 | 
						|
    }
 | 
						|
 | 
						|
  left = s->val[OPT_TL_X].w / 2;
 | 
						|
  top = s->val[OPT_TL_Y].w;
 | 
						|
  if (s->resolution == QC_RES_HIGH)
 | 
						|
    {
 | 
						|
      left /= 2;
 | 
						|
      top /= 2;
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (3, "sane_start: top=%d, left=%d, white=%d, bright=%d, contr=%d\n",
 | 
						|
       top, left, s->val[OPT_WHITE_LEVEL].w, s->val[OPT_BRIGHTNESS].w,
 | 
						|
       s->val[OPT_CONTRAST].w);
 | 
						|
 | 
						|
  qc_send (q, QC_SET_LEFT);
 | 
						|
  qc_send (q, left);
 | 
						|
 | 
						|
  qc_send (q, QC_SET_TOP);
 | 
						|
  qc_send (q, top + 1);		/* not sure why this is so... ;-( */
 | 
						|
 | 
						|
  if (s->value_changed & (1 << OPT_WHITE_LEVEL))
 | 
						|
    {
 | 
						|
      s->value_changed &= ~(1 << OPT_WHITE_LEVEL);
 | 
						|
      qc_send (q, QC_SET_WHITE);
 | 
						|
      qc_send (q, s->val[OPT_WHITE_LEVEL].w);
 | 
						|
    }
 | 
						|
 | 
						|
  DBG (2, "start: %s %d lines of %d pixels each (%ld bytes) => %dx%d\n",
 | 
						|
       (q->port_mode == QC_BIDIR) ? "bidir" : "unidir",
 | 
						|
       height, width, (long) s->bytes_per_frame,
 | 
						|
       s->params.pixels_per_line, s->params.lines);
 | 
						|
 | 
						|
  /* send scan request to reader process: */
 | 
						|
  qc_setscanmode (s, &req.mode);
 | 
						|
  req.num_bytes = width * height;
 | 
						|
  if (q->version == QC_COLOR)
 | 
						|
    {
 | 
						|
      if (s->resolution == QC_RES_LOW)
 | 
						|
	req.num_bytes *= 3;
 | 
						|
      else
 | 
						|
	req.num_bytes *= 4;
 | 
						|
    }
 | 
						|
  req.resolution = s->resolution;
 | 
						|
  req.params = s->params;
 | 
						|
  req.despeckle = s->val[OPT_DESPECKLE].w;
 | 
						|
  write (s->to_child, &req, sizeof (req));
 | 
						|
 | 
						|
  s->scanning = SANE_TRUE;
 | 
						|
  s->deliver_eof = 0;
 | 
						|
 | 
						|
  DBG (5, "sane_start: exit\n");
 | 
						|
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
 | 
						|
	   SANE_Int * lenp)
 | 
						|
{
 | 
						|
  SANE_Status status;
 | 
						|
  QC_Scanner *s = handle;
 | 
						|
  QC_Device *q = s->hw;
 | 
						|
  ssize_t nread;
 | 
						|
  size_t len;
 | 
						|
 | 
						|
  DBG (5, "sane_read: enter\n");
 | 
						|
 | 
						|
  *lenp = 0;
 | 
						|
 | 
						|
  if (s->deliver_eof)
 | 
						|
    {
 | 
						|
      s->deliver_eof = 0;
 | 
						|
      return SANE_STATUS_EOF;
 | 
						|
    }
 | 
						|
 | 
						|
  if (!s->scanning)
 | 
						|
    return SANE_STATUS_CANCELLED;
 | 
						|
 | 
						|
  len = max_len;
 | 
						|
  if (s->num_bytes + len > s->bytes_per_frame)
 | 
						|
    len = s->bytes_per_frame - s->num_bytes;
 | 
						|
 | 
						|
  DBG (8, "read(buf=%p,num_bytes=%ld,max_len=%d,len=%ld)\n",
 | 
						|
       (void *) buf, (long) s->num_bytes, max_len, (long) len);
 | 
						|
 | 
						|
  nread = read (s->read_fd, buf, len);
 | 
						|
  if (nread <= 0)
 | 
						|
    {
 | 
						|
      if (nread == 0 || errno == EAGAIN)
 | 
						|
	{
 | 
						|
	  DBG (3, "read: no more data available\n");
 | 
						|
	  return SANE_STATUS_GOOD;
 | 
						|
	}
 | 
						|
      DBG (3, "read: short read (%s)\n", strerror (errno));
 | 
						|
      return SANE_STATUS_IO_ERROR;
 | 
						|
    }
 | 
						|
 | 
						|
  if (nread > 0 && s->holding_lock)
 | 
						|
    {
 | 
						|
      status = qc_unlock (q);	/* now we can unlock the camera */
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	      DBG(3, "sane_read: qc_unlock error\n");
 | 
						|
      s->holding_lock = SANE_FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
  s->num_bytes += nread;
 | 
						|
  if (s->num_bytes >= s->bytes_per_frame)
 | 
						|
    {
 | 
						|
      s->scanning = SANE_FALSE;
 | 
						|
      close (s->read_fd);
 | 
						|
      s->read_fd = -1;
 | 
						|
      s->deliver_eof = 1;
 | 
						|
    }
 | 
						|
 | 
						|
  if (lenp)
 | 
						|
    *lenp = nread;
 | 
						|
 | 
						|
  DBG (5, "sane_read: exit, read got %d bytes\n", *lenp);
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
sane_cancel (SANE_Handle handle)
 | 
						|
{
 | 
						|
  QC_Scanner *s = handle;
 | 
						|
  SANE_Bool was_scanning;
 | 
						|
  SANE_Status status;
 | 
						|
 | 
						|
  DBG (5, "sane_cancel: enter\n");
 | 
						|
 | 
						|
  was_scanning = s->scanning;
 | 
						|
  s->scanning = SANE_FALSE;
 | 
						|
  s->deliver_eof = 0;
 | 
						|
  if (s->read_fd >= 0)
 | 
						|
    {
 | 
						|
      close (s->read_fd);
 | 
						|
      s->read_fd = -1;
 | 
						|
    }
 | 
						|
 | 
						|
  if (s->reader_pid >= 0 && was_scanning)
 | 
						|
    {
 | 
						|
      char buf[1024];
 | 
						|
      ssize_t nread;
 | 
						|
      int flags;
 | 
						|
 | 
						|
      DBG (1, "cancel: cancelling read request\n");
 | 
						|
 | 
						|
      kill (s->reader_pid, SIGINT);	/* tell reader to stop reading */
 | 
						|
 | 
						|
      /* save non-blocking i/o flags: */
 | 
						|
      flags = fcntl (s->from_child, F_GETFL, 0);
 | 
						|
 | 
						|
      /* block until we read at least one byte: */
 | 
						|
      read (s->from_child, buf, 1);
 | 
						|
 | 
						|
      /* put descriptor in non-blocking i/o: */
 | 
						|
      fcntl (s->from_child, F_SETFL, O_NONBLOCK);
 | 
						|
 | 
						|
      /* read what's left over in the pipe/file buffer: */
 | 
						|
      do
 | 
						|
	{
 | 
						|
	  while ((nread = read (s->from_child, buf, sizeof (buf))) > 0);
 | 
						|
	  usleep (100000);
 | 
						|
	  nread = read (s->from_child, buf, sizeof (buf));
 | 
						|
	}
 | 
						|
      while (nread > 0);
 | 
						|
 | 
						|
      /* now restore non-blocking i/o flag: */
 | 
						|
      fcntl (s->from_child, F_SETFL, flags & O_NONBLOCK);
 | 
						|
 | 
						|
      waitpid (s->reader_pid, 0, 0);
 | 
						|
      s->reader_pid = 0;
 | 
						|
 | 
						|
      DBG (1, "cancel: cancellation completed\n");
 | 
						|
    }
 | 
						|
  if (s->holding_lock)
 | 
						|
    {
 | 
						|
      status = qc_unlock (s->hw);
 | 
						|
      if (status != SANE_STATUS_GOOD)
 | 
						|
	      DBG(3, "sane_cancel: qc_unlock error\n");
 | 
						|
      s->holding_lock = SANE_FALSE;
 | 
						|
    }
 | 
						|
  DBG (5, "sane_cancel: exit\n");
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
 | 
						|
{
 | 
						|
  QC_Scanner *s = handle;
 | 
						|
 | 
						|
  DBG (5, "sane_set_io_mode: enter\n");
 | 
						|
 | 
						|
  if (!s->scanning)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  if (fcntl (s->read_fd, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
 | 
						|
    return SANE_STATUS_IO_ERROR;
 | 
						|
  DBG (5, "sane_set_io_mode: exit\n");
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 | 
						|
 | 
						|
SANE_Status
 | 
						|
sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
 | 
						|
{
 | 
						|
  QC_Scanner *s = handle;
 | 
						|
 | 
						|
  DBG (5, "sane_get_select_fd: enter\n");
 | 
						|
  if (!s->scanning)
 | 
						|
    return SANE_STATUS_INVAL;
 | 
						|
 | 
						|
  *fd = s->read_fd;
 | 
						|
  DBG (5, "sane_get_select_fd: exit\n");
 | 
						|
  return SANE_STATUS_GOOD;
 | 
						|
}
 |