1141cc406Sopenharmony_ci/* sane - Scanner Access Now Easy.
2141cc406Sopenharmony_ci   Copyright (C) 1997 David Mosberger-Tang
3141cc406Sopenharmony_ci   This file is part of the SANE package.
4141cc406Sopenharmony_ci
5141cc406Sopenharmony_ci   This program is free software; you can redistribute it and/or
6141cc406Sopenharmony_ci   modify it under the terms of the GNU General Public License as
7141cc406Sopenharmony_ci   published by the Free Software Foundation; either version 2 of the
8141cc406Sopenharmony_ci   License, or (at your option) any later version.
9141cc406Sopenharmony_ci
10141cc406Sopenharmony_ci   This program is distributed in the hope that it will be useful, but
11141cc406Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
12141cc406Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13141cc406Sopenharmony_ci   General Public License for more details.
14141cc406Sopenharmony_ci
15141cc406Sopenharmony_ci   You should have received a copy of the GNU General Public License
16141cc406Sopenharmony_ci   along with this program.  If not, see <https://www.gnu.org/licenses/>.
17141cc406Sopenharmony_ci
18141cc406Sopenharmony_ci   As a special exception, the authors of SANE give permission for
19141cc406Sopenharmony_ci   additional uses of the libraries contained in this release of SANE.
20141cc406Sopenharmony_ci
21141cc406Sopenharmony_ci   The exception is that, if you link a SANE library with other files
22141cc406Sopenharmony_ci   to produce an executable, this does not by itself cause the
23141cc406Sopenharmony_ci   resulting executable to be covered by the GNU General Public
24141cc406Sopenharmony_ci   License.  Your use of that executable is in no way restricted on
25141cc406Sopenharmony_ci   account of linking the SANE library code into it.
26141cc406Sopenharmony_ci
27141cc406Sopenharmony_ci   This exception does not, however, invalidate any other reasons why
28141cc406Sopenharmony_ci   the executable file might be covered by the GNU General Public
29141cc406Sopenharmony_ci   License.
30141cc406Sopenharmony_ci
31141cc406Sopenharmony_ci   If you submit changes to SANE to the maintainers to be included in
32141cc406Sopenharmony_ci   a subsequent release, you agree by submitting the changes that
33141cc406Sopenharmony_ci   those changes may be distributed with this exception intact.
34141cc406Sopenharmony_ci
35141cc406Sopenharmony_ci   If you write modifications of your own for SANE, it is your choice
36141cc406Sopenharmony_ci   whether to permit this exception to apply to your modifications.
37141cc406Sopenharmony_ci   If you do not wish that, delete this exception notice.
38141cc406Sopenharmony_ci
39141cc406Sopenharmony_ci   This file implements a SANE backend for the Connectix QuickCam.  At
40141cc406Sopenharmony_ci   present, only the color camera is supported though the driver
41141cc406Sopenharmony_ci   should be able to easily accommodate black and white cameras.
42141cc406Sopenharmony_ci
43141cc406Sopenharmony_ci   Portions of this code are derived from Scott Laird's qcam driver.
44141cc406Sopenharmony_ci   It's copyright notice is reproduced here:
45141cc406Sopenharmony_ci
46141cc406Sopenharmony_ci   Copyright (C) 1996 by Scott Laird
47141cc406Sopenharmony_ci
48141cc406Sopenharmony_ci   Permission is hereby granted, free of charge, to any person
49141cc406Sopenharmony_ci   obtaining a copy of this software and associated documentation
50141cc406Sopenharmony_ci   files (the "Software"), to deal in the Software without
51141cc406Sopenharmony_ci   restriction, including without limitation the rights to use, copy,
52141cc406Sopenharmony_ci   modify, merge, publish, distribute, sublicense, and/or sell copies
53141cc406Sopenharmony_ci   of the Software, and to permit persons to whom the Software is
54141cc406Sopenharmony_ci   furnished to do so, subject to the following conditions:
55141cc406Sopenharmony_ci
56141cc406Sopenharmony_ci   The above copyright notice and this permission notice shall be
57141cc406Sopenharmony_ci   included in all copies or substantial portions of the Software.
58141cc406Sopenharmony_ci
59141cc406Sopenharmony_ci   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
60141cc406Sopenharmony_ci   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
61141cc406Sopenharmony_ci   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
62141cc406Sopenharmony_ci   NONINFRINGEMENT.  IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY
63141cc406Sopenharmony_ci   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
64141cc406Sopenharmony_ci   CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
65141cc406Sopenharmony_ci   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
66141cc406Sopenharmony_ci
67141cc406Sopenharmony_ci#ifdef _AIX
68141cc406Sopenharmony_ci# include "lalloca.h"		/* MUST come first for AIX! */
69141cc406Sopenharmony_ci#endif
70141cc406Sopenharmony_ci
71141cc406Sopenharmony_ci#include "../include/sane/config.h"
72141cc406Sopenharmony_ci#include "lalloca.h"
73141cc406Sopenharmony_ci
74141cc406Sopenharmony_ci#include <assert.h>
75141cc406Sopenharmony_ci#include <ctype.h>
76141cc406Sopenharmony_ci#include <errno.h>
77141cc406Sopenharmony_ci#include <fcntl.h>
78141cc406Sopenharmony_ci#include <limits.h>
79141cc406Sopenharmony_ci#include <math.h>
80141cc406Sopenharmony_ci#include <setjmp.h>
81141cc406Sopenharmony_ci#include <signal.h>
82141cc406Sopenharmony_ci#include <stdio.h>
83141cc406Sopenharmony_ci#include <stdlib.h>
84141cc406Sopenharmony_ci#include <string.h>
85141cc406Sopenharmony_ci#include <sys/types.h>
86141cc406Sopenharmony_ci#include <sys/wait.h>
87141cc406Sopenharmony_ci#include <unistd.h>
88141cc406Sopenharmony_ci
89141cc406Sopenharmony_ci#include "../include/sane/sane.h"
90141cc406Sopenharmony_ci#include "../include/sane/sanei.h"
91141cc406Sopenharmony_ci#include "../include/sane/saneopts.h"
92141cc406Sopenharmony_ci
93141cc406Sopenharmony_ci
94141cc406Sopenharmony_ci#define BACKEND_NAME qcam
95141cc406Sopenharmony_ci#include "../include/sane/sanei_backend.h"
96141cc406Sopenharmony_ci
97141cc406Sopenharmony_ci#ifndef PATH_MAX
98141cc406Sopenharmony_ci# define PATH_MAX	1024
99141cc406Sopenharmony_ci#endif
100141cc406Sopenharmony_ci
101141cc406Sopenharmony_ci#include "../include/sane/sanei_config.h"
102141cc406Sopenharmony_ci#define QCAM_CONFIG_FILE "qcam.conf"
103141cc406Sopenharmony_ci
104141cc406Sopenharmony_ci#include "qcam.h"
105141cc406Sopenharmony_ci
106141cc406Sopenharmony_ci/* status bits */
107141cc406Sopenharmony_ci#define NeedRamTable		(1 << 1)
108141cc406Sopenharmony_ci#define BlackBalanceInProgress	(1 << 6)
109141cc406Sopenharmony_ci#define CameraNotReady		(1 << 7)
110141cc406Sopenharmony_ci
111141cc406Sopenharmony_ci/* lpdata bits: */
112141cc406Sopenharmony_ci#define Cmd0_7		0xff
113141cc406Sopenharmony_ci#define CamRdy2		(   1 << 0)	/* byte mode */
114141cc406Sopenharmony_ci#define Data0_6		(0x7f << 1)	/* byte mode */
115141cc406Sopenharmony_ci
116141cc406Sopenharmony_ci/* lpstatus bits: */
117141cc406Sopenharmony_ci#define CamRdy1		(   1 << 3)	/* nibble mode */
118141cc406Sopenharmony_ci#define Nibble0_3	(0x0f << 4)	/* nibble mode */
119141cc406Sopenharmony_ci#define Data7_11	(0x1f << 3)	/* byte mode */
120141cc406Sopenharmony_ci
121141cc406Sopenharmony_ci/* lpcontrol bits: */
122141cc406Sopenharmony_ci#define Strobe		(   1 << 0)	/* unused */
123141cc406Sopenharmony_ci#define Autofeed	(   1 << 1)
124141cc406Sopenharmony_ci#define Reset_N		(   1 << 2)
125141cc406Sopenharmony_ci#define PCAck		(   1 << 3)
126141cc406Sopenharmony_ci#define BiDir		(   1 << 5)
127141cc406Sopenharmony_ci
128141cc406Sopenharmony_cistatic int num_devices;
129141cc406Sopenharmony_cistatic QC_Device *first_dev;
130141cc406Sopenharmony_cistatic QC_Scanner *first_handle;
131141cc406Sopenharmony_ci
132141cc406Sopenharmony_cistatic const SANE_String_Const resolution_list[] = {
133141cc406Sopenharmony_ci  "Low",			/* million-mode */
134141cc406Sopenharmony_ci  "High",			/* billion-mode */
135141cc406Sopenharmony_ci  0
136141cc406Sopenharmony_ci};
137141cc406Sopenharmony_ci
138141cc406Sopenharmony_cistatic const SANE_Int mono_depth_list[] = {
139141cc406Sopenharmony_ci  2,				/* # of elements */
140141cc406Sopenharmony_ci  4, 6
141141cc406Sopenharmony_ci};
142141cc406Sopenharmony_ci
143141cc406Sopenharmony_cistatic const SANE_Int color_depth_list[] = {
144141cc406Sopenharmony_ci  /*2 */ 1,
145141cc406Sopenharmony_ci  /* # of elements */
146141cc406Sopenharmony_ci  /*16, */ 24
147141cc406Sopenharmony_ci    /* "thousand" mode not implemented yet */
148141cc406Sopenharmony_ci};
149141cc406Sopenharmony_ci
150141cc406Sopenharmony_cistatic const SANE_Int xfer_scale_list[] = {
151141cc406Sopenharmony_ci  3,				/* # of elements */
152141cc406Sopenharmony_ci  1, 2, 4
153141cc406Sopenharmony_ci};
154141cc406Sopenharmony_ci
155141cc406Sopenharmony_cistatic const SANE_Range u8_range = {
156141cc406Sopenharmony_ci  /* min, max, quantization */
157141cc406Sopenharmony_ci  0, 255, 0
158141cc406Sopenharmony_ci};
159141cc406Sopenharmony_ci
160141cc406Sopenharmony_cistatic const SANE_Range brightness_range = {
161141cc406Sopenharmony_ci  /* min, max, quantization */
162141cc406Sopenharmony_ci  0, 254, 0			/* 255 is bulb mode! */
163141cc406Sopenharmony_ci};
164141cc406Sopenharmony_ci
165141cc406Sopenharmony_cistatic const SANE_Range x_range[] = {
166141cc406Sopenharmony_ci  /* min, max, quantization */
167141cc406Sopenharmony_ci  {0, 338, 2},			/* million mode */
168141cc406Sopenharmony_ci  {0, 676, 4},			/* billion mode */
169141cc406Sopenharmony_ci};
170141cc406Sopenharmony_ci
171141cc406Sopenharmony_cistatic const SANE_Range odd_x_range[] = {
172141cc406Sopenharmony_ci  /* min, max, quantization */
173141cc406Sopenharmony_ci  {1, 339, 2},			/* million mode */
174141cc406Sopenharmony_ci  {3, 683, 4},			/* billion mode */
175141cc406Sopenharmony_ci};
176141cc406Sopenharmony_ci
177141cc406Sopenharmony_cistatic const SANE_Range y_range[] = {
178141cc406Sopenharmony_ci  /* min, max, quantization */
179141cc406Sopenharmony_ci  {0, 249, 1},			/* million mode */
180141cc406Sopenharmony_ci  {0, 498, 2},			/* billion mode */
181141cc406Sopenharmony_ci};
182141cc406Sopenharmony_ci
183141cc406Sopenharmony_cistatic const SANE_Range odd_y_range[] = {
184141cc406Sopenharmony_ci  /* min, max, quantization */
185141cc406Sopenharmony_ci  {0, 249, 1},			/* million mode */
186141cc406Sopenharmony_ci  {1, 499, 2},			/* billion mode */
187141cc406Sopenharmony_ci};
188141cc406Sopenharmony_ci
189141cc406Sopenharmony_cistatic const SANE_Range bw_x_range = { 0, 334, 2 };
190141cc406Sopenharmony_cistatic const SANE_Range odd_bw_x_range = { 1, 335, 2 };
191141cc406Sopenharmony_cistatic const SANE_Range bw_y_range = { 0, 241, 1 };
192141cc406Sopenharmony_cistatic const SANE_Range odd_bw_y_range = { 1, 242, 1 };
193141cc406Sopenharmony_ci
194141cc406Sopenharmony_ci#include "../include/sane/sanei_directio.h"
195141cc406Sopenharmony_ci
196141cc406Sopenharmony_ci#define read_lpdata(d)		sanei_inb ((d)->port)
197141cc406Sopenharmony_ci#define read_lpstatus(d)	sanei_inb ((d)->port + 1)
198141cc406Sopenharmony_ci#define read_lpcontrol(d)	sanei_inb ((d)->port + 2)
199141cc406Sopenharmony_ci#define write_lpdata(d,v)	sanei_outb ((d)->port, (v))
200141cc406Sopenharmony_ci#define write_lpcontrol(d,v)	sanei_outb ((d)->port + 2, (v))
201141cc406Sopenharmony_ci
202141cc406Sopenharmony_ci
203141cc406Sopenharmony_cistatic SANE_Status
204141cc406Sopenharmony_cienable_ports (QC_Device * q)
205141cc406Sopenharmony_ci{
206141cc406Sopenharmony_ci  /* better safe than sorry */
207141cc406Sopenharmony_ci  if (q->port < 0x278 || q->port > 0x3bc)
208141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
209141cc406Sopenharmony_ci
210141cc406Sopenharmony_ci  if (sanei_ioperm (q->port, 3, 1) < 0)
211141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
212141cc406Sopenharmony_ci
213141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
214141cc406Sopenharmony_ci}
215141cc406Sopenharmony_ci
216141cc406Sopenharmony_cistatic SANE_Status
217141cc406Sopenharmony_cidisable_ports (QC_Device * q)
218141cc406Sopenharmony_ci{
219141cc406Sopenharmony_ci  if (sanei_ioperm (q->port, 3, 0) < 0)
220141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
221141cc406Sopenharmony_ci
222141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
223141cc406Sopenharmony_ci}
224141cc406Sopenharmony_ci
225141cc406Sopenharmony_ci/* We need a short delay loop -- something well under a millisecond.
226141cc406Sopenharmony_ci   Unfortunately, adding 2 usleep(1)'s to qc_command slowed it down by
227141cc406Sopenharmony_ci   a factor of over 1000 over the same loop with 2 usleep(0)'s, and
228141cc406Sopenharmony_ci   that's too slow -- qc_start was taking over a second to run.  This
229141cc406Sopenharmony_ci   seems to help, but if anyone has a good speed-independent pause
230141cc406Sopenharmony_ci   routine, please tell me. -- Scott
231141cc406Sopenharmony_ci
232141cc406Sopenharmony_ci   If you're worried about hogging the CPU: don't worry, the qcam
233141cc406Sopenharmony_ci   interface leaves you no choice, so this doesn't make the situation
234141cc406Sopenharmony_ci   any worse... */
235141cc406Sopenharmony_ci
236141cc406Sopenharmony_cistatic int
237141cc406Sopenharmony_ciqc_wait (QC_Device * q)
238141cc406Sopenharmony_ci{
239141cc406Sopenharmony_ci  return read_lpstatus (q);
240141cc406Sopenharmony_ci}
241141cc406Sopenharmony_ci
242141cc406Sopenharmony_ci
243141cc406Sopenharmony_ci/* This function uses POSIX fcntl-style locking on a file created in
244141cc406Sopenharmony_ci   the /tmp directory.  Because it uses the Unix record locking
245141cc406Sopenharmony_ci   facility, locks are relinquished automatically on process
246141cc406Sopenharmony_ci   termination, so "dead locks" are not a problem.  (FYI, the lock
247141cc406Sopenharmony_ci   file will remain after process termination, but this is actually
248141cc406Sopenharmony_ci   desired so that the next process need not re-creat(2)e it... just
249141cc406Sopenharmony_ci   lock it.)  The wait argument indicates whether or not this function
250141cc406Sopenharmony_ci   should "block" waiting for the previous lock to be relinquished.
251141cc406Sopenharmony_ci   This is ideal so that multiple processes (eg. qcam) taking
252141cc406Sopenharmony_ci   "snapshots" can peacefully coexist.
253141cc406Sopenharmony_ci
254141cc406Sopenharmony_ci   -- Dave Plonka (plonka@carroll1.cc.edu) */
255141cc406Sopenharmony_cistatic SANE_Status
256141cc406Sopenharmony_ciqc_lock_wait (QC_Device * q, int wait)
257141cc406Sopenharmony_ci{
258141cc406Sopenharmony_ci#ifdef F_SETLK
259141cc406Sopenharmony_ci
260141cc406Sopenharmony_ci#ifndef HAVE_STRUCT_FLOCK
261141cc406Sopenharmony_ci  struct flock
262141cc406Sopenharmony_ci  {
263141cc406Sopenharmony_ci    off_t l_start;
264141cc406Sopenharmony_ci    off_t l_len;
265141cc406Sopenharmony_ci    pid_t l_pid;
266141cc406Sopenharmony_ci    short l_type;
267141cc406Sopenharmony_ci    short l_whence;
268141cc406Sopenharmony_ci  };
269141cc406Sopenharmony_ci#endif /* !HAVE_STRUCT_FLOCK */
270141cc406Sopenharmony_ci  struct flock sfl;
271141cc406Sopenharmony_ci#endif
272141cc406Sopenharmony_ci
273141cc406Sopenharmony_ci  DBG (3, "qc_lock_wait: acquiring lock for 0x%x\n", q->port);
274141cc406Sopenharmony_ci
275141cc406Sopenharmony_ci#ifdef F_SETLK
276141cc406Sopenharmony_ci  memset (&sfl, 0, sizeof (sfl));
277141cc406Sopenharmony_ci#endif
278141cc406Sopenharmony_ci
279141cc406Sopenharmony_ci  if (q->lock_fd < 0)
280141cc406Sopenharmony_ci    {
281141cc406Sopenharmony_ci      char lockfile[128];
282141cc406Sopenharmony_ci
283141cc406Sopenharmony_ci      sprintf (lockfile, "/tmp/LOCK.qcam.0x%x", q->port);
284141cc406Sopenharmony_ci      q->lock_fd = open (lockfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
285141cc406Sopenharmony_ci      if (q->lock_fd < 0)
286141cc406Sopenharmony_ci	{
287141cc406Sopenharmony_ci	  DBG (1, "qc_lock_wait: failed to open %s (%s)\n",
288141cc406Sopenharmony_ci	       lockfile, strerror (errno));
289141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
290141cc406Sopenharmony_ci	}
291141cc406Sopenharmony_ci
292141cc406Sopenharmony_ci    }
293141cc406Sopenharmony_ci
294141cc406Sopenharmony_ci#ifdef F_SETLK
295141cc406Sopenharmony_ci  sfl.l_type = F_WRLCK;
296141cc406Sopenharmony_ci  if (fcntl (q->lock_fd, wait ? F_SETLKW : F_SETLK, &sfl) != 0)
297141cc406Sopenharmony_ci    {
298141cc406Sopenharmony_ci      DBG (1, "qc_lock_wait: failed to acquire lock (%s)\n",
299141cc406Sopenharmony_ci	   strerror (errno));
300141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
301141cc406Sopenharmony_ci    }
302141cc406Sopenharmony_ci#endif
303141cc406Sopenharmony_ci
304141cc406Sopenharmony_ci  DBG (3, "qc_lock_wait: got lock for 0x%x\n", q->port);
305141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
306141cc406Sopenharmony_ci}
307141cc406Sopenharmony_ci
308141cc406Sopenharmony_cistatic SANE_Status
309141cc406Sopenharmony_ciqc_unlock (QC_Device * q)
310141cc406Sopenharmony_ci{
311141cc406Sopenharmony_ci  SANE_Status status;
312141cc406Sopenharmony_ci  char lockfile[128];
313141cc406Sopenharmony_ci#ifdef F_SETLK
314141cc406Sopenharmony_ci#ifndef HAVE_STRUCT_FLOCK
315141cc406Sopenharmony_ci  struct flock
316141cc406Sopenharmony_ci  {
317141cc406Sopenharmony_ci    off_t l_start;
318141cc406Sopenharmony_ci    off_t l_len;
319141cc406Sopenharmony_ci    pid_t l_pid;
320141cc406Sopenharmony_ci    short l_type;
321141cc406Sopenharmony_ci    short l_whence;
322141cc406Sopenharmony_ci  };
323141cc406Sopenharmony_ci#endif /* !HAVE_STRUCT_FLOCK */
324141cc406Sopenharmony_ci  struct flock sfl;
325141cc406Sopenharmony_ci#endif
326141cc406Sopenharmony_ci
327141cc406Sopenharmony_ci  DBG (3, "qc_unlock: releasing lock for 0x%x\n", q->port);
328141cc406Sopenharmony_ci
329141cc406Sopenharmony_ci#ifdef F_SETLK
330141cc406Sopenharmony_ci  memset (&sfl, 0, sizeof (sfl));
331141cc406Sopenharmony_ci#endif
332141cc406Sopenharmony_ci  if (q->lock_fd < 0)
333141cc406Sopenharmony_ci    {
334141cc406Sopenharmony_ci      DBG (3, "qc_unlock; port was not locked\n");
335141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
336141cc406Sopenharmony_ci    }
337141cc406Sopenharmony_ci  /* clear the exclusive lock */
338141cc406Sopenharmony_ci
339141cc406Sopenharmony_ci#ifdef F_SETLK
340141cc406Sopenharmony_ci  sfl.l_type = F_UNLCK;
341141cc406Sopenharmony_ci
342141cc406Sopenharmony_ci  if (fcntl (q->lock_fd, F_SETLK, &sfl) != 0)
343141cc406Sopenharmony_ci    {
344141cc406Sopenharmony_ci      DBG (3, "qc_unlock: failed to release lock (%s)\n", strerror (errno));
345141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
346141cc406Sopenharmony_ci    }
347141cc406Sopenharmony_ci#endif
348141cc406Sopenharmony_ci  sprintf (lockfile, "/tmp/LOCK.qcam.0x%x", q->port);
349141cc406Sopenharmony_ci  DBG (1, "qc_unlock: /tmp/LOCK.qcam.0x%x\n", q->port);
350141cc406Sopenharmony_ci  unlink (lockfile);
351141cc406Sopenharmony_ci  close (q->lock_fd);
352141cc406Sopenharmony_ci  q->lock_fd = -1;
353141cc406Sopenharmony_ci  DBG (1, "qc_unlock: exit\n");
354141cc406Sopenharmony_ci  status = SANE_STATUS_GOOD;
355141cc406Sopenharmony_ci  return status;
356141cc406Sopenharmony_ci}
357141cc406Sopenharmony_ci
358141cc406Sopenharmony_cistatic SANE_Status
359141cc406Sopenharmony_ciqc_lock (QC_Device * q)
360141cc406Sopenharmony_ci{
361141cc406Sopenharmony_ci  return qc_lock_wait (q, 1);
362141cc406Sopenharmony_ci}
363141cc406Sopenharmony_ci
364141cc406Sopenharmony_ci/* Busy-waits for a handshake signal from the QuickCam.  Almost all
365141cc406Sopenharmony_ci   communication with the camera requires handshaking.  This is why
366141cc406Sopenharmony_ci   qcam is a CPU hog.  */
367141cc406Sopenharmony_cistatic int
368141cc406Sopenharmony_ciqc_waithand (QC_Device * q, int val)
369141cc406Sopenharmony_ci{
370141cc406Sopenharmony_ci  int status;
371141cc406Sopenharmony_ci
372141cc406Sopenharmony_ci  while (((status = read_lpstatus (q)) & CamRdy1) != val);
373141cc406Sopenharmony_ci  return status;
374141cc406Sopenharmony_ci}
375141cc406Sopenharmony_ci
376141cc406Sopenharmony_ci/* This is used when the qcam is in bidirectional ("byte") mode, and
377141cc406Sopenharmony_ci   the handshaking signal is CamRdy2 (bit 0 of data reg) instead of
378141cc406Sopenharmony_ci   CamRdy1 (bit 3 of status register).  It also returns the last value
379141cc406Sopenharmony_ci   read, since this data is useful.  */
380141cc406Sopenharmony_cistatic unsigned int
381141cc406Sopenharmony_ciqc_waithand2 (QC_Device * q, int val)
382141cc406Sopenharmony_ci{
383141cc406Sopenharmony_ci  unsigned int status;
384141cc406Sopenharmony_ci
385141cc406Sopenharmony_ci  do
386141cc406Sopenharmony_ci    {
387141cc406Sopenharmony_ci      status = read_lpdata (q);
388141cc406Sopenharmony_ci    }
389141cc406Sopenharmony_ci  while ((status & CamRdy2) != (unsigned int) val);
390141cc406Sopenharmony_ci  return status;
391141cc406Sopenharmony_ci}
392141cc406Sopenharmony_ci
393141cc406Sopenharmony_cistatic unsigned int
394141cc406Sopenharmony_ciqc_send (QC_Device * q, unsigned int byte)
395141cc406Sopenharmony_ci{
396141cc406Sopenharmony_ci  unsigned int echo;
397141cc406Sopenharmony_ci  int n1, n2;
398141cc406Sopenharmony_ci
399141cc406Sopenharmony_ci  write_lpdata (q, byte);
400141cc406Sopenharmony_ci  qc_wait (q);
401141cc406Sopenharmony_ci  write_lpcontrol (q, Autofeed | Reset_N);
402141cc406Sopenharmony_ci  qc_wait (q);
403141cc406Sopenharmony_ci
404141cc406Sopenharmony_ci  n1 = qc_waithand (q, CamRdy1);
405141cc406Sopenharmony_ci
406141cc406Sopenharmony_ci  write_lpcontrol (q, Autofeed | Reset_N | PCAck);
407141cc406Sopenharmony_ci  qc_wait (q);
408141cc406Sopenharmony_ci  n2 = qc_waithand (q, 0);
409141cc406Sopenharmony_ci
410141cc406Sopenharmony_ci  echo = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
411141cc406Sopenharmony_ci#ifndef NDEBUG
412141cc406Sopenharmony_ci  if (echo != byte)
413141cc406Sopenharmony_ci    {
414141cc406Sopenharmony_ci      DBG (1, "qc_send: sent 0x%02x, camera echoed 0x%02x\n", byte, echo);
415141cc406Sopenharmony_ci      n2 = read_lpstatus (q);
416141cc406Sopenharmony_ci      echo = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
417141cc406Sopenharmony_ci      if (echo != byte)
418141cc406Sopenharmony_ci	DBG (1, "qc_send: (re-read does not help)\n");
419141cc406Sopenharmony_ci      else
420141cc406Sopenharmony_ci	DBG (1, "qc_send: (fixed on re-read)\n");
421141cc406Sopenharmony_ci    }
422141cc406Sopenharmony_ci#endif
423141cc406Sopenharmony_ci  return echo;
424141cc406Sopenharmony_ci}
425141cc406Sopenharmony_ci
426141cc406Sopenharmony_cistatic int
427141cc406Sopenharmony_ciqc_readparam (QC_Device * q)
428141cc406Sopenharmony_ci{
429141cc406Sopenharmony_ci  int n1, n2;
430141cc406Sopenharmony_ci  int cmd;
431141cc406Sopenharmony_ci
432141cc406Sopenharmony_ci  write_lpcontrol (q, Autofeed | Reset_N);	/* clear PCAck */
433141cc406Sopenharmony_ci  n1 = qc_waithand (q, CamRdy1);
434141cc406Sopenharmony_ci
435141cc406Sopenharmony_ci  write_lpcontrol (q, Autofeed | Reset_N | PCAck);	/* set PCAck */
436141cc406Sopenharmony_ci  n2 = qc_waithand (q, 0);
437141cc406Sopenharmony_ci
438141cc406Sopenharmony_ci  cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
439141cc406Sopenharmony_ci  return cmd;
440141cc406Sopenharmony_ci}
441141cc406Sopenharmony_ci
442141cc406Sopenharmony_cistatic unsigned int
443141cc406Sopenharmony_ciqc_getstatus (QC_Device * q)
444141cc406Sopenharmony_ci{
445141cc406Sopenharmony_ci  unsigned int status;
446141cc406Sopenharmony_ci
447141cc406Sopenharmony_ci  qc_send (q, QC_SEND_STATUS);
448141cc406Sopenharmony_ci  status = qc_readparam (q);
449141cc406Sopenharmony_ci  DBG (3, "qc_getstatus: status=0x%02x\n", status);
450141cc406Sopenharmony_ci  return status;
451141cc406Sopenharmony_ci}
452141cc406Sopenharmony_ci
453141cc406Sopenharmony_cistatic void
454141cc406Sopenharmony_ciqc_setscanmode (QC_Scanner * s, u_int * modep)
455141cc406Sopenharmony_ci{
456141cc406Sopenharmony_ci  QC_Device *q = s->hw;
457141cc406Sopenharmony_ci  u_int mode = 0;
458141cc406Sopenharmony_ci
459141cc406Sopenharmony_ci  if (q->version != QC_COLOR)
460141cc406Sopenharmony_ci    {
461141cc406Sopenharmony_ci      switch (s->val[OPT_XFER_SCALE].w)
462141cc406Sopenharmony_ci	{
463141cc406Sopenharmony_ci	case 1:
464141cc406Sopenharmony_ci	  mode = 0;
465141cc406Sopenharmony_ci	  break;
466141cc406Sopenharmony_ci	case 2:
467141cc406Sopenharmony_ci	  mode = 4;
468141cc406Sopenharmony_ci	  break;
469141cc406Sopenharmony_ci	case 4:
470141cc406Sopenharmony_ci	  mode = 8;
471141cc406Sopenharmony_ci	  break;
472141cc406Sopenharmony_ci	}
473141cc406Sopenharmony_ci      switch (s->val[OPT_DEPTH].w)
474141cc406Sopenharmony_ci	{
475141cc406Sopenharmony_ci	case 4:
476141cc406Sopenharmony_ci	  break;
477141cc406Sopenharmony_ci	case 6:
478141cc406Sopenharmony_ci	  mode += 2;
479141cc406Sopenharmony_ci	  break;
480141cc406Sopenharmony_ci	}
481141cc406Sopenharmony_ci    }
482141cc406Sopenharmony_ci  else
483141cc406Sopenharmony_ci    {
484141cc406Sopenharmony_ci      switch (s->val[OPT_XFER_SCALE].w)
485141cc406Sopenharmony_ci	{
486141cc406Sopenharmony_ci	case 1:
487141cc406Sopenharmony_ci	  mode = 0;
488141cc406Sopenharmony_ci	  break;
489141cc406Sopenharmony_ci	case 2:
490141cc406Sopenharmony_ci	  mode = 2;
491141cc406Sopenharmony_ci	  break;
492141cc406Sopenharmony_ci	case 4:
493141cc406Sopenharmony_ci	  mode = 4;
494141cc406Sopenharmony_ci	  break;
495141cc406Sopenharmony_ci	}
496141cc406Sopenharmony_ci      if (s->resolution == QC_RES_LOW)
497141cc406Sopenharmony_ci	mode |= 0x18;		/* millions mode */
498141cc406Sopenharmony_ci      else
499141cc406Sopenharmony_ci	mode |= 0x10;		/* billions mode */
500141cc406Sopenharmony_ci    }
501141cc406Sopenharmony_ci  if (s->val[OPT_TEST].w)
502141cc406Sopenharmony_ci    mode |= 0x40;		/* test mode */
503141cc406Sopenharmony_ci
504141cc406Sopenharmony_ci  if (q->port_mode == QC_BIDIR)
505141cc406Sopenharmony_ci    mode |= 1;
506141cc406Sopenharmony_ci
507141cc406Sopenharmony_ci  DBG (2, "scanmode (before increment): 0x%x\n", mode);
508141cc406Sopenharmony_ci
509141cc406Sopenharmony_ci  if (q->version == QC_COLOR)
510141cc406Sopenharmony_ci    ++mode;
511141cc406Sopenharmony_ci
512141cc406Sopenharmony_ci  *modep = mode;
513141cc406Sopenharmony_ci}
514141cc406Sopenharmony_ci
515141cc406Sopenharmony_ci/* Read data bytes from the camera.  The number of bytes read is
516141cc406Sopenharmony_ci   returned as the function result.  Depending on the mode, it may be
517141cc406Sopenharmony_ci   either 1, 3 or 6.  On failure, 0 is returned.  If buffer is 0, the
518141cc406Sopenharmony_ci   internal state-machine is reset.  */
519141cc406Sopenharmony_cistatic size_t
520141cc406Sopenharmony_ciqc_readbytes (QC_Scanner * s, unsigned char buffer[])
521141cc406Sopenharmony_ci{
522141cc406Sopenharmony_ci  QC_Device *q = s->hw;
523141cc406Sopenharmony_ci  unsigned int hi, lo;
524141cc406Sopenharmony_ci  unsigned int hi2, lo2;
525141cc406Sopenharmony_ci  size_t bytes = 0;
526141cc406Sopenharmony_ci
527141cc406Sopenharmony_ci  if (!buffer)
528141cc406Sopenharmony_ci    {
529141cc406Sopenharmony_ci      s->readbytes_state = 0;
530141cc406Sopenharmony_ci      return 0;
531141cc406Sopenharmony_ci    }
532141cc406Sopenharmony_ci
533141cc406Sopenharmony_ci  switch (q->port_mode)
534141cc406Sopenharmony_ci    {
535141cc406Sopenharmony_ci    case QC_BIDIR:
536141cc406Sopenharmony_ci      /* bi-directional port */
537141cc406Sopenharmony_ci
538141cc406Sopenharmony_ci      /* read off 24 bits: */
539141cc406Sopenharmony_ci      write_lpcontrol (q, Autofeed | Reset_N | BiDir);
540141cc406Sopenharmony_ci      lo = qc_waithand2 (q, 1) >> 1;
541141cc406Sopenharmony_ci      hi = (read_lpstatus (q) >> 3) & 0x1f;
542141cc406Sopenharmony_ci      write_lpcontrol (q, Autofeed | Reset_N | PCAck | BiDir);
543141cc406Sopenharmony_ci      lo2 = qc_waithand2 (q, 0) >> 1;
544141cc406Sopenharmony_ci      hi2 = (read_lpstatus (q) >> 3) & 0x1f;
545141cc406Sopenharmony_ci      if (q->version == QC_COLOR)
546141cc406Sopenharmony_ci	{
547141cc406Sopenharmony_ci	  /* is Nibble3 inverted for color quickcams only? */
548141cc406Sopenharmony_ci	  hi ^= 0x10;
549141cc406Sopenharmony_ci	  hi2 ^= 0x10;
550141cc406Sopenharmony_ci	}
551141cc406Sopenharmony_ci      switch (s->val[OPT_DEPTH].w)
552141cc406Sopenharmony_ci	{
553141cc406Sopenharmony_ci	case 4:
554141cc406Sopenharmony_ci	  buffer[0] = lo & 0xf;
555141cc406Sopenharmony_ci	  buffer[1] = ((lo & 0x70) >> 4) | ((hi & 1) << 3);
556141cc406Sopenharmony_ci	  buffer[2] = (hi & 0x1e) >> 1;
557141cc406Sopenharmony_ci	  buffer[3] = lo2 & 0xf;
558141cc406Sopenharmony_ci	  buffer[4] = ((lo2 & 0x70) >> 4) | ((hi2 & 1) << 3);
559141cc406Sopenharmony_ci	  buffer[5] = (hi2 & 0x1e) >> 1;
560141cc406Sopenharmony_ci	  bytes = 6;
561141cc406Sopenharmony_ci	  break;
562141cc406Sopenharmony_ci
563141cc406Sopenharmony_ci	case 6:
564141cc406Sopenharmony_ci	  buffer[0] = lo & 0x3f;
565141cc406Sopenharmony_ci	  buffer[1] = ((lo & 0x40) >> 6) | (hi << 1);
566141cc406Sopenharmony_ci	  buffer[2] = lo2 & 0x3f;
567141cc406Sopenharmony_ci	  buffer[3] = ((lo2 & 0x40) >> 6) | (hi2 << 1);
568141cc406Sopenharmony_ci	  bytes = 4;
569141cc406Sopenharmony_ci	  break;
570141cc406Sopenharmony_ci
571141cc406Sopenharmony_ci	case 24:
572141cc406Sopenharmony_ci	  buffer[0] = lo | ((hi & 0x1) << 7);
573141cc406Sopenharmony_ci	  buffer[1] = ((hi2 & 0x1e) >> 1) | ((hi & 0x1e) << 3);
574141cc406Sopenharmony_ci	  buffer[2] = lo2 | ((hi2 & 0x1) << 7);
575141cc406Sopenharmony_ci	  bytes = 3;
576141cc406Sopenharmony_ci	  break;
577141cc406Sopenharmony_ci	}
578141cc406Sopenharmony_ci      break;
579141cc406Sopenharmony_ci
580141cc406Sopenharmony_ci    case QC_UNIDIR:		/* Unidirectional Port */
581141cc406Sopenharmony_ci      write_lpcontrol (q, Autofeed | Reset_N);
582141cc406Sopenharmony_ci      lo = (qc_waithand (q, CamRdy1) & 0xf0) >> 4;
583141cc406Sopenharmony_ci      write_lpcontrol (q, Autofeed | Reset_N | PCAck);
584141cc406Sopenharmony_ci      hi = (qc_waithand (q, 0) & 0xf0) >> 4;
585141cc406Sopenharmony_ci
586141cc406Sopenharmony_ci      if (q->version == QC_COLOR)
587141cc406Sopenharmony_ci	{
588141cc406Sopenharmony_ci	  /* invert Nibble3 */
589141cc406Sopenharmony_ci	  hi ^= 8;
590141cc406Sopenharmony_ci	  lo ^= 8;
591141cc406Sopenharmony_ci	}
592141cc406Sopenharmony_ci
593141cc406Sopenharmony_ci      switch (s->val[OPT_DEPTH].w)
594141cc406Sopenharmony_ci	{
595141cc406Sopenharmony_ci	case 4:
596141cc406Sopenharmony_ci	  buffer[0] = lo;
597141cc406Sopenharmony_ci	  buffer[1] = hi;
598141cc406Sopenharmony_ci	  bytes = 2;
599141cc406Sopenharmony_ci	  break;
600141cc406Sopenharmony_ci
601141cc406Sopenharmony_ci	case 6:
602141cc406Sopenharmony_ci	  switch (s->readbytes_state)
603141cc406Sopenharmony_ci	    {
604141cc406Sopenharmony_ci	    case 0:
605141cc406Sopenharmony_ci	      buffer[0] = (lo << 2) | ((hi & 0xc) >> 2);
606141cc406Sopenharmony_ci	      s->saved_bits = (hi & 3) << 4;
607141cc406Sopenharmony_ci	      s->readbytes_state = 1;
608141cc406Sopenharmony_ci	      bytes = 1;
609141cc406Sopenharmony_ci	      break;
610141cc406Sopenharmony_ci
611141cc406Sopenharmony_ci	    case 1:
612141cc406Sopenharmony_ci	      buffer[0] = lo | s->saved_bits;
613141cc406Sopenharmony_ci	      s->saved_bits = hi << 2;
614141cc406Sopenharmony_ci	      s->readbytes_state = 2;
615141cc406Sopenharmony_ci	      bytes = 1;
616141cc406Sopenharmony_ci	      break;
617141cc406Sopenharmony_ci
618141cc406Sopenharmony_ci	    case 2:
619141cc406Sopenharmony_ci	      buffer[0] = ((lo & 0xc) >> 2) | s->saved_bits;
620141cc406Sopenharmony_ci	      buffer[1] = ((lo & 3) << 4) | hi;
621141cc406Sopenharmony_ci	      s->readbytes_state = 0;
622141cc406Sopenharmony_ci	      bytes = 2;
623141cc406Sopenharmony_ci	      break;
624141cc406Sopenharmony_ci
625141cc406Sopenharmony_ci	    default:
626141cc406Sopenharmony_ci	      DBG (1, "qc_readbytes: bad unidir 6-bit stat %d\n",
627141cc406Sopenharmony_ci		   s->readbytes_state);
628141cc406Sopenharmony_ci	      break;
629141cc406Sopenharmony_ci	    }
630141cc406Sopenharmony_ci	  break;
631141cc406Sopenharmony_ci
632141cc406Sopenharmony_ci	case 24:
633141cc406Sopenharmony_ci	  buffer[0] = (lo << 4) | hi;
634141cc406Sopenharmony_ci	  bytes = 1;
635141cc406Sopenharmony_ci	  break;
636141cc406Sopenharmony_ci
637141cc406Sopenharmony_ci	default:
638141cc406Sopenharmony_ci	  DBG (1, "qc_readbytes: bad unidir bit depth %d\n",
639141cc406Sopenharmony_ci	       s->val[OPT_DEPTH].w);
640141cc406Sopenharmony_ci	  break;
641141cc406Sopenharmony_ci	}
642141cc406Sopenharmony_ci      break;
643141cc406Sopenharmony_ci
644141cc406Sopenharmony_ci    default:
645141cc406Sopenharmony_ci      DBG (1, "qc_readbytes: bad port_mode %d\n", q->port_mode);
646141cc406Sopenharmony_ci      break;
647141cc406Sopenharmony_ci    }
648141cc406Sopenharmony_ci  return bytes;
649141cc406Sopenharmony_ci}
650141cc406Sopenharmony_ci
651141cc406Sopenharmony_cistatic void
652141cc406Sopenharmony_ciqc_reset (QC_Device * q)
653141cc406Sopenharmony_ci{
654141cc406Sopenharmony_ci  write_lpcontrol (q, Strobe | Autofeed | Reset_N | PCAck);
655141cc406Sopenharmony_ci  qc_wait (q);
656141cc406Sopenharmony_ci  write_lpcontrol (q, Strobe | Autofeed | PCAck);
657141cc406Sopenharmony_ci  qc_wait (q);
658141cc406Sopenharmony_ci  write_lpcontrol (q, Strobe | Autofeed | Reset_N | PCAck);
659141cc406Sopenharmony_ci}
660141cc406Sopenharmony_ci
661141cc406Sopenharmony_ci/* This function is executed as a child process.  The reason this is
662141cc406Sopenharmony_ci   executed as a subprocess is because the qcam interface directly reads
663141cc406Sopenharmony_ci   off of a I/O port (rather than a filedescriptor).  Thus, to have
664141cc406Sopenharmony_ci   something to select() on, we transfer the data through a pipe.
665141cc406Sopenharmony_ci
666141cc406Sopenharmony_ci   WARNING: Since this is executed as a subprocess, it's NOT possible
667141cc406Sopenharmony_ci   to update any of the variables in the main process (in particular
668141cc406Sopenharmony_ci   the scanner state cannot be updated).  */
669141cc406Sopenharmony_ci
670141cc406Sopenharmony_cistatic jmp_buf env;
671141cc406Sopenharmony_ci
672141cc406Sopenharmony_cistatic void
673141cc406Sopenharmony_cisighandler (int signal)
674141cc406Sopenharmony_ci{
675141cc406Sopenharmony_ci  DBG (3, "sighandler: got signal %d\n", signal);
676141cc406Sopenharmony_ci  longjmp (env, 1);
677141cc406Sopenharmony_ci}
678141cc406Sopenharmony_ci
679141cc406Sopenharmony_ci/* Original despeckling code by Patrick Reynolds <patrickr@virginia.edu> */
680141cc406Sopenharmony_ci
681141cc406Sopenharmony_cistatic void
682141cc406Sopenharmony_cidespeckle (int width, int height, SANE_Byte * in, SANE_Byte * out)
683141cc406Sopenharmony_ci{
684141cc406Sopenharmony_ci  long x, i;
685141cc406Sopenharmony_ci  /* The light-check threshold.  Higher numbers remove more lights but
686141cc406Sopenharmony_ci     blur the image more.  30 is good for indoor lighting.  */
687141cc406Sopenharmony_ci# define NO_LIGHTS 30
688141cc406Sopenharmony_ci
689141cc406Sopenharmony_ci  /* macros to make the code a little more readable, p=previous, n=next */
690141cc406Sopenharmony_ci# define R	in[i*3]
691141cc406Sopenharmony_ci# define G	in[i*3+1]
692141cc406Sopenharmony_ci# define B	in[i*3+2]
693141cc406Sopenharmony_ci# define pR	in[i*3-3]
694141cc406Sopenharmony_ci# define pG	in[i*3-2]
695141cc406Sopenharmony_ci# define pB	in[i*3-1]
696141cc406Sopenharmony_ci# define nR	in[i*3+3]
697141cc406Sopenharmony_ci# define nG	in[i*3+4]
698141cc406Sopenharmony_ci# define nB	in[i*3+5]
699141cc406Sopenharmony_ci
700141cc406Sopenharmony_ci  DBG (1, "despeckle: width=%d, height=%d\n", width, height);
701141cc406Sopenharmony_ci
702141cc406Sopenharmony_ci  for (x = i = 0; i < width * height; ++i)
703141cc406Sopenharmony_ci    {
704141cc406Sopenharmony_ci      if (x == 0 || x == width - 1)
705141cc406Sopenharmony_ci	memcpy (&out[i * 3], &in[i * 3], 3);
706141cc406Sopenharmony_ci      else
707141cc406Sopenharmony_ci	{
708141cc406Sopenharmony_ci	  if (R - (G + B) / 2 >
709141cc406Sopenharmony_ci	      NO_LIGHTS + ((pR - (pG + pB) / 2) + (nR - (nG + nB) / 2)))
710141cc406Sopenharmony_ci	    out[i * 3] = (pR + nR) / 2;
711141cc406Sopenharmony_ci	  else
712141cc406Sopenharmony_ci	    out[i * 3] = R;
713141cc406Sopenharmony_ci
714141cc406Sopenharmony_ci	  if (G - (R + B) / 2 >
715141cc406Sopenharmony_ci	      NO_LIGHTS + ((pG - (pR + pB) / 2) + (nG - (nR + nB) / 2)))
716141cc406Sopenharmony_ci	    out[i * 3 + 1] = (pG + nG) / 2;
717141cc406Sopenharmony_ci	  else
718141cc406Sopenharmony_ci	    out[i * 3 + 1] = G;
719141cc406Sopenharmony_ci
720141cc406Sopenharmony_ci	  if (B - (G + R) / 2 >
721141cc406Sopenharmony_ci	      NO_LIGHTS + ((pB - (pG + pR) / 2) + (nB - (nG + nR) / 2)))
722141cc406Sopenharmony_ci	    out[i * 3 + 2] = (pB + nB) / 2;
723141cc406Sopenharmony_ci	  else
724141cc406Sopenharmony_ci	    out[i * 3 + 2] = B;
725141cc406Sopenharmony_ci	}
726141cc406Sopenharmony_ci      if (++x >= width)
727141cc406Sopenharmony_ci	x = 0;
728141cc406Sopenharmony_ci    }
729141cc406Sopenharmony_ci# undef R
730141cc406Sopenharmony_ci# undef G
731141cc406Sopenharmony_ci# undef B
732141cc406Sopenharmony_ci# undef pR
733141cc406Sopenharmony_ci# undef pG
734141cc406Sopenharmony_ci# undef pB
735141cc406Sopenharmony_ci# undef nR
736141cc406Sopenharmony_ci# undef nG
737141cc406Sopenharmony_ci# undef nB
738141cc406Sopenharmony_ci}
739141cc406Sopenharmony_ci
740141cc406Sopenharmony_cistatic void
741141cc406Sopenharmony_cidespeckle32 (int width, int height, SANE_Byte * in, SANE_Byte * out)
742141cc406Sopenharmony_ci{
743141cc406Sopenharmony_ci  long x, i;
744141cc406Sopenharmony_ci  /* macros to make the code a little more readable, p=previous, n=next */
745141cc406Sopenharmony_ci# define B	in[i*4]
746141cc406Sopenharmony_ci# define Ga	in[i*4 + 1]
747141cc406Sopenharmony_ci# define Gb	in[i*4 + 1]	/* ignore Gb and use Ga instead---Gb is weird */
748141cc406Sopenharmony_ci# define R	in[i*4 + 3]
749141cc406Sopenharmony_ci# define pB	in[i*4 - 4]
750141cc406Sopenharmony_ci# define pGa	in[i*4 - 3]
751141cc406Sopenharmony_ci# define pGb	in[i*4 - 1]	/* ignore Gb and use Ga instead---Gb is weird */
752141cc406Sopenharmony_ci# define pR	in[i*4 - 1]
753141cc406Sopenharmony_ci# define nB	in[i*4 + 4]
754141cc406Sopenharmony_ci# define nGa	in[i*4 + 5]
755141cc406Sopenharmony_ci# define nGb	in[i*4 + 5]	/* ignore Gb and use Ga instead---Gb is weird */
756141cc406Sopenharmony_ci# define nR	in[i*4 + 7]
757141cc406Sopenharmony_ci
758141cc406Sopenharmony_ci  DBG (1, "despeckle32: width=%d, height=%d\n", width, height);
759141cc406Sopenharmony_ci
760141cc406Sopenharmony_ci  for (x = i = 0; i < width * height; ++i)
761141cc406Sopenharmony_ci    {
762141cc406Sopenharmony_ci      if (x == 0 || x == width - 1)
763141cc406Sopenharmony_ci	memcpy (&out[i * 4], &in[i * 4], 4);
764141cc406Sopenharmony_ci      else
765141cc406Sopenharmony_ci	{
766141cc406Sopenharmony_ci	  if (x >= width - 2)
767141cc406Sopenharmony_ci	    /* the last red pixel seems to be black at all times, use
768141cc406Sopenharmony_ci	       R instead: */
769141cc406Sopenharmony_ci	    nR = R;
770141cc406Sopenharmony_ci
771141cc406Sopenharmony_ci	  if (R - ((Ga + Gb) / 2 + B) / 2 >
772141cc406Sopenharmony_ci	      NO_LIGHTS + ((pR - ((pGa + pGb) / 2 + pB) / 2) +
773141cc406Sopenharmony_ci			   (nR - ((nGa + nGb) / 2 + nB) / 2)))
774141cc406Sopenharmony_ci	    out[i * 4 + 3] = (pR + nR) / 2;
775141cc406Sopenharmony_ci	  else
776141cc406Sopenharmony_ci	    out[i * 4 + 3] = R;
777141cc406Sopenharmony_ci
778141cc406Sopenharmony_ci	  if (Ga - (R + B) / 2 > NO_LIGHTS + ((pGa - (pR + pB) / 2) +
779141cc406Sopenharmony_ci					      (nGa - (nR + nB) / 2)))
780141cc406Sopenharmony_ci	    out[i * 4 + 1] = (pGa + nGa) / 2;
781141cc406Sopenharmony_ci	  else
782141cc406Sopenharmony_ci	    out[i * 4 + 1] = Ga;
783141cc406Sopenharmony_ci
784141cc406Sopenharmony_ci	  if (Gb - (R + B) / 2 > NO_LIGHTS + ((pGb - (pR + pB) / 2) +
785141cc406Sopenharmony_ci					      (nGb - (nR + nB) / 2)))
786141cc406Sopenharmony_ci	    out[i * 4 + 2] = (pGb + nGb) / 2;
787141cc406Sopenharmony_ci	  else
788141cc406Sopenharmony_ci	    out[i * 4 + 2] = Gb;
789141cc406Sopenharmony_ci
790141cc406Sopenharmony_ci	  if (B - ((Ga + Gb) / 2 + R) / 2 >
791141cc406Sopenharmony_ci	      NO_LIGHTS + ((pB - ((pGa + pGb) / 2 + pR) / 2) +
792141cc406Sopenharmony_ci			   (nB - ((nGa + nGb) / 2 + nR) / 2)))
793141cc406Sopenharmony_ci	    out[i * 4 + 0] = (pB + nB) / 2;
794141cc406Sopenharmony_ci	  else
795141cc406Sopenharmony_ci	    out[i * 4 + 0] = B;
796141cc406Sopenharmony_ci	}
797141cc406Sopenharmony_ci      if (++x >= width)
798141cc406Sopenharmony_ci	x = 0;
799141cc406Sopenharmony_ci    }
800141cc406Sopenharmony_ci# undef R
801141cc406Sopenharmony_ci# undef Ga
802141cc406Sopenharmony_ci# undef Gb
803141cc406Sopenharmony_ci# undef B
804141cc406Sopenharmony_ci# undef pR
805141cc406Sopenharmony_ci# undef pGa
806141cc406Sopenharmony_ci# undef pGb
807141cc406Sopenharmony_ci# undef pB
808141cc406Sopenharmony_ci# undef nR
809141cc406Sopenharmony_ci# undef nGa
810141cc406Sopenharmony_ci# undef nGb
811141cc406Sopenharmony_ci# undef nB
812141cc406Sopenharmony_ci}
813141cc406Sopenharmony_ci
814141cc406Sopenharmony_cistatic int
815141cc406Sopenharmony_cireader_process (QC_Scanner * s, int in_fd, int out_fd)
816141cc406Sopenharmony_ci{
817141cc406Sopenharmony_ci  static SANE_Byte *buffer = 0, *extra = 0;
818141cc406Sopenharmony_ci  static size_t buffer_size = 0;
819141cc406Sopenharmony_ci  size_t count, len, num_bytes;
820141cc406Sopenharmony_ci  QC_Device *q = s->hw;
821141cc406Sopenharmony_ci  QC_Scan_Request req;
822141cc406Sopenharmony_ci  int width, height;
823141cc406Sopenharmony_ci  SANE_Byte *src;
824141cc406Sopenharmony_ci  FILE *ofp;
825141cc406Sopenharmony_ci
826141cc406Sopenharmony_ci  DBG (5, "reader_process: enter\n");
827141cc406Sopenharmony_ci
828141cc406Sopenharmony_ci  enable_ports (q);
829141cc406Sopenharmony_ci
830141cc406Sopenharmony_ci  ofp = fdopen (out_fd, "w");
831141cc406Sopenharmony_ci  if (!ofp)
832141cc406Sopenharmony_ci    return 1;
833141cc406Sopenharmony_ci
834141cc406Sopenharmony_ci  while (1)
835141cc406Sopenharmony_ci    {
836141cc406Sopenharmony_ci      if (setjmp (env))
837141cc406Sopenharmony_ci	{
838141cc406Sopenharmony_ci	  char ch;
839141cc406Sopenharmony_ci
840141cc406Sopenharmony_ci	  /* acknowledge the signal: */
841141cc406Sopenharmony_ci	  DBG (1, "reader_process: sending signal ACK\n");
842141cc406Sopenharmony_ci	  fwrite (&ch, 1, 1, ofp);
843141cc406Sopenharmony_ci	  fflush (ofp);		/* force everything out the pipe */
844141cc406Sopenharmony_ci	  continue;
845141cc406Sopenharmony_ci	}
846141cc406Sopenharmony_ci      signal (SIGINT, sighandler);
847141cc406Sopenharmony_ci
848141cc406Sopenharmony_ci      /* the main process gets us started by writing a size_t giving
849141cc406Sopenharmony_ci         the number of bytes we should expect: */
850141cc406Sopenharmony_ci      if (read (in_fd, &req, sizeof (req)) != sizeof (req))
851141cc406Sopenharmony_ci	{
852141cc406Sopenharmony_ci	  perror ("read");
853141cc406Sopenharmony_ci	  return 1;
854141cc406Sopenharmony_ci	}
855141cc406Sopenharmony_ci      num_bytes = req.num_bytes;
856141cc406Sopenharmony_ci
857141cc406Sopenharmony_ci      DBG (3, "reader_process: got request for %lu bytes\n",
858141cc406Sopenharmony_ci	   (u_long) num_bytes);
859141cc406Sopenharmony_ci
860141cc406Sopenharmony_ci      /* Don't do this in sane_start() since there may be a long
861141cc406Sopenharmony_ci         timespan between it and the first sane_read(), which would
862141cc406Sopenharmony_ci         result in poor images.  */
863141cc406Sopenharmony_ci      qc_send (q, QC_SEND_VIDEO_FRAME);
864141cc406Sopenharmony_ci      qc_send (q, req.mode);
865141cc406Sopenharmony_ci
866141cc406Sopenharmony_ci      if (req.despeckle
867141cc406Sopenharmony_ci	  && (!extra || buffer_size < num_bytes
868141cc406Sopenharmony_ci	      || buffer_size >= 2 * num_bytes))
869141cc406Sopenharmony_ci	{
870141cc406Sopenharmony_ci	  if (extra)
871141cc406Sopenharmony_ci	    extra = realloc (extra, num_bytes);
872141cc406Sopenharmony_ci	  else
873141cc406Sopenharmony_ci	    extra = malloc (num_bytes);
874141cc406Sopenharmony_ci	  if (!extra)
875141cc406Sopenharmony_ci	    {
876141cc406Sopenharmony_ci	      DBG (1, "reader_process: malloc(%ld) failed\n",
877141cc406Sopenharmony_ci		   (long) num_bytes);
878141cc406Sopenharmony_ci	      exit (1);
879141cc406Sopenharmony_ci	    }
880141cc406Sopenharmony_ci	}
881141cc406Sopenharmony_ci
882141cc406Sopenharmony_ci      if (buffer_size < num_bytes || buffer_size >= 2 * num_bytes)
883141cc406Sopenharmony_ci	{
884141cc406Sopenharmony_ci	  if (buffer)
885141cc406Sopenharmony_ci	    buffer = realloc (buffer, num_bytes);
886141cc406Sopenharmony_ci	  else
887141cc406Sopenharmony_ci	    buffer = malloc (num_bytes);
888141cc406Sopenharmony_ci	  if (!buffer)
889141cc406Sopenharmony_ci	    {
890141cc406Sopenharmony_ci	      DBG (1, "reader_process: malloc(%ld) failed\n",
891141cc406Sopenharmony_ci		   (long) num_bytes);
892141cc406Sopenharmony_ci	      exit (1);
893141cc406Sopenharmony_ci	    }
894141cc406Sopenharmony_ci	  buffer_size = num_bytes;
895141cc406Sopenharmony_ci	}
896141cc406Sopenharmony_ci
897141cc406Sopenharmony_ci      if (q->port_mode == QC_BIDIR)
898141cc406Sopenharmony_ci	{
899141cc406Sopenharmony_ci	  /* turn port into input port */
900141cc406Sopenharmony_ci	  write_lpcontrol (q, Autofeed | Reset_N | PCAck | BiDir);
901141cc406Sopenharmony_ci	  usleep (3);
902141cc406Sopenharmony_ci	  write_lpcontrol (q, Autofeed | Reset_N | BiDir);
903141cc406Sopenharmony_ci	  qc_waithand (q, CamRdy1);
904141cc406Sopenharmony_ci	  write_lpcontrol (q, Autofeed | Reset_N | PCAck | BiDir);
905141cc406Sopenharmony_ci	  qc_waithand (q, 0);
906141cc406Sopenharmony_ci	}
907141cc406Sopenharmony_ci
908141cc406Sopenharmony_ci      if (q->version == QC_COLOR)
909141cc406Sopenharmony_ci	for (len = 0; len < num_bytes; len += count)
910141cc406Sopenharmony_ci	  count = qc_readbytes (s, buffer + len);
911141cc406Sopenharmony_ci      else
912141cc406Sopenharmony_ci	{
913141cc406Sopenharmony_ci	  /* strange -- should be 15:63 below, but 4bpp is odd */
914141cc406Sopenharmony_ci	  int shift, invert;
915141cc406Sopenharmony_ci	  unsigned int i;
916141cc406Sopenharmony_ci	  u_char val;
917141cc406Sopenharmony_ci
918141cc406Sopenharmony_ci	  switch (s->val[OPT_DEPTH].w)
919141cc406Sopenharmony_ci	    {
920141cc406Sopenharmony_ci	    case 4:
921141cc406Sopenharmony_ci	      invert = 16;
922141cc406Sopenharmony_ci	      shift = 4;
923141cc406Sopenharmony_ci	      break;
924141cc406Sopenharmony_ci
925141cc406Sopenharmony_ci	    case 6:
926141cc406Sopenharmony_ci	      invert = 63;
927141cc406Sopenharmony_ci	      shift = 2;
928141cc406Sopenharmony_ci	      break;
929141cc406Sopenharmony_ci
930141cc406Sopenharmony_ci	    default:
931141cc406Sopenharmony_ci	      DBG (1, "reader_process: unexpected depth %d\n",
932141cc406Sopenharmony_ci		   s->val[OPT_DEPTH].w);
933141cc406Sopenharmony_ci	      return 1;
934141cc406Sopenharmony_ci	    }
935141cc406Sopenharmony_ci
936141cc406Sopenharmony_ci	  for (len = 0; len < num_bytes; len += count)
937141cc406Sopenharmony_ci	    {
938141cc406Sopenharmony_ci	      count = qc_readbytes (s, buffer + len);
939141cc406Sopenharmony_ci	      for (i = 0; i < count; ++i)
940141cc406Sopenharmony_ci		{
941141cc406Sopenharmony_ci		  /* 4bpp is odd (again) -- inverter is 16, not 15,
942141cc406Sopenharmony_ci		     but output must be 0-15 */
943141cc406Sopenharmony_ci		  val = buffer[len + i];
944141cc406Sopenharmony_ci		  if (val > 0 || invert != 16)
945141cc406Sopenharmony_ci		    val = invert - val;
946141cc406Sopenharmony_ci		  buffer[len + i] = (val << shift) | (val >> (8 - 2 * shift));
947141cc406Sopenharmony_ci		}
948141cc406Sopenharmony_ci	    }
949141cc406Sopenharmony_ci	  qc_readbytes (s, 0);	/* reset state machine */
950141cc406Sopenharmony_ci	}
951141cc406Sopenharmony_ci      /* we're done reading this frame: */
952141cc406Sopenharmony_ci      DBG (2, "reader_process: frame complete\n");
953141cc406Sopenharmony_ci
954141cc406Sopenharmony_ci      if (q->port_mode == QC_BIDIR)
955141cc406Sopenharmony_ci	{
956141cc406Sopenharmony_ci	  /* return port to output mode */
957141cc406Sopenharmony_ci	  write_lpcontrol (q, Autofeed);
958141cc406Sopenharmony_ci	  usleep (3);
959141cc406Sopenharmony_ci	  write_lpcontrol (q, Autofeed | Reset_N);
960141cc406Sopenharmony_ci	  usleep (3);
961141cc406Sopenharmony_ci	  write_lpcontrol (q, Autofeed | Reset_N | PCAck);
962141cc406Sopenharmony_ci	}
963141cc406Sopenharmony_ci
964141cc406Sopenharmony_ci      if (req.resolution == QC_RES_HIGH)
965141cc406Sopenharmony_ci	{
966141cc406Sopenharmony_ci	  SANE_Byte buf[6];
967141cc406Sopenharmony_ci	  int x, y;
968141cc406Sopenharmony_ci
969141cc406Sopenharmony_ci	  /* in billions mode, we need to oversample the data: */
970141cc406Sopenharmony_ci	  src = buffer;
971141cc406Sopenharmony_ci	  width = req.params.pixels_per_line;
972141cc406Sopenharmony_ci	  height = req.params.lines;
973141cc406Sopenharmony_ci
974141cc406Sopenharmony_ci	  if (req.despeckle)
975141cc406Sopenharmony_ci	    {
976141cc406Sopenharmony_ci	      despeckle32 (width / 2, req.params.lines / 2, buffer, extra);
977141cc406Sopenharmony_ci	      src = extra;
978141cc406Sopenharmony_ci	    }
979141cc406Sopenharmony_ci
980141cc406Sopenharmony_ci	  assert (!(width & 1));	/* width must be even */
981141cc406Sopenharmony_ci
982141cc406Sopenharmony_ci	  for (y = 0; y < height; ++y)
983141cc406Sopenharmony_ci	    {
984141cc406Sopenharmony_ci	      /* even line */
985141cc406Sopenharmony_ci
986141cc406Sopenharmony_ci	      for (x = 0; x < width; x += 2)
987141cc406Sopenharmony_ci		{
988141cc406Sopenharmony_ci		  int red1, green1, blue1, green2, blue2;
989141cc406Sopenharmony_ci
990141cc406Sopenharmony_ci		  blue1 = src[0];
991141cc406Sopenharmony_ci		  green1 = src[1];
992141cc406Sopenharmony_ci		  red1 = src[3];
993141cc406Sopenharmony_ci		  if (x >= width - 2)
994141cc406Sopenharmony_ci		    {
995141cc406Sopenharmony_ci		      red1 = src[-1];	/* last red seems to be missing */
996141cc406Sopenharmony_ci		      blue2 = blue1;
997141cc406Sopenharmony_ci		      green2 = green1;
998141cc406Sopenharmony_ci		    }
999141cc406Sopenharmony_ci		  else
1000141cc406Sopenharmony_ci		    {
1001141cc406Sopenharmony_ci		      blue2 = src[4];
1002141cc406Sopenharmony_ci		      green2 = src[5];
1003141cc406Sopenharmony_ci		    }
1004141cc406Sopenharmony_ci		  src += 4;
1005141cc406Sopenharmony_ci
1006141cc406Sopenharmony_ci		  buf[0] = red1;
1007141cc406Sopenharmony_ci		  buf[1] = green1;
1008141cc406Sopenharmony_ci		  buf[2] = blue1;
1009141cc406Sopenharmony_ci		  buf[3] = red1;
1010141cc406Sopenharmony_ci		  buf[4] = green2;
1011141cc406Sopenharmony_ci		  buf[5] = blue2;
1012141cc406Sopenharmony_ci		  if (fwrite (buf, 1, 6, ofp) != 6)
1013141cc406Sopenharmony_ci		    {
1014141cc406Sopenharmony_ci		      perror ("fwrite: short write");
1015141cc406Sopenharmony_ci		      return 1;
1016141cc406Sopenharmony_ci		    }
1017141cc406Sopenharmony_ci		}
1018141cc406Sopenharmony_ci	      if (++y >= height)
1019141cc406Sopenharmony_ci		break;
1020141cc406Sopenharmony_ci
1021141cc406Sopenharmony_ci	      src -= 2 * width;	/* 4 bytes/pixel -> 2 pixels of 3 bytes each */
1022141cc406Sopenharmony_ci
1023141cc406Sopenharmony_ci	      /* odd line */
1024141cc406Sopenharmony_ci	      for (x = 0; x < width; x += 2)
1025141cc406Sopenharmony_ci		{
1026141cc406Sopenharmony_ci		  int red1, green3, blue3, green4, blue4;
1027141cc406Sopenharmony_ci		  int yoff;
1028141cc406Sopenharmony_ci
1029141cc406Sopenharmony_ci		  if (x >= width - 2)
1030141cc406Sopenharmony_ci		    red1 = src[-1];	/* last red seems to be missing */
1031141cc406Sopenharmony_ci		  else
1032141cc406Sopenharmony_ci		    red1 = src[3];
1033141cc406Sopenharmony_ci		  yoff = 2 * width;
1034141cc406Sopenharmony_ci		  if (y >= height - 1)
1035141cc406Sopenharmony_ci		    yoff = 0;
1036141cc406Sopenharmony_ci		  green3 = src[yoff + 1];
1037141cc406Sopenharmony_ci		  blue3 = src[yoff + 0];
1038141cc406Sopenharmony_ci		  if (x >= width - 2)
1039141cc406Sopenharmony_ci		    {
1040141cc406Sopenharmony_ci		      blue4 = blue3;
1041141cc406Sopenharmony_ci		      green4 = green3;
1042141cc406Sopenharmony_ci		    }
1043141cc406Sopenharmony_ci		  else
1044141cc406Sopenharmony_ci		    {
1045141cc406Sopenharmony_ci		      blue4 = src[yoff + 4];
1046141cc406Sopenharmony_ci		      green4 = src[yoff + 5];
1047141cc406Sopenharmony_ci		    }
1048141cc406Sopenharmony_ci		  src += 4;
1049141cc406Sopenharmony_ci
1050141cc406Sopenharmony_ci		  buf[0] = red1;
1051141cc406Sopenharmony_ci		  buf[1] = green3;
1052141cc406Sopenharmony_ci		  buf[2] = blue3;
1053141cc406Sopenharmony_ci		  buf[3] = red1;
1054141cc406Sopenharmony_ci		  buf[4] = green4;
1055141cc406Sopenharmony_ci		  buf[5] = blue4;
1056141cc406Sopenharmony_ci		  if (fwrite (buf, 1, 6, ofp) != 6)
1057141cc406Sopenharmony_ci		    {
1058141cc406Sopenharmony_ci		      perror ("fwrite: short write");
1059141cc406Sopenharmony_ci		      return 1;
1060141cc406Sopenharmony_ci		    }
1061141cc406Sopenharmony_ci		}
1062141cc406Sopenharmony_ci	    }
1063141cc406Sopenharmony_ci	}
1064141cc406Sopenharmony_ci      else
1065141cc406Sopenharmony_ci	{
1066141cc406Sopenharmony_ci	  src = buffer;
1067141cc406Sopenharmony_ci	  if (req.despeckle)
1068141cc406Sopenharmony_ci	    {
1069141cc406Sopenharmony_ci	      despeckle (req.params.pixels_per_line, req.params.lines,
1070141cc406Sopenharmony_ci			 buffer, extra);
1071141cc406Sopenharmony_ci	      src = extra;
1072141cc406Sopenharmony_ci	    }
1073141cc406Sopenharmony_ci
1074141cc406Sopenharmony_ci	  /* now write the whole thing to the main process: */
1075141cc406Sopenharmony_ci	  if (fwrite (src, 1, num_bytes, ofp) != num_bytes)
1076141cc406Sopenharmony_ci	    {
1077141cc406Sopenharmony_ci	      perror ("fwrite: short write");
1078141cc406Sopenharmony_ci	      return 1;
1079141cc406Sopenharmony_ci	    }
1080141cc406Sopenharmony_ci	}
1081141cc406Sopenharmony_ci      fflush (ofp);
1082141cc406Sopenharmony_ci    }
1083141cc406Sopenharmony_ci  assert (SANE_FALSE);		/* not reached */
1084141cc406Sopenharmony_ci  DBG (5, "reader_process: exit\n");
1085141cc406Sopenharmony_ci  return 1;
1086141cc406Sopenharmony_ci}
1087141cc406Sopenharmony_ci
1088141cc406Sopenharmony_cistatic SANE_Status
1089141cc406Sopenharmony_ciattach (const char *devname, QC_Device ** devp)
1090141cc406Sopenharmony_ci{
1091141cc406Sopenharmony_ci  int i, n1, n2, s1, s2, cmd, port, force_unidir;
1092141cc406Sopenharmony_ci  SANE_Status result, status;
1093141cc406Sopenharmony_ci  QC_Device *q;
1094141cc406Sopenharmony_ci  char *endp;
1095141cc406Sopenharmony_ci
1096141cc406Sopenharmony_ci  DBG (3, "attach: enter\n");
1097141cc406Sopenharmony_ci  errno = 0;
1098141cc406Sopenharmony_ci  force_unidir = 0;
1099141cc406Sopenharmony_ci  if (devname[0] == 'u')
1100141cc406Sopenharmony_ci    {
1101141cc406Sopenharmony_ci      force_unidir = 1;
1102141cc406Sopenharmony_ci      ++devname;
1103141cc406Sopenharmony_ci    }
1104141cc406Sopenharmony_ci  port = strtol (devname, &endp, 0);
1105141cc406Sopenharmony_ci  if (endp == devname || errno == ERANGE)
1106141cc406Sopenharmony_ci    {
1107141cc406Sopenharmony_ci      DBG (1, "attach: invalid port address `%s'\n", devname);
1108141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
1109141cc406Sopenharmony_ci    }
1110141cc406Sopenharmony_ci
1111141cc406Sopenharmony_ci  for (q = first_dev; q; q = q->next)
1112141cc406Sopenharmony_ci    if (port == q->port)
1113141cc406Sopenharmony_ci      {
1114141cc406Sopenharmony_ci	if (devp)
1115141cc406Sopenharmony_ci	  *devp = q;
1116141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
1117141cc406Sopenharmony_ci      }
1118141cc406Sopenharmony_ci
1119141cc406Sopenharmony_ci  q = malloc (sizeof (*q));
1120141cc406Sopenharmony_ci  if (!q)
1121141cc406Sopenharmony_ci    return SANE_STATUS_NO_MEM;
1122141cc406Sopenharmony_ci
1123141cc406Sopenharmony_ci  memset (q, 0, sizeof (*q));
1124141cc406Sopenharmony_ci  q->port = port;
1125141cc406Sopenharmony_ci  q->lock_fd = -1;
1126141cc406Sopenharmony_ci
1127141cc406Sopenharmony_ci  result = enable_ports (q);
1128141cc406Sopenharmony_ci  if (result != SANE_STATUS_GOOD)
1129141cc406Sopenharmony_ci    {
1130141cc406Sopenharmony_ci      DBG (1, "attach: cannot enable ports (%s)\n", strerror (errno));
1131141cc406Sopenharmony_ci      free (q);
1132141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
1133141cc406Sopenharmony_ci    }
1134141cc406Sopenharmony_ci
1135141cc406Sopenharmony_ci  /* lock camera while we determine its version: */
1136141cc406Sopenharmony_ci  qc_lock (q);
1137141cc406Sopenharmony_ci
1138141cc406Sopenharmony_ci  qc_reset (q);
1139141cc406Sopenharmony_ci
1140141cc406Sopenharmony_ci  write_lpdata (q, QC_SEND_VERSION);
1141141cc406Sopenharmony_ci  qc_wait (q);
1142141cc406Sopenharmony_ci  write_lpcontrol (q, Autofeed | Reset_N);	/* make PCAck inactive */
1143141cc406Sopenharmony_ci  qc_wait (q);
1144141cc406Sopenharmony_ci
1145141cc406Sopenharmony_ci  for (i = 0; (i < 1000) && !(s1 = (n1 = read_lpstatus (q)) & CamRdy1); i++);
1146141cc406Sopenharmony_ci  if (!s1)
1147141cc406Sopenharmony_ci    {
1148141cc406Sopenharmony_ci      DBG (2, "attach: failed to get CamRdy1 at port 0x%x\n", q->port);
1149141cc406Sopenharmony_ci      goto unlock_and_fail;
1150141cc406Sopenharmony_ci    }
1151141cc406Sopenharmony_ci
1152141cc406Sopenharmony_ci  write_lpcontrol (q, Autofeed | Reset_N | PCAck);
1153141cc406Sopenharmony_ci  qc_wait (q);
1154141cc406Sopenharmony_ci
1155141cc406Sopenharmony_ci  for (i = 0; (i < 1000) && (s2 = (n2 = read_lpstatus (q)) & CamRdy1); i++);
1156141cc406Sopenharmony_ci  if (s2)
1157141cc406Sopenharmony_ci    {
1158141cc406Sopenharmony_ci      DBG (2, "attach: CamRdy1 failed to clear at port 0x%x\n", q->port);
1159141cc406Sopenharmony_ci      goto unlock_and_fail;
1160141cc406Sopenharmony_ci    }
1161141cc406Sopenharmony_ci
1162141cc406Sopenharmony_ci  cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4);
1163141cc406Sopenharmony_ci
1164141cc406Sopenharmony_ci  if (cmd != QC_SEND_VERSION)
1165141cc406Sopenharmony_ci    {
1166141cc406Sopenharmony_ci      DBG (2, "attach: got 0x%02x instead of 0x%02x\n", cmd, QC_SEND_VERSION);
1167141cc406Sopenharmony_ci      goto unlock_and_fail;
1168141cc406Sopenharmony_ci    }
1169141cc406Sopenharmony_ci
1170141cc406Sopenharmony_ci  q->version = qc_readparam (q);
1171141cc406Sopenharmony_ci  DBG (1, "attach: found QuickCam version 0x%02x\n", q->version);
1172141cc406Sopenharmony_ci
1173141cc406Sopenharmony_ci  q->port_mode = QC_UNIDIR;
1174141cc406Sopenharmony_ci  if (!force_unidir)
1175141cc406Sopenharmony_ci    {
1176141cc406Sopenharmony_ci      write_lpcontrol (q, BiDir);
1177141cc406Sopenharmony_ci      write_lpdata (q, 0x75);
1178141cc406Sopenharmony_ci      if (read_lpdata (q) != 0x75)
1179141cc406Sopenharmony_ci	q->port_mode = QC_BIDIR;
1180141cc406Sopenharmony_ci    }
1181141cc406Sopenharmony_ci
1182141cc406Sopenharmony_ci  /* For some reason the color quickcam needs two set-black commands
1183141cc406Sopenharmony_ci     after a reset.  Thus, we now set the black-level to some
1184141cc406Sopenharmony_ci     reasonable value (0) so that the next set-black level command
1185141cc406Sopenharmony_ci     will really go through.  */
1186141cc406Sopenharmony_ci  if (q->version == QC_COLOR)
1187141cc406Sopenharmony_ci    {
1188141cc406Sopenharmony_ci      qc_send (q, QC_SET_BLACK);
1189141cc406Sopenharmony_ci      qc_send (q, 0);
1190141cc406Sopenharmony_ci
1191141cc406Sopenharmony_ci      DBG (3, "attach: resetting black_level\n");
1192141cc406Sopenharmony_ci
1193141cc406Sopenharmony_ci      /* wait for set black level command to finish: */
1194141cc406Sopenharmony_ci      while (qc_getstatus (q) & (CameraNotReady | BlackBalanceInProgress))
1195141cc406Sopenharmony_ci	usleep (10000);
1196141cc406Sopenharmony_ci    }
1197141cc406Sopenharmony_ci
1198141cc406Sopenharmony_ci  status = qc_unlock (q);
1199141cc406Sopenharmony_ci  if (status != SANE_STATUS_GOOD)
1200141cc406Sopenharmony_ci    {
1201141cc406Sopenharmony_ci      DBG (1, "attach: status qc_unlock NOK\n");
1202141cc406Sopenharmony_ci      /* status = SANE_STATUS_GOOD;  */
1203141cc406Sopenharmony_ci    }
1204141cc406Sopenharmony_ci  q->sane.name = strdup (devname);
1205141cc406Sopenharmony_ci  q->sane.vendor = "Connectix";
1206141cc406Sopenharmony_ci  q->sane.model =
1207141cc406Sopenharmony_ci    (q->version == QC_COLOR) ? "Color QuickCam" : "B&W QuickCam";
1208141cc406Sopenharmony_ci  q->sane.type = "video camera";
1209141cc406Sopenharmony_ci
1210141cc406Sopenharmony_ci  ++num_devices;
1211141cc406Sopenharmony_ci  q->next = first_dev;
1212141cc406Sopenharmony_ci  first_dev = q;
1213141cc406Sopenharmony_ci
1214141cc406Sopenharmony_ci  if (devp)
1215141cc406Sopenharmony_ci    *devp = q;
1216141cc406Sopenharmony_ci  DBG (3, "attach: exit status OK\n");
1217141cc406Sopenharmony_ci  status = SANE_STATUS_GOOD;
1218141cc406Sopenharmony_ci  return status;
1219141cc406Sopenharmony_ci
1220141cc406Sopenharmony_ci
1221141cc406Sopenharmony_ciunlock_and_fail:
1222141cc406Sopenharmony_ci  status = qc_unlock (q);
1223141cc406Sopenharmony_ci  if (status != SANE_STATUS_GOOD)
1224141cc406Sopenharmony_ci    {
1225141cc406Sopenharmony_ci      DBG (1, "attach: unlock_and_fail status qc_unlock NOK\n");
1226141cc406Sopenharmony_ci    }
1227141cc406Sopenharmony_ci  free (q);
1228141cc406Sopenharmony_ci  DBG (3, "attach: exit status NOK\n");
1229141cc406Sopenharmony_ci  status = SANE_STATUS_INVAL;
1230141cc406Sopenharmony_ci  return status;
1231141cc406Sopenharmony_ci}
1232141cc406Sopenharmony_ci
1233141cc406Sopenharmony_cistatic SANE_Status
1234141cc406Sopenharmony_ciinit_options (QC_Scanner * s)
1235141cc406Sopenharmony_ci{
1236141cc406Sopenharmony_ci  int i;
1237141cc406Sopenharmony_ci
1238141cc406Sopenharmony_ci  DBG (3, "init_options: enter\n");
1239141cc406Sopenharmony_ci
1240141cc406Sopenharmony_ci  memset (s->opt, 0, sizeof (s->opt));
1241141cc406Sopenharmony_ci  memset (s->val, 0, sizeof (s->val));
1242141cc406Sopenharmony_ci
1243141cc406Sopenharmony_ci  for (i = 0; i < NUM_OPTIONS; ++i)
1244141cc406Sopenharmony_ci    {
1245141cc406Sopenharmony_ci      s->opt[i].size = sizeof (SANE_Word);
1246141cc406Sopenharmony_ci      s->opt[i].cap = (SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT);
1247141cc406Sopenharmony_ci    }
1248141cc406Sopenharmony_ci
1249141cc406Sopenharmony_ci  s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
1250141cc406Sopenharmony_ci  s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
1251141cc406Sopenharmony_ci  s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
1252141cc406Sopenharmony_ci  s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
1253141cc406Sopenharmony_ci  s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
1254141cc406Sopenharmony_ci
1255141cc406Sopenharmony_ci  /* "Mode" group: */
1256141cc406Sopenharmony_ci
1257141cc406Sopenharmony_ci  s->opt[OPT_MODE_GROUP].title = "Scan Mode";
1258141cc406Sopenharmony_ci  s->opt[OPT_MODE_GROUP].desc = "";
1259141cc406Sopenharmony_ci  s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
1260141cc406Sopenharmony_ci  s->opt[OPT_MODE_GROUP].cap = 0;
1261141cc406Sopenharmony_ci  s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
1262141cc406Sopenharmony_ci
1263141cc406Sopenharmony_ci  /* resolution */
1264141cc406Sopenharmony_ci  s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
1265141cc406Sopenharmony_ci  s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
1266141cc406Sopenharmony_ci  s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
1267141cc406Sopenharmony_ci  s->opt[OPT_RESOLUTION].type = SANE_TYPE_STRING;
1268141cc406Sopenharmony_ci  s->opt[OPT_RESOLUTION].size = 5;	/* sizeof("High") */
1269141cc406Sopenharmony_ci  s->opt[OPT_RESOLUTION].unit = SANE_UNIT_NONE;
1270141cc406Sopenharmony_ci  s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_STRING_LIST;
1271141cc406Sopenharmony_ci  s->opt[OPT_RESOLUTION].constraint.string_list = resolution_list;
1272141cc406Sopenharmony_ci  s->val[OPT_RESOLUTION].s = strdup (resolution_list[QC_RES_LOW]);
1273141cc406Sopenharmony_ci
1274141cc406Sopenharmony_ci  /* bit-depth */
1275141cc406Sopenharmony_ci  s->opt[OPT_DEPTH].name = SANE_NAME_BIT_DEPTH;
1276141cc406Sopenharmony_ci  s->opt[OPT_DEPTH].title = "Pixel depth";
1277141cc406Sopenharmony_ci  s->opt[OPT_DEPTH].desc = "Number of bits per pixel.";
1278141cc406Sopenharmony_ci  s->opt[OPT_DEPTH].type = SANE_TYPE_INT;
1279141cc406Sopenharmony_ci  s->opt[OPT_DEPTH].unit = SANE_UNIT_BIT;
1280141cc406Sopenharmony_ci  s->opt[OPT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
1281141cc406Sopenharmony_ci  s->opt[OPT_DEPTH].constraint.word_list = color_depth_list;
1282141cc406Sopenharmony_ci  s->val[OPT_DEPTH].w = color_depth_list[NELEMS (color_depth_list) - 1];
1283141cc406Sopenharmony_ci
1284141cc406Sopenharmony_ci  /* test */
1285141cc406Sopenharmony_ci  s->opt[OPT_TEST].name = "test-image";
1286141cc406Sopenharmony_ci  s->opt[OPT_TEST].title = "Force test image";
1287141cc406Sopenharmony_ci  s->opt[OPT_TEST].desc =
1288141cc406Sopenharmony_ci    "Acquire a test-image instead of the image seen by the camera. "
1289141cc406Sopenharmony_ci    "The test image consists of red, green, and blue squares of "
1290141cc406Sopenharmony_ci    "32x32 pixels each.  Use this to find out whether the "
1291141cc406Sopenharmony_ci    "camera is connected properly.";
1292141cc406Sopenharmony_ci  s->opt[OPT_TEST].type = SANE_TYPE_BOOL;
1293141cc406Sopenharmony_ci  s->val[OPT_TEST].w = SANE_FALSE;
1294141cc406Sopenharmony_ci
1295141cc406Sopenharmony_ci  /* "Geometry" group: */
1296141cc406Sopenharmony_ci  s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
1297141cc406Sopenharmony_ci  s->opt[OPT_GEOMETRY_GROUP].desc = "";
1298141cc406Sopenharmony_ci  s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
1299141cc406Sopenharmony_ci  s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
1300141cc406Sopenharmony_ci  s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
1301141cc406Sopenharmony_ci
1302141cc406Sopenharmony_ci  /* top-left x */
1303141cc406Sopenharmony_ci  s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
1304141cc406Sopenharmony_ci  s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
1305141cc406Sopenharmony_ci  s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
1306141cc406Sopenharmony_ci  s->opt[OPT_TL_X].type = SANE_TYPE_INT;
1307141cc406Sopenharmony_ci  s->opt[OPT_TL_X].unit = SANE_UNIT_PIXEL;
1308141cc406Sopenharmony_ci  s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
1309141cc406Sopenharmony_ci  s->opt[OPT_TL_X].constraint.range = &x_range[QC_RES_LOW];
1310141cc406Sopenharmony_ci  s->val[OPT_TL_X].w = 10;
1311141cc406Sopenharmony_ci
1312141cc406Sopenharmony_ci  /* top-left y */
1313141cc406Sopenharmony_ci  s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
1314141cc406Sopenharmony_ci  s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
1315141cc406Sopenharmony_ci  s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
1316141cc406Sopenharmony_ci  s->opt[OPT_TL_Y].type = SANE_TYPE_INT;
1317141cc406Sopenharmony_ci  s->opt[OPT_TL_Y].unit = SANE_UNIT_PIXEL;
1318141cc406Sopenharmony_ci  s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
1319141cc406Sopenharmony_ci  s->opt[OPT_TL_Y].constraint.range = &y_range[QC_RES_LOW];
1320141cc406Sopenharmony_ci  s->val[OPT_TL_Y].w = 0;
1321141cc406Sopenharmony_ci
1322141cc406Sopenharmony_ci  /* bottom-right x */
1323141cc406Sopenharmony_ci  s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
1324141cc406Sopenharmony_ci  s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
1325141cc406Sopenharmony_ci  s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
1326141cc406Sopenharmony_ci  s->opt[OPT_BR_X].type = SANE_TYPE_INT;
1327141cc406Sopenharmony_ci  s->opt[OPT_BR_X].unit = SANE_UNIT_PIXEL;
1328141cc406Sopenharmony_ci  s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
1329141cc406Sopenharmony_ci  s->opt[OPT_BR_X].constraint.range = &odd_x_range[QC_RES_LOW];
1330141cc406Sopenharmony_ci  s->val[OPT_BR_X].w = 339;
1331141cc406Sopenharmony_ci
1332141cc406Sopenharmony_ci  /* bottom-right y */
1333141cc406Sopenharmony_ci  s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
1334141cc406Sopenharmony_ci  s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
1335141cc406Sopenharmony_ci  s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
1336141cc406Sopenharmony_ci  s->opt[OPT_BR_Y].type = SANE_TYPE_INT;
1337141cc406Sopenharmony_ci  s->opt[OPT_BR_Y].unit = SANE_UNIT_PIXEL;
1338141cc406Sopenharmony_ci  s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
1339141cc406Sopenharmony_ci  s->opt[OPT_BR_Y].constraint.range = &odd_y_range[QC_RES_LOW];
1340141cc406Sopenharmony_ci  s->val[OPT_BR_Y].w = 245;
1341141cc406Sopenharmony_ci
1342141cc406Sopenharmony_ci  /* xfer-scale */
1343141cc406Sopenharmony_ci  s->opt[OPT_XFER_SCALE].name = "transfer-scale";
1344141cc406Sopenharmony_ci  s->opt[OPT_XFER_SCALE].title = "Transfer scale";
1345141cc406Sopenharmony_ci  s->opt[OPT_XFER_SCALE].desc =
1346141cc406Sopenharmony_ci    "The transferscale determines how many of the acquired pixels actually "
1347141cc406Sopenharmony_ci    "get sent to the computer.  For example, a transfer scale of 2 would "
1348141cc406Sopenharmony_ci    "request that every other acquired pixel would be omitted.  That is, "
1349141cc406Sopenharmony_ci    "when scanning a 200 pixel wide and 100 pixel tall area, the resulting "
1350141cc406Sopenharmony_ci    "image would be only 100x50 pixels large.  Using a large transfer scale "
1351141cc406Sopenharmony_ci    "improves acquisition speed, but reduces resolution.";
1352141cc406Sopenharmony_ci  s->opt[OPT_XFER_SCALE].type = SANE_TYPE_INT;
1353141cc406Sopenharmony_ci  s->opt[OPT_XFER_SCALE].constraint_type = SANE_CONSTRAINT_WORD_LIST;
1354141cc406Sopenharmony_ci  s->opt[OPT_XFER_SCALE].constraint.word_list = xfer_scale_list;
1355141cc406Sopenharmony_ci  s->val[OPT_XFER_SCALE].w = xfer_scale_list[1];
1356141cc406Sopenharmony_ci
1357141cc406Sopenharmony_ci  /* despeckle */
1358141cc406Sopenharmony_ci  s->opt[OPT_DESPECKLE].name = "despeckle";
1359141cc406Sopenharmony_ci  s->opt[OPT_DESPECKLE].title = "Speckle filter";
1360141cc406Sopenharmony_ci  s->opt[OPT_DESPECKLE].desc = "Turning on this filter will remove the "
1361141cc406Sopenharmony_ci    "christmas lights that are typically present in dark images.";
1362141cc406Sopenharmony_ci  s->opt[OPT_DESPECKLE].type = SANE_TYPE_BOOL;
1363141cc406Sopenharmony_ci  s->opt[OPT_DESPECKLE].constraint_type = SANE_CONSTRAINT_NONE;
1364141cc406Sopenharmony_ci  s->val[OPT_DESPECKLE].w = 0;
1365141cc406Sopenharmony_ci
1366141cc406Sopenharmony_ci
1367141cc406Sopenharmony_ci  /* "Enhancement" group: */
1368141cc406Sopenharmony_ci
1369141cc406Sopenharmony_ci  s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
1370141cc406Sopenharmony_ci  s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
1371141cc406Sopenharmony_ci  s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
1372141cc406Sopenharmony_ci  s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
1373141cc406Sopenharmony_ci  s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
1374141cc406Sopenharmony_ci
1375141cc406Sopenharmony_ci  /* brightness */
1376141cc406Sopenharmony_ci  s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
1377141cc406Sopenharmony_ci  s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
1378141cc406Sopenharmony_ci  s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS
1379141cc406Sopenharmony_ci    "  In a conventional camera, this control corresponds to the "
1380141cc406Sopenharmony_ci    "exposure time.";
1381141cc406Sopenharmony_ci  s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
1382141cc406Sopenharmony_ci  s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_AUTOMATIC;
1383141cc406Sopenharmony_ci  s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
1384141cc406Sopenharmony_ci  s->opt[OPT_BRIGHTNESS].constraint.range = &brightness_range;
1385141cc406Sopenharmony_ci  s->val[OPT_BRIGHTNESS].w = 135;
1386141cc406Sopenharmony_ci
1387141cc406Sopenharmony_ci  /* contrast */
1388141cc406Sopenharmony_ci  s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
1389141cc406Sopenharmony_ci  s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
1390141cc406Sopenharmony_ci  s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
1391141cc406Sopenharmony_ci  s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
1392141cc406Sopenharmony_ci  s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
1393141cc406Sopenharmony_ci  s->opt[OPT_CONTRAST].constraint.range = &u8_range;
1394141cc406Sopenharmony_ci  s->val[OPT_CONTRAST].w = 104;
1395141cc406Sopenharmony_ci
1396141cc406Sopenharmony_ci  /* black-level */
1397141cc406Sopenharmony_ci  s->opt[OPT_BLACK_LEVEL].name = SANE_NAME_BLACK_LEVEL;
1398141cc406Sopenharmony_ci  s->opt[OPT_BLACK_LEVEL].title = SANE_TITLE_BLACK_LEVEL;
1399141cc406Sopenharmony_ci  s->opt[OPT_BLACK_LEVEL].desc = SANE_DESC_BLACK_LEVEL
1400141cc406Sopenharmony_ci    " This value should be selected so that black areas just start "
1401141cc406Sopenharmony_ci    "to look really black (not gray).";
1402141cc406Sopenharmony_ci  s->opt[OPT_BLACK_LEVEL].type = SANE_TYPE_INT;
1403141cc406Sopenharmony_ci  s->opt[OPT_BLACK_LEVEL].constraint_type = SANE_CONSTRAINT_RANGE;
1404141cc406Sopenharmony_ci  s->opt[OPT_BLACK_LEVEL].constraint.range = &u8_range;
1405141cc406Sopenharmony_ci  s->val[OPT_BLACK_LEVEL].w = 0;
1406141cc406Sopenharmony_ci
1407141cc406Sopenharmony_ci  /* white-level */
1408141cc406Sopenharmony_ci  s->opt[OPT_WHITE_LEVEL].name = SANE_NAME_WHITE_LEVEL;
1409141cc406Sopenharmony_ci  s->opt[OPT_WHITE_LEVEL].title = SANE_TITLE_WHITE_LEVEL;
1410141cc406Sopenharmony_ci  s->opt[OPT_WHITE_LEVEL].desc = SANE_DESC_WHITE_LEVEL
1411141cc406Sopenharmony_ci    " This value should be selected so that white areas just start "
1412141cc406Sopenharmony_ci    "to look really white (not gray).";
1413141cc406Sopenharmony_ci  s->opt[OPT_WHITE_LEVEL].type = SANE_TYPE_INT;
1414141cc406Sopenharmony_ci  s->opt[OPT_WHITE_LEVEL].constraint_type = SANE_CONSTRAINT_RANGE;
1415141cc406Sopenharmony_ci  s->opt[OPT_WHITE_LEVEL].constraint.range = &u8_range;
1416141cc406Sopenharmony_ci  s->val[OPT_WHITE_LEVEL].w = 150;
1417141cc406Sopenharmony_ci
1418141cc406Sopenharmony_ci  /* hue */
1419141cc406Sopenharmony_ci  s->opt[OPT_HUE].name = SANE_NAME_HUE;
1420141cc406Sopenharmony_ci  s->opt[OPT_HUE].title = SANE_TITLE_HUE;
1421141cc406Sopenharmony_ci  s->opt[OPT_HUE].desc = SANE_DESC_HUE;
1422141cc406Sopenharmony_ci  s->opt[OPT_HUE].type = SANE_TYPE_INT;
1423141cc406Sopenharmony_ci  s->opt[OPT_HUE].constraint_type = SANE_CONSTRAINT_RANGE;
1424141cc406Sopenharmony_ci  s->opt[OPT_HUE].constraint.range = &u8_range;
1425141cc406Sopenharmony_ci  s->val[OPT_HUE].w = 128;
1426141cc406Sopenharmony_ci
1427141cc406Sopenharmony_ci  /* saturation */
1428141cc406Sopenharmony_ci  s->opt[OPT_SATURATION].name = SANE_NAME_SATURATION;
1429141cc406Sopenharmony_ci  s->opt[OPT_SATURATION].title = SANE_TITLE_SATURATION;
1430141cc406Sopenharmony_ci  s->opt[OPT_SATURATION].desc = SANE_DESC_SATURATION;
1431141cc406Sopenharmony_ci  s->opt[OPT_SATURATION].type = SANE_TYPE_INT;
1432141cc406Sopenharmony_ci  s->opt[OPT_SATURATION].constraint_type = SANE_CONSTRAINT_RANGE;
1433141cc406Sopenharmony_ci  s->opt[OPT_SATURATION].constraint.range = &u8_range;
1434141cc406Sopenharmony_ci  s->val[OPT_SATURATION].w = 100;
1435141cc406Sopenharmony_ci
1436141cc406Sopenharmony_ci  DBG (3, "init_options: exit\n");
1437141cc406Sopenharmony_ci
1438141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
1439141cc406Sopenharmony_ci}
1440141cc406Sopenharmony_ci
1441141cc406Sopenharmony_ciSANE_Status
1442141cc406Sopenharmony_cisane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
1443141cc406Sopenharmony_ci{
1444141cc406Sopenharmony_ci  char dev_name[PATH_MAX], *str;
1445141cc406Sopenharmony_ci  size_t len;
1446141cc406Sopenharmony_ci  FILE *fp;
1447141cc406Sopenharmony_ci  (void) authorize;		/* silence compilation warnings */
1448141cc406Sopenharmony_ci
1449141cc406Sopenharmony_ci  DBG_INIT ();
1450141cc406Sopenharmony_ci
1451141cc406Sopenharmony_ci  DBG (1, "sane_init: enter\n");
1452141cc406Sopenharmony_ci
1453141cc406Sopenharmony_ci  if (version_code)
1454141cc406Sopenharmony_ci    *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0);
1455141cc406Sopenharmony_ci
1456141cc406Sopenharmony_ci  fp = sanei_config_open (QCAM_CONFIG_FILE);
1457141cc406Sopenharmony_ci  if (!fp)
1458141cc406Sopenharmony_ci    {
1459141cc406Sopenharmony_ci      DBG (1, "sane_init: file `%s' not accessible\n", QCAM_CONFIG_FILE);
1460141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
1461141cc406Sopenharmony_ci    }
1462141cc406Sopenharmony_ci
1463141cc406Sopenharmony_ci  while (sanei_config_read (dev_name, sizeof (dev_name), fp))
1464141cc406Sopenharmony_ci    {
1465141cc406Sopenharmony_ci      if (dev_name[0] == '#')	/* ignore line comments */
1466141cc406Sopenharmony_ci	continue;
1467141cc406Sopenharmony_ci      len = strlen (dev_name);
1468141cc406Sopenharmony_ci
1469141cc406Sopenharmony_ci      if (!len)
1470141cc406Sopenharmony_ci	continue;		/* ignore empty lines */
1471141cc406Sopenharmony_ci
1472141cc406Sopenharmony_ci      for (str = dev_name; *str && !isspace (*str) && *str != '#'; ++str);
1473141cc406Sopenharmony_ci      *str = '\0';
1474141cc406Sopenharmony_ci
1475141cc406Sopenharmony_ci      attach (dev_name, 0);
1476141cc406Sopenharmony_ci    }
1477141cc406Sopenharmony_ci  fclose (fp);
1478141cc406Sopenharmony_ci
1479141cc406Sopenharmony_ci  DBG (1, "sane_init: exit\n");
1480141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
1481141cc406Sopenharmony_ci}
1482141cc406Sopenharmony_ci
1483141cc406Sopenharmony_civoid
1484141cc406Sopenharmony_cisane_exit (void)
1485141cc406Sopenharmony_ci{
1486141cc406Sopenharmony_ci  QC_Device *dev, *next;
1487141cc406Sopenharmony_ci  static const SANE_Device **devlist;
1488141cc406Sopenharmony_ci
1489141cc406Sopenharmony_ci  DBG (5, "sane_exit: enter\n");
1490141cc406Sopenharmony_ci
1491141cc406Sopenharmony_ci  for (dev = first_dev; dev; dev = next)
1492141cc406Sopenharmony_ci    {
1493141cc406Sopenharmony_ci      next = dev->next;
1494141cc406Sopenharmony_ci      free ((void *) dev->sane.name);
1495141cc406Sopenharmony_ci      disable_ports (dev);
1496141cc406Sopenharmony_ci      free (dev);
1497141cc406Sopenharmony_ci    }
1498141cc406Sopenharmony_ci  if (devlist) {
1499141cc406Sopenharmony_ci	  free (devlist);
1500141cc406Sopenharmony_ci          devlist = NULL;
1501141cc406Sopenharmony_ci  }
1502141cc406Sopenharmony_ci  DBG (5, "sane_exit: exit\n");
1503141cc406Sopenharmony_ci}
1504141cc406Sopenharmony_ci
1505141cc406Sopenharmony_ciSANE_Status
1506141cc406Sopenharmony_cisane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
1507141cc406Sopenharmony_ci{
1508141cc406Sopenharmony_ci  static const SANE_Device **devlist = 0;
1509141cc406Sopenharmony_ci  QC_Device *dev;
1510141cc406Sopenharmony_ci  int i;
1511141cc406Sopenharmony_ci
1512141cc406Sopenharmony_ci  DBG (5, "sane_get_devices: enter\n");
1513141cc406Sopenharmony_ci
1514141cc406Sopenharmony_ci  (void) local_only;		/* silence compilation warnings */
1515141cc406Sopenharmony_ci
1516141cc406Sopenharmony_ci  if (devlist)
1517141cc406Sopenharmony_ci    free (devlist);
1518141cc406Sopenharmony_ci
1519141cc406Sopenharmony_ci  devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
1520141cc406Sopenharmony_ci  if (!devlist)
1521141cc406Sopenharmony_ci    return SANE_STATUS_NO_MEM;
1522141cc406Sopenharmony_ci
1523141cc406Sopenharmony_ci  i = 0;
1524141cc406Sopenharmony_ci  for (dev = first_dev; i < num_devices; dev = dev->next)
1525141cc406Sopenharmony_ci    devlist[i++] = &dev->sane;
1526141cc406Sopenharmony_ci  devlist[i++] = 0;
1527141cc406Sopenharmony_ci
1528141cc406Sopenharmony_ci  *device_list = devlist;
1529141cc406Sopenharmony_ci
1530141cc406Sopenharmony_ci  DBG (5, "sane_get_devices: exit\n");
1531141cc406Sopenharmony_ci
1532141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
1533141cc406Sopenharmony_ci}
1534141cc406Sopenharmony_ci
1535141cc406Sopenharmony_ciSANE_Status
1536141cc406Sopenharmony_cisane_open (SANE_String_Const devicename, SANE_Handle * handle)
1537141cc406Sopenharmony_ci{
1538141cc406Sopenharmony_ci  SANE_Status status;
1539141cc406Sopenharmony_ci  QC_Device *dev;
1540141cc406Sopenharmony_ci  QC_Scanner *s;
1541141cc406Sopenharmony_ci
1542141cc406Sopenharmony_ci  DBG (5, "sane_open: enter: (devicename = %s)\n", devicename);
1543141cc406Sopenharmony_ci  if (devicename[0])
1544141cc406Sopenharmony_ci    {
1545141cc406Sopenharmony_ci      status = attach (devicename, &dev);
1546141cc406Sopenharmony_ci      if (status != SANE_STATUS_GOOD)
1547141cc406Sopenharmony_ci	return status;
1548141cc406Sopenharmony_ci    }
1549141cc406Sopenharmony_ci  else
1550141cc406Sopenharmony_ci    /* empty devicname -> use first device */
1551141cc406Sopenharmony_ci    dev = first_dev;
1552141cc406Sopenharmony_ci
1553141cc406Sopenharmony_ci  if (!dev)
1554141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
1555141cc406Sopenharmony_ci
1556141cc406Sopenharmony_ci  s = malloc (sizeof (*s));
1557141cc406Sopenharmony_ci  if (!s)
1558141cc406Sopenharmony_ci    return SANE_STATUS_NO_MEM;
1559141cc406Sopenharmony_ci  memset (s, 0, sizeof (*s));
1560141cc406Sopenharmony_ci  s->user_corner = 0;
1561141cc406Sopenharmony_ci  s->hw = dev;
1562141cc406Sopenharmony_ci  s->value_changed = ~0;	/* ensure all options get updated */
1563141cc406Sopenharmony_ci  s->reader_pid = -1;
1564141cc406Sopenharmony_ci  s->to_child = -1;
1565141cc406Sopenharmony_ci  s->from_child = -1;
1566141cc406Sopenharmony_ci  s->read_fd = -1;
1567141cc406Sopenharmony_ci
1568141cc406Sopenharmony_ci  init_options (s);
1569141cc406Sopenharmony_ci
1570141cc406Sopenharmony_ci  /* The contrast option seems to have an effect for b&w cameras only,
1571141cc406Sopenharmony_ci     so don't give the user the impression that this is a useful thing
1572141cc406Sopenharmony_ci     to set... */
1573141cc406Sopenharmony_ci  if (s->hw->version == QC_COLOR)
1574141cc406Sopenharmony_ci    s->opt[OPT_CONTRAST].cap |= SANE_CAP_INACTIVE;
1575141cc406Sopenharmony_ci  else
1576141cc406Sopenharmony_ci    {
1577141cc406Sopenharmony_ci      /* Black level, Hue and Saturation are things the b&w cameras
1578141cc406Sopenharmony_ci         know nothing about.  Despeckle might be useful, but this code
1579141cc406Sopenharmony_ci         seems to work for color cameras only right now. The framesize
1580141cc406Sopenharmony_ci         seems to work better in these ranges.  */
1581141cc406Sopenharmony_ci      s->opt[OPT_DESPECKLE].cap |= SANE_CAP_INACTIVE;
1582141cc406Sopenharmony_ci      s->opt[OPT_BLACK_LEVEL].cap |= SANE_CAP_INACTIVE;
1583141cc406Sopenharmony_ci      s->opt[OPT_HUE].cap |= SANE_CAP_INACTIVE;
1584141cc406Sopenharmony_ci      s->opt[OPT_SATURATION].cap |= SANE_CAP_INACTIVE;
1585141cc406Sopenharmony_ci      s->opt[OPT_RESOLUTION].cap |= SANE_CAP_INACTIVE;
1586141cc406Sopenharmony_ci      s->opt[OPT_TEST].cap |= SANE_CAP_INACTIVE;
1587141cc406Sopenharmony_ci
1588141cc406Sopenharmony_ci      s->opt[OPT_DEPTH].constraint.word_list = mono_depth_list;
1589141cc406Sopenharmony_ci      s->val[OPT_DEPTH].w = mono_depth_list[NELEMS (mono_depth_list) - 1];
1590141cc406Sopenharmony_ci      s->opt[OPT_TL_X].constraint.range = &bw_x_range;
1591141cc406Sopenharmony_ci      s->val[OPT_TL_X].w = 14;
1592141cc406Sopenharmony_ci      s->opt[OPT_TL_Y].constraint.range = &bw_y_range;
1593141cc406Sopenharmony_ci      s->val[OPT_TL_Y].w = 0;
1594141cc406Sopenharmony_ci      s->opt[OPT_BR_X].constraint.range = &odd_bw_x_range;
1595141cc406Sopenharmony_ci      s->val[OPT_BR_X].w = 333;
1596141cc406Sopenharmony_ci      s->opt[OPT_BR_Y].constraint.range = &odd_bw_y_range;
1597141cc406Sopenharmony_ci      s->val[OPT_BR_Y].w = 239;
1598141cc406Sopenharmony_ci
1599141cc406Sopenharmony_ci      s->val[OPT_BRIGHTNESS].w = 170;
1600141cc406Sopenharmony_ci      s->val[OPT_CONTRAST].w = 150;
1601141cc406Sopenharmony_ci      s->val[OPT_WHITE_LEVEL].w = 150;
1602141cc406Sopenharmony_ci    }
1603141cc406Sopenharmony_ci
1604141cc406Sopenharmony_ci  /* insert newly opened handle into list of open handles: */
1605141cc406Sopenharmony_ci  s->next = first_handle;
1606141cc406Sopenharmony_ci  first_handle = s;
1607141cc406Sopenharmony_ci
1608141cc406Sopenharmony_ci  *handle = s;
1609141cc406Sopenharmony_ci
1610141cc406Sopenharmony_ci  DBG (5, "sane_open: exit\n");
1611141cc406Sopenharmony_ci
1612141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
1613141cc406Sopenharmony_ci}
1614141cc406Sopenharmony_ci
1615141cc406Sopenharmony_civoid
1616141cc406Sopenharmony_cisane_close (SANE_Handle handle)
1617141cc406Sopenharmony_ci{
1618141cc406Sopenharmony_ci  QC_Scanner *prev, *s;
1619141cc406Sopenharmony_ci
1620141cc406Sopenharmony_ci  DBG (5, "sane_close: enter\n");
1621141cc406Sopenharmony_ci
1622141cc406Sopenharmony_ci  /* remove handle from list of open handles: */
1623141cc406Sopenharmony_ci  prev = 0;
1624141cc406Sopenharmony_ci  for (s = first_handle; s; s = s->next)
1625141cc406Sopenharmony_ci    {
1626141cc406Sopenharmony_ci      if (s == handle)
1627141cc406Sopenharmony_ci	break;
1628141cc406Sopenharmony_ci      prev = s;
1629141cc406Sopenharmony_ci    }
1630141cc406Sopenharmony_ci  if (!s)
1631141cc406Sopenharmony_ci    {
1632141cc406Sopenharmony_ci      DBG (1, "sane_close: bad handle %p\n", handle);
1633141cc406Sopenharmony_ci      return;			/* oops, not a handle we know about */
1634141cc406Sopenharmony_ci    }
1635141cc406Sopenharmony_ci  if (prev)
1636141cc406Sopenharmony_ci    prev->next = s->next;
1637141cc406Sopenharmony_ci  else
1638141cc406Sopenharmony_ci    first_handle = s->next;
1639141cc406Sopenharmony_ci
1640141cc406Sopenharmony_ci  if (s->scanning)
1641141cc406Sopenharmony_ci    sane_cancel (handle);
1642141cc406Sopenharmony_ci
1643141cc406Sopenharmony_ci  if (s->reader_pid >= 0)
1644141cc406Sopenharmony_ci    {
1645141cc406Sopenharmony_ci      kill (s->reader_pid, SIGTERM);
1646141cc406Sopenharmony_ci      waitpid (s->reader_pid, 0, 0);
1647141cc406Sopenharmony_ci      s->reader_pid = 0;
1648141cc406Sopenharmony_ci    }
1649141cc406Sopenharmony_ci  if (s->to_child >= 0)
1650141cc406Sopenharmony_ci    close (s->to_child);
1651141cc406Sopenharmony_ci  if (s->from_child >= 0)
1652141cc406Sopenharmony_ci    close (s->from_child);
1653141cc406Sopenharmony_ci  if (s->read_fd >= 0)
1654141cc406Sopenharmony_ci    close (s->read_fd);
1655141cc406Sopenharmony_ci
1656141cc406Sopenharmony_ci  free (s);
1657141cc406Sopenharmony_ci
1658141cc406Sopenharmony_ci  DBG (5, "sane_close: exit\n");
1659141cc406Sopenharmony_ci
1660141cc406Sopenharmony_ci}
1661141cc406Sopenharmony_ci
1662141cc406Sopenharmony_ciconst SANE_Option_Descriptor *
1663141cc406Sopenharmony_cisane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
1664141cc406Sopenharmony_ci{
1665141cc406Sopenharmony_ci  QC_Scanner *s = handle;
1666141cc406Sopenharmony_ci
1667141cc406Sopenharmony_ci  DBG (5, "sane_get_option_descriptor: enter\n");
1668141cc406Sopenharmony_ci
1669141cc406Sopenharmony_ci  if ((unsigned) option >= NUM_OPTIONS)
1670141cc406Sopenharmony_ci    return 0;
1671141cc406Sopenharmony_ci
1672141cc406Sopenharmony_ci  DBG (5, "sane_get_option_descriptor: exit\n");
1673141cc406Sopenharmony_ci
1674141cc406Sopenharmony_ci  return s->opt + option;
1675141cc406Sopenharmony_ci}
1676141cc406Sopenharmony_ci
1677141cc406Sopenharmony_ciSANE_Status
1678141cc406Sopenharmony_cisane_control_option (SANE_Handle handle, SANE_Int option,
1679141cc406Sopenharmony_ci		     SANE_Action action, void *val, SANE_Int * info)
1680141cc406Sopenharmony_ci{
1681141cc406Sopenharmony_ci  QC_Scanner *s = handle;
1682141cc406Sopenharmony_ci  QC_Resolution old_res;
1683141cc406Sopenharmony_ci  SANE_Status status;
1684141cc406Sopenharmony_ci  SANE_Word cap;
1685141cc406Sopenharmony_ci  char *old_val;
1686141cc406Sopenharmony_ci  int i;
1687141cc406Sopenharmony_ci
1688141cc406Sopenharmony_ci  DBG (5, "sane_control_option: enter\n");
1689141cc406Sopenharmony_ci
1690141cc406Sopenharmony_ci  if (info)
1691141cc406Sopenharmony_ci    *info = 0;
1692141cc406Sopenharmony_ci
1693141cc406Sopenharmony_ci  if (option >= NUM_OPTIONS)
1694141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
1695141cc406Sopenharmony_ci
1696141cc406Sopenharmony_ci  cap = s->opt[option].cap;
1697141cc406Sopenharmony_ci
1698141cc406Sopenharmony_ci  if (!SANE_OPTION_IS_ACTIVE (cap))
1699141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
1700141cc406Sopenharmony_ci
1701141cc406Sopenharmony_ci  if (action == SANE_ACTION_GET_VALUE)
1702141cc406Sopenharmony_ci    {
1703141cc406Sopenharmony_ci      switch (option)
1704141cc406Sopenharmony_ci	{
1705141cc406Sopenharmony_ci	  /* word options: */
1706141cc406Sopenharmony_ci	case OPT_NUM_OPTS:
1707141cc406Sopenharmony_ci	case OPT_DEPTH:
1708141cc406Sopenharmony_ci	case OPT_DESPECKLE:
1709141cc406Sopenharmony_ci	case OPT_TEST:
1710141cc406Sopenharmony_ci	case OPT_TL_X:
1711141cc406Sopenharmony_ci	case OPT_TL_Y:
1712141cc406Sopenharmony_ci	case OPT_BR_X:
1713141cc406Sopenharmony_ci	case OPT_BR_Y:
1714141cc406Sopenharmony_ci	case OPT_XFER_SCALE:
1715141cc406Sopenharmony_ci	case OPT_BRIGHTNESS:
1716141cc406Sopenharmony_ci	case OPT_CONTRAST:
1717141cc406Sopenharmony_ci	case OPT_BLACK_LEVEL:
1718141cc406Sopenharmony_ci	case OPT_WHITE_LEVEL:
1719141cc406Sopenharmony_ci	case OPT_HUE:
1720141cc406Sopenharmony_ci	case OPT_SATURATION:
1721141cc406Sopenharmony_ci	  *(SANE_Word *) val = s->val[option].w;
1722141cc406Sopenharmony_ci	  return SANE_STATUS_GOOD;
1723141cc406Sopenharmony_ci
1724141cc406Sopenharmony_ci	  /* string options: */
1725141cc406Sopenharmony_ci	case OPT_RESOLUTION:
1726141cc406Sopenharmony_ci	  strcpy (val, s->val[option].s);
1727141cc406Sopenharmony_ci	  return SANE_STATUS_GOOD;
1728141cc406Sopenharmony_ci
1729141cc406Sopenharmony_ci	default:
1730141cc406Sopenharmony_ci	  DBG (1, "control_option: option %d unknown\n", option);
1731141cc406Sopenharmony_ci	}
1732141cc406Sopenharmony_ci    }
1733141cc406Sopenharmony_ci  else if (action == SANE_ACTION_SET_VALUE)
1734141cc406Sopenharmony_ci    {
1735141cc406Sopenharmony_ci      if (!SANE_OPTION_IS_SETTABLE (cap))
1736141cc406Sopenharmony_ci	return SANE_STATUS_INVAL;
1737141cc406Sopenharmony_ci
1738141cc406Sopenharmony_ci      status = sanei_constrain_value (s->opt + option, val, info);
1739141cc406Sopenharmony_ci      if (status != SANE_STATUS_GOOD)
1740141cc406Sopenharmony_ci	return status;
1741141cc406Sopenharmony_ci
1742141cc406Sopenharmony_ci      if (option >= OPT_TL_X && option <= OPT_BR_Y)
1743141cc406Sopenharmony_ci	s->user_corner |= 1 << (option - OPT_TL_X);
1744141cc406Sopenharmony_ci
1745141cc406Sopenharmony_ci      assert (option <= 31);
1746141cc406Sopenharmony_ci      s->value_changed |= 1 << option;
1747141cc406Sopenharmony_ci
1748141cc406Sopenharmony_ci      switch (option)
1749141cc406Sopenharmony_ci	{
1750141cc406Sopenharmony_ci	  /* (mostly) side-effect-free word options: */
1751141cc406Sopenharmony_ci	case OPT_TL_X:
1752141cc406Sopenharmony_ci	case OPT_TL_Y:
1753141cc406Sopenharmony_ci	case OPT_BR_X:
1754141cc406Sopenharmony_ci	case OPT_BR_Y:
1755141cc406Sopenharmony_ci	case OPT_XFER_SCALE:
1756141cc406Sopenharmony_ci	case OPT_DEPTH:
1757141cc406Sopenharmony_ci	  if (!s->scanning && info && s->val[option].w != *(SANE_Word *) val)
1758141cc406Sopenharmony_ci	    /* only signal the reload params if we're not scanning---no point
1759141cc406Sopenharmony_ci	       in creating the frontend useless work */
1760141cc406Sopenharmony_ci	    *info |= SANE_INFO_RELOAD_PARAMS;
1761141cc406Sopenharmony_ci	  /* fall through */
1762141cc406Sopenharmony_ci	case OPT_NUM_OPTS:
1763141cc406Sopenharmony_ci	case OPT_TEST:
1764141cc406Sopenharmony_ci	case OPT_DESPECKLE:
1765141cc406Sopenharmony_ci	case OPT_BRIGHTNESS:
1766141cc406Sopenharmony_ci	case OPT_CONTRAST:
1767141cc406Sopenharmony_ci	case OPT_BLACK_LEVEL:
1768141cc406Sopenharmony_ci	case OPT_WHITE_LEVEL:
1769141cc406Sopenharmony_ci	case OPT_HUE:
1770141cc406Sopenharmony_ci	case OPT_SATURATION:
1771141cc406Sopenharmony_ci	  s->val[option].w = *(SANE_Word *) val;
1772141cc406Sopenharmony_ci	  return SANE_STATUS_GOOD;
1773141cc406Sopenharmony_ci
1774141cc406Sopenharmony_ci	  /* options with side-effects: */
1775141cc406Sopenharmony_ci	case OPT_RESOLUTION:
1776141cc406Sopenharmony_ci	  old_val = s->val[OPT_RESOLUTION].s;
1777141cc406Sopenharmony_ci
1778141cc406Sopenharmony_ci	  if (strcmp (old_val, val) != 0)
1779141cc406Sopenharmony_ci	    return SANE_STATUS_GOOD;	/* no change */
1780141cc406Sopenharmony_ci
1781141cc406Sopenharmony_ci	  if (info)
1782141cc406Sopenharmony_ci	    {
1783141cc406Sopenharmony_ci	      *info |= SANE_INFO_RELOAD_OPTIONS;
1784141cc406Sopenharmony_ci	      if (!s->scanning)
1785141cc406Sopenharmony_ci		*info |= SANE_INFO_RELOAD_PARAMS;
1786141cc406Sopenharmony_ci	    }
1787141cc406Sopenharmony_ci	  free (old_val);
1788141cc406Sopenharmony_ci	  s->val[OPT_RESOLUTION].s = strdup (val);
1789141cc406Sopenharmony_ci
1790141cc406Sopenharmony_ci	  /* low-resolution mode: */
1791141cc406Sopenharmony_ci	  old_res = s->resolution;
1792141cc406Sopenharmony_ci	  s->resolution = QC_RES_LOW;
1793141cc406Sopenharmony_ci	  if (strcmp (val, resolution_list[QC_RES_HIGH]) == 0)
1794141cc406Sopenharmony_ci	    /* high-resolution mode: */
1795141cc406Sopenharmony_ci	    s->resolution = QC_RES_HIGH;
1796141cc406Sopenharmony_ci	  s->opt[OPT_TL_X].constraint.range = &x_range[s->resolution];
1797141cc406Sopenharmony_ci	  s->opt[OPT_BR_X].constraint.range = &odd_x_range[s->resolution];
1798141cc406Sopenharmony_ci	  s->opt[OPT_TL_Y].constraint.range = &y_range[s->resolution];
1799141cc406Sopenharmony_ci	  s->opt[OPT_BR_Y].constraint.range = &odd_y_range[s->resolution];
1800141cc406Sopenharmony_ci
1801141cc406Sopenharmony_ci	  if (old_res == QC_RES_LOW && s->resolution == QC_RES_HIGH)
1802141cc406Sopenharmony_ci	    {
1803141cc406Sopenharmony_ci	      for (i = OPT_TL_X; i <= OPT_BR_Y; ++i)
1804141cc406Sopenharmony_ci		s->val[i].w *= 2;
1805141cc406Sopenharmony_ci	      s->val[OPT_BR_X].w += 1;
1806141cc406Sopenharmony_ci	      s->val[OPT_BR_Y].w += 1;
1807141cc406Sopenharmony_ci	      s->opt[OPT_TEST].cap |= SANE_CAP_INACTIVE;
1808141cc406Sopenharmony_ci	    }
1809141cc406Sopenharmony_ci	  else if (old_res == QC_RES_HIGH && s->resolution == QC_RES_LOW)
1810141cc406Sopenharmony_ci	    {
1811141cc406Sopenharmony_ci	      for (i = OPT_TL_X; i <= OPT_BR_Y; ++i)
1812141cc406Sopenharmony_ci		s->val[i].w /= 2;
1813141cc406Sopenharmony_ci	      s->opt[OPT_TEST].cap &= ~SANE_CAP_INACTIVE;
1814141cc406Sopenharmony_ci	    }
1815141cc406Sopenharmony_ci
1816141cc406Sopenharmony_ci	  if (!(s->user_corner & 0x4))
1817141cc406Sopenharmony_ci	    s->val[OPT_BR_X].w = odd_x_range[s->resolution].max;
1818141cc406Sopenharmony_ci	  if (!(s->user_corner & 0x8))
1819141cc406Sopenharmony_ci	    s->val[OPT_BR_Y].w = odd_y_range[s->resolution].max - 4;
1820141cc406Sopenharmony_ci
1821141cc406Sopenharmony_ci	  /* make sure the affected options have valid values: */
1822141cc406Sopenharmony_ci	  for (i = OPT_TL_X; i <= OPT_BR_Y; ++i)
1823141cc406Sopenharmony_ci	    if (s->val[i].w > s->opt[i].constraint.range->max)
1824141cc406Sopenharmony_ci	      s->val[i].w = s->opt[i].constraint.range->max;
1825141cc406Sopenharmony_ci
1826141cc406Sopenharmony_ci          DBG (5, "sane_control_option: exit\n");
1827141cc406Sopenharmony_ci	  return SANE_STATUS_GOOD;
1828141cc406Sopenharmony_ci	}
1829141cc406Sopenharmony_ci    }
1830141cc406Sopenharmony_ci  else if (action == SANE_ACTION_SET_AUTO)
1831141cc406Sopenharmony_ci    {
1832141cc406Sopenharmony_ci      switch (option)
1833141cc406Sopenharmony_ci	{
1834141cc406Sopenharmony_ci	case OPT_BRIGHTNESS:
1835141cc406Sopenharmony_ci	  /* not implemented yet */
1836141cc406Sopenharmony_ci          DBG (5, "sane_control_option: exit\n");
1837141cc406Sopenharmony_ci	  return SANE_STATUS_GOOD;
1838141cc406Sopenharmony_ci
1839141cc406Sopenharmony_ci	default:
1840141cc406Sopenharmony_ci	  break;
1841141cc406Sopenharmony_ci	}
1842141cc406Sopenharmony_ci    }
1843141cc406Sopenharmony_ci
1844141cc406Sopenharmony_ci  DBG (5, "sane_control_option: NOK exit\n");
1845141cc406Sopenharmony_ci  return SANE_STATUS_INVAL;
1846141cc406Sopenharmony_ci}
1847141cc406Sopenharmony_ci
1848141cc406Sopenharmony_ciSANE_Status
1849141cc406Sopenharmony_cisane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
1850141cc406Sopenharmony_ci{
1851141cc406Sopenharmony_ci  QC_Scanner *s = handle;
1852141cc406Sopenharmony_ci  QC_Device *q = s->hw;
1853141cc406Sopenharmony_ci  int xfer_scale;
1854141cc406Sopenharmony_ci  size_t Bpp = 3;		/* # of bytes per pixel */
1855141cc406Sopenharmony_ci
1856141cc406Sopenharmony_ci  DBG (5, "sane_get_parameters: enter\n");
1857141cc406Sopenharmony_ci
1858141cc406Sopenharmony_ci  if (!s->scanning)
1859141cc406Sopenharmony_ci    {
1860141cc406Sopenharmony_ci      /* Only compute new parameters when not scanning---allows
1861141cc406Sopenharmony_ci         changing width/height etc while scan is in progress.  */
1862141cc406Sopenharmony_ci      xfer_scale = s->val[OPT_XFER_SCALE].w;
1863141cc406Sopenharmony_ci
1864141cc406Sopenharmony_ci      s->params.format = SANE_FRAME_RGB;
1865141cc406Sopenharmony_ci      if (q->version != QC_COLOR)
1866141cc406Sopenharmony_ci	{
1867141cc406Sopenharmony_ci	  s->params.format = SANE_FRAME_GRAY;
1868141cc406Sopenharmony_ci	  Bpp = 1;
1869141cc406Sopenharmony_ci	}
1870141cc406Sopenharmony_ci      s->params.last_frame = SANE_TRUE;
1871141cc406Sopenharmony_ci
1872141cc406Sopenharmony_ci      s->params.pixels_per_line = s->val[OPT_BR_X].w - s->val[OPT_TL_X].w + 1;
1873141cc406Sopenharmony_ci      s->params.pixels_per_line /= xfer_scale;
1874141cc406Sopenharmony_ci      s->params.pixels_per_line &= ~1UL;	/* ensure it's even */
1875141cc406Sopenharmony_ci      if (s->params.pixels_per_line < 2)
1876141cc406Sopenharmony_ci	s->params.pixels_per_line = 2;
1877141cc406Sopenharmony_ci
1878141cc406Sopenharmony_ci      s->params.lines = s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w + 1;
1879141cc406Sopenharmony_ci      s->params.lines /= xfer_scale;
1880141cc406Sopenharmony_ci      if (s->params.lines < 1)
1881141cc406Sopenharmony_ci	s->params.lines = 1;
1882141cc406Sopenharmony_ci
1883141cc406Sopenharmony_ci      s->params.bytes_per_line = Bpp * s->params.pixels_per_line;
1884141cc406Sopenharmony_ci      s->params.depth = 8;
1885141cc406Sopenharmony_ci    }
1886141cc406Sopenharmony_ci  if (params)
1887141cc406Sopenharmony_ci    *params = s->params;
1888141cc406Sopenharmony_ci
1889141cc406Sopenharmony_ci  DBG (5, "sane_get_parameters: exit\n");
1890141cc406Sopenharmony_ci
1891141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
1892141cc406Sopenharmony_ci}
1893141cc406Sopenharmony_ci
1894141cc406Sopenharmony_ciSANE_Status
1895141cc406Sopenharmony_cisane_start (SANE_Handle handle)
1896141cc406Sopenharmony_ci{
1897141cc406Sopenharmony_ci  int top, left, width, height, undecimated_width, undecimated_height;
1898141cc406Sopenharmony_ci  QC_Scanner *s = handle;
1899141cc406Sopenharmony_ci  QC_Device *q = s->hw;
1900141cc406Sopenharmony_ci  QC_Scan_Request req;
1901141cc406Sopenharmony_ci
1902141cc406Sopenharmony_ci  DBG (5, "sane_start: enter\n");
1903141cc406Sopenharmony_ci
1904141cc406Sopenharmony_ci  if (s->scanning)
1905141cc406Sopenharmony_ci    return SANE_STATUS_DEVICE_BUSY;
1906141cc406Sopenharmony_ci
1907141cc406Sopenharmony_ci  if (s->reader_pid < 0)
1908141cc406Sopenharmony_ci    {
1909141cc406Sopenharmony_ci      int p2c_pipe[2];		/* parent->child pipe */
1910141cc406Sopenharmony_ci      int c2p_pipe[2];		/* child->parent pipe */
1911141cc406Sopenharmony_ci
1912141cc406Sopenharmony_ci      if (pipe (p2c_pipe) < 0 || pipe (c2p_pipe) < 0)
1913141cc406Sopenharmony_ci	{
1914141cc406Sopenharmony_ci	  DBG (3, "start: failed to create pipes\n");
1915141cc406Sopenharmony_ci	  return SANE_STATUS_IO_ERROR;
1916141cc406Sopenharmony_ci	}
1917141cc406Sopenharmony_ci
1918141cc406Sopenharmony_ci      s->reader_pid = fork ();
1919141cc406Sopenharmony_ci      if (s->reader_pid == 0)
1920141cc406Sopenharmony_ci	{
1921141cc406Sopenharmony_ci	  /* this is the child */
1922141cc406Sopenharmony_ci	  signal (SIGHUP, SIG_DFL);
1923141cc406Sopenharmony_ci	  signal (SIGINT, SIG_DFL);
1924141cc406Sopenharmony_ci	  signal (SIGPIPE, SIG_DFL);
1925141cc406Sopenharmony_ci	  signal (SIGTERM, SIG_DFL);
1926141cc406Sopenharmony_ci	  _exit (reader_process (s, p2c_pipe[0], c2p_pipe[1]));
1927141cc406Sopenharmony_ci	}
1928141cc406Sopenharmony_ci      close (p2c_pipe[0]);
1929141cc406Sopenharmony_ci      close (c2p_pipe[1]);
1930141cc406Sopenharmony_ci      s->to_child = p2c_pipe[1];
1931141cc406Sopenharmony_ci      s->from_child = c2p_pipe[0];
1932141cc406Sopenharmony_ci    }
1933141cc406Sopenharmony_ci
1934141cc406Sopenharmony_ci  s->read_fd = dup (s->from_child);
1935141cc406Sopenharmony_ci  sane_get_parameters (s, 0);	/* ensure up-to-date parameters */
1936141cc406Sopenharmony_ci
1937141cc406Sopenharmony_ci  qc_lock (q);
1938141cc406Sopenharmony_ci  s->holding_lock = SANE_TRUE;
1939141cc406Sopenharmony_ci
1940141cc406Sopenharmony_ci  if (q->version == QC_COLOR)
1941141cc406Sopenharmony_ci    {
1942141cc406Sopenharmony_ci      qc_send (q, QC_SET_SPEED);
1943141cc406Sopenharmony_ci      qc_send (q, 2);
1944141cc406Sopenharmony_ci
1945141cc406Sopenharmony_ci      /* wait for camera to become ready: */
1946141cc406Sopenharmony_ci      while (qc_getstatus (q) & CameraNotReady)
1947141cc406Sopenharmony_ci	usleep (10000);
1948141cc406Sopenharmony_ci
1949141cc406Sopenharmony_ci      /* Only send black_level if necessary; this optimization may
1950141cc406Sopenharmony_ci         fail if two applications access the camera in an interleaved
1951141cc406Sopenharmony_ci         fashion; but the black-level command is slow enough that it
1952141cc406Sopenharmony_ci         cannot be issued for every image acquisition.  */
1953141cc406Sopenharmony_ci      if (s->value_changed & (1 << OPT_BLACK_LEVEL))
1954141cc406Sopenharmony_ci	{
1955141cc406Sopenharmony_ci	  s->value_changed &= ~(1 << OPT_BLACK_LEVEL);
1956141cc406Sopenharmony_ci
1957141cc406Sopenharmony_ci	  qc_send (q, QC_SET_BLACK);
1958141cc406Sopenharmony_ci	  qc_send (q, s->val[OPT_BLACK_LEVEL].w);
1959141cc406Sopenharmony_ci
1960141cc406Sopenharmony_ci	  DBG (3, "start: black_level=%d\n", s->val[OPT_BLACK_LEVEL].w);
1961141cc406Sopenharmony_ci
1962141cc406Sopenharmony_ci	  /* wait for set black level command to finish: */
1963141cc406Sopenharmony_ci	  while (qc_getstatus (q) & (CameraNotReady | BlackBalanceInProgress))
1964141cc406Sopenharmony_ci	    usleep (10000);
1965141cc406Sopenharmony_ci	}
1966141cc406Sopenharmony_ci
1967141cc406Sopenharmony_ci      if (s->value_changed & (1 << OPT_HUE))
1968141cc406Sopenharmony_ci	{
1969141cc406Sopenharmony_ci	  s->value_changed &= ~(1 << OPT_HUE);
1970141cc406Sopenharmony_ci	  qc_send (q, QC_COL_SET_HUE);
1971141cc406Sopenharmony_ci	  qc_send (q, s->val[OPT_HUE].w);
1972141cc406Sopenharmony_ci	}
1973141cc406Sopenharmony_ci
1974141cc406Sopenharmony_ci      if (s->value_changed & (1 << OPT_SATURATION))
1975141cc406Sopenharmony_ci	{
1976141cc406Sopenharmony_ci	  s->value_changed &= ~(1 << OPT_SATURATION);
1977141cc406Sopenharmony_ci	  qc_send (q, QC_SET_SATURATION);
1978141cc406Sopenharmony_ci	  qc_send (q, s->val[OPT_SATURATION].w);
1979141cc406Sopenharmony_ci	}
1980141cc406Sopenharmony_ci    }
1981141cc406Sopenharmony_ci
1982141cc406Sopenharmony_ci  if (q->version != QC_COLOR)
1983141cc406Sopenharmony_ci    qc_reset (q);
1984141cc406Sopenharmony_ci
1985141cc406Sopenharmony_ci  if (s->value_changed & (1 << OPT_CONTRAST))
1986141cc406Sopenharmony_ci    {
1987141cc406Sopenharmony_ci      s->value_changed &= ~(1 << OPT_CONTRAST);
1988141cc406Sopenharmony_ci      qc_send (q, ((q->version == QC_COLOR)
1989141cc406Sopenharmony_ci		   ? QC_COL_SET_CONTRAST : QC_MONO_SET_CONTRAST));
1990141cc406Sopenharmony_ci      qc_send (q, s->val[OPT_CONTRAST].w);
1991141cc406Sopenharmony_ci    }
1992141cc406Sopenharmony_ci
1993141cc406Sopenharmony_ci  if (s->value_changed & (1 << OPT_BRIGHTNESS))
1994141cc406Sopenharmony_ci    {
1995141cc406Sopenharmony_ci      s->value_changed &= ~(1 << OPT_BRIGHTNESS);
1996141cc406Sopenharmony_ci      qc_send (q, QC_SET_BRIGHTNESS);
1997141cc406Sopenharmony_ci      qc_send (q, s->val[OPT_BRIGHTNESS].w);
1998141cc406Sopenharmony_ci    }
1999141cc406Sopenharmony_ci
2000141cc406Sopenharmony_ci  width = s->params.pixels_per_line;
2001141cc406Sopenharmony_ci  height = s->params.lines;
2002141cc406Sopenharmony_ci  if (s->resolution == QC_RES_HIGH)
2003141cc406Sopenharmony_ci    {
2004141cc406Sopenharmony_ci      width /= 2;		/* the expansion occurs through oversampling */
2005141cc406Sopenharmony_ci      height /= 2;		/* we acquire only half the lines that we generate */
2006141cc406Sopenharmony_ci    }
2007141cc406Sopenharmony_ci  undecimated_width = width * s->val[OPT_XFER_SCALE].w;
2008141cc406Sopenharmony_ci  undecimated_height = height * s->val[OPT_XFER_SCALE].w;
2009141cc406Sopenharmony_ci
2010141cc406Sopenharmony_ci  s->num_bytes = 0;
2011141cc406Sopenharmony_ci  s->bytes_per_frame = s->params.lines * s->params.bytes_per_line;
2012141cc406Sopenharmony_ci
2013141cc406Sopenharmony_ci  qc_send (q, QC_SET_NUM_V);
2014141cc406Sopenharmony_ci  qc_send (q, undecimated_height);
2015141cc406Sopenharmony_ci
2016141cc406Sopenharmony_ci  if (q->version == QC_COLOR)
2017141cc406Sopenharmony_ci    {
2018141cc406Sopenharmony_ci      qc_send (q, QC_SET_NUM_H);
2019141cc406Sopenharmony_ci      qc_send (q, undecimated_width / 2);
2020141cc406Sopenharmony_ci    }
2021141cc406Sopenharmony_ci  else
2022141cc406Sopenharmony_ci    {
2023141cc406Sopenharmony_ci      int val, val2;
2024141cc406Sopenharmony_ci
2025141cc406Sopenharmony_ci      if (q->port_mode == QC_UNIDIR && s->val[OPT_DEPTH].w == 6)
2026141cc406Sopenharmony_ci	{
2027141cc406Sopenharmony_ci	  val = undecimated_width;
2028141cc406Sopenharmony_ci	  val2 = s->val[OPT_XFER_SCALE].w * 4;
2029141cc406Sopenharmony_ci	}
2030141cc406Sopenharmony_ci      else
2031141cc406Sopenharmony_ci	{
2032141cc406Sopenharmony_ci	  val = undecimated_width * s->val[OPT_DEPTH].w;
2033141cc406Sopenharmony_ci	  val2 =
2034141cc406Sopenharmony_ci	    ((q->port_mode == QC_BIDIR) ? 24 : 8) * s->val[OPT_XFER_SCALE].w;
2035141cc406Sopenharmony_ci	}
2036141cc406Sopenharmony_ci      val = (val + val2 - 1) / val2;
2037141cc406Sopenharmony_ci      qc_send (q, QC_SET_NUM_H);
2038141cc406Sopenharmony_ci      qc_send (q, val);
2039141cc406Sopenharmony_ci    }
2040141cc406Sopenharmony_ci
2041141cc406Sopenharmony_ci  left = s->val[OPT_TL_X].w / 2;
2042141cc406Sopenharmony_ci  top = s->val[OPT_TL_Y].w;
2043141cc406Sopenharmony_ci  if (s->resolution == QC_RES_HIGH)
2044141cc406Sopenharmony_ci    {
2045141cc406Sopenharmony_ci      left /= 2;
2046141cc406Sopenharmony_ci      top /= 2;
2047141cc406Sopenharmony_ci    }
2048141cc406Sopenharmony_ci
2049141cc406Sopenharmony_ci  DBG (3, "sane_start: top=%d, left=%d, white=%d, bright=%d, contr=%d\n",
2050141cc406Sopenharmony_ci       top, left, s->val[OPT_WHITE_LEVEL].w, s->val[OPT_BRIGHTNESS].w,
2051141cc406Sopenharmony_ci       s->val[OPT_CONTRAST].w);
2052141cc406Sopenharmony_ci
2053141cc406Sopenharmony_ci  qc_send (q, QC_SET_LEFT);
2054141cc406Sopenharmony_ci  qc_send (q, left);
2055141cc406Sopenharmony_ci
2056141cc406Sopenharmony_ci  qc_send (q, QC_SET_TOP);
2057141cc406Sopenharmony_ci  qc_send (q, top + 1);		/* not sure why this is so... ;-( */
2058141cc406Sopenharmony_ci
2059141cc406Sopenharmony_ci  if (s->value_changed & (1 << OPT_WHITE_LEVEL))
2060141cc406Sopenharmony_ci    {
2061141cc406Sopenharmony_ci      s->value_changed &= ~(1 << OPT_WHITE_LEVEL);
2062141cc406Sopenharmony_ci      qc_send (q, QC_SET_WHITE);
2063141cc406Sopenharmony_ci      qc_send (q, s->val[OPT_WHITE_LEVEL].w);
2064141cc406Sopenharmony_ci    }
2065141cc406Sopenharmony_ci
2066141cc406Sopenharmony_ci  DBG (2, "start: %s %d lines of %d pixels each (%ld bytes) => %dx%d\n",
2067141cc406Sopenharmony_ci       (q->port_mode == QC_BIDIR) ? "bidir" : "unidir",
2068141cc406Sopenharmony_ci       height, width, (long) s->bytes_per_frame,
2069141cc406Sopenharmony_ci       s->params.pixels_per_line, s->params.lines);
2070141cc406Sopenharmony_ci
2071141cc406Sopenharmony_ci  /* send scan request to reader process: */
2072141cc406Sopenharmony_ci  qc_setscanmode (s, &req.mode);
2073141cc406Sopenharmony_ci  req.num_bytes = width * height;
2074141cc406Sopenharmony_ci  if (q->version == QC_COLOR)
2075141cc406Sopenharmony_ci    {
2076141cc406Sopenharmony_ci      if (s->resolution == QC_RES_LOW)
2077141cc406Sopenharmony_ci	req.num_bytes *= 3;
2078141cc406Sopenharmony_ci      else
2079141cc406Sopenharmony_ci	req.num_bytes *= 4;
2080141cc406Sopenharmony_ci    }
2081141cc406Sopenharmony_ci  req.resolution = s->resolution;
2082141cc406Sopenharmony_ci  req.params = s->params;
2083141cc406Sopenharmony_ci  req.despeckle = s->val[OPT_DESPECKLE].w;
2084141cc406Sopenharmony_ci  write (s->to_child, &req, sizeof (req));
2085141cc406Sopenharmony_ci
2086141cc406Sopenharmony_ci  s->scanning = SANE_TRUE;
2087141cc406Sopenharmony_ci  s->deliver_eof = 0;
2088141cc406Sopenharmony_ci
2089141cc406Sopenharmony_ci  DBG (5, "sane_start: exit\n");
2090141cc406Sopenharmony_ci
2091141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
2092141cc406Sopenharmony_ci}
2093141cc406Sopenharmony_ci
2094141cc406Sopenharmony_ciSANE_Status
2095141cc406Sopenharmony_cisane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
2096141cc406Sopenharmony_ci	   SANE_Int * lenp)
2097141cc406Sopenharmony_ci{
2098141cc406Sopenharmony_ci  SANE_Status status;
2099141cc406Sopenharmony_ci  QC_Scanner *s = handle;
2100141cc406Sopenharmony_ci  QC_Device *q = s->hw;
2101141cc406Sopenharmony_ci  ssize_t nread;
2102141cc406Sopenharmony_ci  size_t len;
2103141cc406Sopenharmony_ci
2104141cc406Sopenharmony_ci  DBG (5, "sane_read: enter\n");
2105141cc406Sopenharmony_ci
2106141cc406Sopenharmony_ci  *lenp = 0;
2107141cc406Sopenharmony_ci
2108141cc406Sopenharmony_ci  if (s->deliver_eof)
2109141cc406Sopenharmony_ci    {
2110141cc406Sopenharmony_ci      s->deliver_eof = 0;
2111141cc406Sopenharmony_ci      return SANE_STATUS_EOF;
2112141cc406Sopenharmony_ci    }
2113141cc406Sopenharmony_ci
2114141cc406Sopenharmony_ci  if (!s->scanning)
2115141cc406Sopenharmony_ci    return SANE_STATUS_CANCELLED;
2116141cc406Sopenharmony_ci
2117141cc406Sopenharmony_ci  len = max_len;
2118141cc406Sopenharmony_ci  if (s->num_bytes + len > s->bytes_per_frame)
2119141cc406Sopenharmony_ci    len = s->bytes_per_frame - s->num_bytes;
2120141cc406Sopenharmony_ci
2121141cc406Sopenharmony_ci  DBG (8, "read(buf=%p,num_bytes=%ld,max_len=%d,len=%ld)\n",
2122141cc406Sopenharmony_ci       (void *) buf, (long) s->num_bytes, max_len, (long) len);
2123141cc406Sopenharmony_ci
2124141cc406Sopenharmony_ci  nread = read (s->read_fd, buf, len);
2125141cc406Sopenharmony_ci  if (nread <= 0)
2126141cc406Sopenharmony_ci    {
2127141cc406Sopenharmony_ci      if (nread == 0 || errno == EAGAIN)
2128141cc406Sopenharmony_ci	{
2129141cc406Sopenharmony_ci	  DBG (3, "read: no more data available\n");
2130141cc406Sopenharmony_ci	  return SANE_STATUS_GOOD;
2131141cc406Sopenharmony_ci	}
2132141cc406Sopenharmony_ci      DBG (3, "read: short read (%s)\n", strerror (errno));
2133141cc406Sopenharmony_ci      return SANE_STATUS_IO_ERROR;
2134141cc406Sopenharmony_ci    }
2135141cc406Sopenharmony_ci
2136141cc406Sopenharmony_ci  if (nread > 0 && s->holding_lock)
2137141cc406Sopenharmony_ci    {
2138141cc406Sopenharmony_ci      status = qc_unlock (q);	/* now we can unlock the camera */
2139141cc406Sopenharmony_ci      if (status != SANE_STATUS_GOOD)
2140141cc406Sopenharmony_ci	      DBG(3, "sane_read: qc_unlock error\n");
2141141cc406Sopenharmony_ci      s->holding_lock = SANE_FALSE;
2142141cc406Sopenharmony_ci    }
2143141cc406Sopenharmony_ci
2144141cc406Sopenharmony_ci  s->num_bytes += nread;
2145141cc406Sopenharmony_ci  if (s->num_bytes >= s->bytes_per_frame)
2146141cc406Sopenharmony_ci    {
2147141cc406Sopenharmony_ci      s->scanning = SANE_FALSE;
2148141cc406Sopenharmony_ci      close (s->read_fd);
2149141cc406Sopenharmony_ci      s->read_fd = -1;
2150141cc406Sopenharmony_ci      s->deliver_eof = 1;
2151141cc406Sopenharmony_ci    }
2152141cc406Sopenharmony_ci
2153141cc406Sopenharmony_ci  if (lenp)
2154141cc406Sopenharmony_ci    *lenp = nread;
2155141cc406Sopenharmony_ci
2156141cc406Sopenharmony_ci  DBG (5, "sane_read: exit, read got %d bytes\n", *lenp);
2157141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
2158141cc406Sopenharmony_ci}
2159141cc406Sopenharmony_ci
2160141cc406Sopenharmony_civoid
2161141cc406Sopenharmony_cisane_cancel (SANE_Handle handle)
2162141cc406Sopenharmony_ci{
2163141cc406Sopenharmony_ci  QC_Scanner *s = handle;
2164141cc406Sopenharmony_ci  SANE_Bool was_scanning;
2165141cc406Sopenharmony_ci  SANE_Status status;
2166141cc406Sopenharmony_ci
2167141cc406Sopenharmony_ci  DBG (5, "sane_cancel: enter\n");
2168141cc406Sopenharmony_ci
2169141cc406Sopenharmony_ci  was_scanning = s->scanning;
2170141cc406Sopenharmony_ci  s->scanning = SANE_FALSE;
2171141cc406Sopenharmony_ci  s->deliver_eof = 0;
2172141cc406Sopenharmony_ci  if (s->read_fd >= 0)
2173141cc406Sopenharmony_ci    {
2174141cc406Sopenharmony_ci      close (s->read_fd);
2175141cc406Sopenharmony_ci      s->read_fd = -1;
2176141cc406Sopenharmony_ci    }
2177141cc406Sopenharmony_ci
2178141cc406Sopenharmony_ci  if (s->reader_pid >= 0 && was_scanning)
2179141cc406Sopenharmony_ci    {
2180141cc406Sopenharmony_ci      char buf[1024];
2181141cc406Sopenharmony_ci      ssize_t nread;
2182141cc406Sopenharmony_ci      int flags;
2183141cc406Sopenharmony_ci
2184141cc406Sopenharmony_ci      DBG (1, "cancel: cancelling read request\n");
2185141cc406Sopenharmony_ci
2186141cc406Sopenharmony_ci      kill (s->reader_pid, SIGINT);	/* tell reader to stop reading */
2187141cc406Sopenharmony_ci
2188141cc406Sopenharmony_ci      /* save non-blocking i/o flags: */
2189141cc406Sopenharmony_ci      flags = fcntl (s->from_child, F_GETFL, 0);
2190141cc406Sopenharmony_ci
2191141cc406Sopenharmony_ci      /* block until we read at least one byte: */
2192141cc406Sopenharmony_ci      read (s->from_child, buf, 1);
2193141cc406Sopenharmony_ci
2194141cc406Sopenharmony_ci      /* put descriptor in non-blocking i/o: */
2195141cc406Sopenharmony_ci      fcntl (s->from_child, F_SETFL, O_NONBLOCK);
2196141cc406Sopenharmony_ci
2197141cc406Sopenharmony_ci      /* read what's left over in the pipe/file buffer: */
2198141cc406Sopenharmony_ci      do
2199141cc406Sopenharmony_ci	{
2200141cc406Sopenharmony_ci	  while ((nread = read (s->from_child, buf, sizeof (buf))) > 0);
2201141cc406Sopenharmony_ci	  usleep (100000);
2202141cc406Sopenharmony_ci	  nread = read (s->from_child, buf, sizeof (buf));
2203141cc406Sopenharmony_ci	}
2204141cc406Sopenharmony_ci      while (nread > 0);
2205141cc406Sopenharmony_ci
2206141cc406Sopenharmony_ci      /* now restore non-blocking i/o flag: */
2207141cc406Sopenharmony_ci      fcntl (s->from_child, F_SETFL, flags & O_NONBLOCK);
2208141cc406Sopenharmony_ci
2209141cc406Sopenharmony_ci      waitpid (s->reader_pid, 0, 0);
2210141cc406Sopenharmony_ci      s->reader_pid = 0;
2211141cc406Sopenharmony_ci
2212141cc406Sopenharmony_ci      DBG (1, "cancel: cancellation completed\n");
2213141cc406Sopenharmony_ci    }
2214141cc406Sopenharmony_ci  if (s->holding_lock)
2215141cc406Sopenharmony_ci    {
2216141cc406Sopenharmony_ci      status = qc_unlock (s->hw);
2217141cc406Sopenharmony_ci      if (status != SANE_STATUS_GOOD)
2218141cc406Sopenharmony_ci	      DBG(3, "sane_cancel: qc_unlock error\n");
2219141cc406Sopenharmony_ci      s->holding_lock = SANE_FALSE;
2220141cc406Sopenharmony_ci    }
2221141cc406Sopenharmony_ci  DBG (5, "sane_cancel: exit\n");
2222141cc406Sopenharmony_ci}
2223141cc406Sopenharmony_ci
2224141cc406Sopenharmony_ciSANE_Status
2225141cc406Sopenharmony_cisane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
2226141cc406Sopenharmony_ci{
2227141cc406Sopenharmony_ci  QC_Scanner *s = handle;
2228141cc406Sopenharmony_ci
2229141cc406Sopenharmony_ci  DBG (5, "sane_set_io_mode: enter\n");
2230141cc406Sopenharmony_ci
2231141cc406Sopenharmony_ci  if (!s->scanning)
2232141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
2233141cc406Sopenharmony_ci
2234141cc406Sopenharmony_ci  if (fcntl (s->read_fd, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
2235141cc406Sopenharmony_ci    return SANE_STATUS_IO_ERROR;
2236141cc406Sopenharmony_ci  DBG (5, "sane_set_io_mode: exit\n");
2237141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
2238141cc406Sopenharmony_ci}
2239141cc406Sopenharmony_ci
2240141cc406Sopenharmony_ciSANE_Status
2241141cc406Sopenharmony_cisane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
2242141cc406Sopenharmony_ci{
2243141cc406Sopenharmony_ci  QC_Scanner *s = handle;
2244141cc406Sopenharmony_ci
2245141cc406Sopenharmony_ci  DBG (5, "sane_get_select_fd: enter\n");
2246141cc406Sopenharmony_ci  if (!s->scanning)
2247141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
2248141cc406Sopenharmony_ci
2249141cc406Sopenharmony_ci  *fd = s->read_fd;
2250141cc406Sopenharmony_ci  DBG (5, "sane_get_select_fd: exit\n");
2251141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
2252141cc406Sopenharmony_ci}
2253