1141cc406Sopenharmony_ci/*
2141cc406Sopenharmony_ci  Copyright (C) 2001 Bertrik Sikken (bertrik@zonnet.nl)
3141cc406Sopenharmony_ci
4141cc406Sopenharmony_ci  This program is free software; you can redistribute it and/or
5141cc406Sopenharmony_ci  modify it under the terms of the GNU General Public License
6141cc406Sopenharmony_ci  as published by the Free Software Foundation; either version 2
7141cc406Sopenharmony_ci  of the License, or (at your option) any later version.
8141cc406Sopenharmony_ci
9141cc406Sopenharmony_ci  This program is distributed in the hope that it will be useful,
10141cc406Sopenharmony_ci  but WITHOUT ANY WARRANTY; without even the implied warranty of
11141cc406Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12141cc406Sopenharmony_ci  GNU General Public License for more details.
13141cc406Sopenharmony_ci
14141cc406Sopenharmony_ci  You should have received a copy of the GNU General Public License
15141cc406Sopenharmony_ci  along with this program.  If not, see <https://www.gnu.org/licenses/>.
16141cc406Sopenharmony_ci*/
17141cc406Sopenharmony_ci
18141cc406Sopenharmony_ci/*
19141cc406Sopenharmony_ci    Concept for a backend for scanners based on the NIASH chipset,
20141cc406Sopenharmony_ci    such as HP3300C, HP3400C, HP4300C, Agfa Touch.
21141cc406Sopenharmony_ci    Parts of this source were inspired by other backends.
22141cc406Sopenharmony_ci*/
23141cc406Sopenharmony_ci
24141cc406Sopenharmony_ci#include "../include/sane/config.h"
25141cc406Sopenharmony_ci#include "../include/sane/sane.h"
26141cc406Sopenharmony_ci#include "../include/sane/sanei.h"
27141cc406Sopenharmony_ci#include "../include/sane/sanei_backend.h"
28141cc406Sopenharmony_ci#include "../include/sane/sanei_config.h"
29141cc406Sopenharmony_ci#include "../include/sane/saneopts.h"
30141cc406Sopenharmony_ci
31141cc406Sopenharmony_ci#include <stdlib.h>             /* malloc, free */
32141cc406Sopenharmony_ci#include <string.h>             /* memcpy */
33141cc406Sopenharmony_ci#include <stdio.h>
34141cc406Sopenharmony_ci#include <sys/time.h>
35141cc406Sopenharmony_ci#include <sys/wait.h>
36141cc406Sopenharmony_ci
37141cc406Sopenharmony_ci/* definitions for debug */
38141cc406Sopenharmony_ci#define BACKEND_NAME niash
39141cc406Sopenharmony_ci#define BUILD 1
40141cc406Sopenharmony_ci
41141cc406Sopenharmony_ci#define DBG_ASSERT  1
42141cc406Sopenharmony_ci#define DBG_ERR     16
43141cc406Sopenharmony_ci#define DBG_MSG     32
44141cc406Sopenharmony_ci
45141cc406Sopenharmony_ci/* Just to avoid conflicts between niash backend and testtool */
46141cc406Sopenharmony_ci#define WITH_NIASH 1
47141cc406Sopenharmony_ci
48141cc406Sopenharmony_ci
49141cc406Sopenharmony_ci/* (source) includes for data transfer methods */
50141cc406Sopenharmony_ci#define STATIC static
51141cc406Sopenharmony_ci
52141cc406Sopenharmony_ci#include "niash_core.c"
53141cc406Sopenharmony_ci#include "niash_xfer.c"
54141cc406Sopenharmony_ci
55141cc406Sopenharmony_ci
56141cc406Sopenharmony_ci#define ASSERT(cond) (!(cond) ? DBG(DBG_ASSERT, "!!! ASSERT(%S) FAILED!!!\n",STRINGIFY(cond));)
57141cc406Sopenharmony_ci
58141cc406Sopenharmony_ci
59141cc406Sopenharmony_ci#define MM_TO_PIXEL(_mm_, _dpi_)    ((_mm_) * (_dpi_) / 25.4 )
60141cc406Sopenharmony_ci#define PIXEL_TO_MM(_pixel_, _dpi_) ((_pixel_) * 25.4 / (_dpi_) )
61141cc406Sopenharmony_ci
62141cc406Sopenharmony_ci
63141cc406Sopenharmony_ci/* options enumerator */
64141cc406Sopenharmony_citypedef enum
65141cc406Sopenharmony_ci{
66141cc406Sopenharmony_ci  optCount = 0,
67141cc406Sopenharmony_ci
68141cc406Sopenharmony_ci  optGroupGeometry,
69141cc406Sopenharmony_ci  optTLX, optTLY, optBRX, optBRY,
70141cc406Sopenharmony_ci  optDPI,
71141cc406Sopenharmony_ci
72141cc406Sopenharmony_ci  optGroupImage,
73141cc406Sopenharmony_ci  optGammaTable,                /* gamma table */
74141cc406Sopenharmony_ci
75141cc406Sopenharmony_ci  optGroupMode,
76141cc406Sopenharmony_ci  optMode,
77141cc406Sopenharmony_ci
78141cc406Sopenharmony_ci  optGroupEnhancement,
79141cc406Sopenharmony_ci  optThreshold,
80141cc406Sopenharmony_ci
81141cc406Sopenharmony_ci#ifdef EXPERIMENTAL
82141cc406Sopenharmony_ci  optGroupMisc,
83141cc406Sopenharmony_ci  optLamp,
84141cc406Sopenharmony_ci
85141cc406Sopenharmony_ci  optCalibrate,
86141cc406Sopenharmony_ci  optGamma,                      /* analog gamma = single number */
87141cc406Sopenharmony_ci#endif
88141cc406Sopenharmony_ci  optLast
89141cc406Sopenharmony_ci} EOptionIndex;
90141cc406Sopenharmony_ci
91141cc406Sopenharmony_ci
92141cc406Sopenharmony_citypedef union
93141cc406Sopenharmony_ci{
94141cc406Sopenharmony_ci  SANE_Word w;
95141cc406Sopenharmony_ci  SANE_Word *wa;                /* word array */
96141cc406Sopenharmony_ci  SANE_String s;
97141cc406Sopenharmony_ci} TOptionValue;
98141cc406Sopenharmony_ci
99141cc406Sopenharmony_ci#define HW_GAMMA_SIZE 4096
100141cc406Sopenharmony_ci#define SANE_GAMMA_SIZE 4096
101141cc406Sopenharmony_ci
102141cc406Sopenharmony_citypedef struct
103141cc406Sopenharmony_ci{
104141cc406Sopenharmony_ci  SANE_Option_Descriptor aOptions[optLast];
105141cc406Sopenharmony_ci  TOptionValue aValues[optLast];
106141cc406Sopenharmony_ci
107141cc406Sopenharmony_ci  TScanParams ScanParams;
108141cc406Sopenharmony_ci  THWParams HWParams;
109141cc406Sopenharmony_ci
110141cc406Sopenharmony_ci  TDataPipe DataPipe;
111141cc406Sopenharmony_ci  int iLinesLeft;               /* lines to scan */
112141cc406Sopenharmony_ci  int iBytesLeft;               /* bytes to read */
113141cc406Sopenharmony_ci  int iPixelsPerLine;           /* pixels in one scan line */
114141cc406Sopenharmony_ci
115141cc406Sopenharmony_ci  SANE_Int aGammaTable[SANE_GAMMA_SIZE];        /* a 12-to-8 bit color lookup table */
116141cc406Sopenharmony_ci
117141cc406Sopenharmony_ci  /* fCancelled needed to let sane issue the cancel message
118141cc406Sopenharmony_ci     instead of an error message */
119141cc406Sopenharmony_ci  SANE_Bool fCancelled;         /* SANE_TRUE if scanning cancelled */
120141cc406Sopenharmony_ci
121141cc406Sopenharmony_ci  SANE_Bool fScanning;          /* SANE_TRUE if actively scanning */
122141cc406Sopenharmony_ci
123141cc406Sopenharmony_ci  int WarmUpTime;               /* time to wait before a calibration starts */
124141cc406Sopenharmony_ci  unsigned char CalWhite[3];    /* values for the last calibration of white */
125141cc406Sopenharmony_ci  struct timeval WarmUpStarted;
126141cc406Sopenharmony_ci  /* system type to trace the time elapsed */
127141cc406Sopenharmony_ci} TScanner;
128141cc406Sopenharmony_ci
129141cc406Sopenharmony_ci
130141cc406Sopenharmony_ci/* linked list of SANE_Device structures */
131141cc406Sopenharmony_citypedef struct TDevListEntry
132141cc406Sopenharmony_ci{
133141cc406Sopenharmony_ci  struct TDevListEntry *pNext;
134141cc406Sopenharmony_ci  SANE_Device dev;
135141cc406Sopenharmony_ci} TDevListEntry;
136141cc406Sopenharmony_ci
137141cc406Sopenharmony_ci
138141cc406Sopenharmony_cistatic TDevListEntry *_pFirstSaneDev = 0;
139141cc406Sopenharmony_cistatic int iNumSaneDev = 0;
140141cc406Sopenharmony_cistatic const SANE_Device **_pSaneDevList = 0;
141141cc406Sopenharmony_ci
142141cc406Sopenharmony_ci
143141cc406Sopenharmony_ci/* option constraints */
144141cc406Sopenharmony_cistatic const SANE_Range rangeGammaTable = { 0, 255, 1 };
145141cc406Sopenharmony_ci
146141cc406Sopenharmony_ci/* available scanner resolutions */
147141cc406Sopenharmony_cistatic const SANE_Int setResolutions[] = { 4, 75, 150, 300, 600 };
148141cc406Sopenharmony_ci
149141cc406Sopenharmony_ci#ifdef EXPERIMENTAL
150141cc406Sopenharmony_ci/* range of an analog gamma */
151141cc406Sopenharmony_cistatic const SANE_Range rangeGamma = { SANE_FIX (0.25), SANE_FIX (4.0),
152141cc406Sopenharmony_ci  SANE_FIX (0.0)
153141cc406Sopenharmony_ci};
154141cc406Sopenharmony_ci#endif
155141cc406Sopenharmony_ci
156141cc406Sopenharmony_ci/* interpolate a sane gamma table to a hardware appropriate one
157141cc406Sopenharmony_ci   just in case the sane gamma table would be smaller */
158141cc406Sopenharmony_cistatic void
159141cc406Sopenharmony_ci_ConvertGammaTable (SANE_Word * saneGamma, unsigned char *hwGamma)
160141cc406Sopenharmony_ci{
161141cc406Sopenharmony_ci  int i;
162141cc406Sopenharmony_ci  int current = 0;
163141cc406Sopenharmony_ci  for (i = 0; i < SANE_GAMMA_SIZE; ++i)
164141cc406Sopenharmony_ci    {
165141cc406Sopenharmony_ci      int j;
166141cc406Sopenharmony_ci      int next;
167141cc406Sopenharmony_ci
168141cc406Sopenharmony_ci      /* highest range of copy indices */
169141cc406Sopenharmony_ci      next = ((i + 1) * HW_GAMMA_SIZE) / SANE_GAMMA_SIZE;
170141cc406Sopenharmony_ci
171141cc406Sopenharmony_ci      /* always copy the first */
172141cc406Sopenharmony_ci      hwGamma[current] = saneGamma[i];
173141cc406Sopenharmony_ci
174141cc406Sopenharmony_ci      /* the interpolation of the rest depends on the gap */
175141cc406Sopenharmony_ci      for (j = current + 1; j < HW_GAMMA_SIZE && j < next; ++j)
176141cc406Sopenharmony_ci        {
177141cc406Sopenharmony_ci          hwGamma[j] =
178141cc406Sopenharmony_ci            (saneGamma[i] * (next - j) +
179141cc406Sopenharmony_ci             saneGamma[i + 1] * (j - current)) / (next - current);
180141cc406Sopenharmony_ci        }
181141cc406Sopenharmony_ci      current = next;
182141cc406Sopenharmony_ci    }
183141cc406Sopenharmony_ci}
184141cc406Sopenharmony_ci
185141cc406Sopenharmony_ci/* create a unity gamma table */
186141cc406Sopenharmony_cistatic void
187141cc406Sopenharmony_ci_UnityGammaTable (unsigned char *hwGamma)
188141cc406Sopenharmony_ci{
189141cc406Sopenharmony_ci  int i;
190141cc406Sopenharmony_ci  for (i = 0; i < HW_GAMMA_SIZE; ++i)
191141cc406Sopenharmony_ci    {
192141cc406Sopenharmony_ci      hwGamma[i] = (i * 256) / HW_GAMMA_SIZE;
193141cc406Sopenharmony_ci    }
194141cc406Sopenharmony_ci
195141cc406Sopenharmony_ci}
196141cc406Sopenharmony_ci
197141cc406Sopenharmony_cistatic const SANE_Range rangeXmm = { 0, 220, 1 };
198141cc406Sopenharmony_cistatic const SANE_Range rangeYmm = { 0, 297, 1 };
199141cc406Sopenharmony_cistatic const SANE_Int startUpGamma = SANE_FIX (1.6);
200141cc406Sopenharmony_ci
201141cc406Sopenharmony_cistatic const char colorStr[] = { SANE_VALUE_SCAN_MODE_COLOR };
202141cc406Sopenharmony_cistatic const char grayStr[] = { SANE_VALUE_SCAN_MODE_GRAY };
203141cc406Sopenharmony_cistatic const char lineartStr[] = { SANE_VALUE_SCAN_MODE_LINEART };
204141cc406Sopenharmony_ci
205141cc406Sopenharmony_ci#define DEPTH_LINEART  1
206141cc406Sopenharmony_ci#define DEPTH_GRAY     8
207141cc406Sopenharmony_ci#define DEPTH_COLOR    8
208141cc406Sopenharmony_ci
209141cc406Sopenharmony_ci#define BYTES_PER_PIXEL_GRAY   1
210141cc406Sopenharmony_ci#define BYTES_PER_PIXEL_COLOR  3
211141cc406Sopenharmony_ci
212141cc406Sopenharmony_ci#define BITS_PER_PIXEL_LINEART 1
213141cc406Sopenharmony_ci#define BITS_PER_PIXEL_GRAY    DEPTH_GRAY
214141cc406Sopenharmony_ci#define BITS_PER_PIXEL_COLOR   (DEPTH_COLOR*3)
215141cc406Sopenharmony_ci
216141cc406Sopenharmony_ci#define BITS_PER_BYTE  8
217141cc406Sopenharmony_ci#define BITS_PADDING   (BITS_PER_BYTE-1)
218141cc406Sopenharmony_ci
219141cc406Sopenharmony_ci#define MODE_COLOR   0
220141cc406Sopenharmony_ci#define MODE_GRAY    1
221141cc406Sopenharmony_ci#define MODE_LINEART 2
222141cc406Sopenharmony_ci
223141cc406Sopenharmony_ci/* lineart threshold range */
224141cc406Sopenharmony_cistatic const SANE_Range rangeThreshold = {
225141cc406Sopenharmony_ci  0,
226141cc406Sopenharmony_ci  100,
227141cc406Sopenharmony_ci  1
228141cc406Sopenharmony_ci};
229141cc406Sopenharmony_ci
230141cc406Sopenharmony_ci/* scanning modes */
231141cc406Sopenharmony_cistatic SANE_String_Const modeList[] = {
232141cc406Sopenharmony_ci  colorStr,
233141cc406Sopenharmony_ci  grayStr,
234141cc406Sopenharmony_ci  lineartStr,
235141cc406Sopenharmony_ci  NULL
236141cc406Sopenharmony_ci};
237141cc406Sopenharmony_ci
238141cc406Sopenharmony_cistatic int
239141cc406Sopenharmony_ci_bytesPerLineLineart (int pixelsPerLine)
240141cc406Sopenharmony_ci{
241141cc406Sopenharmony_ci  return (pixelsPerLine * BITS_PER_PIXEL_LINEART +
242141cc406Sopenharmony_ci          BITS_PADDING) / BITS_PER_BYTE;
243141cc406Sopenharmony_ci}
244141cc406Sopenharmony_ci
245141cc406Sopenharmony_cistatic int
246141cc406Sopenharmony_ci_bytesPerLineGray (int pixelsPerLine)
247141cc406Sopenharmony_ci{
248141cc406Sopenharmony_ci  return (pixelsPerLine * BITS_PER_PIXEL_GRAY + BITS_PADDING) / BITS_PER_BYTE;
249141cc406Sopenharmony_ci}
250141cc406Sopenharmony_ci
251141cc406Sopenharmony_cistatic int
252141cc406Sopenharmony_ci_bytesPerLineColor (int pixelsPerLine)
253141cc406Sopenharmony_ci{
254141cc406Sopenharmony_ci  return (pixelsPerLine * BITS_PER_PIXEL_COLOR +
255141cc406Sopenharmony_ci          BITS_PADDING) / BITS_PER_BYTE;
256141cc406Sopenharmony_ci}
257141cc406Sopenharmony_ci
258141cc406Sopenharmony_ci
259141cc406Sopenharmony_ci/* dummy*/
260141cc406Sopenharmony_cistatic void
261141cc406Sopenharmony_ci_rgb2rgb (unsigned char __sane_unused__ *buffer, int __sane_unused__ pixels, int __sane_unused__ threshold)
262141cc406Sopenharmony_ci{
263141cc406Sopenharmony_ci  /* make the compiler content */
264141cc406Sopenharmony_ci}
265141cc406Sopenharmony_ci
266141cc406Sopenharmony_ci
267141cc406Sopenharmony_ci/* convert 24bit RGB to 8bit GRAY */
268141cc406Sopenharmony_cistatic void
269141cc406Sopenharmony_ci_rgb2gray (unsigned char *buffer, int pixels, int __sane_unused__ threshold)
270141cc406Sopenharmony_ci{
271141cc406Sopenharmony_ci#define WEIGHT_R 27
272141cc406Sopenharmony_ci#define WEIGHT_G 54
273141cc406Sopenharmony_ci#define WEIGHT_B 19
274141cc406Sopenharmony_ci#define WEIGHT_W (WEIGHT_R + WEIGHT_G + WEIGHT_B)
275141cc406Sopenharmony_ci  static int aWeight[BYTES_PER_PIXEL_COLOR] =
276141cc406Sopenharmony_ci    { WEIGHT_R, WEIGHT_G, WEIGHT_B };
277141cc406Sopenharmony_ci  int nbyte = pixels * BYTES_PER_PIXEL_COLOR;
278141cc406Sopenharmony_ci  int acc = 0;
279141cc406Sopenharmony_ci  int x;
280141cc406Sopenharmony_ci
281141cc406Sopenharmony_ci  for (x = 0; x < nbyte; ++x)
282141cc406Sopenharmony_ci    {
283141cc406Sopenharmony_ci      acc += aWeight[x % BYTES_PER_PIXEL_COLOR] * buffer[x];
284141cc406Sopenharmony_ci      if ((x + 1) % BYTES_PER_PIXEL_COLOR == 0)
285141cc406Sopenharmony_ci        {
286141cc406Sopenharmony_ci          buffer[x / BYTES_PER_PIXEL_COLOR] =
287141cc406Sopenharmony_ci            (unsigned char) (acc / WEIGHT_W);
288141cc406Sopenharmony_ci          acc = 0;
289141cc406Sopenharmony_ci        }
290141cc406Sopenharmony_ci    }
291141cc406Sopenharmony_ci#undef WEIGHT_R
292141cc406Sopenharmony_ci#undef WEIGHT_G
293141cc406Sopenharmony_ci#undef WEIGHT_B
294141cc406Sopenharmony_ci#undef WEIGHT_W
295141cc406Sopenharmony_ci}
296141cc406Sopenharmony_ci
297141cc406Sopenharmony_ci/* convert 24bit RGB to 1bit B/W */
298141cc406Sopenharmony_cistatic void
299141cc406Sopenharmony_ci_rgb2lineart (unsigned char *buffer, int pixels, int threshold)
300141cc406Sopenharmony_ci{
301141cc406Sopenharmony_ci  static const int aMask[BITS_PER_BYTE] = { 128, 64, 32, 16, 8, 4, 2, 1 };
302141cc406Sopenharmony_ci  int acc = 0;
303141cc406Sopenharmony_ci  int nx;
304141cc406Sopenharmony_ci  int x;
305141cc406Sopenharmony_ci  int thresh;
306141cc406Sopenharmony_ci  _rgb2gray (buffer, pixels, 0);
307141cc406Sopenharmony_ci  nx = ((pixels + BITS_PADDING) / BITS_PER_BYTE) * BITS_PER_BYTE;
308141cc406Sopenharmony_ci  thresh = 255 * threshold / rangeThreshold.max;
309141cc406Sopenharmony_ci  for (x = 0; x < nx; ++x)
310141cc406Sopenharmony_ci    {
311141cc406Sopenharmony_ci      if (x < pixels && buffer[x] < thresh)
312141cc406Sopenharmony_ci        {
313141cc406Sopenharmony_ci          acc |= aMask[x % BITS_PER_BYTE];
314141cc406Sopenharmony_ci        }
315141cc406Sopenharmony_ci      if ((x + 1) % BITS_PER_BYTE == 0)
316141cc406Sopenharmony_ci        {
317141cc406Sopenharmony_ci          buffer[x / BITS_PER_BYTE] = (unsigned char) (acc);
318141cc406Sopenharmony_ci          acc = 0;
319141cc406Sopenharmony_ci        }
320141cc406Sopenharmony_ci    }
321141cc406Sopenharmony_ci}
322141cc406Sopenharmony_ci
323141cc406Sopenharmony_citypedef struct tgModeParam
324141cc406Sopenharmony_ci{
325141cc406Sopenharmony_ci  SANE_Int depth;
326141cc406Sopenharmony_ci  SANE_Frame format;
327141cc406Sopenharmony_ci  int (*bytesPerLine) (int pixelsPerLine);
328141cc406Sopenharmony_ci  void (*adaptFormat) (unsigned char *rgbBuffer, int pixels, int threshold);
329141cc406Sopenharmony_ci
330141cc406Sopenharmony_ci} TModeParam;
331141cc406Sopenharmony_ci
332141cc406Sopenharmony_cistatic const TModeParam modeParam[] = {
333141cc406Sopenharmony_ci  {DEPTH_COLOR, SANE_FRAME_RGB, _bytesPerLineColor, _rgb2rgb},
334141cc406Sopenharmony_ci  {DEPTH_GRAY, SANE_FRAME_GRAY, _bytesPerLineGray, _rgb2gray},
335141cc406Sopenharmony_ci  {DEPTH_LINEART, SANE_FRAME_GRAY, _bytesPerLineLineart, _rgb2lineart}
336141cc406Sopenharmony_ci};
337141cc406Sopenharmony_ci
338141cc406Sopenharmony_ci
339141cc406Sopenharmony_ci#define WARMUP_AFTERSTART    1  /* flag for 1st warm up */
340141cc406Sopenharmony_ci#define WARMUP_INSESSION     0
341141cc406Sopenharmony_ci#define WARMUP_TESTINTERVAL 15  /* test every 15sec */
342141cc406Sopenharmony_ci#define WARMUP_TIME         30  /* first wait is 30sec minimum */
343141cc406Sopenharmony_ci#define WARMUP_MAXTIME      90  /* after one and a half minute start latest */
344141cc406Sopenharmony_ci
345141cc406Sopenharmony_ci#define CAL_DEV_MAX         15
346141cc406Sopenharmony_ci/* maximum deviation of cal values in percent between 2 tests */
347141cc406Sopenharmony_ci
348141cc406Sopenharmony_ci/* different warm up after start and after automatic off */
349141cc406Sopenharmony_cistatic const int aiWarmUpTime[] = { WARMUP_TESTINTERVAL, WARMUP_TIME };
350141cc406Sopenharmony_ci
351141cc406Sopenharmony_ci
352141cc406Sopenharmony_ci
353141cc406Sopenharmony_ci/* returns 1, when the warm up time "iTime" has elasped */
354141cc406Sopenharmony_cistatic int
355141cc406Sopenharmony_ci_TimeElapsed (struct timeval *start, struct timeval *now, int iTime)
356141cc406Sopenharmony_ci{
357141cc406Sopenharmony_ci
358141cc406Sopenharmony_ci  /* this is a bit strange, but can deal with overflows */
359141cc406Sopenharmony_ci  if (start->tv_sec > now->tv_sec)
360141cc406Sopenharmony_ci    return (start->tv_sec / 2 - now->tv_sec / 2 > iTime / 2);
361141cc406Sopenharmony_ci  else
362141cc406Sopenharmony_ci    return (now->tv_sec - start->tv_sec >= iTime);
363141cc406Sopenharmony_ci}
364141cc406Sopenharmony_ci
365141cc406Sopenharmony_cistatic void
366141cc406Sopenharmony_ci_WarmUpLamp (TScanner * s, int iMode)
367141cc406Sopenharmony_ci{
368141cc406Sopenharmony_ci  SANE_Bool fLampOn;
369141cc406Sopenharmony_ci  /* on startup don't care what was before
370141cc406Sopenharmony_ci     assume lamp was off, and the previous
371141cc406Sopenharmony_ci     cal values can never be reached */
372141cc406Sopenharmony_ci  if (iMode == WARMUP_AFTERSTART)
373141cc406Sopenharmony_ci    {
374141cc406Sopenharmony_ci      fLampOn = SANE_FALSE;
375141cc406Sopenharmony_ci      s->CalWhite[0] = s->CalWhite[1] = s->CalWhite[2] = (unsigned char) (-1);
376141cc406Sopenharmony_ci    }
377141cc406Sopenharmony_ci  else
378141cc406Sopenharmony_ci    GetLamp (&s->HWParams, &fLampOn);
379141cc406Sopenharmony_ci
380141cc406Sopenharmony_ci  if (!fLampOn)
381141cc406Sopenharmony_ci    {
382141cc406Sopenharmony_ci      /* get the current system time */
383141cc406Sopenharmony_ci      gettimeofday (&s->WarmUpStarted, 0);
384141cc406Sopenharmony_ci      /* determine the time to wait at least */
385141cc406Sopenharmony_ci      s->WarmUpTime = aiWarmUpTime[iMode];
386141cc406Sopenharmony_ci      /* switch on the lamp */
387141cc406Sopenharmony_ci      SetLamp (&s->HWParams, SANE_TRUE);
388141cc406Sopenharmony_ci    }
389141cc406Sopenharmony_ci}
390141cc406Sopenharmony_ci
391141cc406Sopenharmony_cistatic void
392141cc406Sopenharmony_ci_WaitForLamp (TScanner * s, unsigned char *pabCalibTable)
393141cc406Sopenharmony_ci{
394141cc406Sopenharmony_ci  struct timeval now[2];        /* toggling time holder */
395141cc406Sopenharmony_ci  int i;                        /* rgb loop */
396141cc406Sopenharmony_ci  int iCal = 0;                 /* counter */
397141cc406Sopenharmony_ci  int iCurrent = 0;             /* buffer and time-holder swap flag */
398141cc406Sopenharmony_ci  SANE_Bool fHasCal;
399141cc406Sopenharmony_ci  unsigned char CalWhite[2][3]; /* toggling buffer */
400141cc406Sopenharmony_ci  int iDelay = 0;               /* delay loop counter */
401141cc406Sopenharmony_ci  _WarmUpLamp (s, SANE_FALSE);
402141cc406Sopenharmony_ci
403141cc406Sopenharmony_ci
404141cc406Sopenharmony_ci  /* get the time stamp for the wait loops */
405141cc406Sopenharmony_ci  if (s->WarmUpTime)
406141cc406Sopenharmony_ci    gettimeofday (&now[iCurrent], 0);
407141cc406Sopenharmony_ci  SimpleCalibExt (&s->HWParams, pabCalibTable, CalWhite[iCurrent]);
408141cc406Sopenharmony_ci  fHasCal = SANE_TRUE;
409141cc406Sopenharmony_ci
410141cc406Sopenharmony_ci  DBG (DBG_MSG, "_WaitForLamp: first calibration\n");
411141cc406Sopenharmony_ci
412141cc406Sopenharmony_ci
413141cc406Sopenharmony_ci  /* wait until time has elapsed or for values to stabilze */
414141cc406Sopenharmony_ci  while (s->WarmUpTime)
415141cc406Sopenharmony_ci    {
416141cc406Sopenharmony_ci      /* check if the last scan has lower calibration values than
417141cc406Sopenharmony_ci         the current one would have */
418141cc406Sopenharmony_ci      if (s->WarmUpTime && fHasCal)
419141cc406Sopenharmony_ci        {
420141cc406Sopenharmony_ci          SANE_Bool fOver = SANE_TRUE;
421141cc406Sopenharmony_ci          for (i = 0; fOver && i < 3; ++i)
422141cc406Sopenharmony_ci            {
423141cc406Sopenharmony_ci              if (!s->CalWhite[i])
424141cc406Sopenharmony_ci                fOver = SANE_FALSE;
425141cc406Sopenharmony_ci              else if (CalWhite[iCurrent][i] < s->CalWhite[i])
426141cc406Sopenharmony_ci                fOver = SANE_FALSE;
427141cc406Sopenharmony_ci            }
428141cc406Sopenharmony_ci
429141cc406Sopenharmony_ci          /* warm up is not needed, when calibration data is above
430141cc406Sopenharmony_ci             the calibration data of the last scan */
431141cc406Sopenharmony_ci          if (fOver)
432141cc406Sopenharmony_ci            {
433141cc406Sopenharmony_ci              s->WarmUpTime = 0;
434141cc406Sopenharmony_ci              DBG (DBG_MSG,
435141cc406Sopenharmony_ci                   "_WaitForLamp: Values seem stable, skipping next calibration cycle\n");
436141cc406Sopenharmony_ci            }
437141cc406Sopenharmony_ci        }
438141cc406Sopenharmony_ci
439141cc406Sopenharmony_ci
440141cc406Sopenharmony_ci      /* break the loop, when the longest wait time has expired
441141cc406Sopenharmony_ci         to prevent a hanging application,
442141cc406Sopenharmony_ci         even if the values might not be good, yet */
443141cc406Sopenharmony_ci      if (s->WarmUpTime && fHasCal && iCal)
444141cc406Sopenharmony_ci        {
445141cc406Sopenharmony_ci          /* abort, when we have waited long enough */
446141cc406Sopenharmony_ci          if (_TimeElapsed
447141cc406Sopenharmony_ci              (&s->WarmUpStarted, &now[iCurrent], WARMUP_MAXTIME))
448141cc406Sopenharmony_ci            {
449141cc406Sopenharmony_ci              /* stop idling */
450141cc406Sopenharmony_ci              s->WarmUpTime = 0;
451141cc406Sopenharmony_ci              DBG (DBG_MSG, "_WaitForLamp: WARMUP_MAXTIME=%ds elapsed!\n",
452141cc406Sopenharmony_ci                   WARMUP_MAXTIME);
453141cc406Sopenharmony_ci            }
454141cc406Sopenharmony_ci        }
455141cc406Sopenharmony_ci
456141cc406Sopenharmony_ci
457141cc406Sopenharmony_ci      /* enter a delay loop, when there is still time to wait */
458141cc406Sopenharmony_ci      if (s->WarmUpTime)
459141cc406Sopenharmony_ci        {
460141cc406Sopenharmony_ci          /* if the (too low) calibration values have just been acquired
461141cc406Sopenharmony_ci             we start waiting */
462141cc406Sopenharmony_ci          if (fHasCal)
463141cc406Sopenharmony_ci            DBG (DBG_MSG, "_WaitForLamp: entering delay loop\r");
464141cc406Sopenharmony_ci          else
465141cc406Sopenharmony_ci            DBG (DBG_MSG, "_WaitForLamp: delay loop %d        \r", ++iDelay);
466141cc406Sopenharmony_ci          sleep (1);
467141cc406Sopenharmony_ci          fHasCal = SANE_FALSE;
468141cc406Sopenharmony_ci          gettimeofday (&now[!iCurrent], 0);
469141cc406Sopenharmony_ci        }
470141cc406Sopenharmony_ci
471141cc406Sopenharmony_ci
472141cc406Sopenharmony_ci      /* look if we should check again */
473141cc406Sopenharmony_ci      if (s->WarmUpTime         /* did we have to wait at all */
474141cc406Sopenharmony_ci          /* is the minimum time elapsed */
475141cc406Sopenharmony_ci          && _TimeElapsed (&s->WarmUpStarted, &now[!iCurrent], s->WarmUpTime)
476141cc406Sopenharmony_ci          /* has the minimum time elapsed since the last calibration */
477141cc406Sopenharmony_ci          && _TimeElapsed (&now[iCurrent], &now[!iCurrent],
478141cc406Sopenharmony_ci                           WARMUP_TESTINTERVAL))
479141cc406Sopenharmony_ci        {
480141cc406Sopenharmony_ci          int dev = 0;          /* 0 percent deviation in cal value as default */
481141cc406Sopenharmony_ci          iDelay = 0;           /* all delays processed */
482141cc406Sopenharmony_ci          /* new calibration */
483141cc406Sopenharmony_ci          ++iCal;
484141cc406Sopenharmony_ci          iCurrent = !iCurrent; /* swap the test-buffer, and time-holder */
485141cc406Sopenharmony_ci          SimpleCalibExt (&s->HWParams, pabCalibTable, CalWhite[iCurrent]);
486141cc406Sopenharmony_ci          fHasCal = SANE_TRUE;
487141cc406Sopenharmony_ci
488141cc406Sopenharmony_ci          for (i = 0; i < 3; ++i)
489141cc406Sopenharmony_ci            {
490141cc406Sopenharmony_ci              /* copy for faster and clearer access */
491141cc406Sopenharmony_ci              int cwa;
492141cc406Sopenharmony_ci              int cwb;
493141cc406Sopenharmony_ci              int ldev;
494141cc406Sopenharmony_ci              cwa = CalWhite[!iCurrent][i];
495141cc406Sopenharmony_ci              cwb = CalWhite[iCurrent][i];
496141cc406Sopenharmony_ci              /* find the biggest deviation of one color */
497141cc406Sopenharmony_ci              if (cwa > cwb)
498141cc406Sopenharmony_ci                ldev = 0;
499141cc406Sopenharmony_ci              else if (cwa && cwb)
500141cc406Sopenharmony_ci                ldev = ((cwb - cwa) * 100) / cwb;
501141cc406Sopenharmony_ci              else
502141cc406Sopenharmony_ci                ldev = 100;
503141cc406Sopenharmony_ci              dev = MAX (dev, ldev);
504141cc406Sopenharmony_ci            }
505141cc406Sopenharmony_ci
506141cc406Sopenharmony_ci          /* show the biggest deviation of the calibration values */
507141cc406Sopenharmony_ci          DBG (DBG_MSG, "_WaitForLamp: recalibration #%d, deviation = %d%%\n",
508141cc406Sopenharmony_ci               iCal, dev);
509141cc406Sopenharmony_ci
510141cc406Sopenharmony_ci          /* the deviation to the previous calibration is tolerable */
511141cc406Sopenharmony_ci          if (dev <= CAL_DEV_MAX)
512141cc406Sopenharmony_ci            s->WarmUpTime = 0;
513141cc406Sopenharmony_ci        }
514141cc406Sopenharmony_ci    }
515141cc406Sopenharmony_ci
516141cc406Sopenharmony_ci  /* remember the values of this calibration
517141cc406Sopenharmony_ci     for the next time */
518141cc406Sopenharmony_ci  for (i = 0; i < 3; ++i)
519141cc406Sopenharmony_ci    {
520141cc406Sopenharmony_ci      s->CalWhite[i] = CalWhite[iCurrent][i];
521141cc406Sopenharmony_ci    }
522141cc406Sopenharmony_ci}
523141cc406Sopenharmony_ci
524141cc406Sopenharmony_ci
525141cc406Sopenharmony_ci/* used, when setting gamma as 1 value */
526141cc406Sopenharmony_cistatic void
527141cc406Sopenharmony_ci_SetScalarGamma (SANE_Int * aiGamma, SANE_Int sfGamma)
528141cc406Sopenharmony_ci{
529141cc406Sopenharmony_ci  int j;
530141cc406Sopenharmony_ci  double fGamma;
531141cc406Sopenharmony_ci  fGamma = SANE_UNFIX (sfGamma);
532141cc406Sopenharmony_ci  for (j = 0; j < SANE_GAMMA_SIZE; j++)
533141cc406Sopenharmony_ci    {
534141cc406Sopenharmony_ci      int iData;
535141cc406Sopenharmony_ci      iData =
536141cc406Sopenharmony_ci        floor (256.0 *
537141cc406Sopenharmony_ci               pow (((double) j / (double) SANE_GAMMA_SIZE), 1.0 / fGamma));
538141cc406Sopenharmony_ci      if (iData > 255)
539141cc406Sopenharmony_ci        iData = 255;
540141cc406Sopenharmony_ci      aiGamma[j] = iData;
541141cc406Sopenharmony_ci    }
542141cc406Sopenharmony_ci}
543141cc406Sopenharmony_ci
544141cc406Sopenharmony_ci
545141cc406Sopenharmony_ci/* return size of longest string in a string list */
546141cc406Sopenharmony_cistatic size_t
547141cc406Sopenharmony_ci_MaxStringSize (const SANE_String_Const strings[])
548141cc406Sopenharmony_ci{
549141cc406Sopenharmony_ci  size_t size, max_size = 0;
550141cc406Sopenharmony_ci  int i;
551141cc406Sopenharmony_ci
552141cc406Sopenharmony_ci  for (i = 0; strings[i]; ++i)
553141cc406Sopenharmony_ci    {
554141cc406Sopenharmony_ci      size = strlen (strings[i]) + 1;
555141cc406Sopenharmony_ci      if (size > max_size)
556141cc406Sopenharmony_ci        max_size = size;
557141cc406Sopenharmony_ci    }
558141cc406Sopenharmony_ci  return max_size;
559141cc406Sopenharmony_ci}
560141cc406Sopenharmony_ci
561141cc406Sopenharmony_ci
562141cc406Sopenharmony_ci/* change a sane cap and return true, when a change took place */
563141cc406Sopenharmony_cistatic int
564141cc406Sopenharmony_ci_ChangeCap (SANE_Word * pCap, SANE_Word cap, int isSet)
565141cc406Sopenharmony_ci{
566141cc406Sopenharmony_ci  SANE_Word prevCap = *pCap;
567141cc406Sopenharmony_ci  if (isSet)
568141cc406Sopenharmony_ci    {
569141cc406Sopenharmony_ci      *pCap |= cap;
570141cc406Sopenharmony_ci    }
571141cc406Sopenharmony_ci  else
572141cc406Sopenharmony_ci    {
573141cc406Sopenharmony_ci      *pCap &= ~cap;
574141cc406Sopenharmony_ci    }
575141cc406Sopenharmony_ci  return *pCap != prevCap;
576141cc406Sopenharmony_ci}
577141cc406Sopenharmony_ci
578141cc406Sopenharmony_ci
579141cc406Sopenharmony_cistatic void
580141cc406Sopenharmony_ci_InitOptions (TScanner * s)
581141cc406Sopenharmony_ci{
582141cc406Sopenharmony_ci  int i;
583141cc406Sopenharmony_ci  SANE_Option_Descriptor *pDesc;
584141cc406Sopenharmony_ci  TOptionValue *pVal;
585141cc406Sopenharmony_ci  _SetScalarGamma (s->aGammaTable, startUpGamma);
586141cc406Sopenharmony_ci
587141cc406Sopenharmony_ci  for (i = optCount; i < optLast; i++)
588141cc406Sopenharmony_ci    {
589141cc406Sopenharmony_ci
590141cc406Sopenharmony_ci      pDesc = &s->aOptions[i];
591141cc406Sopenharmony_ci      pVal = &s->aValues[i];
592141cc406Sopenharmony_ci
593141cc406Sopenharmony_ci      /* defaults */
594141cc406Sopenharmony_ci      pDesc->name = "";
595141cc406Sopenharmony_ci      pDesc->title = "";
596141cc406Sopenharmony_ci      pDesc->desc = "";
597141cc406Sopenharmony_ci      pDesc->type = SANE_TYPE_INT;
598141cc406Sopenharmony_ci      pDesc->unit = SANE_UNIT_NONE;
599141cc406Sopenharmony_ci      pDesc->size = sizeof (SANE_Word);
600141cc406Sopenharmony_ci      pDesc->constraint_type = SANE_CONSTRAINT_NONE;
601141cc406Sopenharmony_ci      pDesc->cap = 0;
602141cc406Sopenharmony_ci
603141cc406Sopenharmony_ci      switch (i)
604141cc406Sopenharmony_ci        {
605141cc406Sopenharmony_ci
606141cc406Sopenharmony_ci        case optCount:
607141cc406Sopenharmony_ci          pDesc->title = SANE_TITLE_NUM_OPTIONS;
608141cc406Sopenharmony_ci          pDesc->desc = SANE_DESC_NUM_OPTIONS;
609141cc406Sopenharmony_ci          pDesc->cap = SANE_CAP_SOFT_DETECT;
610141cc406Sopenharmony_ci          pVal->w = (SANE_Word) optLast;
611141cc406Sopenharmony_ci          break;
612141cc406Sopenharmony_ci
613141cc406Sopenharmony_ci        case optGroupGeometry:
614141cc406Sopenharmony_ci          pDesc->title = "Geometry";
615141cc406Sopenharmony_ci          pDesc->type = SANE_TYPE_GROUP;
616141cc406Sopenharmony_ci          pDesc->size = 0;
617141cc406Sopenharmony_ci          break;
618141cc406Sopenharmony_ci
619141cc406Sopenharmony_ci        case optTLX:
620141cc406Sopenharmony_ci          pDesc->name = SANE_NAME_SCAN_TL_X;
621141cc406Sopenharmony_ci          pDesc->title = SANE_TITLE_SCAN_TL_X;
622141cc406Sopenharmony_ci          pDesc->desc = SANE_DESC_SCAN_TL_X;
623141cc406Sopenharmony_ci          pDesc->unit = SANE_UNIT_MM;
624141cc406Sopenharmony_ci          pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
625141cc406Sopenharmony_ci          pDesc->constraint.range = &rangeXmm;
626141cc406Sopenharmony_ci          pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
627141cc406Sopenharmony_ci          pVal->w = rangeXmm.min;
628141cc406Sopenharmony_ci          break;
629141cc406Sopenharmony_ci
630141cc406Sopenharmony_ci        case optTLY:
631141cc406Sopenharmony_ci          pDesc->name = SANE_NAME_SCAN_TL_Y;
632141cc406Sopenharmony_ci          pDesc->title = SANE_TITLE_SCAN_TL_Y;
633141cc406Sopenharmony_ci          pDesc->desc = SANE_DESC_SCAN_TL_Y;
634141cc406Sopenharmony_ci          pDesc->unit = SANE_UNIT_MM;
635141cc406Sopenharmony_ci          pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
636141cc406Sopenharmony_ci          pDesc->constraint.range = &rangeYmm;
637141cc406Sopenharmony_ci          pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
638141cc406Sopenharmony_ci          pVal->w = rangeYmm.min;
639141cc406Sopenharmony_ci          break;
640141cc406Sopenharmony_ci
641141cc406Sopenharmony_ci        case optBRX:
642141cc406Sopenharmony_ci          pDesc->name = SANE_NAME_SCAN_BR_X;
643141cc406Sopenharmony_ci          pDesc->title = SANE_TITLE_SCAN_BR_X;
644141cc406Sopenharmony_ci          pDesc->desc = SANE_DESC_SCAN_BR_X;
645141cc406Sopenharmony_ci          pDesc->unit = SANE_UNIT_MM;
646141cc406Sopenharmony_ci          pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
647141cc406Sopenharmony_ci          pDesc->constraint.range = &rangeXmm;
648141cc406Sopenharmony_ci          pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
649141cc406Sopenharmony_ci          pVal->w = 210 /* A4 width instead of rangeXmm.max */ ;
650141cc406Sopenharmony_ci          break;
651141cc406Sopenharmony_ci
652141cc406Sopenharmony_ci        case optBRY:
653141cc406Sopenharmony_ci          pDesc->name = SANE_NAME_SCAN_BR_Y;
654141cc406Sopenharmony_ci          pDesc->title = SANE_TITLE_SCAN_BR_Y;
655141cc406Sopenharmony_ci          pDesc->desc = SANE_DESC_SCAN_BR_Y;
656141cc406Sopenharmony_ci          pDesc->unit = SANE_UNIT_MM;
657141cc406Sopenharmony_ci          pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
658141cc406Sopenharmony_ci          pDesc->constraint.range = &rangeYmm;
659141cc406Sopenharmony_ci          pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
660141cc406Sopenharmony_ci          pVal->w = 290 /* have a bit reserve instead of rangeYmm.max */ ;
661141cc406Sopenharmony_ci          break;
662141cc406Sopenharmony_ci
663141cc406Sopenharmony_ci        case optDPI:
664141cc406Sopenharmony_ci          pDesc->name = SANE_NAME_SCAN_RESOLUTION;
665141cc406Sopenharmony_ci          pDesc->title = SANE_TITLE_SCAN_RESOLUTION;
666141cc406Sopenharmony_ci          pDesc->desc = SANE_DESC_SCAN_RESOLUTION;
667141cc406Sopenharmony_ci          pDesc->unit = SANE_UNIT_DPI;
668141cc406Sopenharmony_ci          pDesc->constraint_type = SANE_CONSTRAINT_WORD_LIST;
669141cc406Sopenharmony_ci          pDesc->constraint.word_list = setResolutions;
670141cc406Sopenharmony_ci          pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
671141cc406Sopenharmony_ci          pVal->w = setResolutions[2];  /* default to 150dpi */
672141cc406Sopenharmony_ci          break;
673141cc406Sopenharmony_ci
674141cc406Sopenharmony_ci        case optGroupImage:
675141cc406Sopenharmony_ci          pDesc->title = SANE_I18N ("Image");
676141cc406Sopenharmony_ci          pDesc->type = SANE_TYPE_GROUP;
677141cc406Sopenharmony_ci          pDesc->size = 0;
678141cc406Sopenharmony_ci          break;
679141cc406Sopenharmony_ci
680141cc406Sopenharmony_ci#ifdef EXPERIMENTAL
681141cc406Sopenharmony_ci        case optGamma:
682141cc406Sopenharmony_ci          pDesc->name = SANE_NAME_ANALOG_GAMMA;
683141cc406Sopenharmony_ci          pDesc->title = SANE_TITLE_ANALOG_GAMMA;
684141cc406Sopenharmony_ci          pDesc->desc = SANE_DESC_ANALOG_GAMMA;
685141cc406Sopenharmony_ci          pDesc->type = SANE_TYPE_FIXED;
686141cc406Sopenharmony_ci          pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
687141cc406Sopenharmony_ci          pDesc->constraint.range = &rangeGamma;
688141cc406Sopenharmony_ci          pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
689141cc406Sopenharmony_ci          pVal->w = startUpGamma;
690141cc406Sopenharmony_ci          break;
691141cc406Sopenharmony_ci#endif
692141cc406Sopenharmony_ci
693141cc406Sopenharmony_ci        case optGammaTable:
694141cc406Sopenharmony_ci          pDesc->name = SANE_NAME_GAMMA_VECTOR;
695141cc406Sopenharmony_ci          pDesc->title = SANE_TITLE_GAMMA_VECTOR;
696141cc406Sopenharmony_ci          pDesc->desc = SANE_DESC_GAMMA_VECTOR;
697141cc406Sopenharmony_ci          pDesc->size = sizeof (s->aGammaTable);
698141cc406Sopenharmony_ci          pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
699141cc406Sopenharmony_ci          pDesc->constraint.range = &rangeGammaTable;
700141cc406Sopenharmony_ci          pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
701141cc406Sopenharmony_ci          pVal->wa = s->aGammaTable;
702141cc406Sopenharmony_ci          break;
703141cc406Sopenharmony_ci
704141cc406Sopenharmony_ci#ifdef EXPERIMENTAL
705141cc406Sopenharmony_ci        case optGroupMisc:
706141cc406Sopenharmony_ci          pDesc->title = SANE_I18N ("Miscellaneous");
707141cc406Sopenharmony_ci          pDesc->type = SANE_TYPE_GROUP;
708141cc406Sopenharmony_ci          pDesc->size = 0;
709141cc406Sopenharmony_ci          break;
710141cc406Sopenharmony_ci
711141cc406Sopenharmony_ci        case optLamp:
712141cc406Sopenharmony_ci          pDesc->name = "lamp";
713141cc406Sopenharmony_ci          pDesc->title = SANE_I18N ("Lamp status");
714141cc406Sopenharmony_ci          pDesc->desc = SANE_I18N ("Switches the lamp on or off.");
715141cc406Sopenharmony_ci          pDesc->type = SANE_TYPE_BOOL;
716141cc406Sopenharmony_ci          pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
717141cc406Sopenharmony_ci          /* switch the lamp on when starting for first the time */
718141cc406Sopenharmony_ci          pVal->w = SANE_TRUE;
719141cc406Sopenharmony_ci          break;
720141cc406Sopenharmony_ci
721141cc406Sopenharmony_ci        case optCalibrate:
722141cc406Sopenharmony_ci          pDesc->name = "calibrate";
723141cc406Sopenharmony_ci          pDesc->title = SANE_I18N ("Calibrate");
724141cc406Sopenharmony_ci          pDesc->desc = SANE_I18N ("Calibrates for black and white level.");
725141cc406Sopenharmony_ci          pDesc->type = SANE_TYPE_BUTTON;
726141cc406Sopenharmony_ci          pDesc->cap = SANE_CAP_SOFT_SELECT;
727141cc406Sopenharmony_ci          pDesc->size = 0;
728141cc406Sopenharmony_ci          break;
729141cc406Sopenharmony_ci#endif
730141cc406Sopenharmony_ci        case optGroupMode:
731141cc406Sopenharmony_ci          pDesc->title = SANE_I18N ("Scan Mode");
732141cc406Sopenharmony_ci          pDesc->desc = "";
733141cc406Sopenharmony_ci          pDesc->type = SANE_TYPE_GROUP;
734141cc406Sopenharmony_ci          break;
735141cc406Sopenharmony_ci
736141cc406Sopenharmony_ci        case optMode:
737141cc406Sopenharmony_ci          /* scan mode */
738141cc406Sopenharmony_ci          pDesc->name = SANE_NAME_SCAN_MODE;
739141cc406Sopenharmony_ci          pDesc->title = SANE_TITLE_SCAN_MODE;
740141cc406Sopenharmony_ci          pDesc->desc = SANE_DESC_SCAN_MODE;
741141cc406Sopenharmony_ci          pDesc->type = SANE_TYPE_STRING;
742141cc406Sopenharmony_ci          pDesc->size = _MaxStringSize (modeList);
743141cc406Sopenharmony_ci          pDesc->constraint_type = SANE_CONSTRAINT_STRING_LIST;
744141cc406Sopenharmony_ci          pDesc->constraint.string_list = modeList;
745141cc406Sopenharmony_ci          pDesc->cap =
746141cc406Sopenharmony_ci            SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_EMULATED;
747141cc406Sopenharmony_ci          pVal->w = MODE_COLOR;
748141cc406Sopenharmony_ci          break;
749141cc406Sopenharmony_ci
750141cc406Sopenharmony_ci        case optGroupEnhancement:
751141cc406Sopenharmony_ci          pDesc->title = SANE_I18N ("Enhancement");
752141cc406Sopenharmony_ci          pDesc->desc = "";
753141cc406Sopenharmony_ci          pDesc->type = SANE_TYPE_GROUP;
754141cc406Sopenharmony_ci          break;
755141cc406Sopenharmony_ci
756141cc406Sopenharmony_ci        case optThreshold:
757141cc406Sopenharmony_ci          pDesc->name = SANE_NAME_THRESHOLD;
758141cc406Sopenharmony_ci          pDesc->title = SANE_TITLE_THRESHOLD;
759141cc406Sopenharmony_ci          pDesc->desc = SANE_DESC_THRESHOLD;
760141cc406Sopenharmony_ci          pDesc->type = SANE_TYPE_INT;
761141cc406Sopenharmony_ci          pDesc->unit = SANE_UNIT_PERCENT;
762141cc406Sopenharmony_ci          pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
763141cc406Sopenharmony_ci          pDesc->constraint.range = &rangeThreshold;
764141cc406Sopenharmony_ci          pDesc->cap =
765141cc406Sopenharmony_ci            SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_INACTIVE |
766141cc406Sopenharmony_ci            SANE_CAP_EMULATED;
767141cc406Sopenharmony_ci          pVal->w = 50;
768141cc406Sopenharmony_ci          break;
769141cc406Sopenharmony_ci
770141cc406Sopenharmony_ci        default:
771141cc406Sopenharmony_ci          DBG (DBG_ERR, "Uninitialised option %d\n", i);
772141cc406Sopenharmony_ci          break;
773141cc406Sopenharmony_ci        }
774141cc406Sopenharmony_ci    }
775141cc406Sopenharmony_ci}
776141cc406Sopenharmony_ci
777141cc406Sopenharmony_ci
778141cc406Sopenharmony_cistatic int
779141cc406Sopenharmony_ci_ReportDevice (TScannerModel * pModel, const char *pszDeviceName)
780141cc406Sopenharmony_ci{
781141cc406Sopenharmony_ci  TDevListEntry *pNew, *pDev;
782141cc406Sopenharmony_ci
783141cc406Sopenharmony_ci  DBG (DBG_MSG, "niash: _ReportDevice '%s'\n", pszDeviceName);
784141cc406Sopenharmony_ci
785141cc406Sopenharmony_ci  pNew = malloc (sizeof (TDevListEntry));
786141cc406Sopenharmony_ci  if (!pNew)
787141cc406Sopenharmony_ci    {
788141cc406Sopenharmony_ci      DBG (DBG_ERR, "no mem\n");
789141cc406Sopenharmony_ci      return -1;
790141cc406Sopenharmony_ci    }
791141cc406Sopenharmony_ci
792141cc406Sopenharmony_ci  /* add new element to the end of the list */
793141cc406Sopenharmony_ci  if (_pFirstSaneDev == 0)
794141cc406Sopenharmony_ci    {
795141cc406Sopenharmony_ci      _pFirstSaneDev = pNew;
796141cc406Sopenharmony_ci    }
797141cc406Sopenharmony_ci  else
798141cc406Sopenharmony_ci    {
799141cc406Sopenharmony_ci      for (pDev = _pFirstSaneDev; pDev->pNext; pDev = pDev->pNext)
800141cc406Sopenharmony_ci        {
801141cc406Sopenharmony_ci          ;
802141cc406Sopenharmony_ci        }
803141cc406Sopenharmony_ci      pDev->pNext = pNew;
804141cc406Sopenharmony_ci    }
805141cc406Sopenharmony_ci
806141cc406Sopenharmony_ci  /* fill in new element */
807141cc406Sopenharmony_ci  pNew->pNext = 0;
808141cc406Sopenharmony_ci  pNew->dev.name = strdup (pszDeviceName);
809141cc406Sopenharmony_ci  pNew->dev.vendor = pModel->pszVendor;
810141cc406Sopenharmony_ci  pNew->dev.model = pModel->pszName;
811141cc406Sopenharmony_ci  pNew->dev.type = "flatbed scanner";
812141cc406Sopenharmony_ci
813141cc406Sopenharmony_ci  iNumSaneDev++;
814141cc406Sopenharmony_ci
815141cc406Sopenharmony_ci  return 0;
816141cc406Sopenharmony_ci}
817141cc406Sopenharmony_ci
818141cc406Sopenharmony_ci
819141cc406Sopenharmony_ci
820141cc406Sopenharmony_ci/*****************************************************************************/
821141cc406Sopenharmony_ci
822141cc406Sopenharmony_ciSANE_Status
823141cc406Sopenharmony_cisane_init (SANE_Int * piVersion, SANE_Auth_Callback __sane_unused__ pfnAuth)
824141cc406Sopenharmony_ci{
825141cc406Sopenharmony_ci  DBG_INIT ();
826141cc406Sopenharmony_ci  DBG (DBG_MSG, "sane_init\n");
827141cc406Sopenharmony_ci
828141cc406Sopenharmony_ci  if (piVersion != NULL)
829141cc406Sopenharmony_ci    {
830141cc406Sopenharmony_ci      *piVersion = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD);
831141cc406Sopenharmony_ci    }
832141cc406Sopenharmony_ci
833141cc406Sopenharmony_ci  /* initialise transfer methods */
834141cc406Sopenharmony_ci  iNumSaneDev = 0;
835141cc406Sopenharmony_ci  NiashXferInit (_ReportDevice);
836141cc406Sopenharmony_ci
837141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
838141cc406Sopenharmony_ci}
839141cc406Sopenharmony_ci
840141cc406Sopenharmony_ci
841141cc406Sopenharmony_civoid
842141cc406Sopenharmony_cisane_exit (void)
843141cc406Sopenharmony_ci{
844141cc406Sopenharmony_ci  TDevListEntry *pDev, *pNext;
845141cc406Sopenharmony_ci
846141cc406Sopenharmony_ci  DBG (DBG_MSG, "sane_exit\n");
847141cc406Sopenharmony_ci
848141cc406Sopenharmony_ci  /* free device list memory */
849141cc406Sopenharmony_ci  if (_pSaneDevList)
850141cc406Sopenharmony_ci    {
851141cc406Sopenharmony_ci      for (pDev = _pFirstSaneDev; pDev; pDev = pNext)
852141cc406Sopenharmony_ci        {
853141cc406Sopenharmony_ci          pNext = pDev->pNext;
854141cc406Sopenharmony_ci          free ((void *) pDev->dev.name);
855141cc406Sopenharmony_ci          free (pDev);
856141cc406Sopenharmony_ci        }
857141cc406Sopenharmony_ci      _pFirstSaneDev = 0;
858141cc406Sopenharmony_ci      free (_pSaneDevList);
859141cc406Sopenharmony_ci      _pSaneDevList = 0;
860141cc406Sopenharmony_ci    }
861141cc406Sopenharmony_ci}
862141cc406Sopenharmony_ci
863141cc406Sopenharmony_ci
864141cc406Sopenharmony_ciSANE_Status
865141cc406Sopenharmony_cisane_get_devices (const SANE_Device *** device_list, SANE_Bool __sane_unused__ local_only)
866141cc406Sopenharmony_ci{
867141cc406Sopenharmony_ci  TDevListEntry *pDev;
868141cc406Sopenharmony_ci  int i;
869141cc406Sopenharmony_ci
870141cc406Sopenharmony_ci  DBG (DBG_MSG, "sane_get_devices\n");
871141cc406Sopenharmony_ci
872141cc406Sopenharmony_ci  if (_pSaneDevList)
873141cc406Sopenharmony_ci    {
874141cc406Sopenharmony_ci      free (_pSaneDevList);
875141cc406Sopenharmony_ci    }
876141cc406Sopenharmony_ci
877141cc406Sopenharmony_ci  _pSaneDevList = malloc (sizeof (*_pSaneDevList) * (iNumSaneDev + 1));
878141cc406Sopenharmony_ci  if (!_pSaneDevList)
879141cc406Sopenharmony_ci    {
880141cc406Sopenharmony_ci      DBG (DBG_MSG, "no mem\n");
881141cc406Sopenharmony_ci      return SANE_STATUS_NO_MEM;
882141cc406Sopenharmony_ci    }
883141cc406Sopenharmony_ci  i = 0;
884141cc406Sopenharmony_ci  for (pDev = _pFirstSaneDev; pDev; pDev = pDev->pNext)
885141cc406Sopenharmony_ci    {
886141cc406Sopenharmony_ci      _pSaneDevList[i++] = &pDev->dev;
887141cc406Sopenharmony_ci    }
888141cc406Sopenharmony_ci  _pSaneDevList[i++] = 0;       /* last entry is 0 */
889141cc406Sopenharmony_ci
890141cc406Sopenharmony_ci  *device_list = _pSaneDevList;
891141cc406Sopenharmony_ci
892141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
893141cc406Sopenharmony_ci}
894141cc406Sopenharmony_ci
895141cc406Sopenharmony_ci
896141cc406Sopenharmony_ciSANE_Status
897141cc406Sopenharmony_cisane_open (SANE_String_Const name, SANE_Handle * h)
898141cc406Sopenharmony_ci{
899141cc406Sopenharmony_ci  TScanner *s;
900141cc406Sopenharmony_ci
901141cc406Sopenharmony_ci  DBG (DBG_MSG, "sane_open: %s\n", name);
902141cc406Sopenharmony_ci
903141cc406Sopenharmony_ci  /* check the name */
904141cc406Sopenharmony_ci  if (strlen (name) == 0)
905141cc406Sopenharmony_ci    {
906141cc406Sopenharmony_ci      /* default to first available device */
907141cc406Sopenharmony_ci      name = _pFirstSaneDev->dev.name;
908141cc406Sopenharmony_ci    }
909141cc406Sopenharmony_ci
910141cc406Sopenharmony_ci  s = malloc (sizeof (TScanner));
911141cc406Sopenharmony_ci  if (!s)
912141cc406Sopenharmony_ci    {
913141cc406Sopenharmony_ci      DBG (DBG_MSG, "malloc failed\n");
914141cc406Sopenharmony_ci      return SANE_STATUS_NO_MEM;
915141cc406Sopenharmony_ci    }
916141cc406Sopenharmony_ci
917141cc406Sopenharmony_ci  if (NiashOpen (&s->HWParams, name) < 0)
918141cc406Sopenharmony_ci    {
919141cc406Sopenharmony_ci      /* is this OK ? */
920141cc406Sopenharmony_ci      DBG (DBG_ERR, "NiashOpen failed\n");
921141cc406Sopenharmony_ci      free ((void *) s);
922141cc406Sopenharmony_ci      return SANE_STATUS_DEVICE_BUSY;
923141cc406Sopenharmony_ci    }
924141cc406Sopenharmony_ci  _InitOptions (s);
925141cc406Sopenharmony_ci  s->fScanning = SANE_FALSE;
926141cc406Sopenharmony_ci  s->fCancelled = SANE_FALSE;
927141cc406Sopenharmony_ci  *h = s;
928141cc406Sopenharmony_ci
929141cc406Sopenharmony_ci  /* Turn on lamp by default at startup */
930141cc406Sopenharmony_ci  _WarmUpLamp (s, WARMUP_AFTERSTART);
931141cc406Sopenharmony_ci
932141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
933141cc406Sopenharmony_ci}
934141cc406Sopenharmony_ci
935141cc406Sopenharmony_ci
936141cc406Sopenharmony_civoid
937141cc406Sopenharmony_cisane_close (SANE_Handle h)
938141cc406Sopenharmony_ci{
939141cc406Sopenharmony_ci  TScanner *s;
940141cc406Sopenharmony_ci
941141cc406Sopenharmony_ci  DBG (DBG_MSG, "sane_close\n");
942141cc406Sopenharmony_ci
943141cc406Sopenharmony_ci  s = (TScanner *) h;
944141cc406Sopenharmony_ci
945141cc406Sopenharmony_ci  /* turn off scanner lamp */
946141cc406Sopenharmony_ci  SetLamp (&s->HWParams, SANE_FALSE);
947141cc406Sopenharmony_ci
948141cc406Sopenharmony_ci  /* close scanner */
949141cc406Sopenharmony_ci  NiashClose (&s->HWParams);
950141cc406Sopenharmony_ci
951141cc406Sopenharmony_ci  /* free scanner object memory */
952141cc406Sopenharmony_ci  free ((void *) s);
953141cc406Sopenharmony_ci}
954141cc406Sopenharmony_ci
955141cc406Sopenharmony_ci
956141cc406Sopenharmony_ciconst SANE_Option_Descriptor *
957141cc406Sopenharmony_cisane_get_option_descriptor (SANE_Handle h, SANE_Int n)
958141cc406Sopenharmony_ci{
959141cc406Sopenharmony_ci  TScanner *s;
960141cc406Sopenharmony_ci
961141cc406Sopenharmony_ci  DBG (DBG_MSG, "sane_get_option_descriptor %d\n", n);
962141cc406Sopenharmony_ci
963141cc406Sopenharmony_ci  if ((n < optCount) || (n >= optLast))
964141cc406Sopenharmony_ci    {
965141cc406Sopenharmony_ci      return NULL;
966141cc406Sopenharmony_ci    }
967141cc406Sopenharmony_ci
968141cc406Sopenharmony_ci  s = (TScanner *) h;
969141cc406Sopenharmony_ci  return &s->aOptions[n];
970141cc406Sopenharmony_ci}
971141cc406Sopenharmony_ci
972141cc406Sopenharmony_ci
973141cc406Sopenharmony_ciSANE_Status
974141cc406Sopenharmony_cisane_control_option (SANE_Handle h, SANE_Int n, SANE_Action Action,
975141cc406Sopenharmony_ci                     void *pVal, SANE_Int * pInfo)
976141cc406Sopenharmony_ci{
977141cc406Sopenharmony_ci  TScanner *s;
978141cc406Sopenharmony_ci  static char szTable[100];
979141cc406Sopenharmony_ci  int *pi;
980141cc406Sopenharmony_ci  int i;
981141cc406Sopenharmony_ci  SANE_Int info;
982141cc406Sopenharmony_ci  SANE_Status status;
983141cc406Sopenharmony_ci#ifdef EXPERIMENTAL
984141cc406Sopenharmony_ci  SANE_Bool fLampIsOn;
985141cc406Sopenharmony_ci  SANE_Bool fVal;
986141cc406Sopenharmony_ci  SANE_Bool fSame;
987141cc406Sopenharmony_ci#endif
988141cc406Sopenharmony_ci
989141cc406Sopenharmony_ci  DBG (DBG_MSG, "sane_control_option: option %d, action %d\n", n, Action);
990141cc406Sopenharmony_ci
991141cc406Sopenharmony_ci  if ((n < optCount) || (n >= optLast))
992141cc406Sopenharmony_ci    {
993141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
994141cc406Sopenharmony_ci    }
995141cc406Sopenharmony_ci
996141cc406Sopenharmony_ci  if (Action == SANE_ACTION_GET_VALUE || Action == SANE_ACTION_SET_VALUE)
997141cc406Sopenharmony_ci    {
998141cc406Sopenharmony_ci      if (pVal == NULL)
999141cc406Sopenharmony_ci        {
1000141cc406Sopenharmony_ci          return SANE_STATUS_INVAL;
1001141cc406Sopenharmony_ci        }
1002141cc406Sopenharmony_ci    }
1003141cc406Sopenharmony_ci
1004141cc406Sopenharmony_ci  s = (TScanner *) h;
1005141cc406Sopenharmony_ci  info = 0;
1006141cc406Sopenharmony_ci
1007141cc406Sopenharmony_ci  switch (Action)
1008141cc406Sopenharmony_ci    {
1009141cc406Sopenharmony_ci    case SANE_ACTION_GET_VALUE:
1010141cc406Sopenharmony_ci      switch (n)
1011141cc406Sopenharmony_ci        {
1012141cc406Sopenharmony_ci
1013141cc406Sopenharmony_ci          /* Get options of type SANE_Word */
1014141cc406Sopenharmony_ci        case optCount:
1015141cc406Sopenharmony_ci        case optDPI:
1016141cc406Sopenharmony_ci#ifdef EXPERIMENTAL
1017141cc406Sopenharmony_ci        case optGamma:
1018141cc406Sopenharmony_ci#endif
1019141cc406Sopenharmony_ci        case optTLX:
1020141cc406Sopenharmony_ci        case optTLY:
1021141cc406Sopenharmony_ci        case optBRX:
1022141cc406Sopenharmony_ci        case optBRY:
1023141cc406Sopenharmony_ci        case optThreshold:
1024141cc406Sopenharmony_ci          DBG (DBG_MSG,
1025141cc406Sopenharmony_ci               "sane_control_option: SANE_ACTION_GET_VALUE %d = %d\n", n,
1026141cc406Sopenharmony_ci               (int) s->aValues[n].w);
1027141cc406Sopenharmony_ci          *(SANE_Word *) pVal = s->aValues[n].w;
1028141cc406Sopenharmony_ci          break;
1029141cc406Sopenharmony_ci
1030141cc406Sopenharmony_ci          /* Get options of type SANE_Word array */
1031141cc406Sopenharmony_ci        case optGammaTable:
1032141cc406Sopenharmony_ci          DBG (DBG_MSG, "Reading gamma table\n");
1033141cc406Sopenharmony_ci          memcpy (pVal, s->aValues[n].wa, s->aOptions[n].size);
1034141cc406Sopenharmony_ci          break;
1035141cc406Sopenharmony_ci
1036141cc406Sopenharmony_ci        case optMode:
1037141cc406Sopenharmony_ci          DBG (DBG_MSG, "Reading scan mode %s\n",
1038141cc406Sopenharmony_ci               modeList[s->aValues[optMode].w]);
1039141cc406Sopenharmony_ci          strcpy ((char *) pVal, modeList[s->aValues[optMode].w]);
1040141cc406Sopenharmony_ci          break;
1041141cc406Sopenharmony_ci
1042141cc406Sopenharmony_ci#ifdef EXPERIMENTAL
1043141cc406Sopenharmony_ci          /* Get options of type SANE_Bool */
1044141cc406Sopenharmony_ci        case optLamp:
1045141cc406Sopenharmony_ci          GetLamp (&s->HWParams, &fLampIsOn);
1046141cc406Sopenharmony_ci          *(SANE_Bool *) pVal = fLampIsOn;
1047141cc406Sopenharmony_ci          break;
1048141cc406Sopenharmony_ci
1049141cc406Sopenharmony_ci        case optCalibrate:
1050141cc406Sopenharmony_ci          /*  although this option has nothing to read,
1051141cc406Sopenharmony_ci             it's added here to avoid a warning when running scanimage --help */
1052141cc406Sopenharmony_ci          break;
1053141cc406Sopenharmony_ci#endif
1054141cc406Sopenharmony_ci
1055141cc406Sopenharmony_ci        default:
1056141cc406Sopenharmony_ci          DBG (DBG_MSG, "SANE_ACTION_GET_VALUE: Invalid option (%d)\n", n);
1057141cc406Sopenharmony_ci        }
1058141cc406Sopenharmony_ci      break;
1059141cc406Sopenharmony_ci
1060141cc406Sopenharmony_ci
1061141cc406Sopenharmony_ci    case SANE_ACTION_SET_VALUE:
1062141cc406Sopenharmony_ci      if (s->fScanning)
1063141cc406Sopenharmony_ci        {
1064141cc406Sopenharmony_ci          DBG (DBG_ERR,
1065141cc406Sopenharmony_ci               "sane_control_option: SANE_ACTION_SET_VALUE not allowed during scan\n");
1066141cc406Sopenharmony_ci          return SANE_STATUS_INVAL;
1067141cc406Sopenharmony_ci        }
1068141cc406Sopenharmony_ci      switch (n)
1069141cc406Sopenharmony_ci        {
1070141cc406Sopenharmony_ci
1071141cc406Sopenharmony_ci        case optCount:
1072141cc406Sopenharmony_ci          return SANE_STATUS_INVAL;
1073141cc406Sopenharmony_ci
1074141cc406Sopenharmony_ci#ifdef EXPERIMENTAL
1075141cc406Sopenharmony_ci        case optGamma:
1076141cc406Sopenharmony_ci#endif
1077141cc406Sopenharmony_ci        case optThreshold:
1078141cc406Sopenharmony_ci        case optDPI:
1079141cc406Sopenharmony_ci
1080141cc406Sopenharmony_ci          info |= SANE_INFO_RELOAD_PARAMS;
1081141cc406Sopenharmony_ci          /* fall through */
1082141cc406Sopenharmony_ci
1083141cc406Sopenharmony_ci        case optTLX:
1084141cc406Sopenharmony_ci        case optTLY:
1085141cc406Sopenharmony_ci        case optBRX:
1086141cc406Sopenharmony_ci        case optBRY:
1087141cc406Sopenharmony_ci
1088141cc406Sopenharmony_ci          status = sanei_constrain_value (&s->aOptions[n], pVal, &info);
1089141cc406Sopenharmony_ci          if (status != SANE_STATUS_GOOD)
1090141cc406Sopenharmony_ci            {
1091141cc406Sopenharmony_ci              DBG (DBG_ERR, "Failed to constrain option %d (%s)\n", n,
1092141cc406Sopenharmony_ci                   s->aOptions[n].title);
1093141cc406Sopenharmony_ci              return status;
1094141cc406Sopenharmony_ci            }
1095141cc406Sopenharmony_ci
1096141cc406Sopenharmony_ci#ifdef EXPERIMENTAL
1097141cc406Sopenharmony_ci          /* check values if they are equal */
1098141cc406Sopenharmony_ci          fSame = s->aValues[n].w == *(SANE_Word *) pVal;
1099141cc406Sopenharmony_ci#endif
1100141cc406Sopenharmony_ci
1101141cc406Sopenharmony_ci          /* set the values */
1102141cc406Sopenharmony_ci          s->aValues[n].w = *(SANE_Word *) pVal;
1103141cc406Sopenharmony_ci          DBG (DBG_MSG,
1104141cc406Sopenharmony_ci               "sane_control_option: SANE_ACTION_SET_VALUE %d = %d\n", n,
1105141cc406Sopenharmony_ci               (int) s->aValues[n].w);
1106141cc406Sopenharmony_ci#ifdef EXPERIMENTAL
1107141cc406Sopenharmony_ci          if (n == optGamma)
1108141cc406Sopenharmony_ci            {
1109141cc406Sopenharmony_ci              if (!fSame && optLast > optGammaTable)
1110141cc406Sopenharmony_ci                {
1111141cc406Sopenharmony_ci                  info |= SANE_INFO_RELOAD_OPTIONS;
1112141cc406Sopenharmony_ci                }
1113141cc406Sopenharmony_ci              _SetScalarGamma (s->aGammaTable, s->aValues[n].w);
1114141cc406Sopenharmony_ci            }
1115141cc406Sopenharmony_ci#endif
1116141cc406Sopenharmony_ci          break;
1117141cc406Sopenharmony_ci
1118141cc406Sopenharmony_ci        case optGammaTable:
1119141cc406Sopenharmony_ci          DBG (DBG_MSG, "Writing gamma table\n");
1120141cc406Sopenharmony_ci          pi = (SANE_Int *) pVal;
1121141cc406Sopenharmony_ci          memcpy (s->aValues[n].wa, pVal, s->aOptions[n].size);
1122141cc406Sopenharmony_ci
1123141cc406Sopenharmony_ci          /* prepare table for debug */
1124141cc406Sopenharmony_ci          strcpy (szTable, "Gamma table summary:");
1125141cc406Sopenharmony_ci          for (i = 0; i < SANE_GAMMA_SIZE; i++)
1126141cc406Sopenharmony_ci            {
1127141cc406Sopenharmony_ci              if ((SANE_GAMMA_SIZE / 16) && (i % (SANE_GAMMA_SIZE / 16)) == 0)
1128141cc406Sopenharmony_ci                {
1129141cc406Sopenharmony_ci                  DBG (DBG_MSG, "%s\n", szTable);
1130141cc406Sopenharmony_ci		  szTable[0] = '\0';
1131141cc406Sopenharmony_ci                }
1132141cc406Sopenharmony_ci              /* test for number print */
1133141cc406Sopenharmony_ci              if ((SANE_GAMMA_SIZE / 64) && (i % (SANE_GAMMA_SIZE / 64)) == 0)
1134141cc406Sopenharmony_ci                {
1135141cc406Sopenharmony_ci                  sprintf (szTable + strlen(szTable), " %04X", pi[i]);
1136141cc406Sopenharmony_ci                }
1137141cc406Sopenharmony_ci            }
1138141cc406Sopenharmony_ci          if (strlen (szTable))
1139141cc406Sopenharmony_ci            {
1140141cc406Sopenharmony_ci              DBG (DBG_MSG, "%s\n", szTable);
1141141cc406Sopenharmony_ci            }
1142141cc406Sopenharmony_ci          break;
1143141cc406Sopenharmony_ci
1144141cc406Sopenharmony_ci        case optMode:
1145141cc406Sopenharmony_ci          {
1146141cc406Sopenharmony_ci            SANE_Word *pCap;
1147141cc406Sopenharmony_ci            int fCapChanged = 0;
1148141cc406Sopenharmony_ci
1149141cc406Sopenharmony_ci            pCap = &s->aOptions[optThreshold].cap;
1150141cc406Sopenharmony_ci
1151141cc406Sopenharmony_ci            if (strcmp ((char const *) pVal, colorStr) == 0)
1152141cc406Sopenharmony_ci              {
1153141cc406Sopenharmony_ci                s->aValues[optMode].w = MODE_COLOR;
1154141cc406Sopenharmony_ci                fCapChanged = _ChangeCap (pCap, SANE_CAP_INACTIVE, 1);
1155141cc406Sopenharmony_ci              }
1156141cc406Sopenharmony_ci            if (strcmp ((char const *) pVal, grayStr) == 0)
1157141cc406Sopenharmony_ci              {
1158141cc406Sopenharmony_ci                s->aValues[optMode].w = MODE_GRAY;
1159141cc406Sopenharmony_ci                fCapChanged = _ChangeCap (pCap, SANE_CAP_INACTIVE, 1);
1160141cc406Sopenharmony_ci              }
1161141cc406Sopenharmony_ci            if (strcmp ((char const *) pVal, lineartStr) == 0)
1162141cc406Sopenharmony_ci              {
1163141cc406Sopenharmony_ci                s->aValues[optMode].w = MODE_LINEART;
1164141cc406Sopenharmony_ci                fCapChanged = _ChangeCap (pCap, SANE_CAP_INACTIVE, 0);
1165141cc406Sopenharmony_ci
1166141cc406Sopenharmony_ci              }
1167141cc406Sopenharmony_ci            info |= SANE_INFO_RELOAD_PARAMS;
1168141cc406Sopenharmony_ci            if (fCapChanged)
1169141cc406Sopenharmony_ci              {
1170141cc406Sopenharmony_ci                info |= SANE_INFO_RELOAD_OPTIONS;
1171141cc406Sopenharmony_ci              }
1172141cc406Sopenharmony_ci            DBG (DBG_MSG, "setting scan mode: %s\n", (char const *) pVal);
1173141cc406Sopenharmony_ci          }
1174141cc406Sopenharmony_ci          break;
1175141cc406Sopenharmony_ci
1176141cc406Sopenharmony_ci
1177141cc406Sopenharmony_ci
1178141cc406Sopenharmony_ci#ifdef EXPERIMENTAL
1179141cc406Sopenharmony_ci        case optLamp:
1180141cc406Sopenharmony_ci          fVal = *(SANE_Bool *) pVal;
1181141cc406Sopenharmony_ci          DBG (DBG_MSG, "lamp %s\n", fVal ? "on" : "off");
1182141cc406Sopenharmony_ci          if (fVal)
1183141cc406Sopenharmony_ci            _WarmUpLamp (s, WARMUP_INSESSION);
1184141cc406Sopenharmony_ci          else
1185141cc406Sopenharmony_ci            SetLamp (&s->HWParams, SANE_FALSE);
1186141cc406Sopenharmony_ci          break;
1187141cc406Sopenharmony_ci
1188141cc406Sopenharmony_ci        case optCalibrate:
1189141cc406Sopenharmony_ci/*       SimpleCalib(&s->HWParams); */
1190141cc406Sopenharmony_ci          break;
1191141cc406Sopenharmony_ci#endif
1192141cc406Sopenharmony_ci
1193141cc406Sopenharmony_ci        default:
1194141cc406Sopenharmony_ci          DBG (DBG_ERR, "SANE_ACTION_SET_VALUE: Invalid option (%d)\n", n);
1195141cc406Sopenharmony_ci        }
1196141cc406Sopenharmony_ci      if (pInfo != NULL)
1197141cc406Sopenharmony_ci        {
1198141cc406Sopenharmony_ci          *pInfo |= info;
1199141cc406Sopenharmony_ci        }
1200141cc406Sopenharmony_ci      break;
1201141cc406Sopenharmony_ci
1202141cc406Sopenharmony_ci
1203141cc406Sopenharmony_ci    case SANE_ACTION_SET_AUTO:
1204141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
1205141cc406Sopenharmony_ci
1206141cc406Sopenharmony_ci
1207141cc406Sopenharmony_ci    default:
1208141cc406Sopenharmony_ci      DBG (DBG_ERR, "Invalid action (%d)\n", Action);
1209141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
1210141cc406Sopenharmony_ci    }
1211141cc406Sopenharmony_ci
1212141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
1213141cc406Sopenharmony_ci}
1214141cc406Sopenharmony_ci
1215141cc406Sopenharmony_ci
1216141cc406Sopenharmony_ci
1217141cc406Sopenharmony_ci
1218141cc406Sopenharmony_ci
1219141cc406Sopenharmony_ciSANE_Status
1220141cc406Sopenharmony_cisane_get_parameters (SANE_Handle h, SANE_Parameters * p)
1221141cc406Sopenharmony_ci{
1222141cc406Sopenharmony_ci  TScanner *s;
1223141cc406Sopenharmony_ci  TModeParam const *pMode;
1224141cc406Sopenharmony_ci
1225141cc406Sopenharmony_ci  DBG (DBG_MSG, "sane_get_parameters\n");
1226141cc406Sopenharmony_ci
1227141cc406Sopenharmony_ci  s = (TScanner *) h;
1228141cc406Sopenharmony_ci
1229141cc406Sopenharmony_ci  /* first do some checks */
1230141cc406Sopenharmony_ci  if (s->aValues[optTLX].w >= s->aValues[optBRX].w)
1231141cc406Sopenharmony_ci    {
1232141cc406Sopenharmony_ci      DBG (DBG_ERR, "TLX should be smaller than BRX\n");
1233141cc406Sopenharmony_ci      return SANE_STATUS_INVAL; /* proper error code? */
1234141cc406Sopenharmony_ci    }
1235141cc406Sopenharmony_ci  if (s->aValues[optTLY].w >= s->aValues[optBRY].w)
1236141cc406Sopenharmony_ci    {
1237141cc406Sopenharmony_ci      DBG (DBG_ERR, "TLY should be smaller than BRY\n");
1238141cc406Sopenharmony_ci      return SANE_STATUS_INVAL; /* proper error code? */
1239141cc406Sopenharmony_ci    }
1240141cc406Sopenharmony_ci
1241141cc406Sopenharmony_ci
1242141cc406Sopenharmony_ci  pMode = &modeParam[s->aValues[optMode].w];
1243141cc406Sopenharmony_ci
1244141cc406Sopenharmony_ci  /* return the data */
1245141cc406Sopenharmony_ci  p->format = pMode->format;
1246141cc406Sopenharmony_ci  p->last_frame = SANE_TRUE;
1247141cc406Sopenharmony_ci
1248141cc406Sopenharmony_ci  p->lines = MM_TO_PIXEL (s->aValues[optBRY].w - s->aValues[optTLY].w,
1249141cc406Sopenharmony_ci                          s->aValues[optDPI].w);
1250141cc406Sopenharmony_ci  p->depth = pMode->depth;
1251141cc406Sopenharmony_ci  p->pixels_per_line =
1252141cc406Sopenharmony_ci    MM_TO_PIXEL (s->aValues[optBRX].w - s->aValues[optTLX].w,
1253141cc406Sopenharmony_ci                 s->aValues[optDPI].w);
1254141cc406Sopenharmony_ci  p->bytes_per_line = pMode->bytesPerLine (p->pixels_per_line);
1255141cc406Sopenharmony_ci
1256141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
1257141cc406Sopenharmony_ci}
1258141cc406Sopenharmony_ci
1259141cc406Sopenharmony_ci
1260141cc406Sopenharmony_ci/* get the scale down factor for a resolution that is
1261141cc406Sopenharmony_ci  not supported by hardware */
1262141cc406Sopenharmony_cistatic int
1263141cc406Sopenharmony_ci_SaneEmulateScaling (int iDpi)
1264141cc406Sopenharmony_ci{
1265141cc406Sopenharmony_ci  if (iDpi == 75)
1266141cc406Sopenharmony_ci    return 2;
1267141cc406Sopenharmony_ci  else
1268141cc406Sopenharmony_ci    return 1;
1269141cc406Sopenharmony_ci}
1270141cc406Sopenharmony_ci
1271141cc406Sopenharmony_ci
1272141cc406Sopenharmony_ciSANE_Status
1273141cc406Sopenharmony_cisane_start (SANE_Handle h)
1274141cc406Sopenharmony_ci{
1275141cc406Sopenharmony_ci  TScanner *s;
1276141cc406Sopenharmony_ci  SANE_Parameters par;
1277141cc406Sopenharmony_ci  int iLineCorr;
1278141cc406Sopenharmony_ci  int iScaleDown;
1279141cc406Sopenharmony_ci  static unsigned char abGamma[HW_GAMMA_SIZE];
1280141cc406Sopenharmony_ci  static unsigned char abCalibTable[HW_PIXELS * 6];
1281141cc406Sopenharmony_ci
1282141cc406Sopenharmony_ci  DBG (DBG_MSG, "sane_start\n");
1283141cc406Sopenharmony_ci
1284141cc406Sopenharmony_ci  s = (TScanner *) h;
1285141cc406Sopenharmony_ci
1286141cc406Sopenharmony_ci  if (sane_get_parameters (h, &par) != SANE_STATUS_GOOD)
1287141cc406Sopenharmony_ci    {
1288141cc406Sopenharmony_ci      DBG (DBG_MSG, "Invalid scan parameters\n");
1289141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
1290141cc406Sopenharmony_ci    }
1291141cc406Sopenharmony_ci  iScaleDown = _SaneEmulateScaling (s->aValues[optDPI].w);
1292141cc406Sopenharmony_ci  s->iLinesLeft = par.lines;
1293141cc406Sopenharmony_ci
1294141cc406Sopenharmony_ci  /* fill in the scanparams using the option values */
1295141cc406Sopenharmony_ci  s->ScanParams.iDpi = s->aValues[optDPI].w * iScaleDown;
1296141cc406Sopenharmony_ci  s->ScanParams.iLpi = s->aValues[optDPI].w * iScaleDown;
1297141cc406Sopenharmony_ci
1298141cc406Sopenharmony_ci  /* calculate correction for filling of circular buffer */
1299141cc406Sopenharmony_ci  iLineCorr = 3 * s->HWParams.iSensorSkew;      /* usually 16 motor steps */
1300141cc406Sopenharmony_ci  /* calculate correction for garbage lines */
1301141cc406Sopenharmony_ci  iLineCorr += s->HWParams.iSkipLines * (HW_LPI / s->ScanParams.iLpi);
1302141cc406Sopenharmony_ci
1303141cc406Sopenharmony_ci  s->ScanParams.iTop =
1304141cc406Sopenharmony_ci    MM_TO_PIXEL (s->aValues[optTLY].w + s->HWParams.iTopLeftY,
1305141cc406Sopenharmony_ci                 HW_LPI) - iLineCorr;
1306141cc406Sopenharmony_ci  s->ScanParams.iLeft =
1307141cc406Sopenharmony_ci    MM_TO_PIXEL (s->aValues[optTLX].w + s->HWParams.iTopLeftX, HW_DPI);
1308141cc406Sopenharmony_ci
1309141cc406Sopenharmony_ci  s->ScanParams.iWidth = par.pixels_per_line * iScaleDown;
1310141cc406Sopenharmony_ci  s->ScanParams.iHeight = par.lines * iScaleDown;
1311141cc406Sopenharmony_ci  s->ScanParams.iBottom = HP3300C_BOTTOM;
1312141cc406Sopenharmony_ci  s->ScanParams.fCalib = SANE_FALSE;
1313141cc406Sopenharmony_ci
1314141cc406Sopenharmony_ci  /* perform a simple calibration just before scanning */
1315141cc406Sopenharmony_ci  _WaitForLamp (s, abCalibTable);
1316141cc406Sopenharmony_ci
1317141cc406Sopenharmony_ci  if (s->aValues[optMode].w == MODE_LINEART)
1318141cc406Sopenharmony_ci    {
1319141cc406Sopenharmony_ci      /* use a unity gamma table for lineart to be independent from Gamma settings */
1320141cc406Sopenharmony_ci      _UnityGammaTable (abGamma);
1321141cc406Sopenharmony_ci    }
1322141cc406Sopenharmony_ci  else
1323141cc406Sopenharmony_ci    {
1324141cc406Sopenharmony_ci      /* copy gamma table */
1325141cc406Sopenharmony_ci      _ConvertGammaTable (s->aGammaTable, abGamma);
1326141cc406Sopenharmony_ci    }
1327141cc406Sopenharmony_ci
1328141cc406Sopenharmony_ci  WriteGammaCalibTable (abGamma, abGamma, abGamma, abCalibTable, 0, 0,
1329141cc406Sopenharmony_ci                        &s->HWParams);
1330141cc406Sopenharmony_ci
1331141cc406Sopenharmony_ci  /* prepare the actual scan */
1332141cc406Sopenharmony_ci  if (!InitScan (&s->ScanParams, &s->HWParams))
1333141cc406Sopenharmony_ci    {
1334141cc406Sopenharmony_ci      DBG (DBG_MSG, "Invalid scan parameters\n");
1335141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
1336141cc406Sopenharmony_ci    }
1337141cc406Sopenharmony_ci
1338141cc406Sopenharmony_ci  /* init data pipe */
1339141cc406Sopenharmony_ci  s->DataPipe.iSkipLines = s->HWParams.iSkipLines;
1340141cc406Sopenharmony_ci  /* on the hp3400 and hp4300 we cannot set the top of the scan area (yet),
1341141cc406Sopenharmony_ci     so instead we just scan and throw away the data until the top */
1342141cc406Sopenharmony_ci  if (s->HWParams.fReg07)
1343141cc406Sopenharmony_ci    {
1344141cc406Sopenharmony_ci      s->DataPipe.iSkipLines +=
1345141cc406Sopenharmony_ci        MM_TO_PIXEL (s->aValues[optTLY].w + s->HWParams.iTopLeftY,
1346141cc406Sopenharmony_ci                     s->aValues[optDPI].w * iScaleDown);
1347141cc406Sopenharmony_ci    }
1348141cc406Sopenharmony_ci  s->iBytesLeft = 0;
1349141cc406Sopenharmony_ci  s->iPixelsPerLine = par.pixels_per_line;
1350141cc406Sopenharmony_ci
1351141cc406Sopenharmony_ci  /* hack */
1352141cc406Sopenharmony_ci  s->DataPipe.pabLineBuf = (unsigned char *) malloc (HW_PIXELS * 3);
1353141cc406Sopenharmony_ci  CircBufferInit (s->HWParams.iXferHandle, &s->DataPipe,
1354141cc406Sopenharmony_ci                  par.pixels_per_line, s->ScanParams.iHeight,
1355141cc406Sopenharmony_ci                  s->ScanParams.iLpi * s->HWParams.iSensorSkew / HW_LPI,
1356141cc406Sopenharmony_ci                  s->HWParams.iReversedHead, iScaleDown, iScaleDown);
1357141cc406Sopenharmony_ci
1358141cc406Sopenharmony_ci  s->fScanning = SANE_TRUE;
1359141cc406Sopenharmony_ci  s->fCancelled = SANE_FALSE;
1360141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
1361141cc406Sopenharmony_ci}
1362141cc406Sopenharmony_ci
1363141cc406Sopenharmony_ci
1364141cc406Sopenharmony_ciSANE_Status
1365141cc406Sopenharmony_cisane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len)
1366141cc406Sopenharmony_ci{
1367141cc406Sopenharmony_ci  TScanner *s;
1368141cc406Sopenharmony_ci  TDataPipe *p;
1369141cc406Sopenharmony_ci  TModeParam const *pMode;
1370141cc406Sopenharmony_ci
1371141cc406Sopenharmony_ci  DBG (DBG_MSG, "sane_read: buf=%p, maxlen=%d, ", (void *) buf, maxlen);
1372141cc406Sopenharmony_ci
1373141cc406Sopenharmony_ci  s = (TScanner *) h;
1374141cc406Sopenharmony_ci
1375141cc406Sopenharmony_ci  pMode = &modeParam[s->aValues[optMode].w];
1376141cc406Sopenharmony_ci
1377141cc406Sopenharmony_ci  /* sane_read only allowed after sane_start */
1378141cc406Sopenharmony_ci  if (!s->fScanning)
1379141cc406Sopenharmony_ci    {
1380141cc406Sopenharmony_ci      if (s->fCancelled)
1381141cc406Sopenharmony_ci        {
1382141cc406Sopenharmony_ci          DBG (DBG_MSG, "\n");
1383141cc406Sopenharmony_ci          DBG (DBG_MSG, "sane_read: sane_read cancelled\n");
1384141cc406Sopenharmony_ci          s->fCancelled = SANE_FALSE;
1385141cc406Sopenharmony_ci          return SANE_STATUS_CANCELLED;
1386141cc406Sopenharmony_ci        }
1387141cc406Sopenharmony_ci      else
1388141cc406Sopenharmony_ci        {
1389141cc406Sopenharmony_ci          DBG (DBG_ERR,
1390141cc406Sopenharmony_ci               "sane_read: sane_read only allowed after sane_start\n");
1391141cc406Sopenharmony_ci          return SANE_STATUS_INVAL;
1392141cc406Sopenharmony_ci        }
1393141cc406Sopenharmony_ci    }
1394141cc406Sopenharmony_ci
1395141cc406Sopenharmony_ci  p = &s->DataPipe;
1396141cc406Sopenharmony_ci
1397141cc406Sopenharmony_ci  /* anything left to read? */
1398141cc406Sopenharmony_ci  if ((s->iLinesLeft == 0) && (s->iBytesLeft == 0))
1399141cc406Sopenharmony_ci    {
1400141cc406Sopenharmony_ci      CircBufferExit (p);
1401141cc406Sopenharmony_ci      free (p->pabLineBuf);
1402141cc406Sopenharmony_ci      p->pabLineBuf = NULL;
1403141cc406Sopenharmony_ci      FinishScan (&s->HWParams);
1404141cc406Sopenharmony_ci      *len = 0;
1405141cc406Sopenharmony_ci      DBG (DBG_MSG, "\n");
1406141cc406Sopenharmony_ci      DBG (DBG_MSG, "sane_read: end of scan\n");
1407141cc406Sopenharmony_ci      s->fCancelled = SANE_FALSE;
1408141cc406Sopenharmony_ci      s->fScanning = SANE_FALSE;
1409141cc406Sopenharmony_ci      return SANE_STATUS_EOF;
1410141cc406Sopenharmony_ci    }
1411141cc406Sopenharmony_ci
1412141cc406Sopenharmony_ci  /* time to read the next line? */
1413141cc406Sopenharmony_ci  if (s->iBytesLeft == 0)
1414141cc406Sopenharmony_ci    {
1415141cc406Sopenharmony_ci      /* read a line from the transfer buffer */
1416141cc406Sopenharmony_ci      if (CircBufferGetLineEx (s->HWParams.iXferHandle, p, p->pabLineBuf,
1417141cc406Sopenharmony_ci                               s->HWParams.iReversedHead, SANE_TRUE))
1418141cc406Sopenharmony_ci        {
1419141cc406Sopenharmony_ci          pMode->adaptFormat (p->pabLineBuf, s->iPixelsPerLine,
1420141cc406Sopenharmony_ci                              s->aValues[optThreshold].w);
1421141cc406Sopenharmony_ci          s->iBytesLeft = pMode->bytesPerLine (s->iPixelsPerLine);
1422141cc406Sopenharmony_ci          s->iLinesLeft--;
1423141cc406Sopenharmony_ci        }
1424141cc406Sopenharmony_ci      /* stop scanning further, when the read action fails
1425141cc406Sopenharmony_ci         because we try read after the end of the buffer */
1426141cc406Sopenharmony_ci      else
1427141cc406Sopenharmony_ci        {
1428141cc406Sopenharmony_ci          FinishScan (&s->HWParams);
1429141cc406Sopenharmony_ci          CircBufferExit (p);
1430141cc406Sopenharmony_ci          free (p->pabLineBuf);
1431141cc406Sopenharmony_ci          p->pabLineBuf = NULL;
1432141cc406Sopenharmony_ci          *len = 0;
1433141cc406Sopenharmony_ci          DBG (DBG_MSG, "\n");
1434141cc406Sopenharmony_ci          DBG (DBG_MSG, "sane_read: read after end of buffer\n");
1435141cc406Sopenharmony_ci          s->fCancelled = SANE_FALSE;
1436141cc406Sopenharmony_ci          s->fScanning = SANE_FALSE;
1437141cc406Sopenharmony_ci          return SANE_STATUS_EOF;
1438141cc406Sopenharmony_ci        }
1439141cc406Sopenharmony_ci
1440141cc406Sopenharmony_ci    }
1441141cc406Sopenharmony_ci
1442141cc406Sopenharmony_ci  /* copy (part of) a line */
1443141cc406Sopenharmony_ci  *len = MIN (maxlen, s->iBytesLeft);
1444141cc406Sopenharmony_ci  memcpy (buf,
1445141cc406Sopenharmony_ci          &p->pabLineBuf[pMode->bytesPerLine (s->iPixelsPerLine) -
1446141cc406Sopenharmony_ci                         s->iBytesLeft], *len);
1447141cc406Sopenharmony_ci  s->iBytesLeft -= *len;
1448141cc406Sopenharmony_ci
1449141cc406Sopenharmony_ci  DBG (DBG_MSG, " read=%d    \n", *len);
1450141cc406Sopenharmony_ci
1451141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
1452141cc406Sopenharmony_ci}
1453141cc406Sopenharmony_ci
1454141cc406Sopenharmony_ci
1455141cc406Sopenharmony_civoid
1456141cc406Sopenharmony_cisane_cancel (SANE_Handle h)
1457141cc406Sopenharmony_ci{
1458141cc406Sopenharmony_ci  TScanner *s;
1459141cc406Sopenharmony_ci
1460141cc406Sopenharmony_ci  DBG (DBG_MSG, "sane_cancel\n");
1461141cc406Sopenharmony_ci
1462141cc406Sopenharmony_ci  s = (TScanner *) h;
1463141cc406Sopenharmony_ci  /* Make sure the scanner head returns home */
1464141cc406Sopenharmony_ci  FinishScan (&s->HWParams);
1465141cc406Sopenharmony_ci  /* delete allocated data */
1466141cc406Sopenharmony_ci  if (s->fScanning)
1467141cc406Sopenharmony_ci    {
1468141cc406Sopenharmony_ci      CircBufferExit (&s->DataPipe);
1469141cc406Sopenharmony_ci      free (s->DataPipe.pabLineBuf);
1470141cc406Sopenharmony_ci      s->DataPipe.pabLineBuf = NULL;
1471141cc406Sopenharmony_ci      DBG (DBG_MSG, "sane_cancel: freeing buffers\n");
1472141cc406Sopenharmony_ci    }
1473141cc406Sopenharmony_ci  s->fCancelled = SANE_TRUE;
1474141cc406Sopenharmony_ci  s->fScanning = SANE_FALSE;
1475141cc406Sopenharmony_ci}
1476141cc406Sopenharmony_ci
1477141cc406Sopenharmony_ci
1478141cc406Sopenharmony_ciSANE_Status
1479141cc406Sopenharmony_cisane_set_io_mode (SANE_Handle __sane_unused__ h, SANE_Bool m)
1480141cc406Sopenharmony_ci{
1481141cc406Sopenharmony_ci  DBG (DBG_MSG, "sane_set_io_mode %s\n", m ? "non-blocking" : "blocking");
1482141cc406Sopenharmony_ci
1483141cc406Sopenharmony_ci  if (m)
1484141cc406Sopenharmony_ci    {
1485141cc406Sopenharmony_ci      return SANE_STATUS_UNSUPPORTED;
1486141cc406Sopenharmony_ci    }
1487141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
1488141cc406Sopenharmony_ci}
1489141cc406Sopenharmony_ci
1490141cc406Sopenharmony_ci
1491141cc406Sopenharmony_ciSANE_Status
1492141cc406Sopenharmony_cisane_get_select_fd (SANE_Handle __sane_unused__ h, SANE_Int __sane_unused__ * fd)
1493141cc406Sopenharmony_ci{
1494141cc406Sopenharmony_ci  DBG (DBG_MSG, "sane_select_fd\n");
1495141cc406Sopenharmony_ci  return SANE_STATUS_UNSUPPORTED;
1496141cc406Sopenharmony_ci}
1497