kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			2265 wiersze
		
	
	
		
			57 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			2265 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, write to the Free Software
 | |
|    Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 | |
|    MA 02111-1307, USA.
 | |
| 
 | |
|    As a special exception, the authors of SANE give permission for
 | |
|    additional uses of the libraries contained in this release of SANE.
 | |
| 
 | |
|    The exception is that, if you link a SANE library with other files
 | |
|    to produce an executable, this does not by itself cause the
 | |
|    resulting executable to be covered by the GNU General Public
 | |
|    License.  Your use of that executable is in no way restricted on
 | |
|    account of linking the SANE library code into it.
 | |
| 
 | |
|    This exception does not, however, invalidate any other reasons why
 | |
|    the executable file might be covered by the GNU General Public
 | |
|    License.
 | |
| 
 | |
|    If you submit changes to SANE to the maintainers to be included in
 | |
|    a subsequent release, you agree by submitting the changes that
 | |
|    those changes may be distributed with this exception intact.
 | |
| 
 | |
|    If you write modifications of your own for SANE, it is your choice
 | |
|    whether to permit this exception to apply to your modifications.
 | |
|    If you do not wish that, delete this exception notice.
 | |
| 
 | |
|    This file implements a SANE backend for 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 };
 | |
| 
 | |
| #if defined(HAVE_SYS_IO_H) || defined(HAVE_ASM_IO_H) || defined (HAVE_SYS_HW_H)
 | |
| 
 | |
| #ifdef HAVE_SYS_IO_H
 | |
| # include <sys/io.h>		/* GNU libc based OS */
 | |
| #elif HAVE_ASM_IO_H
 | |
| # include <asm/io.h>		/* older Linux */
 | |
| #elif HAVE_SYS_HW_H
 | |
| # include <sys/hw.h>		/* OS/2 */
 | |
| #endif
 | |
| 
 | |
| #endif /* <sys/io.h> || <asm/io.h> || <sys/hw.h> */
 | |
| 
 | |
| #define read_lpdata(d)		inb ((d)->port)
 | |
| #define read_lpstatus(d)	inb ((d)->port + 1)
 | |
| #define read_lpcontrol(d)	inb ((d)->port + 2)
 | |
| #define write_lpdata(d,v)	outb ((v), (d)->port)
 | |
| #define write_lpcontrol(d,v)	outb ((v), (d)->port + 2)
 | |
| 
 | |
| 
 | |
| static SANE_Status
 | |
| enable_ports (QC_Device * q)
 | |
| {
 | |
|   /* better safe than sorry */
 | |
|   if (q->port < 0x278 || q->port > 0x3bc)
 | |
|     return SANE_STATUS_INVAL;
 | |
| 
 | |
|   if (ioperm (q->port, 3, 1) < 0)
 | |
|     return SANE_STATUS_INVAL;
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| static SANE_Status
 | |
| disable_ports (QC_Device * q)
 | |
| {
 | |
|   if (ioperm (q->port, 3, 0) < 0)
 | |
|     return SANE_STATUS_INVAL;
 | |
| 
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| /* We need a short delay loop -- somthing 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 funciton
 | |
|    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;
 | |
|   authorize = authorize;	/* silence compilation warnings */
 | |
| 
 | |
|   DBG_INIT ();
 | |
| 
 | |
|   DBG (1, "sane_init: enter\n");
 | |
| 
 | |
|   if (version_code)
 | |
|     *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_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");
 | |
| 
 | |
|   local_only = 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 uptodate 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",
 | |
|        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;
 | |
| }
 |