1141cc406Sopenharmony_ci/* sane - Scanner Access Now Easy.
2141cc406Sopenharmony_ci   Copyright (C) 1997 Gordon Matzigkeit
3141cc406Sopenharmony_ci   Copyright (C) 1997 David Mosberger-Tang
4141cc406Sopenharmony_ci   This file is part of the SANE package.
5141cc406Sopenharmony_ci
6141cc406Sopenharmony_ci   This program is free software; you can redistribute it and/or
7141cc406Sopenharmony_ci   modify it under the terms of the GNU General Public License as
8141cc406Sopenharmony_ci   published by the Free Software Foundation; either version 2 of the
9141cc406Sopenharmony_ci   License, or (at your option) any later version.
10141cc406Sopenharmony_ci
11141cc406Sopenharmony_ci   This program is distributed in the hope that it will be useful, but
12141cc406Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
13141cc406Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14141cc406Sopenharmony_ci   General Public License for more details.
15141cc406Sopenharmony_ci
16141cc406Sopenharmony_ci   You should have received a copy of the GNU General Public License
17141cc406Sopenharmony_ci   along with this program.  If not, see <https://www.gnu.org/licenses/>.
18141cc406Sopenharmony_ci
19141cc406Sopenharmony_ci   As a special exception, the authors of SANE give permission for
20141cc406Sopenharmony_ci   additional uses of the libraries contained in this release of SANE.
21141cc406Sopenharmony_ci
22141cc406Sopenharmony_ci   The exception is that, if you link a SANE library with other files
23141cc406Sopenharmony_ci   to produce an executable, this does not by itself cause the
24141cc406Sopenharmony_ci   resulting executable to be covered by the GNU General Public
25141cc406Sopenharmony_ci   License.  Your use of that executable is in no way restricted on
26141cc406Sopenharmony_ci   account of linking the SANE library code into it.
27141cc406Sopenharmony_ci
28141cc406Sopenharmony_ci   This exception does not, however, invalidate any other reasons why
29141cc406Sopenharmony_ci   the executable file might be covered by the GNU General Public
30141cc406Sopenharmony_ci   License.
31141cc406Sopenharmony_ci
32141cc406Sopenharmony_ci   If you submit changes to SANE to the maintainers to be included in
33141cc406Sopenharmony_ci   a subsequent release, you agree by submitting the changes that
34141cc406Sopenharmony_ci   those changes may be distributed with this exception intact.
35141cc406Sopenharmony_ci
36141cc406Sopenharmony_ci   If you write modifications of your own for SANE, it is your choice
37141cc406Sopenharmony_ci   whether to permit this exception to apply to your modifications.
38141cc406Sopenharmony_ci   If you do not wish that, delete this exception notice.  */
39141cc406Sopenharmony_ci
40141cc406Sopenharmony_ci#include "../include/sane/config.h"
41141cc406Sopenharmony_ci
42141cc406Sopenharmony_ci#include <limits.h>
43141cc406Sopenharmony_ci#include <stdlib.h>
44141cc406Sopenharmony_ci#include <string.h>
45141cc406Sopenharmony_ciextern int errno;
46141cc406Sopenharmony_ci
47141cc406Sopenharmony_ci#include "../include/sane/sane.h"
48141cc406Sopenharmony_ci#include "../include/sane/saneopts.h"
49141cc406Sopenharmony_ci
50141cc406Sopenharmony_ci#include <unistd.h>
51141cc406Sopenharmony_ci#include <fcntl.h>
52141cc406Sopenharmony_ci
53141cc406Sopenharmony_ci#include "../include/sane/sanei_backend.h"
54141cc406Sopenharmony_ci
55141cc406Sopenharmony_ci#ifndef PATH_MAX
56141cc406Sopenharmony_ci# define PATH_MAX	1024
57141cc406Sopenharmony_ci#endif
58141cc406Sopenharmony_ci
59141cc406Sopenharmony_ci#include "../include/sane/sanei_config.h"
60141cc406Sopenharmony_ci#define PINT_CONFIG_FILE "pint.conf"
61141cc406Sopenharmony_ci
62141cc406Sopenharmony_ci#include "pint.h"
63141cc406Sopenharmony_ci
64141cc406Sopenharmony_ci#define DECIPOINTS_PER_MM	(720.0 / MM_PER_INCH)
65141cc406Sopenharmony_ci#define TWELVEHUNDS_PER_MM	(1200.0 / MM_PER_INCH)
66141cc406Sopenharmony_ci
67141cc406Sopenharmony_ci
68141cc406Sopenharmony_cistatic int num_devices;
69141cc406Sopenharmony_cistatic PINT_Device *first_dev;
70141cc406Sopenharmony_cistatic PINT_Scanner *first_handle;
71141cc406Sopenharmony_ci
72141cc406Sopenharmony_ci/* A zero-terminated list of valid scanner modes. */
73141cc406Sopenharmony_cistatic SANE_String_Const mode_list[8];
74141cc406Sopenharmony_ci
75141cc406Sopenharmony_cistatic const SANE_Range s7_range =
76141cc406Sopenharmony_ci  {
77141cc406Sopenharmony_ci    -127,				/* minimum */
78141cc406Sopenharmony_ci     127,				/* maximum */
79141cc406Sopenharmony_ci       1				/* quantization */
80141cc406Sopenharmony_ci  };
81141cc406Sopenharmony_ci
82141cc406Sopenharmony_cistatic size_t
83141cc406Sopenharmony_cimax_string_size (const SANE_String_Const strings[])
84141cc406Sopenharmony_ci{
85141cc406Sopenharmony_ci  size_t size, max_size = 0;
86141cc406Sopenharmony_ci  int i;
87141cc406Sopenharmony_ci
88141cc406Sopenharmony_ci  for (i = 0; strings[i]; ++i)
89141cc406Sopenharmony_ci    {
90141cc406Sopenharmony_ci      size = strlen (strings[i]) + 1;
91141cc406Sopenharmony_ci      if (size > max_size)
92141cc406Sopenharmony_ci	max_size = size;
93141cc406Sopenharmony_ci    }
94141cc406Sopenharmony_ci  return max_size;
95141cc406Sopenharmony_ci}
96141cc406Sopenharmony_ci
97141cc406Sopenharmony_cistatic SANE_Status
98141cc406Sopenharmony_ciattach (const char *devname, PINT_Device **devp)
99141cc406Sopenharmony_ci{
100141cc406Sopenharmony_ci  int fd;
101141cc406Sopenharmony_ci  long lastguess, inc;
102141cc406Sopenharmony_ci  PINT_Device *dev;
103141cc406Sopenharmony_ci  struct scan_io scanio;
104141cc406Sopenharmony_ci
105141cc406Sopenharmony_ci  for (dev = first_dev; dev; dev = dev->next)
106141cc406Sopenharmony_ci    if (strcmp (dev->sane.name, devname) == 0)
107141cc406Sopenharmony_ci      {
108141cc406Sopenharmony_ci	if (devp)
109141cc406Sopenharmony_ci	  *devp = dev;
110141cc406Sopenharmony_ci	return SANE_STATUS_GOOD;
111141cc406Sopenharmony_ci      }
112141cc406Sopenharmony_ci
113141cc406Sopenharmony_ci  DBG(3, "attach: opening %s\n", devname);
114141cc406Sopenharmony_ci  fd = open (devname, O_RDONLY, 0);
115141cc406Sopenharmony_ci  if (fd < 0)
116141cc406Sopenharmony_ci    {
117141cc406Sopenharmony_ci      DBG(1, "attach: open failed (%s)\n", strerror (errno));
118141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
119141cc406Sopenharmony_ci    }
120141cc406Sopenharmony_ci
121141cc406Sopenharmony_ci  DBG(3, "attach: sending SCIOCGET\n");
122141cc406Sopenharmony_ci  if (ioctl (fd, SCIOCGET, &scanio) < 0)
123141cc406Sopenharmony_ci    {
124141cc406Sopenharmony_ci      DBG(1, "attach: get status failed (%s)\n", strerror (errno));
125141cc406Sopenharmony_ci      close (fd);
126141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
127141cc406Sopenharmony_ci    }
128141cc406Sopenharmony_ci
129141cc406Sopenharmony_ci  dev = malloc (sizeof (*dev));
130141cc406Sopenharmony_ci  if (!dev)
131141cc406Sopenharmony_ci    return SANE_STATUS_NO_MEM;
132141cc406Sopenharmony_ci
133141cc406Sopenharmony_ci  memset(dev, 0, sizeof (*dev));
134141cc406Sopenharmony_ci
135141cc406Sopenharmony_ci  /* Copy the original scanner state to the device structure. */
136141cc406Sopenharmony_ci  memcpy (&dev->scanio, &scanio, sizeof (dev->scanio));
137141cc406Sopenharmony_ci
138141cc406Sopenharmony_ci  /* FIXME: PINT currently has no good way to determine maxima and minima.
139141cc406Sopenharmony_ci     So, do binary searches to find out what limits the driver has. */
140141cc406Sopenharmony_ci
141141cc406Sopenharmony_ci  /* Assume that minimum range of x and y is 0. */
142141cc406Sopenharmony_ci  dev->x_range.min = SANE_FIX (0);
143141cc406Sopenharmony_ci  dev->y_range.min = SANE_FIX (0);
144141cc406Sopenharmony_ci  dev->x_range.quant = 0;
145141cc406Sopenharmony_ci  dev->y_range.quant = 0;
146141cc406Sopenharmony_ci
147141cc406Sopenharmony_ci  /* x range */
148141cc406Sopenharmony_ci  inc = 8.5 * 1200;
149141cc406Sopenharmony_ci
150141cc406Sopenharmony_ci  /* Converge on the maximum scan width. */
151141cc406Sopenharmony_ci  while ((inc /= 2) != 0)
152141cc406Sopenharmony_ci    {
153141cc406Sopenharmony_ci      /* Move towards the extremum until we overflow. */
154141cc406Sopenharmony_ci      do
155141cc406Sopenharmony_ci	{
156141cc406Sopenharmony_ci	  lastguess = scanio.scan_width;
157141cc406Sopenharmony_ci	  scanio.scan_width += inc;
158141cc406Sopenharmony_ci	}
159141cc406Sopenharmony_ci      while (ioctl (fd, SCIOCSET, &scanio) >= 0);
160141cc406Sopenharmony_ci
161141cc406Sopenharmony_ci      /* Pick the last valid guess, divide by two, and try again. */
162141cc406Sopenharmony_ci      scanio.scan_width = lastguess;
163141cc406Sopenharmony_ci    }
164141cc406Sopenharmony_ci  dev->x_range.max = SANE_FIX (scanio.scan_width / TWELVEHUNDS_PER_MM);
165141cc406Sopenharmony_ci
166141cc406Sopenharmony_ci  /* y range */
167141cc406Sopenharmony_ci  inc = 11 * 1200;
168141cc406Sopenharmony_ci  while ((inc /= 2) != 0)
169141cc406Sopenharmony_ci    {
170141cc406Sopenharmony_ci      do
171141cc406Sopenharmony_ci	{
172141cc406Sopenharmony_ci	  lastguess = scanio.scan_height;
173141cc406Sopenharmony_ci	  scanio.scan_height += inc;
174141cc406Sopenharmony_ci	}
175141cc406Sopenharmony_ci      while (ioctl (fd, SCIOCSET, &scanio) >= 0);
176141cc406Sopenharmony_ci      scanio.scan_height = lastguess;
177141cc406Sopenharmony_ci    }
178141cc406Sopenharmony_ci  dev->y_range.max = SANE_FIX (scanio.scan_height / TWELVEHUNDS_PER_MM);
179141cc406Sopenharmony_ci
180141cc406Sopenharmony_ci  /* Converge on the minimum scan resolution. */
181141cc406Sopenharmony_ci  dev->dpi_range.quant = 1;
182141cc406Sopenharmony_ci
183141cc406Sopenharmony_ci  if (scanio.scan_x_resolution > scanio.scan_y_resolution)
184141cc406Sopenharmony_ci    scanio.scan_x_resolution = scanio.scan_y_resolution;
185141cc406Sopenharmony_ci  else
186141cc406Sopenharmony_ci    scanio.scan_y_resolution = scanio.scan_x_resolution;
187141cc406Sopenharmony_ci
188141cc406Sopenharmony_ci  inc = -scanio.scan_x_resolution;
189141cc406Sopenharmony_ci  while ((inc /= 2) != 0)
190141cc406Sopenharmony_ci    {
191141cc406Sopenharmony_ci      do
192141cc406Sopenharmony_ci	{
193141cc406Sopenharmony_ci	  lastguess = scanio.scan_x_resolution;
194141cc406Sopenharmony_ci	  scanio.scan_x_resolution = scanio.scan_y_resolution += inc;
195141cc406Sopenharmony_ci	}
196141cc406Sopenharmony_ci      while (ioctl (fd, SCIOCSET, &scanio) >= 0);
197141cc406Sopenharmony_ci      scanio.scan_x_resolution = scanio.scan_y_resolution = lastguess;
198141cc406Sopenharmony_ci    }
199141cc406Sopenharmony_ci  dev->dpi_range.min = scanio.scan_x_resolution;
200141cc406Sopenharmony_ci
201141cc406Sopenharmony_ci  /* Converge on the maximum scan resolution. */
202141cc406Sopenharmony_ci  inc = 600;
203141cc406Sopenharmony_ci  while ((inc /= 2) != 0)
204141cc406Sopenharmony_ci    {
205141cc406Sopenharmony_ci      do
206141cc406Sopenharmony_ci	{
207141cc406Sopenharmony_ci	  lastguess = scanio.scan_x_resolution;
208141cc406Sopenharmony_ci	  scanio.scan_x_resolution = scanio.scan_y_resolution += inc;
209141cc406Sopenharmony_ci	}
210141cc406Sopenharmony_ci      while (ioctl (fd, SCIOCSET, &scanio) >= 0);
211141cc406Sopenharmony_ci      scanio.scan_x_resolution = scanio.scan_y_resolution = lastguess;
212141cc406Sopenharmony_ci    }
213141cc406Sopenharmony_ci  dev->dpi_range.max = scanio.scan_x_resolution;
214141cc406Sopenharmony_ci
215141cc406Sopenharmony_ci  /* Determine the valid scan modes for mode_list. */
216141cc406Sopenharmony_ci  lastguess = 0;
217141cc406Sopenharmony_ci#define CHECK_MODE(flag,modename) \
218141cc406Sopenharmony_ci  scanio.scan_image_mode = flag; \
219141cc406Sopenharmony_ci  if (ioctl (fd, SCIOCSET, &scanio) >= 0) \
220141cc406Sopenharmony_ci    mode_list[lastguess ++] = modename
221141cc406Sopenharmony_ci
222141cc406Sopenharmony_ci  CHECK_MODE(SIM_BINARY_MONOCHROME, SANE_VALUE_SCAN_MODE_LINEART);
223141cc406Sopenharmony_ci  CHECK_MODE(SIM_DITHERED_MONOCHROME, SANE_VALUE_SCAN_MODE_HALFTONE);
224141cc406Sopenharmony_ci  CHECK_MODE(SIM_GRAYSCALE, SANE_VALUE_SCAN_MODE_GRAY);
225141cc406Sopenharmony_ci  CHECK_MODE(SIM_COLOR, SANE_VALUE_SCAN_MODE_COLOR);
226141cc406Sopenharmony_ci  CHECK_MODE(SIM_RED, "Red");
227141cc406Sopenharmony_ci  CHECK_MODE(SIM_GREEN, "Green");
228141cc406Sopenharmony_ci  CHECK_MODE(SIM_BLUE, "Blue");
229141cc406Sopenharmony_ci#undef CHECK_MODE
230141cc406Sopenharmony_ci
231141cc406Sopenharmony_ci  /* Zero-terminate the list of modes. */
232141cc406Sopenharmony_ci  mode_list[lastguess] = 0;
233141cc406Sopenharmony_ci
234141cc406Sopenharmony_ci  /* Restore the scanner state. */
235141cc406Sopenharmony_ci  if (ioctl (fd, SCIOCSET, &dev->scanio))
236141cc406Sopenharmony_ci    DBG (2, "cannot reset original scanner state: %s\n", strerror (errno));
237141cc406Sopenharmony_ci  close (fd);
238141cc406Sopenharmony_ci
239141cc406Sopenharmony_ci  dev->sane.name   = strdup (devname);
240141cc406Sopenharmony_ci
241141cc406Sopenharmony_ci  /* Determine vendor. */
242141cc406Sopenharmony_ci  switch (scanio.scan_scanner_type)
243141cc406Sopenharmony_ci    {
244141cc406Sopenharmony_ci    case EPSON_ES300C:
245141cc406Sopenharmony_ci      dev->sane.vendor = "Epson";
246141cc406Sopenharmony_ci      break;
247141cc406Sopenharmony_ci
248141cc406Sopenharmony_ci    case FUJITSU_M3096G:
249141cc406Sopenharmony_ci      dev->sane.vendor = "Fujitsu";
250141cc406Sopenharmony_ci      break;
251141cc406Sopenharmony_ci
252141cc406Sopenharmony_ci    case HP_SCANJET_IIC:
253141cc406Sopenharmony_ci      dev->sane.vendor = "HP";
254141cc406Sopenharmony_ci      break;
255141cc406Sopenharmony_ci
256141cc406Sopenharmony_ci    case IBM_2456:
257141cc406Sopenharmony_ci      dev->sane.vendor = "IBM";
258141cc406Sopenharmony_ci      break;
259141cc406Sopenharmony_ci
260141cc406Sopenharmony_ci    case MUSTEK_06000CX:
261141cc406Sopenharmony_ci    case MUSTEK_12000CX:
262141cc406Sopenharmony_ci      dev->sane.vendor = "Mustek";
263141cc406Sopenharmony_ci      break;
264141cc406Sopenharmony_ci
265141cc406Sopenharmony_ci    case RICOH_FS1:
266141cc406Sopenharmony_ci    case RICOH_IS410:
267141cc406Sopenharmony_ci    case RICOH_IS50:
268141cc406Sopenharmony_ci      dev->sane.vendor = "Ricoh";
269141cc406Sopenharmony_ci      break;
270141cc406Sopenharmony_ci
271141cc406Sopenharmony_ci    case SHARP_JX600:
272141cc406Sopenharmony_ci      dev->sane.vendor = "Sharp";
273141cc406Sopenharmony_ci      break;
274141cc406Sopenharmony_ci
275141cc406Sopenharmony_ci    case UMAX_UC630:
276141cc406Sopenharmony_ci    case UMAX_UG630:
277141cc406Sopenharmony_ci      dev->sane.vendor = "UMAX";
278141cc406Sopenharmony_ci      break;
279141cc406Sopenharmony_ci
280141cc406Sopenharmony_ci    default:
281141cc406Sopenharmony_ci      dev->sane.vendor = "PINT";
282141cc406Sopenharmony_ci    }
283141cc406Sopenharmony_ci
284141cc406Sopenharmony_ci  /* Determine model. */
285141cc406Sopenharmony_ci  switch (scanio.scan_scanner_type)
286141cc406Sopenharmony_ci    {
287141cc406Sopenharmony_ci    case EPSON_ES300C:
288141cc406Sopenharmony_ci      dev->sane.vendor = "Epson";
289141cc406Sopenharmony_ci      break;
290141cc406Sopenharmony_ci
291141cc406Sopenharmony_ci    case FUJITSU_M3096G:
292141cc406Sopenharmony_ci      dev->sane.model = "M3096G";
293141cc406Sopenharmony_ci      break;
294141cc406Sopenharmony_ci
295141cc406Sopenharmony_ci    case HP_SCANJET_IIC:
296141cc406Sopenharmony_ci      dev->sane.model = "ScanJet IIc";
297141cc406Sopenharmony_ci      break;
298141cc406Sopenharmony_ci
299141cc406Sopenharmony_ci    case IBM_2456:
300141cc406Sopenharmony_ci      dev->sane.vendor = "IBM";
301141cc406Sopenharmony_ci      break;
302141cc406Sopenharmony_ci
303141cc406Sopenharmony_ci    case MUSTEK_06000CX:
304141cc406Sopenharmony_ci    case MUSTEK_12000CX:
305141cc406Sopenharmony_ci      dev->sane.vendor = "Mustek";
306141cc406Sopenharmony_ci      break;
307141cc406Sopenharmony_ci
308141cc406Sopenharmony_ci    case RICOH_FS1:
309141cc406Sopenharmony_ci      dev->sane.model = "FS1";
310141cc406Sopenharmony_ci      break;
311141cc406Sopenharmony_ci
312141cc406Sopenharmony_ci    case RICOH_IS410:
313141cc406Sopenharmony_ci      dev->sane.model = "IS-410";
314141cc406Sopenharmony_ci      break;
315141cc406Sopenharmony_ci
316141cc406Sopenharmony_ci    case RICOH_IS50:
317141cc406Sopenharmony_ci      dev->sane.vendor = "Ricoh";
318141cc406Sopenharmony_ci      break;
319141cc406Sopenharmony_ci
320141cc406Sopenharmony_ci    case SHARP_JX600:
321141cc406Sopenharmony_ci      dev->sane.vendor = "Sharp";
322141cc406Sopenharmony_ci      break;
323141cc406Sopenharmony_ci
324141cc406Sopenharmony_ci    case UMAX_UC630:
325141cc406Sopenharmony_ci    case UMAX_UG630:
326141cc406Sopenharmony_ci      dev->sane.vendor = "UMAX";
327141cc406Sopenharmony_ci      break;
328141cc406Sopenharmony_ci
329141cc406Sopenharmony_ci    default:
330141cc406Sopenharmony_ci      dev->sane.model = "unknown";
331141cc406Sopenharmony_ci    }
332141cc406Sopenharmony_ci
333141cc406Sopenharmony_ci  /* Determine the scanner type. */
334141cc406Sopenharmony_ci  switch (scanio.scan_scanner_type)
335141cc406Sopenharmony_ci    {
336141cc406Sopenharmony_ci    case HP_SCANJET_IIC:
337141cc406Sopenharmony_ci      dev->sane.type = "flatbed scanner";
338141cc406Sopenharmony_ci
339141cc406Sopenharmony_ci      /* FIXME: which of these are flatbed or handhelds? */
340141cc406Sopenharmony_ci    case EPSON_ES300C:
341141cc406Sopenharmony_ci    case FUJITSU_M3096G:
342141cc406Sopenharmony_ci    case IBM_2456:
343141cc406Sopenharmony_ci    case MUSTEK_06000CX:
344141cc406Sopenharmony_ci    case MUSTEK_12000CX:
345141cc406Sopenharmony_ci    case RICOH_FS1:
346141cc406Sopenharmony_ci    case RICOH_IS410:
347141cc406Sopenharmony_ci    case RICOH_IS50:
348141cc406Sopenharmony_ci    case SHARP_JX600:
349141cc406Sopenharmony_ci    case UMAX_UC630:
350141cc406Sopenharmony_ci    case UMAX_UG630:
351141cc406Sopenharmony_ci    default:
352141cc406Sopenharmony_ci      dev->sane.type = "generic scanner";
353141cc406Sopenharmony_ci    }
354141cc406Sopenharmony_ci
355141cc406Sopenharmony_ci  DBG(1, "attach: found %s %s, x=%g-%gmm, y=%g-%gmm, "
356141cc406Sopenharmony_ci      "resolution=%d-%ddpi\n", dev->sane.vendor, dev->sane.model,
357141cc406Sopenharmony_ci      SANE_UNFIX (dev->x_range.min), SANE_UNFIX (dev->x_range.max),
358141cc406Sopenharmony_ci      SANE_UNFIX (dev->y_range.min), SANE_UNFIX (dev->y_range.max),
359141cc406Sopenharmony_ci      dev->dpi_range.min, dev->dpi_range.max);
360141cc406Sopenharmony_ci
361141cc406Sopenharmony_ci  ++num_devices;
362141cc406Sopenharmony_ci  dev->next = first_dev;
363141cc406Sopenharmony_ci  first_dev = dev;
364141cc406Sopenharmony_ci
365141cc406Sopenharmony_ci  if (devp)
366141cc406Sopenharmony_ci    *devp = dev;
367141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
368141cc406Sopenharmony_ci}
369141cc406Sopenharmony_ci
370141cc406Sopenharmony_cistatic SANE_Status
371141cc406Sopenharmony_ciinit_options (PINT_Scanner *s)
372141cc406Sopenharmony_ci{
373141cc406Sopenharmony_ci  int i;
374141cc406Sopenharmony_ci  int x0, x1, y0, y1;
375141cc406Sopenharmony_ci
376141cc406Sopenharmony_ci  memset (s->opt, 0, sizeof (s->opt));
377141cc406Sopenharmony_ci  memset (s->val, 0, sizeof (s->val));
378141cc406Sopenharmony_ci
379141cc406Sopenharmony_ci  for (i = 0; i < NUM_OPTIONS; ++i)
380141cc406Sopenharmony_ci    {
381141cc406Sopenharmony_ci      s->opt[i].size = sizeof (SANE_Word);
382141cc406Sopenharmony_ci      s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
383141cc406Sopenharmony_ci    }
384141cc406Sopenharmony_ci
385141cc406Sopenharmony_ci  s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
386141cc406Sopenharmony_ci  s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
387141cc406Sopenharmony_ci  s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
388141cc406Sopenharmony_ci  s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
389141cc406Sopenharmony_ci  s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
390141cc406Sopenharmony_ci
391141cc406Sopenharmony_ci  /* "Mode" group: */
392141cc406Sopenharmony_ci  s->opt[OPT_MODE_GROUP].title = "Scan Mode";
393141cc406Sopenharmony_ci  s->opt[OPT_MODE_GROUP].desc = "";
394141cc406Sopenharmony_ci  s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
395141cc406Sopenharmony_ci  s->opt[OPT_MODE_GROUP].cap = 0;
396141cc406Sopenharmony_ci  s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
397141cc406Sopenharmony_ci
398141cc406Sopenharmony_ci  /* scan mode */
399141cc406Sopenharmony_ci  s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
400141cc406Sopenharmony_ci  s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
401141cc406Sopenharmony_ci  s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
402141cc406Sopenharmony_ci  s->opt[OPT_MODE].type = SANE_TYPE_STRING;
403141cc406Sopenharmony_ci  s->opt[OPT_MODE].size = max_string_size (mode_list);
404141cc406Sopenharmony_ci  s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
405141cc406Sopenharmony_ci  s->opt[OPT_MODE].constraint.string_list = mode_list;
406141cc406Sopenharmony_ci
407141cc406Sopenharmony_ci  /* Translate the current PINT mode into a string. */
408141cc406Sopenharmony_ci  switch (s->hw->scanio.scan_image_mode)
409141cc406Sopenharmony_ci    {
410141cc406Sopenharmony_ci    case SIM_BINARY_MONOCHROME:
411141cc406Sopenharmony_ci      s->val[OPT_MODE].s = strdup (mode_list[0]);
412141cc406Sopenharmony_ci      break;
413141cc406Sopenharmony_ci
414141cc406Sopenharmony_ci    case SIM_DITHERED_MONOCHROME:
415141cc406Sopenharmony_ci      s->val[OPT_MODE].s = strdup (mode_list[1]);
416141cc406Sopenharmony_ci      break;
417141cc406Sopenharmony_ci
418141cc406Sopenharmony_ci    case SIM_COLOR:
419141cc406Sopenharmony_ci      s->val[OPT_MODE].s = strdup (mode_list[3]);
420141cc406Sopenharmony_ci      break;
421141cc406Sopenharmony_ci
422141cc406Sopenharmony_ci    case SIM_RED:
423141cc406Sopenharmony_ci      s->val[OPT_MODE].s = strdup (mode_list[4]);
424141cc406Sopenharmony_ci      break;
425141cc406Sopenharmony_ci
426141cc406Sopenharmony_ci    case SIM_GREEN:
427141cc406Sopenharmony_ci      s->val[OPT_MODE].s = strdup (mode_list[5]);
428141cc406Sopenharmony_ci      break;
429141cc406Sopenharmony_ci
430141cc406Sopenharmony_ci    case SIM_BLUE:
431141cc406Sopenharmony_ci      s->val[OPT_MODE].s = strdup (mode_list[6]);
432141cc406Sopenharmony_ci      break;
433141cc406Sopenharmony_ci
434141cc406Sopenharmony_ci    case SIM_GRAYSCALE:
435141cc406Sopenharmony_ci    default:
436141cc406Sopenharmony_ci      s->val[OPT_MODE].s = strdup (mode_list[2]);
437141cc406Sopenharmony_ci    }
438141cc406Sopenharmony_ci
439141cc406Sopenharmony_ci  /* resolution */
440141cc406Sopenharmony_ci  s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
441141cc406Sopenharmony_ci  s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
442141cc406Sopenharmony_ci  s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
443141cc406Sopenharmony_ci  s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
444141cc406Sopenharmony_ci  s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
445141cc406Sopenharmony_ci  s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
446141cc406Sopenharmony_ci  s->opt[OPT_RESOLUTION].constraint.range = &s->hw->dpi_range;
447141cc406Sopenharmony_ci  s->val[OPT_RESOLUTION].w =
448141cc406Sopenharmony_ci    (s->hw->scanio.scan_x_resolution > s->hw->scanio.scan_y_resolution) ?
449141cc406Sopenharmony_ci    s->hw->scanio.scan_x_resolution : s->hw->scanio.scan_y_resolution;
450141cc406Sopenharmony_ci
451141cc406Sopenharmony_ci  /* "Geometry" group: */
452141cc406Sopenharmony_ci
453141cc406Sopenharmony_ci  s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
454141cc406Sopenharmony_ci  s->opt[OPT_GEOMETRY_GROUP].desc = "";
455141cc406Sopenharmony_ci  s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
456141cc406Sopenharmony_ci  s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
457141cc406Sopenharmony_ci  s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
458141cc406Sopenharmony_ci
459141cc406Sopenharmony_ci  /* Calculate the x and y millimetre coordinates from the scanio. */
460141cc406Sopenharmony_ci  x0 = SANE_FIX (s->hw->scanio.scan_x_origin / TWELVEHUNDS_PER_MM);
461141cc406Sopenharmony_ci  y0 = SANE_FIX (s->hw->scanio.scan_y_origin / TWELVEHUNDS_PER_MM);
462141cc406Sopenharmony_ci  x1 = SANE_FIX ((s->hw->scanio.scan_x_origin + s->hw->scanio.scan_width)
463141cc406Sopenharmony_ci		 / TWELVEHUNDS_PER_MM);
464141cc406Sopenharmony_ci  y1 = SANE_FIX ((s->hw->scanio.scan_y_origin + s->hw->scanio.scan_height)
465141cc406Sopenharmony_ci		 / TWELVEHUNDS_PER_MM);
466141cc406Sopenharmony_ci
467141cc406Sopenharmony_ci  /* top-left x */
468141cc406Sopenharmony_ci  s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
469141cc406Sopenharmony_ci  s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
470141cc406Sopenharmony_ci  s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
471141cc406Sopenharmony_ci  s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
472141cc406Sopenharmony_ci  s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
473141cc406Sopenharmony_ci  s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
474141cc406Sopenharmony_ci  s->opt[OPT_TL_X].constraint.range = &s->hw->x_range;
475141cc406Sopenharmony_ci  s->val[OPT_TL_X].w = x0;
476141cc406Sopenharmony_ci
477141cc406Sopenharmony_ci  /* top-left y */
478141cc406Sopenharmony_ci  s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
479141cc406Sopenharmony_ci  s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
480141cc406Sopenharmony_ci  s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
481141cc406Sopenharmony_ci  s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
482141cc406Sopenharmony_ci  s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
483141cc406Sopenharmony_ci  s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
484141cc406Sopenharmony_ci  s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range;
485141cc406Sopenharmony_ci  s->val[OPT_TL_Y].w = y0;
486141cc406Sopenharmony_ci
487141cc406Sopenharmony_ci  /* bottom-right x */
488141cc406Sopenharmony_ci  s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
489141cc406Sopenharmony_ci  s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
490141cc406Sopenharmony_ci  s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
491141cc406Sopenharmony_ci  s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
492141cc406Sopenharmony_ci  s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
493141cc406Sopenharmony_ci  s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
494141cc406Sopenharmony_ci  s->opt[OPT_BR_X].constraint.range = &s->hw->x_range;
495141cc406Sopenharmony_ci  s->val[OPT_BR_X].w = x1;
496141cc406Sopenharmony_ci
497141cc406Sopenharmony_ci  /* bottom-right y */
498141cc406Sopenharmony_ci  s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
499141cc406Sopenharmony_ci  s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
500141cc406Sopenharmony_ci  s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
501141cc406Sopenharmony_ci  s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
502141cc406Sopenharmony_ci  s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
503141cc406Sopenharmony_ci  s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
504141cc406Sopenharmony_ci  s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range;
505141cc406Sopenharmony_ci  s->val[OPT_BR_Y].w = y1;
506141cc406Sopenharmony_ci
507141cc406Sopenharmony_ci  /* "Enhancement" group: */
508141cc406Sopenharmony_ci
509141cc406Sopenharmony_ci  s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
510141cc406Sopenharmony_ci  s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
511141cc406Sopenharmony_ci  s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
512141cc406Sopenharmony_ci  s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
513141cc406Sopenharmony_ci  s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
514141cc406Sopenharmony_ci
515141cc406Sopenharmony_ci  /* brightness */
516141cc406Sopenharmony_ci  s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
517141cc406Sopenharmony_ci  s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
518141cc406Sopenharmony_ci  s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
519141cc406Sopenharmony_ci  s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
520141cc406Sopenharmony_ci  s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
521141cc406Sopenharmony_ci  s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
522141cc406Sopenharmony_ci  s->opt[OPT_BRIGHTNESS].constraint.range = &s7_range;
523141cc406Sopenharmony_ci  s->val[OPT_BRIGHTNESS].w = s->hw->scanio.scan_brightness - 128;
524141cc406Sopenharmony_ci
525141cc406Sopenharmony_ci  /* contrast */
526141cc406Sopenharmony_ci  s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
527141cc406Sopenharmony_ci  s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
528141cc406Sopenharmony_ci  s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
529141cc406Sopenharmony_ci  s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
530141cc406Sopenharmony_ci  s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
531141cc406Sopenharmony_ci  s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
532141cc406Sopenharmony_ci  s->opt[OPT_CONTRAST].constraint.range = &s7_range;
533141cc406Sopenharmony_ci  s->val[OPT_CONTRAST].w = s->hw->scanio.scan_contrast - 128;
534141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
535141cc406Sopenharmony_ci}
536141cc406Sopenharmony_ci
537141cc406Sopenharmony_cistatic SANE_Status
538141cc406Sopenharmony_cido_cancel (PINT_Scanner *s)
539141cc406Sopenharmony_ci{
540141cc406Sopenharmony_ci  /* FIXME: PINT doesn't have any good way to cancel ScanJets right now. */
541141cc406Sopenharmony_ci#define gobble_up_buf_len 1024
542141cc406Sopenharmony_ci  char buf[gobble_up_buf_len];
543141cc406Sopenharmony_ci
544141cc406Sopenharmony_ci  /* Send the restart code. */
545141cc406Sopenharmony_ci  buf[0] = ioctl (s->fd, SCIOCRESTART, 0);
546141cc406Sopenharmony_ci
547141cc406Sopenharmony_ci  if (!s->scanning)
548141cc406Sopenharmony_ci    return SANE_STATUS_CANCELLED;
549141cc406Sopenharmony_ci
550141cc406Sopenharmony_ci  s->scanning = SANE_FALSE;
551141cc406Sopenharmony_ci
552141cc406Sopenharmony_ci  /* Read to the end of the file. */
553141cc406Sopenharmony_ci  while (read (s->fd, buf, gobble_up_buf_len) > 0)
554141cc406Sopenharmony_ci    ;
555141cc406Sopenharmony_ci#undef gobble_up_buf_len
556141cc406Sopenharmony_ci
557141cc406Sopenharmony_ci  /* Finally, close the file descriptor. */
558141cc406Sopenharmony_ci  if (s->fd >= 0)
559141cc406Sopenharmony_ci    {
560141cc406Sopenharmony_ci      close (s->fd);
561141cc406Sopenharmony_ci      s->fd = -1;
562141cc406Sopenharmony_ci    }
563141cc406Sopenharmony_ci  return SANE_STATUS_CANCELLED;
564141cc406Sopenharmony_ci}
565141cc406Sopenharmony_ci
566141cc406Sopenharmony_ciSANE_Status
567141cc406Sopenharmony_cisane_init (SANE_Int *version_code, SANE_Auth_Callback authorize)
568141cc406Sopenharmony_ci{
569141cc406Sopenharmony_ci  char dev_name[PATH_MAX];
570141cc406Sopenharmony_ci  size_t len;
571141cc406Sopenharmony_ci  FILE *fp;
572141cc406Sopenharmony_ci
573141cc406Sopenharmony_ci  DBG_INIT();
574141cc406Sopenharmony_ci
575141cc406Sopenharmony_ci  if (version_code)
576141cc406Sopenharmony_ci    *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0);
577141cc406Sopenharmony_ci
578141cc406Sopenharmony_ci  fp = sanei_config_open (PINT_CONFIG_FILE);
579141cc406Sopenharmony_ci  if (!fp)
580141cc406Sopenharmony_ci    {
581141cc406Sopenharmony_ci      /* default to /dev/scanner instead of insisting on config file */
582141cc406Sopenharmony_ci      attach ("/dev/scanner", 0);
583141cc406Sopenharmony_ci      return SANE_STATUS_GOOD;
584141cc406Sopenharmony_ci    }
585141cc406Sopenharmony_ci
586141cc406Sopenharmony_ci  while (sanei_config_read (dev_name, sizeof (dev_name), fp))
587141cc406Sopenharmony_ci    {
588141cc406Sopenharmony_ci      if (dev_name[0] == '#')		/* ignore line comments */
589141cc406Sopenharmony_ci	continue;
590141cc406Sopenharmony_ci      len = strlen (dev_name);
591141cc406Sopenharmony_ci
592141cc406Sopenharmony_ci      if (!len)
593141cc406Sopenharmony_ci	continue;			/* ignore empty lines */
594141cc406Sopenharmony_ci
595141cc406Sopenharmony_ci      attach (dev_name, 0);
596141cc406Sopenharmony_ci    }
597141cc406Sopenharmony_ci  fclose (fp);
598141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
599141cc406Sopenharmony_ci}
600141cc406Sopenharmony_ci
601141cc406Sopenharmony_civoid
602141cc406Sopenharmony_cisane_exit (void)
603141cc406Sopenharmony_ci{
604141cc406Sopenharmony_ci  PINT_Device *dev, *next;
605141cc406Sopenharmony_ci
606141cc406Sopenharmony_ci  for (dev = first_dev; dev; dev = next)
607141cc406Sopenharmony_ci    {
608141cc406Sopenharmony_ci      next = dev->next;
609141cc406Sopenharmony_ci      free ((void *) dev->sane.name);
610141cc406Sopenharmony_ci      free (dev);
611141cc406Sopenharmony_ci    }
612141cc406Sopenharmony_ci}
613141cc406Sopenharmony_ci
614141cc406Sopenharmony_ciSANE_Status
615141cc406Sopenharmony_cisane_get_devices (const SANE_Device ***device_list, SANE_Bool local_only)
616141cc406Sopenharmony_ci{
617141cc406Sopenharmony_ci  static const SANE_Device **devlist = 0;
618141cc406Sopenharmony_ci  PINT_Device *dev;
619141cc406Sopenharmony_ci  int i;
620141cc406Sopenharmony_ci
621141cc406Sopenharmony_ci  if (devlist)
622141cc406Sopenharmony_ci    free (devlist);
623141cc406Sopenharmony_ci
624141cc406Sopenharmony_ci  devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
625141cc406Sopenharmony_ci  if (!devlist)
626141cc406Sopenharmony_ci    return SANE_STATUS_NO_MEM;
627141cc406Sopenharmony_ci
628141cc406Sopenharmony_ci  i = 0;
629141cc406Sopenharmony_ci  for (dev = first_dev; i < num_devices; dev = dev->next)
630141cc406Sopenharmony_ci    devlist[i++] = &dev->sane;
631141cc406Sopenharmony_ci  devlist[i++] = 0;
632141cc406Sopenharmony_ci
633141cc406Sopenharmony_ci  *device_list = devlist;
634141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
635141cc406Sopenharmony_ci}
636141cc406Sopenharmony_ci
637141cc406Sopenharmony_ciSANE_Status
638141cc406Sopenharmony_cisane_open (SANE_String_Const devicename, SANE_Handle *handle)
639141cc406Sopenharmony_ci{
640141cc406Sopenharmony_ci  SANE_Status status;
641141cc406Sopenharmony_ci  PINT_Device *dev;
642141cc406Sopenharmony_ci  PINT_Scanner *s;
643141cc406Sopenharmony_ci
644141cc406Sopenharmony_ci  if (devicename[0])
645141cc406Sopenharmony_ci    {
646141cc406Sopenharmony_ci      for (dev = first_dev; dev; dev = dev->next)
647141cc406Sopenharmony_ci	if (strcmp (dev->sane.name, devicename) == 0)
648141cc406Sopenharmony_ci	  break;
649141cc406Sopenharmony_ci
650141cc406Sopenharmony_ci      if (!dev)
651141cc406Sopenharmony_ci	{
652141cc406Sopenharmony_ci	  status = attach (devicename, &dev);
653141cc406Sopenharmony_ci	  if (status != SANE_STATUS_GOOD)
654141cc406Sopenharmony_ci	    return status;
655141cc406Sopenharmony_ci	}
656141cc406Sopenharmony_ci    }
657141cc406Sopenharmony_ci  else
658141cc406Sopenharmony_ci    /* empty devicename -> use first device */
659141cc406Sopenharmony_ci    dev = first_dev;
660141cc406Sopenharmony_ci
661141cc406Sopenharmony_ci  if (!dev)
662141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
663141cc406Sopenharmony_ci
664141cc406Sopenharmony_ci  s = malloc (sizeof (*s));
665141cc406Sopenharmony_ci  if (!s)
666141cc406Sopenharmony_ci    return SANE_STATUS_NO_MEM;
667141cc406Sopenharmony_ci  memset (s, 0, sizeof (*s));
668141cc406Sopenharmony_ci  s->hw = dev;
669141cc406Sopenharmony_ci  s->fd = -1;
670141cc406Sopenharmony_ci
671141cc406Sopenharmony_ci  init_options (s);
672141cc406Sopenharmony_ci
673141cc406Sopenharmony_ci  /* insert newly opened handle into list of open handles: */
674141cc406Sopenharmony_ci  s->next = first_handle;
675141cc406Sopenharmony_ci  first_handle = s;
676141cc406Sopenharmony_ci
677141cc406Sopenharmony_ci  *handle = s;
678141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
679141cc406Sopenharmony_ci}
680141cc406Sopenharmony_ci
681141cc406Sopenharmony_civoid
682141cc406Sopenharmony_cisane_close (SANE_Handle handle)
683141cc406Sopenharmony_ci{
684141cc406Sopenharmony_ci  PINT_Scanner *prev, *s;
685141cc406Sopenharmony_ci
686141cc406Sopenharmony_ci  /* remove handle from list of open handles: */
687141cc406Sopenharmony_ci  prev = 0;
688141cc406Sopenharmony_ci  for (s = first_handle; s; s = s->next)
689141cc406Sopenharmony_ci    {
690141cc406Sopenharmony_ci      if (s == handle)
691141cc406Sopenharmony_ci	break;
692141cc406Sopenharmony_ci      prev = s;
693141cc406Sopenharmony_ci    }
694141cc406Sopenharmony_ci  if (!s)
695141cc406Sopenharmony_ci    {
696141cc406Sopenharmony_ci      DBG(1, "close: invalid handle %p\n", handle);
697141cc406Sopenharmony_ci      return;		/* oops, not a handle we know about */
698141cc406Sopenharmony_ci    }
699141cc406Sopenharmony_ci
700141cc406Sopenharmony_ci  if (s->scanning)
701141cc406Sopenharmony_ci    do_cancel (handle);
702141cc406Sopenharmony_ci
703141cc406Sopenharmony_ci  if (prev)
704141cc406Sopenharmony_ci    prev->next = s->next;
705141cc406Sopenharmony_ci  else
706141cc406Sopenharmony_ci    first_handle = s->next;
707141cc406Sopenharmony_ci
708141cc406Sopenharmony_ci  free (handle);
709141cc406Sopenharmony_ci}
710141cc406Sopenharmony_ci
711141cc406Sopenharmony_ciconst SANE_Option_Descriptor *
712141cc406Sopenharmony_cisane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
713141cc406Sopenharmony_ci{
714141cc406Sopenharmony_ci  PINT_Scanner *s = handle;
715141cc406Sopenharmony_ci
716141cc406Sopenharmony_ci  if ((unsigned) option >= NUM_OPTIONS)
717141cc406Sopenharmony_ci    return 0;
718141cc406Sopenharmony_ci  return s->opt + option;
719141cc406Sopenharmony_ci}
720141cc406Sopenharmony_ci
721141cc406Sopenharmony_ciSANE_Status
722141cc406Sopenharmony_cisane_control_option (SANE_Handle handle, SANE_Int option,
723141cc406Sopenharmony_ci		     SANE_Action action, void *val, SANE_Int *info)
724141cc406Sopenharmony_ci{
725141cc406Sopenharmony_ci  PINT_Scanner *s = handle;
726141cc406Sopenharmony_ci  SANE_Status status;
727141cc406Sopenharmony_ci  SANE_Word cap;
728141cc406Sopenharmony_ci
729141cc406Sopenharmony_ci  if (info)
730141cc406Sopenharmony_ci    *info = 0;
731141cc406Sopenharmony_ci
732141cc406Sopenharmony_ci  if (s->scanning)
733141cc406Sopenharmony_ci    return SANE_STATUS_DEVICE_BUSY;
734141cc406Sopenharmony_ci
735141cc406Sopenharmony_ci  if (option >= NUM_OPTIONS)
736141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
737141cc406Sopenharmony_ci
738141cc406Sopenharmony_ci  cap = s->opt[option].cap;
739141cc406Sopenharmony_ci
740141cc406Sopenharmony_ci  if (!SANE_OPTION_IS_ACTIVE (cap))
741141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
742141cc406Sopenharmony_ci
743141cc406Sopenharmony_ci  if (action == SANE_ACTION_GET_VALUE)
744141cc406Sopenharmony_ci    {
745141cc406Sopenharmony_ci      switch (option)
746141cc406Sopenharmony_ci	{
747141cc406Sopenharmony_ci	  /* word options: */
748141cc406Sopenharmony_ci	case OPT_RESOLUTION:
749141cc406Sopenharmony_ci	case OPT_TL_X:
750141cc406Sopenharmony_ci	case OPT_TL_Y:
751141cc406Sopenharmony_ci	case OPT_BR_X:
752141cc406Sopenharmony_ci	case OPT_BR_Y:
753141cc406Sopenharmony_ci	case OPT_NUM_OPTS:
754141cc406Sopenharmony_ci	case OPT_BRIGHTNESS:
755141cc406Sopenharmony_ci	case OPT_CONTRAST:
756141cc406Sopenharmony_ci	  *(SANE_Word *) val = s->val[option].w;
757141cc406Sopenharmony_ci	  return SANE_STATUS_GOOD;
758141cc406Sopenharmony_ci
759141cc406Sopenharmony_ci	  /* string options: */
760141cc406Sopenharmony_ci	case OPT_MODE:
761141cc406Sopenharmony_ci	  strcpy (val, s->val[option].s);
762141cc406Sopenharmony_ci	  return SANE_STATUS_GOOD;
763141cc406Sopenharmony_ci	}
764141cc406Sopenharmony_ci    }
765141cc406Sopenharmony_ci  else if (action == SANE_ACTION_SET_VALUE)
766141cc406Sopenharmony_ci    {
767141cc406Sopenharmony_ci      if (!SANE_OPTION_IS_SETTABLE (cap))
768141cc406Sopenharmony_ci	return SANE_STATUS_INVAL;
769141cc406Sopenharmony_ci
770141cc406Sopenharmony_ci      status = sanei_constrain_value (s->opt + option, val, info);
771141cc406Sopenharmony_ci      if (status != SANE_STATUS_GOOD)
772141cc406Sopenharmony_ci	return status;
773141cc406Sopenharmony_ci
774141cc406Sopenharmony_ci      switch (option)
775141cc406Sopenharmony_ci	{
776141cc406Sopenharmony_ci	  /* (mostly) side-effect-free word options: */
777141cc406Sopenharmony_ci	case OPT_RESOLUTION:
778141cc406Sopenharmony_ci	case OPT_TL_X:
779141cc406Sopenharmony_ci	case OPT_TL_Y:
780141cc406Sopenharmony_ci	case OPT_BR_X:
781141cc406Sopenharmony_ci	case OPT_BR_Y:
782141cc406Sopenharmony_ci	  if (info)
783141cc406Sopenharmony_ci	    *info |= SANE_INFO_RELOAD_PARAMS;
784141cc406Sopenharmony_ci	  /* fall through */
785141cc406Sopenharmony_ci	case OPT_NUM_OPTS:
786141cc406Sopenharmony_ci	case OPT_BRIGHTNESS:
787141cc406Sopenharmony_ci	case OPT_CONTRAST:
788141cc406Sopenharmony_ci	  s->val[option].w = *(SANE_Word *) val;
789141cc406Sopenharmony_ci	  return SANE_STATUS_GOOD;
790141cc406Sopenharmony_ci
791141cc406Sopenharmony_ci	case OPT_MODE:
792141cc406Sopenharmony_ci	  if (s->val[option].s)
793141cc406Sopenharmony_ci	    free (s->val[option].s);
794141cc406Sopenharmony_ci	  s->val[option].s = strdup (val);
795141cc406Sopenharmony_ci	  if (info)
796141cc406Sopenharmony_ci	    *info |= SANE_INFO_RELOAD_PARAMS;
797141cc406Sopenharmony_ci	  return SANE_STATUS_GOOD;
798141cc406Sopenharmony_ci	}
799141cc406Sopenharmony_ci    }
800141cc406Sopenharmony_ci  return SANE_STATUS_INVAL;
801141cc406Sopenharmony_ci}
802141cc406Sopenharmony_ci
803141cc406Sopenharmony_ciSANE_Status
804141cc406Sopenharmony_cisane_get_parameters (SANE_Handle handle, SANE_Parameters *params)
805141cc406Sopenharmony_ci{
806141cc406Sopenharmony_ci  PINT_Scanner *s = handle;
807141cc406Sopenharmony_ci  struct scan_io scanio;
808141cc406Sopenharmony_ci
809141cc406Sopenharmony_ci  if (!s->scanning)
810141cc406Sopenharmony_ci    {
811141cc406Sopenharmony_ci      u_long x0, y0, width, height;
812141cc406Sopenharmony_ci      const char *mode;
813141cc406Sopenharmony_ci
814141cc406Sopenharmony_ci      /* Grab the scanio for this device. */
815141cc406Sopenharmony_ci      if (s->fd < 0)
816141cc406Sopenharmony_ci	{
817141cc406Sopenharmony_ci	  s->fd = open (s->hw->sane.name, O_RDONLY, 0);
818141cc406Sopenharmony_ci	  if (s->fd < 0)
819141cc406Sopenharmony_ci	    {
820141cc406Sopenharmony_ci	      DBG(1, "open of %s failed: %s\n",
821141cc406Sopenharmony_ci		  s->hw->sane.name, strerror (errno));
822141cc406Sopenharmony_ci	      return SANE_STATUS_INVAL;
823141cc406Sopenharmony_ci	    }
824141cc406Sopenharmony_ci	}
825141cc406Sopenharmony_ci
826141cc406Sopenharmony_ci      if (ioctl (s->fd, SCIOCGET, &scanio) < 0)
827141cc406Sopenharmony_ci	{
828141cc406Sopenharmony_ci	  DBG(1, "getting scanner state failed: %s", strerror (errno));
829141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
830141cc406Sopenharmony_ci	}
831141cc406Sopenharmony_ci
832141cc406Sopenharmony_ci      memset (&s->params, 0, sizeof (s->params));
833141cc406Sopenharmony_ci
834141cc406Sopenharmony_ci      /* FIXME: there is some lossage here: the parameters change due to
835141cc406Sopenharmony_ci	 roundoff errors between converting to fixed point millimetres
836141cc406Sopenharmony_ci	 and back. */
837141cc406Sopenharmony_ci      x0 = SANE_UNFIX (s->val[OPT_TL_X].w * TWELVEHUNDS_PER_MM);
838141cc406Sopenharmony_ci      y0 = SANE_UNFIX (s->val[OPT_TL_Y].w * TWELVEHUNDS_PER_MM);
839141cc406Sopenharmony_ci      width  = SANE_UNFIX ((s->val[OPT_BR_X].w - s->val[OPT_TL_X].w)
840141cc406Sopenharmony_ci			   * TWELVEHUNDS_PER_MM);
841141cc406Sopenharmony_ci      height = SANE_UNFIX ((s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w)
842141cc406Sopenharmony_ci			   * TWELVEHUNDS_PER_MM);
843141cc406Sopenharmony_ci
844141cc406Sopenharmony_ci      /* x and y dpi: */
845141cc406Sopenharmony_ci      scanio.scan_x_resolution = s->val[OPT_RESOLUTION].w;
846141cc406Sopenharmony_ci      scanio.scan_y_resolution = s->val[OPT_RESOLUTION].w;
847141cc406Sopenharmony_ci
848141cc406Sopenharmony_ci      /* set scan extents, in 1/1200'ths of an inch */
849141cc406Sopenharmony_ci      scanio.scan_x_origin = x0;
850141cc406Sopenharmony_ci      scanio.scan_y_origin = y0;
851141cc406Sopenharmony_ci      scanio.scan_width = width;
852141cc406Sopenharmony_ci      scanio.scan_height = height;
853141cc406Sopenharmony_ci
854141cc406Sopenharmony_ci      /* brightness and contrast */
855141cc406Sopenharmony_ci      scanio.scan_brightness = s->val[OPT_BRIGHTNESS].w + 128;
856141cc406Sopenharmony_ci      scanio.scan_contrast = s->val[OPT_CONTRAST].w + 128;
857141cc406Sopenharmony_ci
858141cc406Sopenharmony_ci      /* set the scan image mode */
859141cc406Sopenharmony_ci      mode = s->val[OPT_MODE].s;
860141cc406Sopenharmony_ci      if (!strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART))
861141cc406Sopenharmony_ci	{
862141cc406Sopenharmony_ci	  s->params.format = SANE_FRAME_GRAY;
863141cc406Sopenharmony_ci	  scanio.scan_image_mode = SIM_BINARY_MONOCHROME;
864141cc406Sopenharmony_ci	}
865141cc406Sopenharmony_ci      else if (!strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE))
866141cc406Sopenharmony_ci	{
867141cc406Sopenharmony_ci	  s->params.format = SANE_FRAME_GRAY;
868141cc406Sopenharmony_ci	  scanio.scan_image_mode = SIM_DITHERED_MONOCHROME;
869141cc406Sopenharmony_ci	}
870141cc406Sopenharmony_ci      else if (!strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY))
871141cc406Sopenharmony_ci	{
872141cc406Sopenharmony_ci	  s->params.format = SANE_FRAME_GRAY;
873141cc406Sopenharmony_ci	  scanio.scan_image_mode = SIM_GRAYSCALE;
874141cc406Sopenharmony_ci	}
875141cc406Sopenharmony_ci      else if (!strcmp (mode, "Red"))
876141cc406Sopenharmony_ci	{
877141cc406Sopenharmony_ci	  s->params.format = SANE_FRAME_RED;
878141cc406Sopenharmony_ci	  scanio.scan_image_mode = SIM_RED;
879141cc406Sopenharmony_ci	}
880141cc406Sopenharmony_ci      else if (!strcmp (mode, "Green"))
881141cc406Sopenharmony_ci	{
882141cc406Sopenharmony_ci	  s->params.format = SANE_FRAME_GREEN;
883141cc406Sopenharmony_ci	  scanio.scan_image_mode = SIM_GREEN;
884141cc406Sopenharmony_ci	}
885141cc406Sopenharmony_ci      else if (!strcmp (mode, "Blue"))
886141cc406Sopenharmony_ci	{
887141cc406Sopenharmony_ci	  s->params.format = SANE_FRAME_BLUE;
888141cc406Sopenharmony_ci	  scanio.scan_image_mode = SIM_BLUE;
889141cc406Sopenharmony_ci	}
890141cc406Sopenharmony_ci      else
891141cc406Sopenharmony_ci	{
892141cc406Sopenharmony_ci	  s->params.format = SANE_FRAME_RGB;
893141cc406Sopenharmony_ci	  scanio.scan_image_mode = SIM_COLOR;
894141cc406Sopenharmony_ci	}
895141cc406Sopenharmony_ci
896141cc406Sopenharmony_ci      /* inquire resulting size of image after setting it up */
897141cc406Sopenharmony_ci      if (ioctl (s->fd, SCIOCSET, &scanio) < 0)
898141cc406Sopenharmony_ci	{
899141cc406Sopenharmony_ci	  DBG(1, "setting scan parameters failed: %s", strerror (errno));
900141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
901141cc406Sopenharmony_ci	}
902141cc406Sopenharmony_ci      if (ioctl (s->fd, SCIOCGET, &scanio) < 0)
903141cc406Sopenharmony_ci	{
904141cc406Sopenharmony_ci	  DBG(1, "getting scan parameters failed: %s", strerror (errno));
905141cc406Sopenharmony_ci	  return SANE_STATUS_INVAL;
906141cc406Sopenharmony_ci	}
907141cc406Sopenharmony_ci
908141cc406Sopenharmony_ci      /* Save all the PINT-computed values. */
909141cc406Sopenharmony_ci      s->params.pixels_per_line = scanio.scan_pixels_per_line;
910141cc406Sopenharmony_ci      s->params.bytes_per_line =
911141cc406Sopenharmony_ci	(scanio.scan_bits_per_pixel * scanio.scan_pixels_per_line + 7) / 8;
912141cc406Sopenharmony_ci      s->params.lines = scanio.scan_lines;
913141cc406Sopenharmony_ci      s->params.depth = (scanio.scan_image_mode == SIM_COLOR) ?
914141cc406Sopenharmony_ci	scanio.scan_bits_per_pixel / 3 : scanio.scan_bits_per_pixel;
915141cc406Sopenharmony_ci
916141cc406Sopenharmony_ci      /* FIXME: this will need to be different for hand scanners. */
917141cc406Sopenharmony_ci      s->params.last_frame = SANE_TRUE;
918141cc406Sopenharmony_ci    }
919141cc406Sopenharmony_ci  if (params)
920141cc406Sopenharmony_ci    *params = s->params;
921141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
922141cc406Sopenharmony_ci}
923141cc406Sopenharmony_ci
924141cc406Sopenharmony_ciSANE_Status
925141cc406Sopenharmony_cisane_start (SANE_Handle handle)
926141cc406Sopenharmony_ci{
927141cc406Sopenharmony_ci  PINT_Scanner *s = handle;
928141cc406Sopenharmony_ci  SANE_Status status;
929141cc406Sopenharmony_ci
930141cc406Sopenharmony_ci  /* First make sure we have a current parameter set.  This call actually
931141cc406Sopenharmony_ci     uses the PINT driver to do the calculations, so we trust its results. */
932141cc406Sopenharmony_ci  status = sane_get_parameters (s, 0);
933141cc406Sopenharmony_ci  if (status != SANE_STATUS_GOOD)
934141cc406Sopenharmony_ci    return status;
935141cc406Sopenharmony_ci
936141cc406Sopenharmony_ci  DBG(1, "%d pixels per line, %d bytes, %d lines high, dpi=%d\n",
937141cc406Sopenharmony_ci      s->params.pixels_per_line, s->params.bytes_per_line, s->params.lines,
938141cc406Sopenharmony_ci      s->val[OPT_RESOLUTION].w);
939141cc406Sopenharmony_ci
940141cc406Sopenharmony_ci  /* The scan is triggered in sane_read. */
941141cc406Sopenharmony_ci  s->scanning = SANE_TRUE;
942141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
943141cc406Sopenharmony_ci}
944141cc406Sopenharmony_ci
945141cc406Sopenharmony_ciSANE_Status
946141cc406Sopenharmony_cisane_read (SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len, SANE_Int *len)
947141cc406Sopenharmony_ci{
948141cc406Sopenharmony_ci  PINT_Scanner *s = handle;
949141cc406Sopenharmony_ci  ssize_t nread;
950141cc406Sopenharmony_ci
951141cc406Sopenharmony_ci  *len = 0;
952141cc406Sopenharmony_ci
953141cc406Sopenharmony_ci  if (!s->scanning)
954141cc406Sopenharmony_ci    return do_cancel (s);
955141cc406Sopenharmony_ci
956141cc406Sopenharmony_ci  /* Verrry simple.  Just suck up all the data PINT passes to us. */
957141cc406Sopenharmony_ci  nread = read (s->fd, buf, max_len);
958141cc406Sopenharmony_ci  if (nread <= 0)
959141cc406Sopenharmony_ci    {
960141cc406Sopenharmony_ci      do_cancel (s);
961141cc406Sopenharmony_ci      return (nread == 0) ? SANE_STATUS_EOF : SANE_STATUS_IO_ERROR;
962141cc406Sopenharmony_ci    }
963141cc406Sopenharmony_ci
964141cc406Sopenharmony_ci  *len = nread;
965141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
966141cc406Sopenharmony_ci}
967141cc406Sopenharmony_ci
968141cc406Sopenharmony_civoid
969141cc406Sopenharmony_cisane_cancel (SANE_Handle handle)
970141cc406Sopenharmony_ci{
971141cc406Sopenharmony_ci  PINT_Scanner *s = handle;
972141cc406Sopenharmony_ci  do_cancel (s);
973141cc406Sopenharmony_ci}
974141cc406Sopenharmony_ci
975141cc406Sopenharmony_ciSANE_Status
976141cc406Sopenharmony_cisane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
977141cc406Sopenharmony_ci{
978141cc406Sopenharmony_ci  return SANE_STATUS_UNSUPPORTED;
979141cc406Sopenharmony_ci}
980141cc406Sopenharmony_ci
981141cc406Sopenharmony_ciSANE_Status
982141cc406Sopenharmony_cisane_get_select_fd (SANE_Handle handle, SANE_Int *fd)
983141cc406Sopenharmony_ci{
984141cc406Sopenharmony_ci  return SANE_STATUS_UNSUPPORTED;
985141cc406Sopenharmony_ci}
986