xref: /third_party/backends/backend/tamarack.c (revision 141cc406)
1/* sane - Scanner Access Now Easy.
2   Copyright (C) 1996 David Mosberger-Tang
3   Copyright (C) 1997 R.E.Wolff@BitWizard.nl
4   This file is part of the SANE package.
5
6   This program is free software; you can redistribute it and/or
7   modify it under the terms of the GNU General Public License as
8   published by the Free Software Foundation; either version 2 of the
9   License, or (at your option) any later version.
10
11   This program is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program.  If not, see <https://www.gnu.org/licenses/>.
18
19   Note: The exception that is mentioned in the other source files is
20   not here. If a case arises where you need the rights that that
21   exception gives you, Please do contact me, and we'll work something
22   out.
23
24   R.E.Wolff@BitWizard.nl
25   tel: +31-152137555
26   fax: +31-152138217
27
28   This file implements a SANE backend for Tamarack flatbed scanners.  */
29
30/*
31   This driver was written initially by changing all occurrences of
32   "mustek" to "tamarack". This actually worked without modification
33   for the manufacturer detection code! :-)
34
35 */
36
37
38#include "../include/sane/config.h"
39
40#include <errno.h>
41#include <fcntl.h>
42#include <limits.h>
43#include <signal.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <sys/types.h>
48#include <unistd.h>
49#include <sys/time.h>
50
51#include "../include/_stdint.h"
52
53#include "../include/sane/sane.h"
54#include "../include/sane/sanei.h"
55#include "../include/sane/saneopts.h"
56#include "../include/sane/sanei_scsi.h"
57#include "../include/sane/sanei_thread.h"
58#include "../include/sane/sanei_config.h"
59
60/* For timeval... */
61#ifdef DEBUG
62#include <sys/time.h>
63#endif
64
65
66#define BACKEND_NAME	tamarack
67#include "../include/sane/sanei_backend.h"
68
69#include "tamarack.h"
70
71#ifndef PATH_MAX
72# define PATH_MAX	1024
73#endif
74
75#define TAMARACK_CONFIG_FILE "tamarack.conf"
76
77
78static const SANE_Device **devlist = NULL;
79static int num_devices;
80static Tamarack_Device *first_dev;
81static Tamarack_Scanner *first_handle;
82
83static const SANE_String_Const mode_list[] =
84  {
85    SANE_VALUE_SCAN_MODE_LINEART,
86    SANE_VALUE_SCAN_MODE_HALFTONE,
87    SANE_VALUE_SCAN_MODE_GRAY,
88    SANE_VALUE_SCAN_MODE_COLOR,
89    0
90  };
91
92
93#if 0
94static const SANE_Range u8_range =
95  {
96      0,				/* minimum */
97    255,				/* maximum */
98      0				/* quantization */
99  };
100#endif
101
102
103/* David used " 100 << SANE_FIXED_SCALE_SHIFT ". This assumes that
104 * it is implemented that way. I want to hide the datatype.
105 */
106static const SANE_Range percentage_range =
107  {
108    SANE_FIX(-100),	/* minimum */
109    SANE_FIX( 100),	/* maximum */
110    SANE_FIX( 1  )	/* quantization */
111  };
112
113/* David used " 100 << SANE_FIXED_SCALE_SHIFT ". This assumes that
114 * it is implemented that way. I want to hide the datatype.
115 */
116static const SANE_Range abs_percentage_range =
117  {
118    SANE_FIX( 0),	/* minimum */
119    SANE_FIX( 100),	/* maximum */
120    SANE_FIX( 1  )	/* quantization */
121  };
122
123
124#define INQ_LEN	0x60
125static const uint8_t inquiry[] =
126{
127  TAMARACK_SCSI_INQUIRY, 0x00, 0x00, 0x00, INQ_LEN, 0x00
128};
129
130static const uint8_t test_unit_ready[] =
131{
132  TAMARACK_SCSI_TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00
133};
134
135static const uint8_t stop[] =
136{
137  TAMARACK_SCSI_START_STOP, 0x00, 0x00, 0x00, 0x00, 0x00
138};
139
140static const uint8_t get_status[] =
141{
142  TAMARACK_SCSI_GET_DATA_STATUS, 0x00, 0x00, 0x00, 0x00, 0x00,
143                                       0x00, 0x00, 0x0c, 0x00
144};
145
146
147
148static SANE_Status
149wait_ready (int fd)
150{
151  SANE_Status status;
152  int i;
153
154  for (i = 0; i < 1000; ++i)
155    {
156      DBG(3, "wait_ready: sending TEST_UNIT_READY\n");
157      status = sanei_scsi_cmd (fd, test_unit_ready, sizeof (test_unit_ready),
158			       0, 0);
159      switch (status)
160	{
161	default:
162	  /* Ignore errors while waiting for scanner to become ready.
163	     Some SCSI drivers return EIO while the scanner is
164	     returning to the home position.  */
165	  DBG(1, "wait_ready: test unit ready failed (%s)\n",
166	      sane_strstatus (status));
167	  /* fall through */
168	case SANE_STATUS_DEVICE_BUSY:
169	  usleep (100000);	/* retry after 100ms */
170	  break;
171
172	case SANE_STATUS_GOOD:
173	  return status;
174	}
175    }
176  DBG(1, "wait_ready: timed out after %d attempts\n", i);
177  return SANE_STATUS_INVAL;
178}
179
180
181
182static SANE_Status
183sense_handler (int scsi_fd, u_char *result, void *arg)
184{
185  (void) scsi_fd;
186  (void) arg; /* silence compilation warnings */
187
188  switch (result[0])
189    {
190    case 0x00:
191      break;
192
193    default:
194      DBG(1, "sense_handler: got unknown sense code %02x\n", result[0]);
195      return SANE_STATUS_IO_ERROR;
196    }
197  return SANE_STATUS_GOOD;
198}
199
200
201/* XXX This might leak the memory to a TAMARACK string */
202
203static SANE_Status
204attach (const char *devname, Tamarack_Device **devp)
205{
206  char result[INQ_LEN];
207  int fd;
208  Tamarack_Device *dev;
209  SANE_Status status;
210  size_t size;
211  char *mfg, *model;
212  char *p;
213
214  for (dev = first_dev; dev; dev = dev->next)
215    if (strcmp (dev->sane.name, devname) == 0) {
216      if (devp)
217	*devp = dev;
218      return SANE_STATUS_GOOD;
219    }
220
221  DBG(3, "attach: opening %s\n", devname);
222  status = sanei_scsi_open (devname, &fd, sense_handler, 0);
223  if (status != SANE_STATUS_GOOD) {
224    DBG(1, "attach: open failed (%s)\n", sane_strstatus (status));
225    return SANE_STATUS_INVAL;
226  }
227
228  DBG(3, "attach: sending INQUIRY\n");
229  size = sizeof (result);
230  status = sanei_scsi_cmd (fd, inquiry, sizeof (inquiry), result, &size);
231  if (status != SANE_STATUS_GOOD || size != INQ_LEN) {
232    DBG(1, "attach: inquiry failed (%s)\n", sane_strstatus (status));
233    sanei_scsi_close (fd);
234    return status;
235  }
236
237  status = wait_ready (fd);
238  sanei_scsi_close (fd);
239  if (status != SANE_STATUS_GOOD)
240    return status;
241
242  result[33]= '\0';
243  p = strchr(result+16,' ');
244  if (p) *p = '\0';
245  model = strdup (result+16);
246
247  result[16]= '\0';
248  p = strchr(result+8,' ');
249  if (p) *p = '\0';
250  mfg = strdup (result+8);
251
252  DBG(1, "attach: Inquiry gives mfg=%s, model=%s.\n", mfg, model);
253
254  if (strcmp (mfg, "TAMARACK") != 0) {
255    DBG(1, "attach: device doesn't look like a Tamarack scanner "
256	   "(result[0]=%#02x)\n", result[0]);
257    return SANE_STATUS_INVAL;
258  }
259
260  dev = malloc (sizeof (*dev));
261  if (!dev)
262    return SANE_STATUS_NO_MEM;
263
264  memset (dev, 0, sizeof (*dev));
265
266  dev->sane.name   = strdup (devname);
267  dev->sane.vendor = "Tamarack";
268  dev->sane.model  = model;
269  dev->sane.type   = "flatbed scanner";
270
271  dev->x_range.min = 0;
272  dev->y_range.min = 0;
273  dev->x_range.quant = 0;
274  dev->y_range.quant = 0;
275  dev->dpi_range.min = SANE_FIX (1);
276  dev->dpi_range.quant = SANE_FIX (1);
277
278  dev->x_range.max = SANE_FIX (8.5 * MM_PER_INCH);
279  dev->y_range.max = SANE_FIX (11.0 * MM_PER_INCH);
280  dev->dpi_range.max = SANE_FIX (600);
281
282  DBG(3, "attach: found Tamarack scanner model %s (%s)\n",
283      dev->sane.model, dev->sane.type);
284
285  ++num_devices;
286  dev->next = first_dev;
287  first_dev = dev;
288
289  if (devp)
290    *devp = dev;
291  return SANE_STATUS_GOOD;
292}
293
294
295static size_t
296max_string_size (const SANE_String_Const strings[])
297{
298  size_t size, max_size = 0;
299  int i;
300
301  for (i = 0; strings[i]; ++i)
302    {
303      size = strlen (strings[i]) + 1;
304      if (size > max_size)
305	max_size = size;
306    }
307  return max_size;
308}
309
310
311static SANE_Status
312constrain_value (Tamarack_Scanner *s, SANE_Int option, void *value,
313		 SANE_Int *info)
314{
315  return sanei_constrain_value (s->opt + option, value, info);
316}
317
318
319static unsigned char sign_mag (double val)
320{
321  if (val >  100) val =  100;
322  if (val < -100) val = -100;
323  if (val >= 0) return ( val);
324  else          return ((unsigned char)(-val)) | 0x80;
325}
326
327
328
329static SANE_Status
330scan_area_and_windows (Tamarack_Scanner *s)
331{
332  struct def_win_par dwp;
333
334  memset (&dwp,'\0',sizeof (dwp));
335  dwp.dwph.opc = TAMARACK_SCSI_AREA_AND_WINDOWS;
336  set_triple (dwp.dwph.len,8 + sizeof (dwp.wdb));
337
338  set_double (dwp.wdh.wpll, sizeof (dwp.wdb));
339
340  dwp.wdb.winid = WINID;
341  set_double (dwp.wdb.xres, (int) SANE_UNFIX (s->val[OPT_RESOLUTION].w));
342  set_double (dwp.wdb.yres, (int) SANE_UNFIX (s->val[OPT_RESOLUTION].w));
343
344  set_quad (dwp.wdb.ulx, (int) (47.2 * SANE_UNFIX (s->val[OPT_TL_X].w)));
345  set_quad (dwp.wdb.uly, (int) (47.2 * SANE_UNFIX (s->val[OPT_TL_Y].w)));
346  set_quad (dwp.wdb.width,
347     (int) (47.2 * SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w)));
348  set_quad (dwp.wdb.length,
349     (int) (47.2 * SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w)));
350
351  dwp.wdb.brightness = sign_mag (SANE_UNFIX (s->val[OPT_BRIGHTNESS].w));
352  dwp.wdb.contrast   = sign_mag (SANE_UNFIX (s->val[OPT_CONTRAST].w));
353  dwp.wdb.thresh     = 0x80;
354
355
356  switch (s->mode) {
357  case THRESHOLDED:
358    dwp.wdb.bpp = 1;
359    dwp.wdb.image_comp = 0;
360    dwp.wdb.thresh     = 1 + 2.55 * (SANE_UNFIX (s->val[OPT_THRESHOLD].w));
361    break;
362  case DITHERED:
363    dwp.wdb.bpp = 1;
364    dwp.wdb.image_comp = 1;
365    break;
366  case GREYSCALE:
367    dwp.wdb.bpp = 8;
368    dwp.wdb.image_comp = 2;
369    break;
370  case TRUECOLOR:
371    dwp.wdb.bpp = 8;
372    dwp.wdb.image_comp = 2;
373    break;
374  default:
375    DBG(1, "Invalid mode. %d\n", s->mode);
376    return SANE_STATUS_INVAL;
377  }
378  DBG(1, "bright, thresh, contrast = %d(%5.1f), %d, %d(%5.1f)\n",
379      dwp.wdb.brightness, SANE_UNFIX (s->val[OPT_BRIGHTNESS].w),
380      dwp.wdb.thresh    ,
381      dwp.wdb.contrast  , SANE_UNFIX (s->val[OPT_CONTRAST].w));
382
383  set_double (dwp.wdb.halftone, 1); /* XXX What does this do again ? */
384  dwp.wdb.pad_type   = 3;           /* This is the only usable pad-type. */
385  dwp.wdb.exposure   = 0x6f;        /* XXX Option? */
386  dwp.wdb.compr_type = 0;
387
388  /* XXX Shouldn't this be sizeof (dwp) */
389  return sanei_scsi_cmd (s->fd, &dwp, (10+8+38), 0, 0);
390}
391
392
393static SANE_Status
394mode_select (Tamarack_Scanner *s)
395{
396  struct  {
397    struct command_header cmd;
398    struct page_header hdr;
399    struct tamarack_page page;
400  } c;
401
402  memset (&c, '\0', sizeof (c));
403  c.cmd.opc = TAMARACK_SCSI_MODE_SELECT;
404  c.cmd.pad0[0] = 0x10;    /* Suddenly the pad bytes are no long pad... */
405  c.cmd.pad0[1] = 0;
406  c.cmd.len = sizeof (struct page_header) + sizeof (struct tamarack_page);
407  c.hdr.code = 0;
408  c.hdr.length = 6;
409  c.page.gamma = 2;
410  c.page.thresh = 0x80;    /* XXX Option? */
411  switch (s->mode) {
412  case THRESHOLDED:
413  case DITHERED:
414  case GREYSCALE:
415    c.page.masks = 0x80;
416    break;
417  case TRUECOLOR:
418    c.page.masks = 0x40 >> s->pass;
419    break;
420  }
421  c.page.delay = 0x10;      /* XXX Option? */
422  c.page.features = (s->val[OPT_TRANS].w ? TAM_TRANS_ON:0) | 1;
423  return sanei_scsi_cmd (s->fd, &c, sizeof (c), 0, 0);
424}
425
426
427static SANE_Status
428start_scan (Tamarack_Scanner *s)
429{
430  struct  {
431    struct command_header cmd;
432    unsigned char winid[1];
433  } c;
434
435  memset (&c,'\0',sizeof (c));
436  c.cmd.opc = TAMARACK_SCSI_START_STOP;
437  c.cmd.len = sizeof (c.winid);
438  c.winid[0] = WINID;
439  return sanei_scsi_cmd (s->fd, &c, sizeof (c), 0, 0);
440}
441
442
443static SANE_Status
444stop_scan (Tamarack_Scanner *s)
445{
446  /* XXX I don't think a TAMARACK can stop in mid-scan. Just stop
447     sending it requests for data....
448   */
449  return sanei_scsi_cmd (s->fd, stop, sizeof (stop), 0, 0);
450}
451
452
453static SANE_Status
454do_eof (Tamarack_Scanner *s)
455{
456  if (s->pipe >= 0)
457    {
458      close (s->pipe);
459      s->pipe = -1;
460    }
461  return SANE_STATUS_EOF;
462}
463
464
465static SANE_Status
466do_cancel (Tamarack_Scanner *s)
467{
468  s->scanning = SANE_FALSE;
469  s->pass = 0;
470
471  do_eof (s);
472
473  if (sanei_thread_is_valid (s->reader_pid))
474    {
475      int exit_status;
476
477      /* ensure child knows it's time to stop: */
478      sanei_thread_kill (s->reader_pid);
479      sanei_thread_waitpid (s->reader_pid, &exit_status);
480      sanei_thread_invalidate (s->reader_pid);
481    }
482
483  if (s->fd >= 0)
484    {
485      stop_scan (s);
486      sanei_scsi_close (s->fd);
487      s->fd = -1;
488    }
489
490  return SANE_STATUS_CANCELLED;
491}
492
493
494static SANE_Status
495get_image_status (Tamarack_Scanner *s)
496{
497  uint8_t result[12];
498  SANE_Status status;
499  size_t len;
500  int busy;
501
502#if 1
503  do
504    {
505      len = sizeof (result);
506      status = sanei_scsi_cmd (s->fd, get_status, sizeof (get_status),
507			    result, &len);
508      if ((status != SANE_STATUS_GOOD) && (status != SANE_STATUS_DEVICE_BUSY))
509	return status;
510
511      busy = (result[2] != 8) || (status == SANE_STATUS_DEVICE_BUSY);
512      if (busy)
513	usleep (100000);
514
515      if (!s->scanning)
516	return do_cancel (s);
517    }
518  while (busy);
519#else
520  /* XXX Test if this works one day... */
521  wait_ready (s);
522#endif
523
524  len = sizeof (result);
525  status = sanei_scsi_cmd (s->fd, get_status, sizeof (get_status),
526			   result, &len);
527  if ((status != SANE_STATUS_GOOD) && (status != SANE_STATUS_DEVICE_BUSY))
528    return status;
529
530  s->params.bytes_per_line =
531    result[ 8] | (result[ 7] << 8) | (result[6] << 16);
532  s->params.lines =
533    result[11] | (result[10] << 8) | (result[9] << 16);
534
535  switch (s->mode) {
536  case DITHERED:
537  case THRESHOLDED:
538    s->params.pixels_per_line = 8 * s->params.bytes_per_line;
539    break;
540  case GREYSCALE:
541  case TRUECOLOR:
542    s->params.pixels_per_line =     s->params.bytes_per_line;
543    break;
544  }
545
546
547  DBG(1, "get_image_status: bytes_per_line=%d, lines=%d\n",
548      s->params.bytes_per_line, s->params.lines);
549  return SANE_STATUS_GOOD;
550}
551
552
553static SANE_Status
554read_data (Tamarack_Scanner *s, SANE_Byte *buf, int lines, int bpl)
555{
556  struct command_header_10 cmd;
557  size_t nbytes;
558  SANE_Status status;
559#ifdef DEBUG
560  int dt;
561  struct timeval tv_start,tv_end;
562#endif
563
564  nbytes = bpl * lines;
565  memset (&cmd,'\0',sizeof (cmd));
566  cmd.opc = 0x28;
567  set_triple (cmd.len,nbytes);
568
569#ifdef DEBUG
570  if (verbose) DBG (1, "Doing read_data... \n");
571  gettimeofday (&tv_start,NULL);
572#endif
573
574  status = sanei_scsi_cmd (s->fd, &cmd, sizeof (cmd), buf, &nbytes);
575
576#ifdef DEBUG
577  gettimeofday (&tv_end,NULL);
578  dt =  tv_end.tv_usec - tv_start.tv_usec +
579       (tv_end.tv_sec  - tv_start.tv_sec) * 1000000;
580  if (verbose) DBG(1, "Read took %d.%06d seconds.",
581		   dt/1000000,dt%1000000);
582  dt = 1000000 * nbytes / dt;
583  if (verbose) DBG(1, "which is %d.%03d bytes per second.\n",dt,0);
584#endif
585  return status;
586}
587
588
589
590static SANE_Status
591init_options (Tamarack_Scanner *s)
592{
593  int i;
594
595  memset (s->opt, 0, sizeof (s->opt));
596  memset (s->val, 0, sizeof (s->val));
597
598  for (i = 0; i < NUM_OPTIONS; ++i) {
599    s->opt[i].size = sizeof (SANE_Word);
600    s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
601  }
602
603  s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
604  s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
605  s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
606  s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
607  s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
608
609  /* "Mode" group: */
610  s->opt[OPT_MODE_GROUP].title = "Scan Mode";
611  s->opt[OPT_MODE_GROUP].desc = "";
612  s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
613  s->opt[OPT_MODE_GROUP].cap = 0;
614  s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
615
616  /* scan mode */
617  s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
618  s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
619  s->opt[OPT_MODE].desc = "Select the scan mode";
620  s->opt[OPT_MODE].type = SANE_TYPE_STRING;
621  s->opt[OPT_MODE].size = max_string_size (mode_list);
622  s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
623  s->opt[OPT_MODE].constraint.string_list = mode_list;
624  s->val[OPT_MODE].s = strdup (mode_list[OPT_MODE_DEFAULT]);
625
626  /* resolution */
627  s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
628  s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
629  s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
630  s->opt[OPT_RESOLUTION].type = SANE_TYPE_FIXED;
631  s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
632  s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
633  s->opt[OPT_RESOLUTION].constraint.range = &s->hw->dpi_range;
634  s->val[OPT_RESOLUTION].w = SANE_FIX (OPT_RESOLUTION_DEFAULT);
635
636  /* preview */
637  s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
638  s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
639  s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
640  s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
641  s->val[OPT_PREVIEW].w = 0;
642
643  /* gray preview */
644  s->opt[OPT_GRAY_PREVIEW].name = SANE_NAME_GRAY_PREVIEW;
645  s->opt[OPT_GRAY_PREVIEW].title = SANE_TITLE_GRAY_PREVIEW;
646  s->opt[OPT_GRAY_PREVIEW].desc = SANE_DESC_GRAY_PREVIEW;
647  s->opt[OPT_GRAY_PREVIEW].type = SANE_TYPE_BOOL;
648  s->val[OPT_GRAY_PREVIEW].w = SANE_FALSE;
649
650  /* "Geometry" group: */
651  s->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
652  s->opt[OPT_GEOMETRY_GROUP].desc = "";
653  s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
654  s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
655  s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
656
657  /* top-left x */
658  s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
659  s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
660  s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
661  s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
662  s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
663  s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
664  s->opt[OPT_TL_X].constraint.range = &s->hw->x_range;
665  s->val[OPT_TL_X].w = 0;
666
667  /* top-left y */
668  s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
669  s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
670  s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
671  s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
672  s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
673  s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
674  s->opt[OPT_TL_Y].constraint.range = &s->hw->y_range;
675  s->val[OPT_TL_Y].w = 0;
676
677  /* bottom-right x */
678  s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
679  s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
680  s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
681  s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
682  s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
683  s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
684  s->opt[OPT_BR_X].constraint.range = &s->hw->x_range;
685  s->val[OPT_BR_X].w = s->hw->x_range.max;
686
687  /* bottom-right y */
688  s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
689  s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
690  s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
691  s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
692  s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
693  s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
694  s->opt[OPT_BR_Y].constraint.range = &s->hw->y_range;
695  s->val[OPT_BR_Y].w = s->hw->y_range.max;
696
697  /* "Enhancement" group: */
698  s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
699  s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
700  s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
701  s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
702  s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
703
704  /* transparency adapter. */
705  s->opt[OPT_TRANS].name = "transparency";
706  s->opt[OPT_TRANS].title = "transparency";
707  s->opt[OPT_TRANS].desc = "Turn on the transparency adapter.";
708  s->opt[OPT_TRANS].type = SANE_TYPE_BOOL;
709  s->opt[OPT_TRANS].unit = SANE_UNIT_NONE;
710  s->val[OPT_TRANS].w = SANE_FALSE;
711
712  /* brightness */
713  s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
714  s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
715  s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS
716    "  This option is active for lineart/halftone modes only.  "
717    "For multibit modes (grey/color) use the gamma-table(s).";
718  s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_FIXED;
719  s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_PERCENT;
720  s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
721  s->opt[OPT_BRIGHTNESS].constraint.range = &percentage_range;
722  s->val[OPT_BRIGHTNESS].w = SANE_FIX(0);
723
724  /* contrast */
725  s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
726  s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
727  s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST
728    "  This option is active for lineart/halftone modes only.  "
729    "For multibit modes (grey/color) use the gamma-table(s).";
730  s->opt[OPT_CONTRAST].type = SANE_TYPE_FIXED;
731  s->opt[OPT_CONTRAST].unit = SANE_UNIT_PERCENT;
732  s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
733  s->opt[OPT_CONTRAST].constraint.range = &percentage_range;
734  s->val[OPT_CONTRAST].w = SANE_FIX(0);
735
736  /* Threshold */
737  s->opt[OPT_THRESHOLD].name = "Threshold";
738  s->opt[OPT_THRESHOLD].title = "Threshold";
739  s->opt[OPT_THRESHOLD].desc = "Threshold: below this level is black, above is white"
740    "  This option is active for bitmap modes only.  ";
741  s->opt[OPT_THRESHOLD].type = SANE_TYPE_FIXED;
742  s->opt[OPT_THRESHOLD].unit = SANE_UNIT_PERCENT;
743  s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
744  s->opt[OPT_THRESHOLD].constraint.range = &abs_percentage_range;
745  s->val[OPT_THRESHOLD].w = SANE_FIX(50);
746  s->opt[OPT_THRESHOLD].cap  |= SANE_CAP_INACTIVE;
747
748#if 0
749  /* custom-gamma table */
750  s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
751  s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
752  s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
753  s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
754  s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
755  s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
756
757  /* grayscale gamma vector */
758  s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
759  s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
760  s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
761  s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
762  s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
763  s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
764  s->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word);
765  s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
766  s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range;
767  s->val[OPT_GAMMA_VECTOR].wa = &s->gamma_table[0][0];
768
769  /* red gamma vector */
770  s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
771  s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
772  s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
773  s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
774  s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
775  s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
776  s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word);
777  s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
778  s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
779  s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[1][0];
780
781  /* green gamma vector */
782  s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
783  s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
784  s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
785  s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
786  s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
787  s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
788  s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word);
789  s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
790  s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
791  s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[2][0];
792
793  /* blue gamma vector */
794  s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
795  s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
796  s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
797  s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
798  s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
799  s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
800  s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word);
801  s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
802  s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
803  s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[3][0];
804#endif
805  return SANE_STATUS_GOOD;
806}
807
808
809/* This function is executed as a child process.  The reason this is
810   executed as a subprocess is because some (most?) generic SCSI
811   interfaces block a SCSI request until it has completed.  With a
812   subprocess, we can let it block waiting for the request to finish
813   while the main process can go about to do more important things
814   (such as recognizing when the user presses a cancel button).
815
816   WARNING: Since this is executed as a subprocess, it's NOT possible
817   to update any of the variables in the main process (in particular
818   the scanner state cannot be updated).  */
819static int
820reader_process (void *scanner)
821{
822  Tamarack_Scanner *s = (Tamarack_Scanner *) scanner;
823  int fd = s->reader_pipe;
824
825  SANE_Byte *data;
826  int lines_per_buffer, bpl;
827  SANE_Status status;
828  sigset_t sigterm_set;
829  sigset_t ignore_set;
830  struct SIGACTION act;
831  FILE *fp;
832
833  if (sanei_thread_is_forked()) close (s->pipe);
834
835  sigfillset (&ignore_set);
836  sigdelset (&ignore_set, SIGTERM);
837#if defined (__APPLE__) && defined (__MACH__)
838  sigdelset (&ignore_set, SIGUSR2);
839#endif
840  sigprocmask (SIG_SETMASK, &ignore_set, 0);
841
842  memset (&act, 0, sizeof (act));
843  sigaction (SIGTERM, &act, 0);
844
845  sigemptyset (&sigterm_set);
846  sigaddset (&sigterm_set, SIGTERM);
847
848  fp = fdopen (fd, "w");
849  if (!fp)
850    return 1;
851
852  bpl = s->params.bytes_per_line;
853
854  lines_per_buffer = sanei_scsi_max_request_size / bpl;
855  if (!lines_per_buffer)
856    return 2;			/* resolution is too high */
857
858  /* Limit the size of a single transfer to one inch.
859     XXX Add a stripsize option. */
860  if (lines_per_buffer > SANE_UNFIX (s->val[OPT_RESOLUTION].w))
861      lines_per_buffer = SANE_UNFIX (s->val[OPT_RESOLUTION].w);
862
863  DBG(3, "lines_per_buffer=%d, bytes_per_line=%d\n", lines_per_buffer, bpl);
864
865  data = malloc (lines_per_buffer * bpl);
866
867  for (s->line = 0; s->line < s->params.lines; s->line += lines_per_buffer) {
868    if (s->line + lines_per_buffer > s->params.lines)
869      /* do the last few lines: */
870      lines_per_buffer = s->params.lines - s->line;
871
872    sigprocmask (SIG_BLOCK, &sigterm_set, 0);
873    status = read_data (s, data, lines_per_buffer, bpl);
874    sigprocmask (SIG_UNBLOCK, &sigterm_set, 0);
875    if (status != SANE_STATUS_GOOD) {
876      DBG(1, "reader_process: read_data failed with status=%d\n", status);
877      return 3;
878    }
879    DBG(3, "reader_process: read %d lines\n", lines_per_buffer);
880
881    if ((s->mode == TRUECOLOR) || (s->mode == GREYSCALE)) {
882      fwrite (data, lines_per_buffer, bpl, fp);
883    } else {
884      /* in singlebit mode, the scanner returns 1 for black. ;-( --DM */
885      /* Hah! Same for Tamarack... -- REW */
886      int i;
887
888      for (i = 0; i < lines_per_buffer * bpl; ++i)
889	fputc (~data[i], fp);
890    }
891  }
892  fclose (fp);
893  return 0;
894}
895
896
897static SANE_Status
898attach_one (const char *dev)
899{
900  attach (dev, 0);
901  return SANE_STATUS_GOOD;
902}
903
904
905SANE_Status
906sane_init (SANE_Int *version_code, SANE_Auth_Callback authorize)
907{
908  char dev_name[PATH_MAX];
909  size_t len;
910  FILE *fp;
911
912  (void) authorize; /* silence compilation warnings */
913
914  DBG_INIT();
915
916  sanei_thread_init();
917
918  if (version_code)
919    *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0);
920
921  fp = sanei_config_open (TAMARACK_CONFIG_FILE);
922  if (!fp) {
923    /* default to /dev/scanner instead of insisting on config file */
924    attach ("/dev/scanner", 0);
925    return SANE_STATUS_GOOD;
926  }
927
928  while (sanei_config_read (dev_name, sizeof (dev_name), fp)) {
929    if (dev_name[0] == '#')		/* ignore line comments */
930      continue;
931    len = strlen (dev_name);
932
933    if (!len)
934      continue;			/* ignore empty lines */
935
936    sanei_config_attach_matching_devices (dev_name, attach_one);
937  }
938  fclose (fp);
939  return SANE_STATUS_GOOD;
940}
941
942
943void
944sane_exit (void)
945{
946  Tamarack_Device *dev, *next;
947
948  for (dev = first_dev; dev; dev = next) {
949    next = dev->next;
950    free ((void *) dev->sane.name);
951    free ((void *) dev->sane.model);
952    free (dev);
953  }
954
955  if (devlist)
956    free (devlist);
957}
958
959SANE_Status
960sane_get_devices (const SANE_Device ***device_list, SANE_Bool local_only)
961{
962  Tamarack_Device *dev;
963  int i;
964
965  (void) local_only; /* silence compilation warnings */
966
967  if (devlist)
968    free (devlist);
969
970  devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
971  if (!devlist)
972    return SANE_STATUS_NO_MEM;
973
974  i = 0;
975  for (dev = first_dev; i < num_devices; dev = dev->next)
976    devlist[i++] = &dev->sane;
977  devlist[i++] = 0;
978
979  *device_list = devlist;
980  return SANE_STATUS_GOOD;
981}
982
983
984SANE_Status
985sane_open (SANE_String_Const devicename, SANE_Handle *handle)
986{
987  Tamarack_Device *dev;
988  SANE_Status status;
989  Tamarack_Scanner *s;
990  int i, j;
991
992  if (devicename[0]) {
993    for (dev = first_dev; dev; dev = dev->next)
994      if (strcmp (dev->sane.name, devicename) == 0)
995	break;
996
997    if (!dev) {
998      status = attach (devicename, &dev);
999      if (status != SANE_STATUS_GOOD)
1000	return status;
1001    }
1002  } else {
1003    /* empty devicname -> use first device */
1004    dev = first_dev;
1005  }
1006
1007  if (!dev)
1008    return SANE_STATUS_INVAL;
1009
1010  s = malloc (sizeof (*s));
1011  if (!s)
1012    return SANE_STATUS_NO_MEM;
1013  memset (s, 0, sizeof (*s));
1014  s->fd = -1;
1015  s->pipe = -1;
1016  s->hw = dev;
1017  for (i = 0; i < 4; ++i)
1018    for (j = 0; j < 256; ++j)
1019      s->gamma_table[i][j] = j;
1020
1021  init_options (s);
1022
1023  /* insert newly opened handle into list of open handles: */
1024  s->next = first_handle;
1025  first_handle = s;
1026
1027  *handle = s;
1028  return SANE_STATUS_GOOD;
1029}
1030
1031
1032void
1033sane_close (SANE_Handle handle)
1034{
1035  Tamarack_Scanner *prev, *s;
1036
1037  /* remove handle from list of open handles: */
1038  prev = 0;
1039  for (s = first_handle; s; s = s->next) {
1040    if (s == handle)
1041      break;
1042    prev = s;
1043  }
1044
1045  if (!s) {
1046    DBG(1, "close: invalid handle %p\n", handle);
1047    return;		/* oops, not a handle we know about */
1048  }
1049
1050  if (s->scanning)
1051    do_cancel (handle);
1052
1053  if (prev)
1054    prev->next = s->next;
1055  else
1056    first_handle = s->next;
1057
1058  free (handle);
1059}
1060
1061
1062const SANE_Option_Descriptor *
1063sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
1064{
1065  Tamarack_Scanner *s = handle;
1066
1067  if ((unsigned) option >= NUM_OPTIONS)
1068    return 0;
1069  return s->opt + option;
1070}
1071
1072
1073
1074static int make_mode (char *mode)
1075{
1076    if (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0)
1077      return THRESHOLDED;
1078    if (strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0)
1079      return DITHERED;
1080    else if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
1081      return GREYSCALE;
1082    else if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0)
1083      return TRUECOLOR;
1084
1085    return -1;
1086}
1087
1088
1089SANE_Status
1090sane_control_option (SANE_Handle handle, SANE_Int option,
1091		     SANE_Action action, void *val, SANE_Int *info)
1092{
1093  Tamarack_Scanner *s = handle;
1094  SANE_Status status;
1095  SANE_Word cap;
1096
1097  if (info)
1098    *info = 0;
1099
1100  if (s->scanning)
1101    return SANE_STATUS_DEVICE_BUSY;
1102
1103  if (option >= NUM_OPTIONS)
1104    return SANE_STATUS_INVAL;
1105
1106  cap = s->opt[option].cap;
1107
1108  if (!SANE_OPTION_IS_ACTIVE (cap))
1109    return SANE_STATUS_INVAL;
1110
1111  if (action == SANE_ACTION_GET_VALUE) {
1112    switch (option) {
1113      /* word options: */
1114    case OPT_PREVIEW:
1115    case OPT_GRAY_PREVIEW:
1116    case OPT_RESOLUTION:
1117    case OPT_TL_X:
1118    case OPT_TL_Y:
1119    case OPT_BR_X:
1120    case OPT_BR_Y:
1121    case OPT_NUM_OPTS:
1122    case OPT_TRANS:
1123    case OPT_BRIGHTNESS:
1124    case OPT_CONTRAST:
1125    case OPT_THRESHOLD:
1126#if 0
1127    case OPT_CUSTOM_GAMMA:
1128#endif
1129      *(SANE_Word *) val = s->val[option].w;
1130      return SANE_STATUS_GOOD;
1131
1132#if 0
1133      /* word-array options: */
1134    case OPT_GAMMA_VECTOR:
1135    case OPT_GAMMA_VECTOR_R:
1136    case OPT_GAMMA_VECTOR_G:
1137    case OPT_GAMMA_VECTOR_B:
1138      memcpy (val, s->val[option].wa, s->opt[option].size);
1139      return SANE_STATUS_GOOD;
1140#endif
1141
1142      /* string options: */
1143    case OPT_MODE:
1144      strcpy (val, s->val[option].s);
1145      return SANE_STATUS_GOOD;
1146    }
1147  } else if (action == SANE_ACTION_SET_VALUE) {
1148    if (!SANE_OPTION_IS_SETTABLE (cap))
1149      return SANE_STATUS_INVAL;
1150
1151    status = constrain_value (s, option, val, info);
1152    if (status != SANE_STATUS_GOOD)
1153      return status;
1154
1155    switch (option)
1156      {
1157	/* (mostly) side-effect-free word options: */
1158      case OPT_RESOLUTION:
1159      case OPT_TL_X:
1160      case OPT_TL_Y:
1161      case OPT_BR_X:
1162      case OPT_BR_Y:
1163	if (info)
1164	  *info |= SANE_INFO_RELOAD_PARAMS;
1165	/* fall through */
1166      case OPT_PREVIEW:
1167      case OPT_GRAY_PREVIEW:
1168      case OPT_BRIGHTNESS:
1169      case OPT_CONTRAST:
1170      case OPT_THRESHOLD:
1171      case OPT_TRANS:
1172	s->val[option].w = *(SANE_Word *) val;
1173	return SANE_STATUS_GOOD;
1174
1175#if 0
1176	/* side-effect-free word-array options: */
1177      case OPT_GAMMA_VECTOR:
1178      case OPT_GAMMA_VECTOR_R:
1179      case OPT_GAMMA_VECTOR_G:
1180      case OPT_GAMMA_VECTOR_B:
1181	memcpy (s->val[option].wa, val, s->opt[option].size);
1182	return SANE_STATUS_GOOD;
1183
1184	/* options with side-effects: */
1185
1186      case OPT_CUSTOM_GAMMA:
1187	w = *(SANE_Word *) val;
1188	if (w == s->val[OPT_CUSTOM_GAMMA].w)
1189	  return SANE_STATUS_GOOD;		/* no change */
1190
1191	s->val[OPT_CUSTOM_GAMMA].w = w;
1192	if (w) {
1193	  s->mode = make_mode (s->val[OPT_MODE].s);
1194
1195	  if (s->mode == GREYSCALE) {
1196	    s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
1197	  } else if (s->mode == TRUECOLOR) {
1198	    s->opt[OPT_GAMMA_VECTOR].cap   &= ~SANE_CAP_INACTIVE;
1199	    s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
1200	    s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
1201	    s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
1202	  }
1203	} else {
1204	  s->opt[OPT_GAMMA_VECTOR].cap   |= SANE_CAP_INACTIVE;
1205	  s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
1206	  s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
1207	  s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
1208	}
1209	if (info)
1210	  *info |= SANE_INFO_RELOAD_OPTIONS;
1211	return SANE_STATUS_GOOD;
1212#endif
1213
1214      case OPT_MODE:
1215	{
1216
1217	  if (s->val[option].s)
1218	    free (s->val[option].s);
1219	  s->val[option].s = strdup (val);
1220
1221	  s->mode = make_mode (s->val[OPT_MODE].s);
1222
1223	  if (info)
1224	    *info |= SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
1225
1226	  s->opt[OPT_BRIGHTNESS].cap |= SANE_CAP_INACTIVE;
1227	  s->opt[OPT_CONTRAST].cap   |= SANE_CAP_INACTIVE;
1228	  s->opt[OPT_THRESHOLD].cap  |= SANE_CAP_INACTIVE;
1229#if 0
1230	  s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
1231	  s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
1232	  s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
1233	  s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
1234	  s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
1235#endif
1236
1237
1238	  if (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0)
1239	    s->opt[OPT_THRESHOLD].cap  &= ~SANE_CAP_INACTIVE;
1240	  else {
1241	    s->opt[OPT_BRIGHTNESS].cap &= ~SANE_CAP_INACTIVE;
1242	    s->opt[OPT_CONTRAST].cap   &= ~SANE_CAP_INACTIVE;
1243	  }
1244#if 0
1245	  if (!binary)
1246	    s->opt[OPT_CUSTOM_GAMMA].cap &= ~SANE_CAP_INACTIVE;
1247
1248	  if (s->val[OPT_CUSTOM_GAMMA].w) {
1249	    if (strcmp (val, SANE_VALUE_SCAN_MODE_GRAY) == 0)
1250	      s->opt[OPT_GAMMA_VECTOR].cap &= ~SANE_CAP_INACTIVE;
1251	    else if (strcmp (val, SANE_VALUE_SCAN_MODE_COLOR) == 0) {
1252	      s->opt[OPT_GAMMA_VECTOR].cap   &= ~SANE_CAP_INACTIVE;
1253	      s->opt[OPT_GAMMA_VECTOR_R].cap &= ~SANE_CAP_INACTIVE;
1254	      s->opt[OPT_GAMMA_VECTOR_G].cap &= ~SANE_CAP_INACTIVE;
1255	      s->opt[OPT_GAMMA_VECTOR_B].cap &= ~SANE_CAP_INACTIVE;
1256	    }
1257	  }
1258#endif
1259	  return SANE_STATUS_GOOD;
1260	}
1261      }
1262    }
1263  return SANE_STATUS_INVAL;
1264}
1265
1266
1267SANE_Status
1268sane_get_parameters (SANE_Handle handle, SANE_Parameters *params)
1269{
1270  Tamarack_Scanner *s = handle;
1271
1272  if (!s->scanning) {
1273    double width, height, dpi;
1274
1275
1276    memset (&s->params, 0, sizeof (s->params));
1277
1278    width  = SANE_UNFIX (s->val[OPT_BR_X].w - s->val[OPT_TL_X].w);
1279    height = SANE_UNFIX (s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w);
1280    dpi    = SANE_UNFIX (s->val[OPT_RESOLUTION].w);
1281    s->mode = make_mode (s->val[OPT_MODE].s);
1282    DBG(1, "got mode '%s' -> %d.\n", s->val[OPT_MODE].s, s->mode);
1283    /* make best-effort guess at what parameters will look like once
1284       scanning starts.  */
1285    if (dpi > 0.0 && width > 0.0 && height > 0.0) {
1286      double dots_per_mm = dpi / MM_PER_INCH;
1287
1288      s->params.pixels_per_line = width * dots_per_mm;
1289      s->params.lines = height * dots_per_mm;
1290    }
1291    if ((s->mode == THRESHOLDED) || (s->mode == DITHERED)) {
1292      s->params.format = SANE_FRAME_GRAY;
1293      s->params.bytes_per_line = (s->params.pixels_per_line + 7) / 8;
1294      s->params.depth = 1;
1295    } else if (s->mode == GREYSCALE) {
1296      s->params.format = SANE_FRAME_GRAY;
1297      s->params.bytes_per_line = s->params.pixels_per_line;
1298      s->params.depth = 8;
1299    } else {
1300      s->params.format = SANE_FRAME_RED + s->pass;
1301      s->params.bytes_per_line = s->params.pixels_per_line;
1302      s->params.depth = 8;
1303    }
1304    s->pass = 0;
1305  } else {
1306    if (s->mode == TRUECOLOR)
1307      s->params.format = SANE_FRAME_RED + s->pass;
1308  }
1309
1310  s->params.last_frame =  (s->mode != TRUECOLOR) || (s->pass == 2);
1311
1312  if (params)
1313    *params = s->params;
1314
1315  DBG(1, "Got parameters: format:%d, ppl: %d, bpl:%d, depth:%d, "
1316	   "last %d pass %d\n",
1317	   s->params.format, s->params.pixels_per_line,
1318	   s->params.bytes_per_line, s->params.depth,
1319	   s->params.last_frame, s->pass);
1320  return SANE_STATUS_GOOD;
1321}
1322
1323
1324SANE_Status
1325sane_start (SANE_Handle handle)
1326{
1327  Tamarack_Scanner *s = handle;
1328  SANE_Status status;
1329  int fds[2];
1330
1331  /* First make sure we have a current parameter set.  Some of the
1332     parameters will be overwritten below, but that's OK.  */
1333  status = sane_get_parameters (s, 0);
1334
1335  if (status != SANE_STATUS_GOOD)
1336      return status;
1337
1338  if (s->fd < 0) {
1339    /* translate options into s->mode for convenient access: */
1340    s->mode = make_mode (s->val[OPT_MODE].s);
1341
1342    if (s->mode == TRUECOLOR)
1343      {
1344	if (s->val[OPT_PREVIEW].w && s->val[OPT_GRAY_PREVIEW].w) {
1345	  /* Force gray-scale mode when previewing.  */
1346	  s->mode = GREYSCALE;
1347	  s->params.format = SANE_FRAME_GRAY;
1348	  s->params.bytes_per_line = s->params.pixels_per_line;
1349	  s->params.last_frame = SANE_TRUE;
1350	}
1351      }
1352
1353    status = sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, 0);
1354    if (status != SANE_STATUS_GOOD) {
1355      DBG(1, "open: open of %s failed: %s\n",
1356	  s->hw->sane.name, sane_strstatus (status));
1357      return status;
1358    }
1359  }
1360
1361  status = wait_ready (s->fd);
1362  if (status != SANE_STATUS_GOOD) {
1363    DBG(1, "open: wait_ready() failed: %s\n", sane_strstatus (status));
1364    goto stop_scanner_and_return;
1365  }
1366
1367  status = scan_area_and_windows (s);
1368  if (status != SANE_STATUS_GOOD) {
1369    DBG(1, "open: set scan area command failed: %s\n",
1370	sane_strstatus (status));
1371    goto stop_scanner_and_return;
1372  }
1373
1374  status = mode_select (s);
1375  if (status != SANE_STATUS_GOOD)
1376    goto stop_scanner_and_return;
1377
1378  s->scanning = SANE_TRUE;
1379
1380  status = start_scan (s);
1381  if (status != SANE_STATUS_GOOD)
1382    goto stop_scanner_and_return;
1383
1384  status = get_image_status (s);
1385  if (status != SANE_STATUS_GOOD)
1386    goto stop_scanner_and_return;
1387
1388  s->line = 0;
1389
1390  if (pipe (fds) < 0)
1391    return SANE_STATUS_IO_ERROR;
1392
1393  s->pipe = fds[0];
1394  s->reader_pipe = fds[1];
1395  s->reader_pid = sanei_thread_begin (reader_process, (void *) s);
1396
1397  if (sanei_thread_is_forked()) close (s->reader_pipe);
1398
1399  return SANE_STATUS_GOOD;
1400
1401stop_scanner_and_return:
1402  do_cancel (s);
1403  return status;
1404}
1405
1406
1407SANE_Status
1408sane_read (SANE_Handle handle, SANE_Byte *buf, SANE_Int max_len, SANE_Int *len)
1409{
1410  Tamarack_Scanner *s = handle;
1411  ssize_t nread;
1412
1413  *len = 0;
1414
1415  nread = read (s->pipe, buf, max_len);
1416  DBG(3, "read %ld bytes\n", (long) nread);
1417
1418  if (!s->scanning)
1419    return do_cancel (s);
1420
1421  if (nread < 0) {
1422    if (errno == EAGAIN) {
1423      return SANE_STATUS_GOOD;
1424    } else {
1425      do_cancel (s);
1426      return SANE_STATUS_IO_ERROR;
1427    }
1428  }
1429
1430  *len = nread;
1431
1432  if (nread == 0) {
1433    s->pass++;
1434    return do_eof (s);
1435  }
1436  return SANE_STATUS_GOOD;
1437}
1438
1439
1440void
1441sane_cancel (SANE_Handle handle)
1442{
1443  Tamarack_Scanner *s = handle;
1444
1445  if (sanei_thread_is_valid (s->reader_pid))
1446    sanei_thread_kill (s->reader_pid);
1447  s->scanning = SANE_FALSE;
1448}
1449
1450
1451SANE_Status
1452sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
1453{
1454  Tamarack_Scanner *s = handle;
1455
1456  if (!s->scanning)
1457    return SANE_STATUS_INVAL;
1458
1459  if (fcntl (s->pipe, F_SETFL, non_blocking ? O_NONBLOCK : 0) < 0)
1460    return SANE_STATUS_IO_ERROR;
1461
1462  return SANE_STATUS_GOOD;
1463}
1464
1465
1466SANE_Status
1467sane_get_select_fd (SANE_Handle handle, SANE_Int *fd)
1468{
1469  Tamarack_Scanner *s = handle;
1470
1471  if (!s->scanning)
1472    return SANE_STATUS_INVAL;
1473
1474  *fd = s->pipe;
1475  return SANE_STATUS_GOOD;
1476}
1477