1141cc406Sopenharmony_ci/* sane - Scanner Access Now Easy.
2141cc406Sopenharmony_ci
3141cc406Sopenharmony_ci   pieusb_specific.c
4141cc406Sopenharmony_ci
5141cc406Sopenharmony_ci   Copyright (C) 2012-2015 Jan Vleeshouwers, Michael Rickmann, Klaus Kaempf
6141cc406Sopenharmony_ci
7141cc406Sopenharmony_ci   This file is part of the SANE package.
8141cc406Sopenharmony_ci
9141cc406Sopenharmony_ci   This program is free software; you can redistribute it and/or
10141cc406Sopenharmony_ci   modify it under the terms of the GNU General Public License as
11141cc406Sopenharmony_ci   published by the Free Software Foundation; either version 2 of the
12141cc406Sopenharmony_ci   License, or (at your option) any later version.
13141cc406Sopenharmony_ci
14141cc406Sopenharmony_ci   This program is distributed in the hope that it will be useful, but
15141cc406Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
16141cc406Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17141cc406Sopenharmony_ci   General Public License for more details.
18141cc406Sopenharmony_ci
19141cc406Sopenharmony_ci   You should have received a copy of the GNU General Public License
20141cc406Sopenharmony_ci   along with this program.  If not, see <https://www.gnu.org/licenses/>.
21141cc406Sopenharmony_ci
22141cc406Sopenharmony_ci   As a special exception, the authors of SANE give permission for
23141cc406Sopenharmony_ci   additional uses of the libraries contained in this release of SANE.
24141cc406Sopenharmony_ci
25141cc406Sopenharmony_ci   The exception is that, if you link a SANE library with other files
26141cc406Sopenharmony_ci   to produce an executable, this does not by itself cause the
27141cc406Sopenharmony_ci   resulting executable to be covered by the GNU General Public
28141cc406Sopenharmony_ci   License.  Your use of that executable is in no way restricted on
29141cc406Sopenharmony_ci   account of linking the SANE library code into it.
30141cc406Sopenharmony_ci
31141cc406Sopenharmony_ci   This exception does not, however, invalidate any other reasons why
32141cc406Sopenharmony_ci   the executable file might be covered by the GNU General Public
33141cc406Sopenharmony_ci   License.
34141cc406Sopenharmony_ci
35141cc406Sopenharmony_ci   If you submit changes to SANE to the maintainers to be included in
36141cc406Sopenharmony_ci   a subsequent release, you agree by submitting the changes that
37141cc406Sopenharmony_ci   those changes may be distributed with this exception intact.
38141cc406Sopenharmony_ci
39141cc406Sopenharmony_ci   If you write modifications of your own for SANE, it is your choice
40141cc406Sopenharmony_ci   whether to permit this exception to apply to your modifications.
41141cc406Sopenharmony_ci   If you do not wish that, delete this exception notice.  */
42141cc406Sopenharmony_ci
43141cc406Sopenharmony_ci/* =========================================================================
44141cc406Sopenharmony_ci *
45141cc406Sopenharmony_ci * Various Pieusb backend specific functions
46141cc406Sopenharmony_ci *
47141cc406Sopenharmony_ci * Option handling, configuration file handling, post-processing
48141cc406Sopenharmony_ci *
49141cc406Sopenharmony_ci * ========================================================================= */
50141cc406Sopenharmony_ci
51141cc406Sopenharmony_ci#define DEBUG_DECLARE_ONLY
52141cc406Sopenharmony_ci#include "pieusb.h"
53141cc406Sopenharmony_ci
54141cc406Sopenharmony_ci#include <stdlib.h>
55141cc406Sopenharmony_ci#include <unistd.h>
56141cc406Sopenharmony_ci#include <string.h>
57141cc406Sopenharmony_ci#include "../include/sane/sane.h"
58141cc406Sopenharmony_ci#include "../include/sane/saneopts.h"
59141cc406Sopenharmony_ci#include "../include/sane/sanei_config.h"
60141cc406Sopenharmony_ci
61141cc406Sopenharmony_ci#include <errno.h>
62141cc406Sopenharmony_ci#include <math.h>
63141cc406Sopenharmony_ci#include <time.h>
64141cc406Sopenharmony_ci
65141cc406Sopenharmony_ci#include "pieusb_usb.h"
66141cc406Sopenharmony_ci#include "pieusb_scancmd.h"
67141cc406Sopenharmony_ci#include "pieusb_buffer.h"
68141cc406Sopenharmony_ci#include "pieusb_specific.h"
69141cc406Sopenharmony_ci
70141cc406Sopenharmony_ci/* Pieusb specific */
71141cc406Sopenharmony_ci
72141cc406Sopenharmony_ci/* sub to sanei_pieusb_find_device_callback() */
73141cc406Sopenharmony_cistatic SANE_Status pieusb_initialize_device_definition (Pieusb_Device_Definition* dev, Pieusb_Scanner_Properties* inq, const char* devicename, SANE_Word vendor_id, SANE_Word product_id);
74141cc406Sopenharmony_cistatic void pieusb_print_inquiry (Pieusb_Device_Definition * dev);
75141cc406Sopenharmony_ci
76141cc406Sopenharmony_ci/* sub to sane_start() */
77141cc406Sopenharmony_cistatic void pieusb_calculate_shading(struct Pieusb_Scanner *scanner, SANE_Byte* buffer);
78141cc406Sopenharmony_ci
79141cc406Sopenharmony_ci/* MR */
80141cc406Sopenharmony_ci/* sub to sanei_pieusb_post() */
81141cc406Sopenharmony_cistatic SANE_Status pieusb_write_pnm_file (char *filename, uint16_t *data, int depth, int channels, int pixels_per_line, int lines);
82141cc406Sopenharmony_ci
83141cc406Sopenharmony_ci/* Auxiliary */
84141cc406Sopenharmony_cistatic size_t max_string_size (SANE_String_Const const strings[]);
85141cc406Sopenharmony_cistatic double getGain(int gain);
86141cc406Sopenharmony_cistatic int getGainSetting(double gain);
87141cc406Sopenharmony_ci/*
88141cc406Sopenharmony_cistatic void updateGain(Pieusb_Scanner *scanner, int color_index);
89141cc406Sopenharmony_ci*/
90141cc406Sopenharmony_cistatic void updateGain2(Pieusb_Scanner *scanner, int color_index, double gain_increase);
91141cc406Sopenharmony_ci
92141cc406Sopenharmony_ci/* --------------------------------------------------------------------------
93141cc406Sopenharmony_ci *
94141cc406Sopenharmony_ci * SPECIFIC PIEUSB
95141cc406Sopenharmony_ci *
96141cc406Sopenharmony_ci * --------------------------------------------------------------------------*/
97141cc406Sopenharmony_ci
98141cc406Sopenharmony_ci/* Settings for byte order */
99141cc406Sopenharmony_ci#define SCAN_IMG_FMT_OKLINE          0x08
100141cc406Sopenharmony_ci#define SCAN_IMG_FMT_BLK_ONE         0x04
101141cc406Sopenharmony_ci#define SCAN_IMG_FMT_MOTOROLA        0x02
102141cc406Sopenharmony_ci#define SCAN_IMG_FMT_INTEL           0x01
103141cc406Sopenharmony_ci
104141cc406Sopenharmony_ci/* Settings for scanner capabilities */
105141cc406Sopenharmony_ci#define SCAN_CAP_PWRSAV              0x80
106141cc406Sopenharmony_ci#define SCAN_CAP_EXT_CAL             0x40
107141cc406Sopenharmony_ci#define SCAN_CAP_FAST_PREVIEW        0x10
108141cc406Sopenharmony_ci#define SCAN_CAP_DISABLE_CAL         0x08
109141cc406Sopenharmony_ci#define SCAN_CAP_SPEEDS              0x07
110141cc406Sopenharmony_ci
111141cc406Sopenharmony_ci/* Available scanner options */
112141cc406Sopenharmony_ci#define SCAN_OPT_DEV_MPCL            0x80
113141cc406Sopenharmony_ci#define SCAN_OPT_DEV_TP1             0x04
114141cc406Sopenharmony_ci#define SCAN_OPT_DEV_TP              0x02
115141cc406Sopenharmony_ci#define SCAN_OPT_DEV_ADF             0x01
116141cc406Sopenharmony_ci
117141cc406Sopenharmony_ci/* Options */
118141cc406Sopenharmony_ci#define SANE_NAME_EXPOSURE_R         "exposure-time-r"
119141cc406Sopenharmony_ci#define SANE_TITLE_EXPOSURE_R        "Exposure time red"
120141cc406Sopenharmony_ci#define SANE_DESC_EXPOSURE_R         "The time the red color filter of the CCD is exposed"
121141cc406Sopenharmony_ci#define SANE_NAME_EXPOSURE_G         "exposure-time-g"
122141cc406Sopenharmony_ci#define SANE_TITLE_EXPOSURE_G        "Exposure time green"
123141cc406Sopenharmony_ci#define SANE_DESC_EXPOSURE_G         "The time the green color filter of the CCD is exposed"
124141cc406Sopenharmony_ci#define SANE_NAME_EXPOSURE_B         "exposure-time-b"
125141cc406Sopenharmony_ci#define SANE_TITLE_EXPOSURE_B        "Exposure time blue"
126141cc406Sopenharmony_ci#define SANE_DESC_EXPOSURE_B         "The time the blue color filter of the CCD is exposed"
127141cc406Sopenharmony_ci#define SANE_NAME_EXPOSURE_I         "exposure-time-i"
128141cc406Sopenharmony_ci#define SANE_TITLE_EXPOSURE_I        "Exposure time infrared"
129141cc406Sopenharmony_ci#define SANE_DESC_EXPOSURE_I         "The time the infrared color filter of the CCD is exposed"
130141cc406Sopenharmony_ci#define SANE_EXPOSURE_DEFAULT        DEFAULT_EXPOSURE
131141cc406Sopenharmony_ci#if 1
132141cc406Sopenharmony_ci#define SANE_NAME_GAIN_R               "gain-r"
133141cc406Sopenharmony_ci#define SANE_TITLE_GAIN_R              "Gain red"
134141cc406Sopenharmony_ci#define SANE_DESC_GAIN_R               "The gain of the signal processor for red"
135141cc406Sopenharmony_ci#define SANE_NAME_GAIN_G               "gain-g"
136141cc406Sopenharmony_ci#define SANE_TITLE_GAIN_G              "Gain green"
137141cc406Sopenharmony_ci#define SANE_DESC_GAIN_G               "The gain of the signal processor for green"
138141cc406Sopenharmony_ci#define SANE_NAME_GAIN_B               "gain-b"
139141cc406Sopenharmony_ci#define SANE_TITLE_GAIN_B              "Gain blue"
140141cc406Sopenharmony_ci#define SANE_DESC_GAIN_B               "The gain of the signal processor for blue"
141141cc406Sopenharmony_ci#define SANE_NAME_GAIN_I               "gain-i"
142141cc406Sopenharmony_ci#define SANE_TITLE_GAIN_I              "Gain infrared"
143141cc406Sopenharmony_ci#define SANE_DESC_GAIN_I               "The gain of the signal processor for infrared"
144141cc406Sopenharmony_ci#define SANE_GAIN_DEFAULT            DEFAULT_GAIN
145141cc406Sopenharmony_ci
146141cc406Sopenharmony_ci#define SANE_NAME_OFFSET_R             "offset-r"
147141cc406Sopenharmony_ci#define SANE_TITLE_OFFSET_R            "Offset red"
148141cc406Sopenharmony_ci#define SANE_DESC_OFFSET_R             "The offset of the signal processor for red"
149141cc406Sopenharmony_ci#define SANE_NAME_OFFSET_G             "offset-g"
150141cc406Sopenharmony_ci#define SANE_TITLE_OFFSET_G            "Offset greed"
151141cc406Sopenharmony_ci#define SANE_DESC_OFFSET_G             "The offset of the signal processor for green"
152141cc406Sopenharmony_ci#define SANE_NAME_OFFSET_B             "offset-b"
153141cc406Sopenharmony_ci#define SANE_TITLE_OFFSET_B            "Offset blue"
154141cc406Sopenharmony_ci#define SANE_DESC_OFFSET_B             "The offset of the signal processor for blue"
155141cc406Sopenharmony_ci#define SANE_NAME_OFFSET_I             "offset-i"
156141cc406Sopenharmony_ci#define SANE_TITLE_OFFSET_I            "Offset infrared"
157141cc406Sopenharmony_ci#define SANE_DESC_OFFSET_I             "The offset of the signal processor for infrared"
158141cc406Sopenharmony_ci#define SANE_OFFSET_DEFAULT          DEFAULT_OFFSET
159141cc406Sopenharmony_ci#else
160141cc406Sopenharmony_ci#define SANE_NAME_GAIN               "gain"
161141cc406Sopenharmony_ci#define SANE_TITLE_GAIN              "Gain"
162141cc406Sopenharmony_ci#define SANE_DESC_GAIN               "The gain of the signal processor for the 4 CCD color filters (R,G,B,I)"
163141cc406Sopenharmony_ci#define SANE_GAIN_DEFAULT            0x13
164141cc406Sopenharmony_ci
165141cc406Sopenharmony_ci#define SANE_NAME_OFFSET             "offset"
166141cc406Sopenharmony_ci#define SANE_TITLE_OFFSET            "Offset"
167141cc406Sopenharmony_ci#define SANE_DESC_OFFSET             "The offset of the signal processor for the 4 CCD color filters (R,G,B,I)"
168141cc406Sopenharmony_ci#define SANE_OFFSET_DEFAULT          0
169141cc406Sopenharmony_ci#endif
170141cc406Sopenharmony_ci#define min(a,b) (((a)<(b))?(a):(b))
171141cc406Sopenharmony_ci#define max(a,b) (((a)>(b))?(a):(b))
172141cc406Sopenharmony_ci
173141cc406Sopenharmony_cistatic const SANE_Range percentage_range_100 = {
174141cc406Sopenharmony_ci  0 << SANE_FIXED_SCALE_SHIFT,	  /* minimum */
175141cc406Sopenharmony_ci  100 << SANE_FIXED_SCALE_SHIFT,  /* maximum */
176141cc406Sopenharmony_ci  0 << SANE_FIXED_SCALE_SHIFT	  /* quantization */
177141cc406Sopenharmony_ci};
178141cc406Sopenharmony_ci
179141cc406Sopenharmony_ci/* From the firmware disassembly */
180141cc406Sopenharmony_cistatic const SANE_Range gain_range = {
181141cc406Sopenharmony_ci  0,	  /* minimum */
182141cc406Sopenharmony_ci  63,     /* maximum */
183141cc406Sopenharmony_ci  0	  /* quantization */
184141cc406Sopenharmony_ci};
185141cc406Sopenharmony_ci
186141cc406Sopenharmony_ci/* From the firmware disassembly */
187141cc406Sopenharmony_cistatic const SANE_Range offset_range = {
188141cc406Sopenharmony_ci  0,      /* minimum */
189141cc406Sopenharmony_ci  255,    /* maximum */
190141cc406Sopenharmony_ci  0	  /* quantization */
191141cc406Sopenharmony_ci};
192141cc406Sopenharmony_ci
193141cc406Sopenharmony_cistatic const double gains[] = {
194141cc406Sopenharmony_ci1.000, 1.075, 1.154, 1.251, 1.362, 1.491, 1.653, /*  0,  5, 10, 15, 20, 25, 30 */
195141cc406Sopenharmony_ci1.858, 2.115, 2.458, 2.935, 3.638, 4.627         /* 35, 40, 45, 50, 55, 60 */
196141cc406Sopenharmony_ci};
197141cc406Sopenharmony_ci
198141cc406Sopenharmony_ci/**
199141cc406Sopenharmony_ci * Callback called whenever a connected USB device reports a supported vendor
200141cc406Sopenharmony_ci * and product id combination.
201141cc406Sopenharmony_ci * Used by sane_init() and by sane_open().
202141cc406Sopenharmony_ci *
203141cc406Sopenharmony_ci * @param name Device name which has required vendor and product id
204141cc406Sopenharmony_ci * @return SANE_STATUS_GOOD
205141cc406Sopenharmony_ci */
206141cc406Sopenharmony_ciSANE_Status
207141cc406Sopenharmony_cisanei_pieusb_find_device_callback (const char *devicename)
208141cc406Sopenharmony_ci{
209141cc406Sopenharmony_ci    struct Pieusb_Command_Status status;
210141cc406Sopenharmony_ci    SANE_Status r;
211141cc406Sopenharmony_ci    Pieusb_Device_Definition *dev;
212141cc406Sopenharmony_ci    int device_number; /* index in usb devices list maintained by sani_usb */
213141cc406Sopenharmony_ci    Pieusb_Scanner_Properties inq;
214141cc406Sopenharmony_ci    int retry;
215141cc406Sopenharmony_ci
216141cc406Sopenharmony_ci    DBG (DBG_info_proc, "sanei_pieusb_find_device_callback: %s\n", devicename);
217141cc406Sopenharmony_ci
218141cc406Sopenharmony_ci    /* Check if device is present in the Pieusb device list */
219141cc406Sopenharmony_ci    for (dev = pieusb_definition_list_head; dev; dev = dev->next) {
220141cc406Sopenharmony_ci        if (strcmp (dev->sane.name, devicename) == 0) {
221141cc406Sopenharmony_ci	    return SANE_STATUS_GOOD;
222141cc406Sopenharmony_ci        }
223141cc406Sopenharmony_ci    }
224141cc406Sopenharmony_ci
225141cc406Sopenharmony_ci    /* If not, create a new device struct */
226141cc406Sopenharmony_ci    dev = malloc (sizeof (*dev));
227141cc406Sopenharmony_ci    if (!dev) {
228141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
229141cc406Sopenharmony_ci    }
230141cc406Sopenharmony_ci
231141cc406Sopenharmony_ci    /* Get device number: index of the device in the sanei_usb devices list */
232141cc406Sopenharmony_ci    r = sanei_usb_open (devicename, &device_number);
233141cc406Sopenharmony_ci    if (r != SANE_STATUS_GOOD) {
234141cc406Sopenharmony_ci        free (dev);
235141cc406Sopenharmony_ci        DBG (DBG_error, "sanei_pieusb_find_device_callback: sanei_usb_open failed for device %s: %s\n",devicename,sane_strstatus(r));
236141cc406Sopenharmony_ci        return r;
237141cc406Sopenharmony_ci    }
238141cc406Sopenharmony_ci
239141cc406Sopenharmony_ci    /* Get device properties */
240141cc406Sopenharmony_ci
241141cc406Sopenharmony_ci    retry = 2;
242141cc406Sopenharmony_ci    while (retry > 0) {
243141cc406Sopenharmony_ci      retry--;
244141cc406Sopenharmony_ci      /* get inquiry data length */
245141cc406Sopenharmony_ci      sanei_pieusb_cmd_inquiry (device_number, &inq, 5, &status);
246141cc406Sopenharmony_ci      if (status.pieusb_status == PIEUSB_STATUS_GOOD) {
247141cc406Sopenharmony_ci	break;
248141cc406Sopenharmony_ci      }
249141cc406Sopenharmony_ci      else if (status.pieusb_status == PIEUSB_STATUS_IO_ERROR) {
250141cc406Sopenharmony_ci	if (retry > 0) {
251141cc406Sopenharmony_ci	  DBG (DBG_info_proc, "inquiry failed, resetting usb\n");
252141cc406Sopenharmony_ci	  if (sanei_pieusb_usb_reset(device_number) == SANE_STATUS_GOOD) {
253141cc406Sopenharmony_ci	    continue; /* retry after IEEE1284 reset */
254141cc406Sopenharmony_ci	  }
255141cc406Sopenharmony_ci	  if (sanei_usb_reset(device_number) == SANE_STATUS_GOOD) {
256141cc406Sopenharmony_ci	    continue; /* retry after USB reset */
257141cc406Sopenharmony_ci	  }
258141cc406Sopenharmony_ci	}
259141cc406Sopenharmony_ci      }
260141cc406Sopenharmony_ci      free (dev);
261141cc406Sopenharmony_ci      DBG (DBG_error, "sanei_pieusb_find_device_callback: get scanner properties (5 bytes) failed with %d\n", status.pieusb_status);
262141cc406Sopenharmony_ci      sanei_usb_close (device_number);
263141cc406Sopenharmony_ci      return sanei_pieusb_convert_status (status.pieusb_status);
264141cc406Sopenharmony_ci    }
265141cc406Sopenharmony_ci    /* get full inquiry data */
266141cc406Sopenharmony_ci    sanei_pieusb_cmd_inquiry(device_number, &inq, inq.additionalLength+4, &status);
267141cc406Sopenharmony_ci    if (status.pieusb_status != PIEUSB_STATUS_GOOD) {
268141cc406Sopenharmony_ci        free (dev);
269141cc406Sopenharmony_ci        DBG (DBG_error, "sanei_pieusb_find_device_callback: get scanner properties failed\n");
270141cc406Sopenharmony_ci        sanei_usb_close (device_number);
271141cc406Sopenharmony_ci        return sanei_pieusb_convert_status (status.pieusb_status);
272141cc406Sopenharmony_ci    }
273141cc406Sopenharmony_ci
274141cc406Sopenharmony_ci    /* Close the device again */
275141cc406Sopenharmony_ci    sanei_usb_close(device_number);
276141cc406Sopenharmony_ci
277141cc406Sopenharmony_ci    /* Initialize device definition */
278141cc406Sopenharmony_ci    r = pieusb_initialize_device_definition(dev, &inq, devicename, pieusb_supported_usb_device.vendor, pieusb_supported_usb_device.product);
279141cc406Sopenharmony_ci    if (r != SANE_STATUS_GOOD) {
280141cc406Sopenharmony_ci      return r;
281141cc406Sopenharmony_ci    }
282141cc406Sopenharmony_ci
283141cc406Sopenharmony_ci    /* Output */
284141cc406Sopenharmony_ci    pieusb_print_inquiry (dev);
285141cc406Sopenharmony_ci
286141cc406Sopenharmony_ci    /* Check model number */
287141cc406Sopenharmony_ci    if (inq.model != pieusb_supported_usb_device.model) {
288141cc406Sopenharmony_ci        free (dev);
289141cc406Sopenharmony_ci        DBG (DBG_error, "sanei_pieusb_find_device_callback: wrong model number %d\n", inq.model);
290141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
291141cc406Sopenharmony_ci    }
292141cc406Sopenharmony_ci
293141cc406Sopenharmony_ci    dev->flags = pieusb_supported_usb_device.flags;
294141cc406Sopenharmony_ci
295141cc406Sopenharmony_ci    /* Found a supported scanner, put it in the definitions list*/
296141cc406Sopenharmony_ci    DBG (DBG_info_proc, "sanei_pieusb_find_device_callback: success\n");
297141cc406Sopenharmony_ci    dev->next = pieusb_definition_list_head;
298141cc406Sopenharmony_ci    pieusb_definition_list_head = dev;
299141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
300141cc406Sopenharmony_ci}
301141cc406Sopenharmony_ci
302141cc406Sopenharmony_ci/**
303141cc406Sopenharmony_ci * Full initialization of a Pieusb_Device structure from INQUIRY data.
304141cc406Sopenharmony_ci * The function is used in find_device_callback(), so when sane_init() or
305141cc406Sopenharmony_ci * sane_open() is called.
306141cc406Sopenharmony_ci *
307141cc406Sopenharmony_ci * @param dev
308141cc406Sopenharmony_ci */
309141cc406Sopenharmony_cistatic SANE_Status
310141cc406Sopenharmony_cipieusb_initialize_device_definition (Pieusb_Device_Definition* dev, Pieusb_Scanner_Properties* inq, const char* devicename,
311141cc406Sopenharmony_ci        SANE_Word vendor_id, SANE_Word product_id)
312141cc406Sopenharmony_ci{
313141cc406Sopenharmony_ci    char *pp, *buf;
314141cc406Sopenharmony_ci
315141cc406Sopenharmony_ci    /* Initialize device definition */
316141cc406Sopenharmony_ci    dev->next = NULL;
317141cc406Sopenharmony_ci    dev->sane.name = strdup(devicename);
318141cc406Sopenharmony_ci
319141cc406Sopenharmony_ci    /* Create 0-terminated string without trailing spaces for vendor */
320141cc406Sopenharmony_ci    buf = malloc(9);
321141cc406Sopenharmony_ci    if (buf == NULL)
322141cc406Sopenharmony_ci      return SANE_STATUS_NO_MEM;
323141cc406Sopenharmony_ci    memcpy(buf, inq->vendor, 8);
324141cc406Sopenharmony_ci    pp = buf + 8;
325141cc406Sopenharmony_ci    *pp-- = '\0';
326141cc406Sopenharmony_ci    while (*pp == ' ') *pp-- = '\0';
327141cc406Sopenharmony_ci    dev->sane.vendor = buf;
328141cc406Sopenharmony_ci
329141cc406Sopenharmony_ci    /* Create 0-terminated string without trailing spaces for model */
330141cc406Sopenharmony_ci    buf = malloc(17);
331141cc406Sopenharmony_ci    if (buf == NULL)
332141cc406Sopenharmony_ci      return SANE_STATUS_NO_MEM;
333141cc406Sopenharmony_ci    memcpy(buf, inq->product, 16);
334141cc406Sopenharmony_ci    pp = buf + 16;
335141cc406Sopenharmony_ci    *pp-- = '\0';
336141cc406Sopenharmony_ci    while (*pp == ' ') *pp-- = '\0';
337141cc406Sopenharmony_ci    dev->sane.model = buf;
338141cc406Sopenharmony_ci
339141cc406Sopenharmony_ci    dev->sane.type = "film scanner";
340141cc406Sopenharmony_ci    dev->vendorId = vendor_id;
341141cc406Sopenharmony_ci    dev->productId = product_id;
342141cc406Sopenharmony_ci
343141cc406Sopenharmony_ci    /* Create 0-terminated strings without trailing spaces for revision */
344141cc406Sopenharmony_ci    buf = malloc(5);
345141cc406Sopenharmony_ci    if (buf == NULL)
346141cc406Sopenharmony_ci      return SANE_STATUS_NO_MEM;
347141cc406Sopenharmony_ci    memcpy(buf, inq->productRevision, 4);
348141cc406Sopenharmony_ci    pp = buf + 4;
349141cc406Sopenharmony_ci    *pp-- = '\0';
350141cc406Sopenharmony_ci    while (*pp == ' ') *pp-- = '\0';
351141cc406Sopenharmony_ci    dev->version = buf;
352141cc406Sopenharmony_ci
353141cc406Sopenharmony_ci    dev->model = inq->model;
354141cc406Sopenharmony_ci
355141cc406Sopenharmony_ci    /* Maximum resolution values */
356141cc406Sopenharmony_ci    dev->maximum_resolution_x = inq->maxResolutionX;
357141cc406Sopenharmony_ci    dev->maximum_resolution_y = inq->maxResolutionY;
358141cc406Sopenharmony_ci    if (dev->maximum_resolution_y < 256) {
359141cc406Sopenharmony_ci        /* y res is a multiplier */
360141cc406Sopenharmony_ci        dev->maximum_resolution = dev->maximum_resolution_x;
361141cc406Sopenharmony_ci        dev->maximum_resolution_x *= dev->maximum_resolution_y;
362141cc406Sopenharmony_ci        dev->maximum_resolution_y = dev->maximum_resolution_x;
363141cc406Sopenharmony_ci    } else {
364141cc406Sopenharmony_ci      /* y res really is resolution */
365141cc406Sopenharmony_ci      dev->maximum_resolution = min (dev->maximum_resolution_x, dev->maximum_resolution_y);
366141cc406Sopenharmony_ci    }
367141cc406Sopenharmony_ci
368141cc406Sopenharmony_ci    /* Geometry */
369141cc406Sopenharmony_ci    dev->scan_bed_width = (double) inq->maxScanWidth / dev->maximum_resolution;
370141cc406Sopenharmony_ci    dev->scan_bed_height = (double) inq->maxScanHeight / dev->maximum_resolution;
371141cc406Sopenharmony_ci    dev->slide_top_left_x = inq->x0;
372141cc406Sopenharmony_ci    dev->slide_top_left_y = inq->y0;
373141cc406Sopenharmony_ci    dev->slide_width = (double) (inq->x1 - inq->x0) / dev->maximum_resolution;
374141cc406Sopenharmony_ci    dev->slide_height = (double) (inq->y1 - inq->y0) / dev->maximum_resolution;
375141cc406Sopenharmony_ci
376141cc406Sopenharmony_ci    /* Integer and bit-encoded properties */
377141cc406Sopenharmony_ci    dev->halftone_patterns = inq->halftones & 0x0f;
378141cc406Sopenharmony_ci    dev->color_filters = inq->filters;
379141cc406Sopenharmony_ci    dev->color_depths = inq->colorDepths;
380141cc406Sopenharmony_ci    dev->color_formats = inq->colorFormat;
381141cc406Sopenharmony_ci    dev->image_formats = inq->imageFormat;
382141cc406Sopenharmony_ci    dev->scan_capabilities = inq->scanCapability;
383141cc406Sopenharmony_ci    dev->optional_devices = inq->optionalDevices;
384141cc406Sopenharmony_ci    dev->enhancements = inq->enhancements;
385141cc406Sopenharmony_ci    dev->gamma_bits = inq->gammaBits;
386141cc406Sopenharmony_ci    dev->fast_preview_resolution = inq->previewScanResolution;
387141cc406Sopenharmony_ci    dev->minimum_highlight = inq->minumumHighlight;
388141cc406Sopenharmony_ci    dev->maximum_shadow = inq->maximumShadow;
389141cc406Sopenharmony_ci    dev->calibration_equation = inq->calibrationEquation;
390141cc406Sopenharmony_ci    dev->minimum_exposure = inq->minimumExposure;
391141cc406Sopenharmony_ci    dev->maximum_exposure = inq->maximumExposure*4; /* *4 to solve the strange situation that the default value is out of range */
392141cc406Sopenharmony_ci
393141cc406Sopenharmony_ci    dev->x0 = inq->x0;
394141cc406Sopenharmony_ci    dev->y0 = inq->y0;
395141cc406Sopenharmony_ci    dev->x1 = inq->x1;
396141cc406Sopenharmony_ci    dev->y1 = inq->y1;
397141cc406Sopenharmony_ci    dev->production = strndup(inq->production, 4);
398141cc406Sopenharmony_ci    dev->timestamp = strndup(inq->timestamp, 20);
399141cc406Sopenharmony_ci    dev->signature = (char *)strndup((char *)inq->signature, 40);
400141cc406Sopenharmony_ci
401141cc406Sopenharmony_ci    /* Ranges for various quantities */
402141cc406Sopenharmony_ci    dev->x_range.min = SANE_FIX (0);
403141cc406Sopenharmony_ci    dev->x_range.quant = SANE_FIX (0);
404141cc406Sopenharmony_ci    dev->x_range.max = SANE_FIX (dev->scan_bed_width * MM_PER_INCH);
405141cc406Sopenharmony_ci
406141cc406Sopenharmony_ci    dev->y_range.min = SANE_FIX (0);
407141cc406Sopenharmony_ci    dev->y_range.quant = SANE_FIX (0);
408141cc406Sopenharmony_ci    dev->y_range.max = SANE_FIX (dev->scan_bed_height * MM_PER_INCH);
409141cc406Sopenharmony_ci
410141cc406Sopenharmony_ci    dev->dpi_range.min = SANE_FIX (25);
411141cc406Sopenharmony_ci    dev->dpi_range.quant = SANE_FIX (1);
412141cc406Sopenharmony_ci    dev->dpi_range.max = SANE_FIX (max (dev->maximum_resolution_x, dev->maximum_resolution_y));
413141cc406Sopenharmony_ci
414141cc406Sopenharmony_ci    dev->shadow_range.min = SANE_FIX (0);
415141cc406Sopenharmony_ci    dev->shadow_range.quant = SANE_FIX (1);
416141cc406Sopenharmony_ci    dev->shadow_range.max = SANE_FIX (dev->maximum_shadow);
417141cc406Sopenharmony_ci
418141cc406Sopenharmony_ci    dev->highlight_range.min = SANE_FIX (dev->minimum_highlight);
419141cc406Sopenharmony_ci    dev->highlight_range.quant = SANE_FIX (1);
420141cc406Sopenharmony_ci    dev->highlight_range.max = SANE_FIX (100);
421141cc406Sopenharmony_ci
422141cc406Sopenharmony_ci    dev->exposure_range.min = dev->minimum_exposure;
423141cc406Sopenharmony_ci    dev->exposure_range.quant = 1;
424141cc406Sopenharmony_ci    dev->exposure_range.max = dev->maximum_exposure;
425141cc406Sopenharmony_ci
426141cc406Sopenharmony_ci    dev->dust_range.min = 0;
427141cc406Sopenharmony_ci    dev->dust_range.quant = 1;
428141cc406Sopenharmony_ci    dev->dust_range.max = 100;
429141cc406Sopenharmony_ci
430141cc406Sopenharmony_ci    /* Enumerated ranges vor various quantities */
431141cc406Sopenharmony_ci    /*TODO: create from inq->filters */
432141cc406Sopenharmony_ci    dev->scan_mode_list[0] = SANE_VALUE_SCAN_MODE_LINEART;
433141cc406Sopenharmony_ci    dev->scan_mode_list[1] = SANE_VALUE_SCAN_MODE_HALFTONE;
434141cc406Sopenharmony_ci    dev->scan_mode_list[2] = SANE_VALUE_SCAN_MODE_GRAY;
435141cc406Sopenharmony_ci    dev->scan_mode_list[3] = SANE_VALUE_SCAN_MODE_COLOR;
436141cc406Sopenharmony_ci    dev->scan_mode_list[4] = SANE_VALUE_SCAN_MODE_RGBI;
437141cc406Sopenharmony_ci    dev->scan_mode_list[5] = 0;
438141cc406Sopenharmony_ci
439141cc406Sopenharmony_ci    dev->calibration_mode_list[0] = SCAN_CALIBRATION_DEFAULT;
440141cc406Sopenharmony_ci    dev->calibration_mode_list[1] = SCAN_CALIBRATION_AUTO;
441141cc406Sopenharmony_ci    dev->calibration_mode_list[2] = SCAN_CALIBRATION_PREVIEW;
442141cc406Sopenharmony_ci    dev->calibration_mode_list[3] = SCAN_CALIBRATION_OPTIONS;
443141cc406Sopenharmony_ci    dev->calibration_mode_list[4] = 0;
444141cc406Sopenharmony_ci
445141cc406Sopenharmony_ci    dev->gain_adjust_list[0] = SCAN_GAIN_ADJUST_03;
446141cc406Sopenharmony_ci    dev->gain_adjust_list[1] = SCAN_GAIN_ADJUST_05;
447141cc406Sopenharmony_ci    dev->gain_adjust_list[2] = SCAN_GAIN_ADJUST_08;
448141cc406Sopenharmony_ci    dev->gain_adjust_list[3] = SCAN_GAIN_ADJUST_10;
449141cc406Sopenharmony_ci    dev->gain_adjust_list[4] = SCAN_GAIN_ADJUST_12;
450141cc406Sopenharmony_ci    dev->gain_adjust_list[5] = SCAN_GAIN_ADJUST_16;
451141cc406Sopenharmony_ci    dev->gain_adjust_list[6] = SCAN_GAIN_ADJUST_19;
452141cc406Sopenharmony_ci    dev->gain_adjust_list[7] = SCAN_GAIN_ADJUST_24;
453141cc406Sopenharmony_ci    dev->gain_adjust_list[8] = SCAN_GAIN_ADJUST_30;
454141cc406Sopenharmony_ci    dev->gain_adjust_list[9] = 0;
455141cc406Sopenharmony_ci
456141cc406Sopenharmony_ci    /*TODO: create from inq->colorDepths? Maybe not: didn't experiment with
457141cc406Sopenharmony_ci     * 4 and 12 bit depths. Don;t know how they behave. */
458141cc406Sopenharmony_ci    dev->bpp_list[0] = 3; /* count */
459141cc406Sopenharmony_ci    dev->bpp_list[1] = 1;
460141cc406Sopenharmony_ci    dev->bpp_list[2] = 8;
461141cc406Sopenharmony_ci    dev->bpp_list[3] = 16;
462141cc406Sopenharmony_ci
463141cc406Sopenharmony_ci    /* Infrared */
464141cc406Sopenharmony_ci    dev->ir_sw_list[0] = "None";
465141cc406Sopenharmony_ci    dev->ir_sw_list[1] = "Reduce red overlap";
466141cc406Sopenharmony_ci    dev->ir_sw_list[2] = "Remove dirt";
467141cc406Sopenharmony_ci    dev->ir_sw_list[3] = 0;
468141cc406Sopenharmony_ci
469141cc406Sopenharmony_ci    dev->grain_sw_list[0] = 4;
470141cc406Sopenharmony_ci    dev->grain_sw_list[1] = 0;
471141cc406Sopenharmony_ci    dev->grain_sw_list[2] = 1;
472141cc406Sopenharmony_ci    dev->grain_sw_list[3] = 2;
473141cc406Sopenharmony_ci    dev->grain_sw_list[4] = 3;
474141cc406Sopenharmony_ci    dev->grain_sw_list[5] = 0;
475141cc406Sopenharmony_ci
476141cc406Sopenharmony_ci    dev->crop_sw_list[0] = "None";
477141cc406Sopenharmony_ci    dev->crop_sw_list[1] = "Outside";
478141cc406Sopenharmony_ci    dev->crop_sw_list[2] = "Inside";
479141cc406Sopenharmony_ci    dev->crop_sw_list[3] = 0;
480141cc406Sopenharmony_ci
481141cc406Sopenharmony_ci    /* halftone_list */
482141cc406Sopenharmony_ci    dev->halftone_list[0] = "53lpi 45d ROUND"; /* 8x8 pattern */
483141cc406Sopenharmony_ci    dev->halftone_list[1] = "70lpi 45d ROUND"; /* 6x6 pattern */
484141cc406Sopenharmony_ci    dev->halftone_list[2] = "75lpi Hori. Line"; /* 4x4 pattern */
485141cc406Sopenharmony_ci    dev->halftone_list[3] = "4X4 BAYER"; /* 4x4 pattern */
486141cc406Sopenharmony_ci    dev->halftone_list[4] = "4X4 SCROLL"; /* 4x4 pattern */
487141cc406Sopenharmony_ci    dev->halftone_list[5] = "5x5 26 Levels"; /* 5x5 pattern */
488141cc406Sopenharmony_ci    dev->halftone_list[6] = "4x4 SQUARE"; /* 4x4 pattern */
489141cc406Sopenharmony_ci    dev->halftone_list[7] = "5x5 TILE"; /* 5x5 pattern */
490141cc406Sopenharmony_ci    dev->halftone_list[8] = 0;
491141cc406Sopenharmony_ci
492141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
493141cc406Sopenharmony_ci}
494141cc406Sopenharmony_ci
495141cc406Sopenharmony_ci/**
496141cc406Sopenharmony_ci * Output device definition.
497141cc406Sopenharmony_ci * The function is used in find_device_callback(), so when sane_init() or
498141cc406Sopenharmony_ci * sane_open() is called.
499141cc406Sopenharmony_ci *
500141cc406Sopenharmony_ci * @param dev Device to output
501141cc406Sopenharmony_ci */
502141cc406Sopenharmony_cistatic void
503141cc406Sopenharmony_cipieusb_print_inquiry (Pieusb_Device_Definition * dev)
504141cc406Sopenharmony_ci{
505141cc406Sopenharmony_ci  DBG (DBG_inquiry, "INQUIRY:\n");
506141cc406Sopenharmony_ci  DBG (DBG_inquiry, "========\n");
507141cc406Sopenharmony_ci  DBG (DBG_inquiry, "\n");
508141cc406Sopenharmony_ci  DBG (DBG_inquiry, "vendor........................: '%s'\n", dev->sane.vendor);
509141cc406Sopenharmony_ci  DBG (DBG_inquiry, "product.......................: '%s'\n", dev->sane.model);
510141cc406Sopenharmony_ci  DBG (DBG_inquiry, "model  .......................: 0x%04x\n", dev->model);
511141cc406Sopenharmony_ci  DBG (DBG_inquiry, "version.......................: '%s'\n", dev->version);
512141cc406Sopenharmony_ci
513141cc406Sopenharmony_ci  DBG (DBG_inquiry, "X resolution..................: %d dpi\n",
514141cc406Sopenharmony_ci       dev->maximum_resolution_x);
515141cc406Sopenharmony_ci  DBG (DBG_inquiry, "Y resolution..................: %d dpi\n",
516141cc406Sopenharmony_ci       dev->maximum_resolution_y);
517141cc406Sopenharmony_ci  DBG (DBG_inquiry, "pixel resolution..............: %d dpi\n",
518141cc406Sopenharmony_ci       dev->maximum_resolution);
519141cc406Sopenharmony_ci  DBG (DBG_inquiry, "fb width......................: %f in\n",
520141cc406Sopenharmony_ci       dev->scan_bed_width);
521141cc406Sopenharmony_ci  DBG (DBG_inquiry, "fb length.....................: %f in\n",
522141cc406Sopenharmony_ci       dev->scan_bed_height);
523141cc406Sopenharmony_ci
524141cc406Sopenharmony_ci  DBG (DBG_inquiry, "transparency width............: %f in\n",
525141cc406Sopenharmony_ci       dev->slide_width);
526141cc406Sopenharmony_ci  DBG (DBG_inquiry, "transparency length...........: %f in\n",
527141cc406Sopenharmony_ci       dev->slide_height);
528141cc406Sopenharmony_ci  DBG (DBG_inquiry, "transparency offset...........: %d,%d\n",
529141cc406Sopenharmony_ci       dev->slide_top_left_x, dev->slide_top_left_y);
530141cc406Sopenharmony_ci
531141cc406Sopenharmony_ci  DBG (DBG_inquiry, "# of halftones................: %d\n",
532141cc406Sopenharmony_ci       dev->halftone_patterns);
533141cc406Sopenharmony_ci
534141cc406Sopenharmony_ci  DBG (DBG_inquiry, "One pass color................: %s\n",
535141cc406Sopenharmony_ci       dev->color_filters & SCAN_ONE_PASS_COLOR ? "yes" : "no");
536141cc406Sopenharmony_ci
537141cc406Sopenharmony_ci  DBG (DBG_inquiry, "Filters.......................: %s%s%s%s%s (%02x)\n",
538141cc406Sopenharmony_ci       dev->color_filters & SCAN_FILTER_INFRARED ? "Infrared " : "",
539141cc406Sopenharmony_ci       dev->color_filters & SCAN_FILTER_RED ? "Red " : "",
540141cc406Sopenharmony_ci       dev->color_filters & SCAN_FILTER_GREEN ? "Green " : "",
541141cc406Sopenharmony_ci       dev->color_filters & SCAN_FILTER_BLUE ? "Blue " : "",
542141cc406Sopenharmony_ci       dev->color_filters & SCAN_FILTER_NEUTRAL ? "Neutral " : "",
543141cc406Sopenharmony_ci       dev->color_filters);
544141cc406Sopenharmony_ci
545141cc406Sopenharmony_ci  DBG (DBG_inquiry, "Color depths..................: %s%s%s%s%s%s (%02x)\n",
546141cc406Sopenharmony_ci       dev->color_depths & SCAN_COLOR_DEPTH_16 ? "16 bit " : "",
547141cc406Sopenharmony_ci       dev->color_depths & SCAN_COLOR_DEPTH_12 ? "12 bit " : "",
548141cc406Sopenharmony_ci       dev->color_depths & SCAN_COLOR_DEPTH_10 ? "10 bit " : "",
549141cc406Sopenharmony_ci       dev->color_depths & SCAN_COLOR_DEPTH_8 ? "8 bit " : "",
550141cc406Sopenharmony_ci       dev->color_depths & SCAN_COLOR_DEPTH_4 ? "4 bit " : "",
551141cc406Sopenharmony_ci       dev->color_depths & SCAN_COLOR_DEPTH_1 ? "1 bit " : "",
552141cc406Sopenharmony_ci       dev->color_depths);
553141cc406Sopenharmony_ci
554141cc406Sopenharmony_ci  DBG (DBG_inquiry, "Color Format..................: %s%s%s (%02x)\n",
555141cc406Sopenharmony_ci       dev->color_formats & SCAN_COLOR_FORMAT_INDEX ? "Indexed " : "",
556141cc406Sopenharmony_ci       dev->color_formats & SCAN_COLOR_FORMAT_LINE ? "Line " : "",
557141cc406Sopenharmony_ci       dev->color_formats & SCAN_COLOR_FORMAT_PIXEL ? "Pixel " : "",
558141cc406Sopenharmony_ci       dev->color_formats);
559141cc406Sopenharmony_ci
560141cc406Sopenharmony_ci  DBG (DBG_inquiry, "Image Format..................: %s%s%s%s (%02x)\n",
561141cc406Sopenharmony_ci       dev->image_formats & SCAN_IMG_FMT_OKLINE ? "OKLine " : "",
562141cc406Sopenharmony_ci       dev->image_formats & SCAN_IMG_FMT_BLK_ONE ? "BlackOne " : "",
563141cc406Sopenharmony_ci       dev->image_formats & SCAN_IMG_FMT_MOTOROLA ? "Motorola " : "",
564141cc406Sopenharmony_ci       dev->image_formats & SCAN_IMG_FMT_INTEL ? "Intel" : "",
565141cc406Sopenharmony_ci       dev->image_formats);
566141cc406Sopenharmony_ci
567141cc406Sopenharmony_ci  DBG (DBG_inquiry,
568141cc406Sopenharmony_ci       "Scan Capability...............: %s%s%s%s%d speeds (%02x)\n",
569141cc406Sopenharmony_ci       dev->scan_capabilities & SCAN_CAP_PWRSAV ? "PowerSave " : "",
570141cc406Sopenharmony_ci       dev->scan_capabilities & SCAN_CAP_EXT_CAL ? "ExtCal " : "",
571141cc406Sopenharmony_ci       dev->scan_capabilities & SCAN_CAP_FAST_PREVIEW ? "FastPreview" :
572141cc406Sopenharmony_ci       "",
573141cc406Sopenharmony_ci       dev->scan_capabilities & SCAN_CAP_DISABLE_CAL ? "DisCal " : "",
574141cc406Sopenharmony_ci       dev->scan_capabilities & SCAN_CAP_SPEEDS,
575141cc406Sopenharmony_ci       dev->scan_capabilities);
576141cc406Sopenharmony_ci
577141cc406Sopenharmony_ci  DBG (DBG_inquiry, "Optional Devices..............: %s%s%s%s (%02x)\n",
578141cc406Sopenharmony_ci       dev->optional_devices & SCAN_OPT_DEV_MPCL ? "MultiPageLoad " :
579141cc406Sopenharmony_ci       "",
580141cc406Sopenharmony_ci       dev->optional_devices & SCAN_OPT_DEV_TP1 ? "TransModule1 " : "",
581141cc406Sopenharmony_ci       dev->optional_devices & SCAN_OPT_DEV_TP ? "TransModule " : "",
582141cc406Sopenharmony_ci       dev->optional_devices & SCAN_OPT_DEV_ADF ? "ADF " : "",
583141cc406Sopenharmony_ci       dev->optional_devices);
584141cc406Sopenharmony_ci
585141cc406Sopenharmony_ci  DBG (DBG_inquiry, "Enhancement...................: %02x\n",
586141cc406Sopenharmony_ci       dev->enhancements);
587141cc406Sopenharmony_ci  DBG (DBG_inquiry, "Gamma bits....................: %d\n",
588141cc406Sopenharmony_ci       dev->gamma_bits);
589141cc406Sopenharmony_ci
590141cc406Sopenharmony_ci  DBG (DBG_inquiry, "Fast Preview Resolution.......: %d\n",
591141cc406Sopenharmony_ci       dev->fast_preview_resolution);
592141cc406Sopenharmony_ci  DBG (DBG_inquiry, "Min Highlight.................: %d\n",
593141cc406Sopenharmony_ci       dev->minimum_highlight);
594141cc406Sopenharmony_ci  DBG (DBG_inquiry, "Max Shadow....................: %d\n",
595141cc406Sopenharmony_ci       dev->maximum_shadow);
596141cc406Sopenharmony_ci  DBG (DBG_inquiry, "Cal Eqn.......................: %d\n",
597141cc406Sopenharmony_ci       dev->calibration_equation);
598141cc406Sopenharmony_ci  DBG (DBG_inquiry, "Min Exposure..................: %d\n",
599141cc406Sopenharmony_ci       dev->minimum_exposure);
600141cc406Sopenharmony_ci  DBG (DBG_inquiry, "Max Exposure..................: %d\n",
601141cc406Sopenharmony_ci       dev->maximum_exposure);
602141cc406Sopenharmony_ci
603141cc406Sopenharmony_ci  DBG (DBG_inquiry, "x0,y0 x1,y1...................: %d,%d %d,%d\n",
604141cc406Sopenharmony_ci       dev->x0, dev->y0, dev->x1, dev->y1);
605141cc406Sopenharmony_ci  DBG (DBG_inquiry, "production....................: '%s'\n",
606141cc406Sopenharmony_ci       dev->production);
607141cc406Sopenharmony_ci  DBG (DBG_inquiry, "timestamp.....................: '%s'\n",
608141cc406Sopenharmony_ci       dev->timestamp);
609141cc406Sopenharmony_ci  DBG (DBG_inquiry, "signature.....................: '%s'\n",
610141cc406Sopenharmony_ci       dev->signature);
611141cc406Sopenharmony_ci
612141cc406Sopenharmony_ci}
613141cc406Sopenharmony_ci
614141cc406Sopenharmony_ci/**
615141cc406Sopenharmony_ci * Initiaize scanner options from the device definition and from exposure,
616141cc406Sopenharmony_ci * gain and offset defaults. The function is called by sane_open(), when no
617141cc406Sopenharmony_ci * optimized settings are available yet. The scanner object is fully
618141cc406Sopenharmony_ci * initialized in sane_start().
619141cc406Sopenharmony_ci *
620141cc406Sopenharmony_ci * @param scanner Scanner to initialize
621141cc406Sopenharmony_ci * @return SANE_STATUS_GOOD
622141cc406Sopenharmony_ci */
623141cc406Sopenharmony_ciSANE_Status
624141cc406Sopenharmony_cisanei_pieusb_init_options (Pieusb_Scanner* scanner)
625141cc406Sopenharmony_ci{
626141cc406Sopenharmony_ci    int i;
627141cc406Sopenharmony_ci
628141cc406Sopenharmony_ci    DBG (DBG_info_proc, "sanei_pieusb_init_options\n");
629141cc406Sopenharmony_ci
630141cc406Sopenharmony_ci    memset (scanner->opt, 0, sizeof (scanner->opt));
631141cc406Sopenharmony_ci    memset (scanner->val, 0, sizeof (scanner->val));
632141cc406Sopenharmony_ci
633141cc406Sopenharmony_ci    for (i = 0; i < NUM_OPTIONS; ++i) {
634141cc406Sopenharmony_ci        scanner->opt[i].size = sizeof (SANE_Word);
635141cc406Sopenharmony_ci        scanner->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
636141cc406Sopenharmony_ci    }
637141cc406Sopenharmony_ci
638141cc406Sopenharmony_ci    /* Number of options (a pseudo-option) */
639141cc406Sopenharmony_ci    scanner->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
640141cc406Sopenharmony_ci    scanner->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
641141cc406Sopenharmony_ci    scanner->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
642141cc406Sopenharmony_ci    scanner->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
643141cc406Sopenharmony_ci    scanner->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
644141cc406Sopenharmony_ci    scanner->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
645141cc406Sopenharmony_ci
646141cc406Sopenharmony_ci    /* "Mode" group: */
647141cc406Sopenharmony_ci    scanner->opt[OPT_MODE_GROUP].title = "Scan Mode";
648141cc406Sopenharmony_ci    scanner->opt[OPT_MODE_GROUP].desc = "";
649141cc406Sopenharmony_ci    scanner->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
650141cc406Sopenharmony_ci    scanner->opt[OPT_MODE_GROUP].cap = 0;
651141cc406Sopenharmony_ci    scanner->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
652141cc406Sopenharmony_ci
653141cc406Sopenharmony_ci    /* scan mode */
654141cc406Sopenharmony_ci    scanner->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
655141cc406Sopenharmony_ci    scanner->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
656141cc406Sopenharmony_ci    scanner->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
657141cc406Sopenharmony_ci    scanner->opt[OPT_MODE].type = SANE_TYPE_STRING;
658141cc406Sopenharmony_ci    scanner->opt[OPT_MODE].size = max_string_size ((SANE_String_Const const *) scanner->device->scan_mode_list);
659141cc406Sopenharmony_ci    scanner->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
660141cc406Sopenharmony_ci    scanner->opt[OPT_MODE].constraint.string_list = (SANE_String_Const const *) scanner->device->scan_mode_list;
661141cc406Sopenharmony_ci    scanner->val[OPT_MODE].s = (SANE_Char *) strdup (scanner->device->scan_mode_list[3]); /* default RGB */
662141cc406Sopenharmony_ci
663141cc406Sopenharmony_ci    /* bit depth */
664141cc406Sopenharmony_ci    scanner->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
665141cc406Sopenharmony_ci    scanner->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
666141cc406Sopenharmony_ci    scanner->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
667141cc406Sopenharmony_ci    scanner->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
668141cc406Sopenharmony_ci    scanner->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
669141cc406Sopenharmony_ci    scanner->opt[OPT_BIT_DEPTH].size = sizeof (SANE_Word);
670141cc406Sopenharmony_ci    scanner->opt[OPT_BIT_DEPTH].constraint.word_list = scanner->device->bpp_list;
671141cc406Sopenharmony_ci    scanner->val[OPT_BIT_DEPTH].w = scanner->device->bpp_list[2];
672141cc406Sopenharmony_ci
673141cc406Sopenharmony_ci    /* resolution */
674141cc406Sopenharmony_ci    scanner->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
675141cc406Sopenharmony_ci    scanner->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
676141cc406Sopenharmony_ci    scanner->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
677141cc406Sopenharmony_ci    scanner->opt[OPT_RESOLUTION].type = SANE_TYPE_FIXED;
678141cc406Sopenharmony_ci    scanner->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
679141cc406Sopenharmony_ci    scanner->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
680141cc406Sopenharmony_ci    scanner->opt[OPT_RESOLUTION].constraint.range = &scanner->device->dpi_range;
681141cc406Sopenharmony_ci    scanner->val[OPT_RESOLUTION].w = scanner->device->fast_preview_resolution << SANE_FIXED_SCALE_SHIFT;
682141cc406Sopenharmony_ci
683141cc406Sopenharmony_ci    /* halftone pattern */
684141cc406Sopenharmony_ci    scanner->opt[OPT_HALFTONE_PATTERN].name = SANE_NAME_HALFTONE_PATTERN;
685141cc406Sopenharmony_ci    scanner->opt[OPT_HALFTONE_PATTERN].title = SANE_TITLE_HALFTONE_PATTERN;
686141cc406Sopenharmony_ci    scanner->opt[OPT_HALFTONE_PATTERN].desc = SANE_DESC_HALFTONE_PATTERN;
687141cc406Sopenharmony_ci    scanner->opt[OPT_HALFTONE_PATTERN].type = SANE_TYPE_STRING;
688141cc406Sopenharmony_ci    scanner->opt[OPT_HALFTONE_PATTERN].size = max_string_size ((SANE_String_Const const *) scanner->device->halftone_list);
689141cc406Sopenharmony_ci    scanner->opt[OPT_HALFTONE_PATTERN].constraint_type = SANE_CONSTRAINT_STRING_LIST;
690141cc406Sopenharmony_ci    scanner->opt[OPT_HALFTONE_PATTERN].constraint.string_list = (SANE_String_Const const *) scanner->device->halftone_list;
691141cc406Sopenharmony_ci    scanner->val[OPT_HALFTONE_PATTERN].s = (SANE_Char *) strdup (scanner->device->halftone_list[6]);
692141cc406Sopenharmony_ci    scanner->opt[OPT_HALFTONE_PATTERN].cap |= SANE_CAP_INACTIVE; /* Not implemented, and only meaningful at depth 1 */
693141cc406Sopenharmony_ci
694141cc406Sopenharmony_ci    /* lineart threshold */
695141cc406Sopenharmony_ci    scanner->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
696141cc406Sopenharmony_ci    scanner->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
697141cc406Sopenharmony_ci    scanner->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
698141cc406Sopenharmony_ci    scanner->opt[OPT_THRESHOLD].type = SANE_TYPE_FIXED;
699141cc406Sopenharmony_ci    scanner->opt[OPT_THRESHOLD].unit = SANE_UNIT_PERCENT;
700141cc406Sopenharmony_ci    scanner->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
701141cc406Sopenharmony_ci    scanner->opt[OPT_THRESHOLD].constraint.range = &percentage_range_100;
702141cc406Sopenharmony_ci    scanner->val[OPT_THRESHOLD].w = SANE_FIX (50);
703141cc406Sopenharmony_ci    /* scanner->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; Not implemented, and only meaningful at depth 1 */
704141cc406Sopenharmony_ci
705141cc406Sopenharmony_ci    /* create a sharper scan at the cost of scan time */
706141cc406Sopenharmony_ci    scanner->opt[OPT_SHARPEN].name = "sharpen";
707141cc406Sopenharmony_ci    scanner->opt[OPT_SHARPEN].title = "Sharpen scan";
708141cc406Sopenharmony_ci    scanner->opt[OPT_SHARPEN].desc = "Sharpen scan by taking more time to discharge the CCD.";
709141cc406Sopenharmony_ci    scanner->opt[OPT_SHARPEN].type = SANE_TYPE_BOOL;
710141cc406Sopenharmony_ci    scanner->opt[OPT_SHARPEN].unit = SANE_UNIT_NONE;
711141cc406Sopenharmony_ci    scanner->opt[OPT_SHARPEN].constraint_type = SANE_CONSTRAINT_NONE;
712141cc406Sopenharmony_ci    scanner->val[OPT_SHARPEN].b = SANE_FALSE;
713141cc406Sopenharmony_ci    scanner->opt[OPT_SHARPEN].cap |= SANE_CAP_SOFT_SELECT;
714141cc406Sopenharmony_ci
715141cc406Sopenharmony_ci    /* skip the auto-calibration phase before the scan */
716141cc406Sopenharmony_ci    scanner->opt[OPT_SHADING_ANALYSIS].name = "shading-analysis";
717141cc406Sopenharmony_ci    scanner->opt[OPT_SHADING_ANALYSIS].title = "Perform shading analysis";
718141cc406Sopenharmony_ci    scanner->opt[OPT_SHADING_ANALYSIS].desc = "Collect shading reference data before scanning the image. If set to 'no', this option may be overridden by the scanner.";
719141cc406Sopenharmony_ci    scanner->opt[OPT_SHADING_ANALYSIS].type = SANE_TYPE_BOOL;
720141cc406Sopenharmony_ci    scanner->opt[OPT_SHADING_ANALYSIS].unit = SANE_UNIT_NONE;
721141cc406Sopenharmony_ci    scanner->opt[OPT_SHADING_ANALYSIS].constraint_type = SANE_CONSTRAINT_NONE;
722141cc406Sopenharmony_ci    scanner->val[OPT_SHADING_ANALYSIS].b = SANE_FALSE;
723141cc406Sopenharmony_ci    scanner->opt[OPT_SHADING_ANALYSIS].cap |= SANE_CAP_SOFT_SELECT;
724141cc406Sopenharmony_ci
725141cc406Sopenharmony_ci    /* use auto-calibration settings for scan */
726141cc406Sopenharmony_ci    scanner->opt[OPT_CALIBRATION_MODE].name = "calibration";
727141cc406Sopenharmony_ci    scanner->opt[OPT_CALIBRATION_MODE].title = "Calibration mode";
728141cc406Sopenharmony_ci    scanner->opt[OPT_CALIBRATION_MODE].desc = "How to calibrate the scanner.";
729141cc406Sopenharmony_ci    scanner->opt[OPT_CALIBRATION_MODE].type = SANE_TYPE_STRING;
730141cc406Sopenharmony_ci    scanner->opt[OPT_CALIBRATION_MODE].size = max_string_size ((SANE_String_Const const *) scanner->device->calibration_mode_list);
731141cc406Sopenharmony_ci    scanner->opt[OPT_CALIBRATION_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
732141cc406Sopenharmony_ci    scanner->opt[OPT_CALIBRATION_MODE].constraint.string_list = (SANE_String_Const const *) scanner->device->calibration_mode_list;
733141cc406Sopenharmony_ci    scanner->val[OPT_CALIBRATION_MODE].s = (SANE_Char *) strdup (scanner->device->calibration_mode_list[1]); /* default auto */
734141cc406Sopenharmony_ci
735141cc406Sopenharmony_ci    /* OPT_GAIN_ADJUST */
736141cc406Sopenharmony_ci    scanner->opt[OPT_GAIN_ADJUST].name = "gain-adjust";
737141cc406Sopenharmony_ci    scanner->opt[OPT_GAIN_ADJUST].title = "Adjust gain";
738141cc406Sopenharmony_ci    scanner->opt[OPT_GAIN_ADJUST].desc = "Adjust gain determined by calibration procedure.";
739141cc406Sopenharmony_ci    scanner->opt[OPT_GAIN_ADJUST].type = SANE_TYPE_STRING;
740141cc406Sopenharmony_ci    scanner->opt[OPT_GAIN_ADJUST].size = max_string_size ((SANE_String_Const const *) scanner->device->gain_adjust_list);
741141cc406Sopenharmony_ci    scanner->opt[OPT_GAIN_ADJUST].constraint_type = SANE_CONSTRAINT_STRING_LIST;
742141cc406Sopenharmony_ci    scanner->opt[OPT_GAIN_ADJUST].constraint.string_list = (SANE_String_Const const *) scanner->device->gain_adjust_list;
743141cc406Sopenharmony_ci    scanner->val[OPT_GAIN_ADJUST].s = (SANE_Char *) strdup (scanner->device->gain_adjust_list[2]); /* x 1.0 (no change) */
744141cc406Sopenharmony_ci
745141cc406Sopenharmony_ci    /* scan infrared channel faster but less accurate */
746141cc406Sopenharmony_ci    scanner->opt[OPT_FAST_INFRARED].name = "fast-infrared";
747141cc406Sopenharmony_ci    scanner->opt[OPT_FAST_INFRARED].title = "Fast infrared scan";
748141cc406Sopenharmony_ci    scanner->opt[OPT_FAST_INFRARED].desc = "Do not reposition scan head before scanning infrared line. Results in an infrared offset which may deteriorate IR dust and scratch removal.";
749141cc406Sopenharmony_ci    scanner->opt[OPT_FAST_INFRARED].type = SANE_TYPE_BOOL;
750141cc406Sopenharmony_ci    scanner->opt[OPT_FAST_INFRARED].unit = SANE_UNIT_NONE;
751141cc406Sopenharmony_ci    scanner->opt[OPT_FAST_INFRARED].constraint_type = SANE_CONSTRAINT_NONE;
752141cc406Sopenharmony_ci    scanner->val[OPT_FAST_INFRARED].b = SANE_FALSE;
753141cc406Sopenharmony_ci    scanner->opt[OPT_FAST_INFRARED].cap |= SANE_CAP_SOFT_SELECT;
754141cc406Sopenharmony_ci
755141cc406Sopenharmony_ci    /* automatically advance to next slide after scan */
756141cc406Sopenharmony_ci    scanner->opt[OPT_ADVANCE_SLIDE].name = "advance";
757141cc406Sopenharmony_ci    scanner->opt[OPT_ADVANCE_SLIDE].title = "Advance slide";
758141cc406Sopenharmony_ci    scanner->opt[OPT_ADVANCE_SLIDE].desc = "Automatically advance to next slide after scan";
759141cc406Sopenharmony_ci    scanner->opt[OPT_ADVANCE_SLIDE].type = SANE_TYPE_BOOL;
760141cc406Sopenharmony_ci    scanner->opt[OPT_ADVANCE_SLIDE].unit = SANE_UNIT_NONE;
761141cc406Sopenharmony_ci    scanner->opt[OPT_ADVANCE_SLIDE].constraint_type = SANE_CONSTRAINT_NONE;
762141cc406Sopenharmony_ci    scanner->val[OPT_ADVANCE_SLIDE].w = SANE_TRUE;
763141cc406Sopenharmony_ci    scanner->opt[OPT_ADVANCE_SLIDE].cap |= SANE_CAP_SOFT_SELECT;
764141cc406Sopenharmony_ci
765141cc406Sopenharmony_ci    /* "Geometry" group: */
766141cc406Sopenharmony_ci    scanner->opt[OPT_GEOMETRY_GROUP].title = "Geometry";
767141cc406Sopenharmony_ci    scanner->opt[OPT_GEOMETRY_GROUP].desc = "";
768141cc406Sopenharmony_ci    scanner->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
769141cc406Sopenharmony_ci    scanner->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
770141cc406Sopenharmony_ci    scanner->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
771141cc406Sopenharmony_ci
772141cc406Sopenharmony_ci    /* top-left x */
773141cc406Sopenharmony_ci    scanner->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
774141cc406Sopenharmony_ci    scanner->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
775141cc406Sopenharmony_ci    scanner->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
776141cc406Sopenharmony_ci    scanner->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
777141cc406Sopenharmony_ci    scanner->opt[OPT_TL_X].unit = SANE_UNIT_MM;
778141cc406Sopenharmony_ci    scanner->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
779141cc406Sopenharmony_ci    scanner->opt[OPT_TL_X].constraint.range = &(scanner->device->x_range);
780141cc406Sopenharmony_ci    scanner->val[OPT_TL_X].w = 0;
781141cc406Sopenharmony_ci
782141cc406Sopenharmony_ci    /* top-left y */
783141cc406Sopenharmony_ci    scanner->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
784141cc406Sopenharmony_ci    scanner->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
785141cc406Sopenharmony_ci    scanner->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
786141cc406Sopenharmony_ci    scanner->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
787141cc406Sopenharmony_ci    scanner->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
788141cc406Sopenharmony_ci    scanner->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
789141cc406Sopenharmony_ci    scanner->opt[OPT_TL_Y].constraint.range = &(scanner->device->y_range);
790141cc406Sopenharmony_ci    scanner->val[OPT_TL_Y].w = 0;
791141cc406Sopenharmony_ci
792141cc406Sopenharmony_ci    /* bottom-right x */
793141cc406Sopenharmony_ci    scanner->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
794141cc406Sopenharmony_ci    scanner->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
795141cc406Sopenharmony_ci    scanner->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
796141cc406Sopenharmony_ci    scanner->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
797141cc406Sopenharmony_ci    scanner->opt[OPT_BR_X].unit = SANE_UNIT_MM;
798141cc406Sopenharmony_ci    scanner->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
799141cc406Sopenharmony_ci    scanner->opt[OPT_BR_X].constraint.range = &(scanner->device->x_range);
800141cc406Sopenharmony_ci    scanner->val[OPT_BR_X].w = scanner->device->x_range.max;
801141cc406Sopenharmony_ci
802141cc406Sopenharmony_ci    /* bottom-right y */
803141cc406Sopenharmony_ci    scanner->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
804141cc406Sopenharmony_ci    scanner->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
805141cc406Sopenharmony_ci    scanner->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
806141cc406Sopenharmony_ci    scanner->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
807141cc406Sopenharmony_ci    scanner->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
808141cc406Sopenharmony_ci    scanner->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
809141cc406Sopenharmony_ci    scanner->opt[OPT_BR_Y].constraint.range = &(scanner->device->y_range);
810141cc406Sopenharmony_ci    scanner->val[OPT_BR_Y].w = scanner->device->y_range.max;
811141cc406Sopenharmony_ci
812141cc406Sopenharmony_ci    /* "Enhancement" group: */
813141cc406Sopenharmony_ci    scanner->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
814141cc406Sopenharmony_ci    scanner->opt[OPT_ENHANCEMENT_GROUP].desc = "";
815141cc406Sopenharmony_ci    scanner->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
816141cc406Sopenharmony_ci    scanner->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
817141cc406Sopenharmony_ci    scanner->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
818141cc406Sopenharmony_ci
819141cc406Sopenharmony_ci    /* correct data for lamp variations (shading) */
820141cc406Sopenharmony_ci    scanner->opt[OPT_CORRECT_SHADING].name = "correct-shading";
821141cc406Sopenharmony_ci    scanner->opt[OPT_CORRECT_SHADING].title = "Correct shading";
822141cc406Sopenharmony_ci    scanner->opt[OPT_CORRECT_SHADING].desc = "Correct data for lamp variations (shading)";
823141cc406Sopenharmony_ci    scanner->opt[OPT_CORRECT_SHADING].type = SANE_TYPE_BOOL;
824141cc406Sopenharmony_ci    scanner->opt[OPT_CORRECT_SHADING].unit = SANE_UNIT_NONE;
825141cc406Sopenharmony_ci    scanner->val[OPT_CORRECT_SHADING].w = SANE_TRUE;
826141cc406Sopenharmony_ci
827141cc406Sopenharmony_ci    /* correct infrared for red crosstalk */
828141cc406Sopenharmony_ci    scanner->opt[OPT_CORRECT_INFRARED].name = "correct-infrared";
829141cc406Sopenharmony_ci    scanner->opt[OPT_CORRECT_INFRARED].title = "Correct infrared";
830141cc406Sopenharmony_ci    scanner->opt[OPT_CORRECT_INFRARED].desc = "Correct infrared for red crosstalk";
831141cc406Sopenharmony_ci    scanner->opt[OPT_CORRECT_INFRARED].type = SANE_TYPE_BOOL;
832141cc406Sopenharmony_ci    scanner->opt[OPT_CORRECT_INFRARED].unit = SANE_UNIT_NONE;
833141cc406Sopenharmony_ci    scanner->val[OPT_CORRECT_INFRARED].w = SANE_FALSE;
834141cc406Sopenharmony_ci
835141cc406Sopenharmony_ci    /* detect and remove dust and scratch artifacts */
836141cc406Sopenharmony_ci    scanner->opt[OPT_CLEAN_IMAGE].name = "clean-image";
837141cc406Sopenharmony_ci    scanner->opt[OPT_CLEAN_IMAGE].title = "Clean image";
838141cc406Sopenharmony_ci    scanner->opt[OPT_CLEAN_IMAGE].desc = "Detect and remove dust and scratch artifacts";
839141cc406Sopenharmony_ci    scanner->opt[OPT_CLEAN_IMAGE].type = SANE_TYPE_BOOL;
840141cc406Sopenharmony_ci    scanner->opt[OPT_CLEAN_IMAGE].unit = SANE_UNIT_NONE;
841141cc406Sopenharmony_ci    scanner->val[OPT_CLEAN_IMAGE].w = SANE_FALSE;
842141cc406Sopenharmony_ci
843141cc406Sopenharmony_ci    /* strength of grain filtering */
844141cc406Sopenharmony_ci    scanner->opt[OPT_SMOOTH_IMAGE].name = "smooth";
845141cc406Sopenharmony_ci    scanner->opt[OPT_SMOOTH_IMAGE].title = "Attenuate film grain";
846141cc406Sopenharmony_ci    scanner->opt[OPT_SMOOTH_IMAGE].desc = "Amount of smoothening";
847141cc406Sopenharmony_ci    scanner->opt[OPT_SMOOTH_IMAGE].type = SANE_TYPE_INT;
848141cc406Sopenharmony_ci    scanner->opt[OPT_SMOOTH_IMAGE].constraint_type = SANE_CONSTRAINT_WORD_LIST;
849141cc406Sopenharmony_ci    scanner->opt[OPT_SMOOTH_IMAGE].size = sizeof (SANE_Word);
850141cc406Sopenharmony_ci    scanner->opt[OPT_SMOOTH_IMAGE].constraint.word_list = scanner->device->grain_sw_list;
851141cc406Sopenharmony_ci    scanner->val[OPT_SMOOTH_IMAGE].w = scanner->device->grain_sw_list[1];
852141cc406Sopenharmony_ci    if (scanner->opt[OPT_SMOOTH_IMAGE].constraint.word_list[0] < 2) {
853141cc406Sopenharmony_ci        scanner->opt[OPT_SMOOTH_IMAGE].cap |= SANE_CAP_INACTIVE;
854141cc406Sopenharmony_ci    }
855141cc406Sopenharmony_ci
856141cc406Sopenharmony_ci    /* gamma correction, to make image sRGB like */
857141cc406Sopenharmony_ci    scanner->opt[OPT_TRANSFORM_TO_SRGB].name = "srgb";
858141cc406Sopenharmony_ci    scanner->opt[OPT_TRANSFORM_TO_SRGB].title = "sRGB colors";
859141cc406Sopenharmony_ci    scanner->opt[OPT_TRANSFORM_TO_SRGB].desc = "Transform image to approximate sRGB color space";
860141cc406Sopenharmony_ci    scanner->opt[OPT_TRANSFORM_TO_SRGB].type = SANE_TYPE_BOOL;
861141cc406Sopenharmony_ci    scanner->opt[OPT_TRANSFORM_TO_SRGB].unit = SANE_UNIT_NONE;
862141cc406Sopenharmony_ci    scanner->val[OPT_TRANSFORM_TO_SRGB].w = SANE_FALSE;
863141cc406Sopenharmony_ci    scanner->opt[OPT_TRANSFORM_TO_SRGB].cap |= SANE_CAP_INACTIVE;
864141cc406Sopenharmony_ci
865141cc406Sopenharmony_ci    /* color correction for generic negative film */
866141cc406Sopenharmony_ci    scanner->opt[OPT_INVERT_IMAGE].name = "invert";
867141cc406Sopenharmony_ci    scanner->opt[OPT_INVERT_IMAGE].title = "Invert colors";
868141cc406Sopenharmony_ci    scanner->opt[OPT_INVERT_IMAGE].desc = "Correct for generic negative film";
869141cc406Sopenharmony_ci    scanner->opt[OPT_INVERT_IMAGE].type = SANE_TYPE_BOOL;
870141cc406Sopenharmony_ci    scanner->opt[OPT_INVERT_IMAGE].unit = SANE_UNIT_NONE;
871141cc406Sopenharmony_ci    scanner->val[OPT_INVERT_IMAGE].w = SANE_FALSE;
872141cc406Sopenharmony_ci    scanner->opt[OPT_INVERT_IMAGE].cap |= SANE_CAP_INACTIVE;
873141cc406Sopenharmony_ci
874141cc406Sopenharmony_ci    /* crop image */
875141cc406Sopenharmony_ci    scanner->opt[OPT_CROP_IMAGE].name = "crop";
876141cc406Sopenharmony_ci    scanner->opt[OPT_CROP_IMAGE].title = "Cropping";
877141cc406Sopenharmony_ci    scanner->opt[OPT_CROP_IMAGE].desc = "How to crop the image";
878141cc406Sopenharmony_ci    scanner->opt[OPT_CROP_IMAGE].type = SANE_TYPE_STRING;
879141cc406Sopenharmony_ci    scanner->opt[OPT_CROP_IMAGE].size = max_string_size ((SANE_String_Const const *)(void*) scanner->device->crop_sw_list);
880141cc406Sopenharmony_ci    scanner->opt[OPT_CROP_IMAGE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
881141cc406Sopenharmony_ci    scanner->opt[OPT_CROP_IMAGE].constraint.string_list = (SANE_String_Const const *)(void*) scanner->device->crop_sw_list;
882141cc406Sopenharmony_ci    scanner->val[OPT_CROP_IMAGE].s = (SANE_Char *) strdup (scanner->device->crop_sw_list[2]);
883141cc406Sopenharmony_ci
884141cc406Sopenharmony_ci    /* "Advanced" group: */
885141cc406Sopenharmony_ci    scanner->opt[OPT_ADVANCED_GROUP].title = "Advanced";
886141cc406Sopenharmony_ci    scanner->opt[OPT_ADVANCED_GROUP].desc = "";
887141cc406Sopenharmony_ci    scanner->opt[OPT_ADVANCED_GROUP].type = SANE_TYPE_GROUP;
888141cc406Sopenharmony_ci    scanner->opt[OPT_ADVANCED_GROUP].cap = SANE_CAP_ADVANCED;
889141cc406Sopenharmony_ci    scanner->opt[OPT_ADVANCED_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
890141cc406Sopenharmony_ci
891141cc406Sopenharmony_ci    /* preview */
892141cc406Sopenharmony_ci    scanner->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
893141cc406Sopenharmony_ci    scanner->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
894141cc406Sopenharmony_ci    scanner->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
895141cc406Sopenharmony_ci    scanner->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
896141cc406Sopenharmony_ci    scanner->val[OPT_PREVIEW].w = SANE_FALSE;
897141cc406Sopenharmony_ci
898141cc406Sopenharmony_ci    /* save shading data */
899141cc406Sopenharmony_ci    scanner->opt[OPT_SAVE_SHADINGDATA].name = "save-shading-data";
900141cc406Sopenharmony_ci    scanner->opt[OPT_SAVE_SHADINGDATA].title = "Save shading data";
901141cc406Sopenharmony_ci    scanner->opt[OPT_SAVE_SHADINGDATA].desc = "Save shading data in 'pieusb.shading'";
902141cc406Sopenharmony_ci    scanner->opt[OPT_SAVE_SHADINGDATA].type = SANE_TYPE_BOOL;
903141cc406Sopenharmony_ci    scanner->val[OPT_SAVE_SHADINGDATA].w = SANE_FALSE;
904141cc406Sopenharmony_ci
905141cc406Sopenharmony_ci    /* save CCD mask */
906141cc406Sopenharmony_ci    scanner->opt[OPT_SAVE_CCDMASK].name = "save-ccdmask";
907141cc406Sopenharmony_ci    scanner->opt[OPT_SAVE_CCDMASK].title = "Save CCD mask";
908141cc406Sopenharmony_ci    scanner->opt[OPT_SAVE_CCDMASK].desc = "Save CCD mask 'pieusb.ccd'";
909141cc406Sopenharmony_ci    scanner->opt[OPT_SAVE_CCDMASK].type = SANE_TYPE_BOOL;
910141cc406Sopenharmony_ci    scanner->val[OPT_SAVE_CCDMASK].w = SANE_FALSE;
911141cc406Sopenharmony_ci
912141cc406Sopenharmony_ci    scanner->opt[OPT_LIGHT].name = "light";
913141cc406Sopenharmony_ci    scanner->opt[OPT_LIGHT].title = "Light";
914141cc406Sopenharmony_ci    scanner->opt[OPT_LIGHT].desc = "Light";
915141cc406Sopenharmony_ci    scanner->opt[OPT_LIGHT].type = SANE_TYPE_INT;
916141cc406Sopenharmony_ci    scanner->opt[OPT_LIGHT].unit = SANE_UNIT_MICROSECOND;
917141cc406Sopenharmony_ci    scanner->opt[OPT_LIGHT].cap |= SANE_CAP_SOFT_SELECT;
918141cc406Sopenharmony_ci    scanner->opt[OPT_LIGHT].size = sizeof(SANE_Word);
919141cc406Sopenharmony_ci    scanner->val[OPT_LIGHT].w = DEFAULT_LIGHT;
920141cc406Sopenharmony_ci
921141cc406Sopenharmony_ci    scanner->opt[OPT_DOUBLE_TIMES].name = "double-times";
922141cc406Sopenharmony_ci    scanner->opt[OPT_DOUBLE_TIMES].title = "Double times";
923141cc406Sopenharmony_ci    scanner->opt[OPT_DOUBLE_TIMES].desc = "Double times";
924141cc406Sopenharmony_ci    scanner->opt[OPT_DOUBLE_TIMES].type = SANE_TYPE_INT;
925141cc406Sopenharmony_ci    scanner->opt[OPT_DOUBLE_TIMES].unit = SANE_UNIT_MICROSECOND;
926141cc406Sopenharmony_ci    scanner->opt[OPT_DOUBLE_TIMES].cap |= SANE_CAP_SOFT_SELECT;
927141cc406Sopenharmony_ci    scanner->opt[OPT_DOUBLE_TIMES].size = sizeof(SANE_Word);
928141cc406Sopenharmony_ci    scanner->val[OPT_DOUBLE_TIMES].w = DEFAULT_DOUBLE_TIMES;
929141cc406Sopenharmony_ci
930141cc406Sopenharmony_ci    /* exposure times for R, G, B and I */
931141cc406Sopenharmony_ci    scanner->opt[OPT_SET_EXPOSURE_R].name = SANE_NAME_EXPOSURE_R;
932141cc406Sopenharmony_ci    scanner->opt[OPT_SET_EXPOSURE_R].title = SANE_TITLE_EXPOSURE_R;
933141cc406Sopenharmony_ci    scanner->opt[OPT_SET_EXPOSURE_R].desc = SANE_DESC_EXPOSURE_R;
934141cc406Sopenharmony_ci    scanner->opt[OPT_SET_EXPOSURE_G].name = SANE_NAME_EXPOSURE_G;
935141cc406Sopenharmony_ci    scanner->opt[OPT_SET_EXPOSURE_G].title = SANE_TITLE_EXPOSURE_G;
936141cc406Sopenharmony_ci    scanner->opt[OPT_SET_EXPOSURE_G].desc = SANE_DESC_EXPOSURE_G;
937141cc406Sopenharmony_ci    scanner->opt[OPT_SET_EXPOSURE_B].name = SANE_NAME_EXPOSURE_B;
938141cc406Sopenharmony_ci    scanner->opt[OPT_SET_EXPOSURE_B].title = SANE_TITLE_EXPOSURE_B;
939141cc406Sopenharmony_ci    scanner->opt[OPT_SET_EXPOSURE_B].desc = SANE_DESC_EXPOSURE_B;
940141cc406Sopenharmony_ci    scanner->opt[OPT_SET_EXPOSURE_I].name = SANE_NAME_EXPOSURE_I;
941141cc406Sopenharmony_ci    scanner->opt[OPT_SET_EXPOSURE_I].title = SANE_TITLE_EXPOSURE_I;
942141cc406Sopenharmony_ci    scanner->opt[OPT_SET_EXPOSURE_I].desc = SANE_DESC_EXPOSURE_I;
943141cc406Sopenharmony_ci    for (i = OPT_SET_EXPOSURE_R; i <= OPT_SET_EXPOSURE_I; ++i) {
944141cc406Sopenharmony_ci    scanner->opt[i].type = SANE_TYPE_INT;
945141cc406Sopenharmony_ci    scanner->opt[i].unit = SANE_UNIT_MICROSECOND;
946141cc406Sopenharmony_ci    scanner->opt[i].cap |= SANE_CAP_SOFT_SELECT;
947141cc406Sopenharmony_ci    scanner->opt[i].constraint_type = SANE_CONSTRAINT_RANGE;
948141cc406Sopenharmony_ci    scanner->opt[i].constraint.range = &(scanner->device->exposure_range);
949141cc406Sopenharmony_ci    scanner->opt[i].size = sizeof(SANE_Word);
950141cc406Sopenharmony_ci    scanner->val[i].w = SANE_EXPOSURE_DEFAULT;
951141cc406Sopenharmony_ci    }
952141cc406Sopenharmony_ci
953141cc406Sopenharmony_ci    /* gain for R, G, B and I */
954141cc406Sopenharmony_ci    scanner->opt[OPT_SET_GAIN_R].name = SANE_NAME_GAIN_R;
955141cc406Sopenharmony_ci    scanner->opt[OPT_SET_GAIN_R].title = SANE_TITLE_GAIN_R;
956141cc406Sopenharmony_ci    scanner->opt[OPT_SET_GAIN_R].desc = SANE_DESC_GAIN_R;
957141cc406Sopenharmony_ci    scanner->opt[OPT_SET_GAIN_G].name = SANE_NAME_GAIN_G;
958141cc406Sopenharmony_ci    scanner->opt[OPT_SET_GAIN_G].title = SANE_TITLE_GAIN_G;
959141cc406Sopenharmony_ci    scanner->opt[OPT_SET_GAIN_G].desc = SANE_DESC_GAIN_G;
960141cc406Sopenharmony_ci    scanner->opt[OPT_SET_GAIN_B].name = SANE_NAME_GAIN_B;
961141cc406Sopenharmony_ci    scanner->opt[OPT_SET_GAIN_B].title = SANE_TITLE_GAIN_B;
962141cc406Sopenharmony_ci    scanner->opt[OPT_SET_GAIN_B].desc = SANE_DESC_GAIN_B;
963141cc406Sopenharmony_ci    scanner->opt[OPT_SET_GAIN_I].name = SANE_NAME_GAIN_I;
964141cc406Sopenharmony_ci    scanner->opt[OPT_SET_GAIN_I].title = SANE_TITLE_GAIN_I;
965141cc406Sopenharmony_ci    scanner->opt[OPT_SET_GAIN_I].desc = SANE_DESC_GAIN_I;
966141cc406Sopenharmony_ci    for (i = OPT_SET_GAIN_R; i <= OPT_SET_GAIN_I; ++i) {
967141cc406Sopenharmony_ci      scanner->opt[i].type = SANE_TYPE_INT;
968141cc406Sopenharmony_ci      scanner->opt[i].unit = SANE_UNIT_NONE;
969141cc406Sopenharmony_ci      scanner->opt[i].constraint_type = SANE_CONSTRAINT_RANGE;
970141cc406Sopenharmony_ci      scanner->opt[i].constraint.range = &gain_range;
971141cc406Sopenharmony_ci      scanner->opt[i].size = sizeof(SANE_Word);
972141cc406Sopenharmony_ci      scanner->val[i].w = SANE_GAIN_DEFAULT;
973141cc406Sopenharmony_ci    }
974141cc406Sopenharmony_ci    /* offsets for R, G, B and I */
975141cc406Sopenharmony_ci    scanner->opt[OPT_SET_OFFSET_R].name = SANE_NAME_OFFSET_R;
976141cc406Sopenharmony_ci    scanner->opt[OPT_SET_OFFSET_R].title = SANE_TITLE_OFFSET_R;
977141cc406Sopenharmony_ci    scanner->opt[OPT_SET_OFFSET_R].desc = SANE_DESC_OFFSET_R;
978141cc406Sopenharmony_ci    scanner->opt[OPT_SET_OFFSET_G].name = SANE_NAME_OFFSET_G;
979141cc406Sopenharmony_ci    scanner->opt[OPT_SET_OFFSET_G].title = SANE_TITLE_OFFSET_G;
980141cc406Sopenharmony_ci    scanner->opt[OPT_SET_OFFSET_G].desc = SANE_DESC_OFFSET_G;
981141cc406Sopenharmony_ci    scanner->opt[OPT_SET_OFFSET_B].name = SANE_NAME_OFFSET_B;
982141cc406Sopenharmony_ci    scanner->opt[OPT_SET_OFFSET_B].title = SANE_TITLE_OFFSET_B;
983141cc406Sopenharmony_ci    scanner->opt[OPT_SET_OFFSET_B].desc = SANE_DESC_OFFSET_B;
984141cc406Sopenharmony_ci    scanner->opt[OPT_SET_OFFSET_I].name = SANE_NAME_OFFSET_I;
985141cc406Sopenharmony_ci    scanner->opt[OPT_SET_OFFSET_I].title = SANE_TITLE_OFFSET_I;
986141cc406Sopenharmony_ci    scanner->opt[OPT_SET_OFFSET_I].desc = SANE_DESC_OFFSET_I;
987141cc406Sopenharmony_ci    for (i = OPT_SET_OFFSET_R; i <= OPT_SET_OFFSET_I; ++i) {
988141cc406Sopenharmony_ci      scanner->opt[i].type = SANE_TYPE_INT;
989141cc406Sopenharmony_ci      scanner->opt[i].unit = SANE_UNIT_NONE;
990141cc406Sopenharmony_ci      scanner->opt[i].constraint_type = SANE_CONSTRAINT_RANGE;
991141cc406Sopenharmony_ci      scanner->opt[i].constraint.range = &offset_range;
992141cc406Sopenharmony_ci      scanner->opt[i].size = sizeof(SANE_Word);
993141cc406Sopenharmony_ci      scanner->val[i].w = SANE_OFFSET_DEFAULT;
994141cc406Sopenharmony_ci    }
995141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
996141cc406Sopenharmony_ci}
997141cc406Sopenharmony_ci
998141cc406Sopenharmony_ci/**
999141cc406Sopenharmony_ci * Parse line from config file into a vendor id, product id, model number, and flags
1000141cc406Sopenharmony_ci *
1001141cc406Sopenharmony_ci * @param config_line Text to parse
1002141cc406Sopenharmony_ci * @param vendor_id
1003141cc406Sopenharmony_ci * @param product_id
1004141cc406Sopenharmony_ci * @param model_number
1005141cc406Sopenharmony_ci * @param flags
1006141cc406Sopenharmony_ci * @return SANE_STATUS_GOOD, or SANE_STATUS_INVAL in case of a parse error
1007141cc406Sopenharmony_ci */
1008141cc406Sopenharmony_ciSANE_Status
1009141cc406Sopenharmony_cisanei_pieusb_parse_config_line(const char* config_line,
1010141cc406Sopenharmony_ci                               SANE_Word* vendor_id,
1011141cc406Sopenharmony_ci                               SANE_Word* product_id,
1012141cc406Sopenharmony_ci                               SANE_Int* model_number,
1013141cc406Sopenharmony_ci                               SANE_Int* flags)
1014141cc406Sopenharmony_ci{
1015141cc406Sopenharmony_ci    char *vendor_id_string, *product_id_string, *model_number_string, *flags_string;
1016141cc406Sopenharmony_ci
1017141cc406Sopenharmony_ci    if (strncmp (config_line, "usb ", 4) != 0) {
1018141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
1019141cc406Sopenharmony_ci    }
1020141cc406Sopenharmony_ci    /* Detect vendor-id */
1021141cc406Sopenharmony_ci    config_line += 4;
1022141cc406Sopenharmony_ci    config_line = sanei_config_skip_whitespace (config_line);
1023141cc406Sopenharmony_ci    if (*config_line) {
1024141cc406Sopenharmony_ci        config_line = sanei_config_get_string (config_line, &vendor_id_string);
1025141cc406Sopenharmony_ci        if (vendor_id_string) {
1026141cc406Sopenharmony_ci            *vendor_id = strtol (vendor_id_string, 0, 0);
1027141cc406Sopenharmony_ci            free (vendor_id_string);
1028141cc406Sopenharmony_ci        } else {
1029141cc406Sopenharmony_ci            return SANE_STATUS_INVAL;
1030141cc406Sopenharmony_ci        }
1031141cc406Sopenharmony_ci        config_line = sanei_config_skip_whitespace (config_line);
1032141cc406Sopenharmony_ci    } else {
1033141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
1034141cc406Sopenharmony_ci    }
1035141cc406Sopenharmony_ci    /* Detect product-id */
1036141cc406Sopenharmony_ci    config_line = sanei_config_skip_whitespace (config_line);
1037141cc406Sopenharmony_ci    if (*config_line) {
1038141cc406Sopenharmony_ci        config_line = sanei_config_get_string (config_line, &product_id_string);
1039141cc406Sopenharmony_ci        if (product_id_string) {
1040141cc406Sopenharmony_ci            *product_id = strtol (product_id_string, 0, 0);
1041141cc406Sopenharmony_ci            free (product_id_string);
1042141cc406Sopenharmony_ci        } else {
1043141cc406Sopenharmony_ci            return SANE_STATUS_INVAL;
1044141cc406Sopenharmony_ci        }
1045141cc406Sopenharmony_ci        config_line = sanei_config_skip_whitespace (config_line);
1046141cc406Sopenharmony_ci    } else {
1047141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
1048141cc406Sopenharmony_ci    }
1049141cc406Sopenharmony_ci    /* Detect model number */
1050141cc406Sopenharmony_ci    config_line = sanei_config_skip_whitespace (config_line);
1051141cc406Sopenharmony_ci    if (*config_line) {
1052141cc406Sopenharmony_ci        config_line = sanei_config_get_string (config_line, &model_number_string);
1053141cc406Sopenharmony_ci        if (model_number_string) {
1054141cc406Sopenharmony_ci            *model_number = (SANE_Int) strtol (model_number_string, 0, 0);
1055141cc406Sopenharmony_ci            free (model_number_string);
1056141cc406Sopenharmony_ci        } else {
1057141cc406Sopenharmony_ci            return SANE_STATUS_INVAL;
1058141cc406Sopenharmony_ci        }
1059141cc406Sopenharmony_ci        config_line = sanei_config_skip_whitespace (config_line);
1060141cc406Sopenharmony_ci    } else {
1061141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
1062141cc406Sopenharmony_ci    }
1063141cc406Sopenharmony_ci    /* Detect (optional) flags */
1064141cc406Sopenharmony_ci    *flags = 0;
1065141cc406Sopenharmony_ci    config_line = sanei_config_skip_whitespace (config_line);
1066141cc406Sopenharmony_ci    if (*config_line) {
1067141cc406Sopenharmony_ci        config_line = sanei_config_get_string (config_line, &flags_string);
1068141cc406Sopenharmony_ci        if (flags_string) {
1069141cc406Sopenharmony_ci            *flags = (SANE_Int) strtol (flags_string, 0, 0);
1070141cc406Sopenharmony_ci            free (flags_string);
1071141cc406Sopenharmony_ci        }
1072141cc406Sopenharmony_ci    }
1073141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
1074141cc406Sopenharmony_ci}
1075141cc406Sopenharmony_ci
1076141cc406Sopenharmony_ci/**
1077141cc406Sopenharmony_ci * Check if current list of supported devices contains the given specifications.
1078141cc406Sopenharmony_ci *
1079141cc406Sopenharmony_ci * @param vendor_id
1080141cc406Sopenharmony_ci * @param product_id
1081141cc406Sopenharmony_ci * @param model_number
1082141cc406Sopenharmony_ci * @param flags
1083141cc406Sopenharmony_ci * @return
1084141cc406Sopenharmony_ci */
1085141cc406Sopenharmony_ciSANE_Bool
1086141cc406Sopenharmony_cisanei_pieusb_supported_device_list_contains(SANE_Word vendor_id, SANE_Word product_id, SANE_Int model_number, SANE_Int flags)
1087141cc406Sopenharmony_ci{
1088141cc406Sopenharmony_ci    int i = 0;
1089141cc406Sopenharmony_ci    while (pieusb_supported_usb_device_list[i].vendor != 0) {
1090141cc406Sopenharmony_ci        if (pieusb_supported_usb_device_list[i].vendor == vendor_id
1091141cc406Sopenharmony_ci              && pieusb_supported_usb_device_list[i].product == product_id
1092141cc406Sopenharmony_ci              && pieusb_supported_usb_device_list[i].model == model_number
1093141cc406Sopenharmony_ci              && pieusb_supported_usb_device_list[i].flags == flags) {
1094141cc406Sopenharmony_ci            return SANE_TRUE;
1095141cc406Sopenharmony_ci        }
1096141cc406Sopenharmony_ci        i++;
1097141cc406Sopenharmony_ci    }
1098141cc406Sopenharmony_ci    return SANE_FALSE;
1099141cc406Sopenharmony_ci}
1100141cc406Sopenharmony_ci
1101141cc406Sopenharmony_ci/**
1102141cc406Sopenharmony_ci * Add the given specifications to the current list of supported devices
1103141cc406Sopenharmony_ci * @param vendor_id
1104141cc406Sopenharmony_ci * @param product_id
1105141cc406Sopenharmony_ci * @param model_number
1106141cc406Sopenharmony_ci * @param flags
1107141cc406Sopenharmony_ci * @return
1108141cc406Sopenharmony_ci */
1109141cc406Sopenharmony_ciSANE_Status
1110141cc406Sopenharmony_cisanei_pieusb_supported_device_list_add(SANE_Word vendor_id, SANE_Word product_id, SANE_Int model_number, SANE_Int flags)
1111141cc406Sopenharmony_ci{
1112141cc406Sopenharmony_ci    int i = 0, k;
1113141cc406Sopenharmony_ci    struct Pieusb_USB_Device_Entry* dl;
1114141cc406Sopenharmony_ci
1115141cc406Sopenharmony_ci    while (pieusb_supported_usb_device_list[i].vendor != 0) {
1116141cc406Sopenharmony_ci        i++;
1117141cc406Sopenharmony_ci    }
1118141cc406Sopenharmony_ci    /* i is index of last entry */
1119141cc406Sopenharmony_ci    for (k=0; k<=i; k++) {
1120141cc406Sopenharmony_ci        DBG(DBG_info_proc,"sanei_pieusb_supported_device_list_add(): current %03d: %04x %04x %02x %02x\n", i,
1121141cc406Sopenharmony_ci            pieusb_supported_usb_device_list[k].vendor,
1122141cc406Sopenharmony_ci            pieusb_supported_usb_device_list[k].product,
1123141cc406Sopenharmony_ci            pieusb_supported_usb_device_list[k].model,
1124141cc406Sopenharmony_ci            pieusb_supported_usb_device_list[k].flags);
1125141cc406Sopenharmony_ci    }
1126141cc406Sopenharmony_ci
1127141cc406Sopenharmony_ci    dl = realloc(pieusb_supported_usb_device_list,(i+2)*sizeof(struct Pieusb_USB_Device_Entry)); /* Add one entry to list */
1128141cc406Sopenharmony_ci    if (dl == NULL) {
1129141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
1130141cc406Sopenharmony_ci    }
1131141cc406Sopenharmony_ci    /* Copy values */
1132141cc406Sopenharmony_ci    pieusb_supported_usb_device_list = dl;
1133141cc406Sopenharmony_ci    pieusb_supported_usb_device_list[i].vendor = vendor_id;
1134141cc406Sopenharmony_ci    pieusb_supported_usb_device_list[i].product = product_id;
1135141cc406Sopenharmony_ci    pieusb_supported_usb_device_list[i].model = model_number;
1136141cc406Sopenharmony_ci    pieusb_supported_usb_device_list[i].flags = flags;
1137141cc406Sopenharmony_ci    pieusb_supported_usb_device_list[i+1].vendor = 0;
1138141cc406Sopenharmony_ci    pieusb_supported_usb_device_list[i+1].product = 0;
1139141cc406Sopenharmony_ci    pieusb_supported_usb_device_list[i+1].model = 0;
1140141cc406Sopenharmony_ci    pieusb_supported_usb_device_list[i+1].flags = 0;
1141141cc406Sopenharmony_ci    for (k=0; k<=i+1; k++) {
1142141cc406Sopenharmony_ci        DBG(DBG_info_proc,"sanei_pieusb_supported_device_list_add() add: %03d: %04x %04x %02x %02x\n", i,
1143141cc406Sopenharmony_ci            pieusb_supported_usb_device_list[k].vendor,
1144141cc406Sopenharmony_ci            pieusb_supported_usb_device_list[k].product,
1145141cc406Sopenharmony_ci            pieusb_supported_usb_device_list[k].model,
1146141cc406Sopenharmony_ci            pieusb_supported_usb_device_list[k].flags);
1147141cc406Sopenharmony_ci    }
1148141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
1149141cc406Sopenharmony_ci}
1150141cc406Sopenharmony_ci
1151141cc406Sopenharmony_ci/**
1152141cc406Sopenharmony_ci * Actions to perform when a cancel request has been received.
1153141cc406Sopenharmony_ci *
1154141cc406Sopenharmony_ci * @param scanner scanner to stop scanning
1155141cc406Sopenharmony_ci * @return SANE_STATUS_CANCELLED
1156141cc406Sopenharmony_ci */
1157141cc406Sopenharmony_ciSANE_Status
1158141cc406Sopenharmony_cisanei_pieusb_on_cancel (Pieusb_Scanner * scanner)
1159141cc406Sopenharmony_ci{
1160141cc406Sopenharmony_ci    struct Pieusb_Command_Status status;
1161141cc406Sopenharmony_ci
1162141cc406Sopenharmony_ci    DBG (DBG_info_proc, "sanei_pieusb_on_cancel()\n");
1163141cc406Sopenharmony_ci
1164141cc406Sopenharmony_ci    sanei_pieusb_cmd_stop_scan (scanner->device_number, &status);
1165141cc406Sopenharmony_ci    sanei_pieusb_cmd_set_scan_head (scanner->device_number, 1, 0, &status);
1166141cc406Sopenharmony_ci    sanei_pieusb_buffer_delete (&scanner->buffer);
1167141cc406Sopenharmony_ci    scanner->scanning = SANE_FALSE;
1168141cc406Sopenharmony_ci    return SANE_STATUS_CANCELLED;
1169141cc406Sopenharmony_ci}
1170141cc406Sopenharmony_ci
1171141cc406Sopenharmony_ci/**
1172141cc406Sopenharmony_ci * Determine maximum length of a set of strings.
1173141cc406Sopenharmony_ci *
1174141cc406Sopenharmony_ci * @param strings Set of strings
1175141cc406Sopenharmony_ci * @return maximum length
1176141cc406Sopenharmony_ci */
1177141cc406Sopenharmony_cistatic size_t
1178141cc406Sopenharmony_cimax_string_size (SANE_String_Const const strings[])
1179141cc406Sopenharmony_ci{
1180141cc406Sopenharmony_ci    size_t size, max_size = 0;
1181141cc406Sopenharmony_ci    int i;
1182141cc406Sopenharmony_ci
1183141cc406Sopenharmony_ci    for (i = 0; strings[i]; ++i) {
1184141cc406Sopenharmony_ci        size = strlen (strings[i]) + 1;
1185141cc406Sopenharmony_ci        if (size > max_size) {
1186141cc406Sopenharmony_ci            max_size = size;
1187141cc406Sopenharmony_ci        }
1188141cc406Sopenharmony_ci    }
1189141cc406Sopenharmony_ci
1190141cc406Sopenharmony_ci    return max_size;
1191141cc406Sopenharmony_ci}
1192141cc406Sopenharmony_ci
1193141cc406Sopenharmony_ci/* From MR's pie.c */
1194141cc406Sopenharmony_ci
1195141cc406Sopenharmony_ci/* ------------------------- PIEUSB_CORRECT_SHADING -------------------------- */
1196141cc406Sopenharmony_ci
1197141cc406Sopenharmony_ci/**
1198141cc406Sopenharmony_ci * Correct the given buffer for shading using shading data in scanner.
1199141cc406Sopenharmony_ci * If the loop order is width->color->height, a 7200 dpi scan correction takes
1200141cc406Sopenharmony_ci * 45 minutes. If the loop order is color->height->width, this is less than 3
1201141cc406Sopenharmony_ci * minutes. So it is worthwhile to find the used pixels first (array width_to_loc).
1202141cc406Sopenharmony_ci *
1203141cc406Sopenharmony_ci * @param scanner Scanner
1204141cc406Sopenharmony_ci * @param buffer Buffer to correct
1205141cc406Sopenharmony_ci */
1206141cc406Sopenharmony_civoid
1207141cc406Sopenharmony_cisanei_pieusb_correct_shading(struct Pieusb_Scanner *scanner, struct Pieusb_Read_Buffer *buffer)
1208141cc406Sopenharmony_ci{
1209141cc406Sopenharmony_ci
1210141cc406Sopenharmony_ci    int i, j, c, k;
1211141cc406Sopenharmony_ci    SANE_Uint val, val_org, *p;
1212141cc406Sopenharmony_ci    int *width_to_loc;
1213141cc406Sopenharmony_ci
1214141cc406Sopenharmony_ci    DBG (DBG_info_proc, "sanei_pieusb_correct_shading()\n");
1215141cc406Sopenharmony_ci
1216141cc406Sopenharmony_ci    /* Loop through CCD-mask to find used pixels */
1217141cc406Sopenharmony_ci    width_to_loc = calloc(buffer->width,sizeof(int));
1218141cc406Sopenharmony_ci    j = 0;
1219141cc406Sopenharmony_ci    for (i = 0; i < scanner->ccd_mask_size; i++) {
1220141cc406Sopenharmony_ci        if (scanner->ccd_mask[i] == 0) {
1221141cc406Sopenharmony_ci            width_to_loc[j++] = i;
1222141cc406Sopenharmony_ci        }
1223141cc406Sopenharmony_ci    }
1224141cc406Sopenharmony_ci    /* Correct complete image */
1225141cc406Sopenharmony_ci    for (c = 0; c < buffer->colors; c++) {
1226141cc406Sopenharmony_ci        DBG(DBG_info,"sanei_pieusb_correct_shading() correct color %d\n",c);
1227141cc406Sopenharmony_ci        for (k = 0; k < buffer->height; k++) {
1228141cc406Sopenharmony_ci            /* DBG(DBG_info,"Correct line %d\n",k); */
1229141cc406Sopenharmony_ci            p = buffer->data + c * buffer->width * buffer->height + k * buffer->width;
1230141cc406Sopenharmony_ci            for (j = 0; j < buffer->width; j++) {
1231141cc406Sopenharmony_ci                val_org = *p;
1232141cc406Sopenharmony_ci                val = lround((double)scanner->shading_mean[c] / scanner->shading_ref[c][width_to_loc[j]] * val_org);
1233141cc406Sopenharmony_ci                /* DBG(DBG_info,"Correct [%d,%d,%d] %d -> %d\n",k,j,c,val_org,val); */
1234141cc406Sopenharmony_ci                *p++ = val;
1235141cc406Sopenharmony_ci            }
1236141cc406Sopenharmony_ci        }
1237141cc406Sopenharmony_ci    }
1238141cc406Sopenharmony_ci    /* Free memory */
1239141cc406Sopenharmony_ci    free(width_to_loc);
1240141cc406Sopenharmony_ci}
1241141cc406Sopenharmony_ci
1242141cc406Sopenharmony_ci/* === functions copied from MR's code === */
1243141cc406Sopenharmony_ci
1244141cc406Sopenharmony_ci/**
1245141cc406Sopenharmony_ci *
1246141cc406Sopenharmony_ci * @param scanner
1247141cc406Sopenharmony_ci * @param in_img
1248141cc406Sopenharmony_ci * @param planes
1249141cc406Sopenharmony_ci * @param out_planes
1250141cc406Sopenharmony_ci * @return
1251141cc406Sopenharmony_ci */
1252141cc406Sopenharmony_ciSANE_Status
1253141cc406Sopenharmony_cisanei_pieusb_post (Pieusb_Scanner *scanner, uint16_t **in_img, int planes)
1254141cc406Sopenharmony_ci{
1255141cc406Sopenharmony_ci  uint16_t *cplane[PLANES];    /* R, G, B, I gray scale planes */
1256141cc406Sopenharmony_ci  SANE_Parameters parameters;   /* describes the image */
1257141cc406Sopenharmony_ci  int winsize_smooth;           /* for adapting replaced pixels */
1258141cc406Sopenharmony_ci  char filename[64];
1259141cc406Sopenharmony_ci  SANE_Status status;
1260141cc406Sopenharmony_ci  int smooth, i;
1261141cc406Sopenharmony_ci
1262141cc406Sopenharmony_ci  memcpy (&parameters, &scanner->scan_parameters, sizeof (SANE_Parameters));
1263141cc406Sopenharmony_ci  parameters.format = SANE_FRAME_GRAY;
1264141cc406Sopenharmony_ci  parameters.bytes_per_line = parameters.pixels_per_line;
1265141cc406Sopenharmony_ci  if (parameters.depth > 8)
1266141cc406Sopenharmony_ci    parameters.bytes_per_line *= 2;
1267141cc406Sopenharmony_ci  parameters.last_frame = 0;
1268141cc406Sopenharmony_ci
1269141cc406Sopenharmony_ci  DBG (DBG_info, "pie_usb_post: %d ppl, %d lines, %d bits, %d planes, %d dpi\n",
1270141cc406Sopenharmony_ci       parameters.pixels_per_line, parameters.lines,
1271141cc406Sopenharmony_ci       parameters.depth, planes, scanner->mode.resolution);
1272141cc406Sopenharmony_ci
1273141cc406Sopenharmony_ci  if (planes > PLANES) {
1274141cc406Sopenharmony_ci    DBG (DBG_error, "pie_usb_post: too many planes: %d (max %d)\n", planes, PLANES);
1275141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
1276141cc406Sopenharmony_ci  }
1277141cc406Sopenharmony_ci
1278141cc406Sopenharmony_ci  for (i = 0; i < planes; i++)
1279141cc406Sopenharmony_ci    cplane[i] = in_img[i];
1280141cc406Sopenharmony_ci
1281141cc406Sopenharmony_ci  /* dirt is rather resolution invariant, so
1282141cc406Sopenharmony_ci   * setup resolution dependent parameters
1283141cc406Sopenharmony_ci   */
1284141cc406Sopenharmony_ci  /* film grain reduction */
1285141cc406Sopenharmony_ci  smooth = scanner->val[OPT_SMOOTH_IMAGE].w;
1286141cc406Sopenharmony_ci  winsize_smooth = (scanner->mode.resolution / 540) | 1;
1287141cc406Sopenharmony_ci  /* smoothen whole image or only replaced pixels */
1288141cc406Sopenharmony_ci  if (smooth)
1289141cc406Sopenharmony_ci    {
1290141cc406Sopenharmony_ci      winsize_smooth += 2 * (smooth - 3);       /* even */
1291141cc406Sopenharmony_ci      if (winsize_smooth < 3)
1292141cc406Sopenharmony_ci        smooth = 0;
1293141cc406Sopenharmony_ci    }
1294141cc406Sopenharmony_ci  if (winsize_smooth < 3)
1295141cc406Sopenharmony_ci    winsize_smooth = 3;
1296141cc406Sopenharmony_ci  DBG (DBG_info, "pie_usb_sw_post: winsize_smooth %d\n", winsize_smooth);
1297141cc406Sopenharmony_ci
1298141cc406Sopenharmony_ci  /* RGBI post-processing if selected:
1299141cc406Sopenharmony_ci   * 1) remove spectral overlay from ired plane,
1300141cc406Sopenharmony_ci   * 2) remove dirt, smoothen if, crop if */
1301141cc406Sopenharmony_ci  if (scanner->val[OPT_CORRECT_INFRARED].b) /* (scanner->processing & POST_SW_IRED_MASK) */
1302141cc406Sopenharmony_ci    {
1303141cc406Sopenharmony_ci      /* remove spectral overlay from ired plane */
1304141cc406Sopenharmony_ci      status = sanei_ir_spectral_clean (&parameters, scanner->ln_lut, cplane[0], cplane[3]);
1305141cc406Sopenharmony_ci      if (status != SANE_STATUS_GOOD)
1306141cc406Sopenharmony_ci        return status;
1307141cc406Sopenharmony_ci      if (DBG_LEVEL >= 15)
1308141cc406Sopenharmony_ci        {
1309141cc406Sopenharmony_ci          snprintf (filename, 63, "/tmp/ir-spectral.pnm");
1310141cc406Sopenharmony_ci          pieusb_write_pnm_file (filename, cplane[3],
1311141cc406Sopenharmony_ci                                  parameters.depth, 1,
1312141cc406Sopenharmony_ci                                  parameters.pixels_per_line, parameters.lines);
1313141cc406Sopenharmony_ci        }
1314141cc406Sopenharmony_ci      if (scanner->cancel_request)          /* asynchronous cancel ? */
1315141cc406Sopenharmony_ci        return SANE_STATUS_CANCELLED;
1316141cc406Sopenharmony_ci  } /* scanner-> processing & POST_SW_IRED_MASK */
1317141cc406Sopenharmony_ci
1318141cc406Sopenharmony_ci  /* remove dirt, smoothen if, crop if */
1319141cc406Sopenharmony_ci  if (scanner->val[OPT_CLEAN_IMAGE].b) /* (scanner->processing & POST_SW_DIRT) */
1320141cc406Sopenharmony_ci    {
1321141cc406Sopenharmony_ci      double *norm_histo;
1322141cc406Sopenharmony_ci      uint16_t *thresh_data;
1323141cc406Sopenharmony_ci      int static_thresh, too_thresh;    /* static thresholds */
1324141cc406Sopenharmony_ci      int winsize_filter;               /* primary size of filtering window */
1325141cc406Sopenharmony_ci      int size_dilate;                  /* the dirt mask */
1326141cc406Sopenharmony_ci
1327141cc406Sopenharmony_ci      /* size of filter detecting dirt */
1328141cc406Sopenharmony_ci      winsize_filter = (int) (5.0 * (double) scanner->mode.resolution / 300.0) | 1;
1329141cc406Sopenharmony_ci      if (winsize_filter < 3)
1330141cc406Sopenharmony_ci        winsize_filter = 3;
1331141cc406Sopenharmony_ci      /* dirt usually has smooth edges which also need correction */
1332141cc406Sopenharmony_ci      size_dilate = scanner->mode.resolution / 1000 + 1;
1333141cc406Sopenharmony_ci
1334141cc406Sopenharmony_ci      /* first detect large dirt by a static threshold */
1335141cc406Sopenharmony_ci      status = sanei_ir_create_norm_histogram (&parameters, cplane[3], &norm_histo);
1336141cc406Sopenharmony_ci      if (status != SANE_STATUS_GOOD)
1337141cc406Sopenharmony_ci        {
1338141cc406Sopenharmony_ci          DBG (DBG_error, "pie_usb_sw_post: no buffer\n");
1339141cc406Sopenharmony_ci          return SANE_STATUS_NO_MEM;
1340141cc406Sopenharmony_ci        }
1341141cc406Sopenharmony_ci      /* generate a "bimodal" static threshold */
1342141cc406Sopenharmony_ci      status = sanei_ir_threshold_yen (&parameters, norm_histo, &static_thresh);
1343141cc406Sopenharmony_ci      if (status != SANE_STATUS_GOOD)
1344141cc406Sopenharmony_ci        return status;
1345141cc406Sopenharmony_ci      /* generate traditional static threshold */
1346141cc406Sopenharmony_ci      status = sanei_ir_threshold_otsu (&parameters, norm_histo, &too_thresh);
1347141cc406Sopenharmony_ci      if (status != SANE_STATUS_GOOD)
1348141cc406Sopenharmony_ci        return status;
1349141cc406Sopenharmony_ci      /* choose lower one */
1350141cc406Sopenharmony_ci      if (too_thresh < static_thresh)
1351141cc406Sopenharmony_ci        static_thresh = too_thresh;
1352141cc406Sopenharmony_ci      free (norm_histo);
1353141cc406Sopenharmony_ci
1354141cc406Sopenharmony_ci      /* then generate dirt mask with adaptive thresholding filter
1355141cc406Sopenharmony_ci       * and add the dirt from the static threshold */
1356141cc406Sopenharmony_ci      /* last two parameters: 10, 50 detects more, 20, 75 less */
1357141cc406Sopenharmony_ci      status = sanei_ir_filter_madmean (&parameters, cplane[3], &thresh_data, winsize_filter, 20, 100);
1358141cc406Sopenharmony_ci      if (status != SANE_STATUS_GOOD) {
1359141cc406Sopenharmony_ci        free (thresh_data);
1360141cc406Sopenharmony_ci        return status;
1361141cc406Sopenharmony_ci      }
1362141cc406Sopenharmony_ci      sanei_ir_add_threshold (&parameters, cplane[3], thresh_data, static_thresh);
1363141cc406Sopenharmony_ci      if (DBG_LEVEL >= 15)
1364141cc406Sopenharmony_ci        {
1365141cc406Sopenharmony_ci          snprintf (filename, 63, "/tmp/ir-threshold.pnm");
1366141cc406Sopenharmony_ci          pieusb_write_pnm_file (filename, thresh_data,
1367141cc406Sopenharmony_ci                                  8, 1, parameters.pixels_per_line,
1368141cc406Sopenharmony_ci                                  parameters.lines);
1369141cc406Sopenharmony_ci        }
1370141cc406Sopenharmony_ci      if (scanner->cancel_request) {         /* asynchronous cancel ? */
1371141cc406Sopenharmony_ci        free (thresh_data);
1372141cc406Sopenharmony_ci        return SANE_STATUS_CANCELLED;
1373141cc406Sopenharmony_ci      }
1374141cc406Sopenharmony_ci      /* replace the dirt and smoothen film grain and crop if possible */
1375141cc406Sopenharmony_ci      status = sanei_ir_dilate_mean (&parameters, cplane, thresh_data,
1376141cc406Sopenharmony_ci              500, size_dilate, winsize_smooth, smooth,
1377141cc406Sopenharmony_ci              0, NULL);
1378141cc406Sopenharmony_ci      if (status != SANE_STATUS_GOOD) {
1379141cc406Sopenharmony_ci        free (thresh_data);
1380141cc406Sopenharmony_ci        return status;
1381141cc406Sopenharmony_ci      }
1382141cc406Sopenharmony_ci      smooth = 0;
1383141cc406Sopenharmony_ci      free (thresh_data);
1384141cc406Sopenharmony_ci    }
1385141cc406Sopenharmony_ci
1386141cc406Sopenharmony_ci  if (DBG_LEVEL >= 15)
1387141cc406Sopenharmony_ci    {
1388141cc406Sopenharmony_ci      pieusb_write_pnm_file ("/tmp/RGBi-img.pnm", scanner->buffer.data,
1389141cc406Sopenharmony_ci        scanner->scan_parameters.depth, 3, scanner->scan_parameters.pixels_per_line,
1390141cc406Sopenharmony_ci        scanner->scan_parameters.lines);
1391141cc406Sopenharmony_ci    }
1392141cc406Sopenharmony_ci
1393141cc406Sopenharmony_ci  return status;
1394141cc406Sopenharmony_ci}
1395141cc406Sopenharmony_ci
1396141cc406Sopenharmony_ci/* ------------------------------ PIE_USB_WRITE_PNM_FILE ------------------------------- */
1397141cc406Sopenharmony_cistatic SANE_Status
1398141cc406Sopenharmony_cipieusb_write_pnm_file (char *filename, SANE_Uint *data, int depth,
1399141cc406Sopenharmony_ci                        int channels, int pixels_per_line, int lines)
1400141cc406Sopenharmony_ci{
1401141cc406Sopenharmony_ci  FILE *out;
1402141cc406Sopenharmony_ci  int r, c, ch;
1403141cc406Sopenharmony_ci  SANE_Uint val;
1404141cc406Sopenharmony_ci  uint8_t b = 0;
1405141cc406Sopenharmony_ci
1406141cc406Sopenharmony_ci  DBG (DBG_info_proc,
1407141cc406Sopenharmony_ci       "pie_usb_write_pnm_file: depth=%d, channels=%d, ppl=%d, lines=%d\n",
1408141cc406Sopenharmony_ci       depth, channels, pixels_per_line, lines);
1409141cc406Sopenharmony_ci
1410141cc406Sopenharmony_ci  out = fopen (filename, "w");
1411141cc406Sopenharmony_ci  if (!out)
1412141cc406Sopenharmony_ci    {
1413141cc406Sopenharmony_ci      DBG (DBG_error,
1414141cc406Sopenharmony_ci           "pie_usb_write_pnm_file: could not open %s for writing: %s\n",
1415141cc406Sopenharmony_ci           filename, strerror (errno));
1416141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
1417141cc406Sopenharmony_ci    }
1418141cc406Sopenharmony_ci
1419141cc406Sopenharmony_ci  switch (depth) {
1420141cc406Sopenharmony_ci      case 1:
1421141cc406Sopenharmony_ci          fprintf (out, "P4\n%d\n%d\n", pixels_per_line, lines);
1422141cc406Sopenharmony_ci          for (r = 0; r < lines; r++) {
1423141cc406Sopenharmony_ci              int i;
1424141cc406Sopenharmony_ci              i = 0;
1425141cc406Sopenharmony_ci              b = 0;
1426141cc406Sopenharmony_ci              for (c = 0; c < pixels_per_line; c++) {
1427141cc406Sopenharmony_ci                  val = *(data + r * pixels_per_line + c);
1428141cc406Sopenharmony_ci                  if (val > 0) b |= (0x80 >> i);
1429141cc406Sopenharmony_ci                  i++;
1430141cc406Sopenharmony_ci                  if (i == 7) {
1431141cc406Sopenharmony_ci                      fputc(b, out);
1432141cc406Sopenharmony_ci                      i = 0;
1433141cc406Sopenharmony_ci                      b = 0;
1434141cc406Sopenharmony_ci                  }
1435141cc406Sopenharmony_ci              }
1436141cc406Sopenharmony_ci              if (i != 0) {
1437141cc406Sopenharmony_ci                  fputc(b, out);
1438141cc406Sopenharmony_ci              }
1439141cc406Sopenharmony_ci          }
1440141cc406Sopenharmony_ci          break;
1441141cc406Sopenharmony_ci      case 8:
1442141cc406Sopenharmony_ci          fprintf (out, "P%c\n%d\n%d\n%d\n", channels == 1 ? '5' : '6', pixels_per_line, lines, 255);
1443141cc406Sopenharmony_ci          for (r = 0; r < lines; r++) {
1444141cc406Sopenharmony_ci              for (c = 0; c < pixels_per_line; c++) {
1445141cc406Sopenharmony_ci                  for (ch = 0; ch < channels; ch++) {
1446141cc406Sopenharmony_ci                      val = *(data + ch * lines * pixels_per_line + r * pixels_per_line + c);
1447141cc406Sopenharmony_ci                      b = val & 0xFF;
1448141cc406Sopenharmony_ci                      fputc(b, out);
1449141cc406Sopenharmony_ci                  }
1450141cc406Sopenharmony_ci              }
1451141cc406Sopenharmony_ci          }
1452141cc406Sopenharmony_ci          break;
1453141cc406Sopenharmony_ci      case 16:
1454141cc406Sopenharmony_ci          fprintf (out, "P%c\n%d\n%d\n%d\n", channels == 1 ? '5' : '6', pixels_per_line, lines, 65535);
1455141cc406Sopenharmony_ci          for (r = 0; r < lines; r++) {
1456141cc406Sopenharmony_ci              for (c = 0; c < pixels_per_line; c++) {
1457141cc406Sopenharmony_ci                  for (ch = 0; ch < channels; ch++) {
1458141cc406Sopenharmony_ci                      val = *(data + ch * lines * pixels_per_line + r * pixels_per_line + c);
1459141cc406Sopenharmony_ci                      b = (val >> 8) & 0xFF;
1460141cc406Sopenharmony_ci                      fputc(b, out);
1461141cc406Sopenharmony_ci                      b = val & 0xFF;
1462141cc406Sopenharmony_ci                      fputc(b, out);
1463141cc406Sopenharmony_ci                  }
1464141cc406Sopenharmony_ci              }
1465141cc406Sopenharmony_ci          }
1466141cc406Sopenharmony_ci          break;
1467141cc406Sopenharmony_ci      default:
1468141cc406Sopenharmony_ci          DBG (DBG_error, "pie_usb_write_pnm_file: depth %d not implemented\n", depth);
1469141cc406Sopenharmony_ci  }
1470141cc406Sopenharmony_ci  fclose (out);
1471141cc406Sopenharmony_ci
1472141cc406Sopenharmony_ci  DBG (DBG_info, "pie_usb_write_pnm_file: finished\n");
1473141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
1474141cc406Sopenharmony_ci}
1475141cc406Sopenharmony_ci
1476141cc406Sopenharmony_ci/**
1477141cc406Sopenharmony_ci * Check option inconsistencies.
1478141cc406Sopenharmony_ci * In most cases an inconsistency can be solved by ignoring an option setting.
1479141cc406Sopenharmony_ci * Message these situations and return 1 to indicate we can work with the
1480141cc406Sopenharmony_ci * current set op options. If the settings are really inconsistent, return 0.
1481141cc406Sopenharmony_ci */
1482141cc406Sopenharmony_ciint
1483141cc406Sopenharmony_cisanei_pieusb_analyse_options(struct Pieusb_Scanner *scanner)
1484141cc406Sopenharmony_ci{
1485141cc406Sopenharmony_ci    /* Checks*/
1486141cc406Sopenharmony_ci    if (scanner->val[OPT_TL_X].w > scanner->val[OPT_BR_X].w) {
1487141cc406Sopenharmony_ci        DBG (DBG_error, "sane_start: %s (%.1f mm) is bigger than %s (%.1f mm) -- aborting\n",
1488141cc406Sopenharmony_ci	   scanner->opt[OPT_TL_X].title,
1489141cc406Sopenharmony_ci	   SANE_UNFIX (scanner->val[OPT_TL_X].w),
1490141cc406Sopenharmony_ci	   scanner->opt[OPT_BR_X].title,
1491141cc406Sopenharmony_ci	   SANE_UNFIX (scanner->val[OPT_BR_X].w));
1492141cc406Sopenharmony_ci        return 0;
1493141cc406Sopenharmony_ci    }
1494141cc406Sopenharmony_ci    if (scanner->val[OPT_TL_Y].w > scanner->val[OPT_BR_Y].w) {
1495141cc406Sopenharmony_ci        DBG (DBG_error, "sane_start: %s (%.1f mm) is bigger than %s (%.1f mm) -- aborting\n",
1496141cc406Sopenharmony_ci	   scanner->opt[OPT_TL_Y].title,
1497141cc406Sopenharmony_ci	   SANE_UNFIX (scanner->val[OPT_TL_Y].w),
1498141cc406Sopenharmony_ci	   scanner->opt[OPT_BR_Y].title,
1499141cc406Sopenharmony_ci	   SANE_UNFIX (scanner->val[OPT_BR_Y].w));
1500141cc406Sopenharmony_ci        return 0;
1501141cc406Sopenharmony_ci    }
1502141cc406Sopenharmony_ci    /* Modes sometimes limit other choices */
1503141cc406Sopenharmony_ci    if (scanner->val[OPT_PREVIEW].b) {
1504141cc406Sopenharmony_ci        /* Preview uses its own specific settings */
1505141cc406Sopenharmony_ci        if (scanner->val[OPT_RESOLUTION].w != (scanner->device->fast_preview_resolution << SANE_FIXED_SCALE_SHIFT)) {
1506141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %f ignored during preview\n", scanner->opt[OPT_RESOLUTION].name, SANE_UNFIX(scanner->val[OPT_RESOLUTION].w));
1507141cc406Sopenharmony_ci        }
1508141cc406Sopenharmony_ci        if (scanner->val[OPT_SHARPEN].b) {
1509141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored during preview\n", scanner->opt[OPT_SHARPEN].name, scanner->val[OPT_SHARPEN].b);
1510141cc406Sopenharmony_ci        }
1511141cc406Sopenharmony_ci        if (!scanner->val[OPT_FAST_INFRARED].b) {
1512141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored during preview\n", scanner->opt[OPT_FAST_INFRARED].name, scanner->val[OPT_FAST_INFRARED].b);
1513141cc406Sopenharmony_ci        }
1514141cc406Sopenharmony_ci        if (scanner->val[OPT_CORRECT_INFRARED].b) {
1515141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored during preview\n", scanner->opt[OPT_CORRECT_INFRARED].name, scanner->val[OPT_CORRECT_INFRARED].b);
1516141cc406Sopenharmony_ci        }
1517141cc406Sopenharmony_ci        if (scanner->val[OPT_CLEAN_IMAGE].b) {
1518141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored during preview\n", scanner->opt[OPT_CLEAN_IMAGE].name, scanner->val[OPT_CLEAN_IMAGE].b);
1519141cc406Sopenharmony_ci        }
1520141cc406Sopenharmony_ci        if (scanner->val[OPT_SMOOTH_IMAGE].w != 0) {
1521141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored during preview\n", scanner->opt[OPT_SMOOTH_IMAGE].name, scanner->val[OPT_SMOOTH_IMAGE].w);
1522141cc406Sopenharmony_ci        }
1523141cc406Sopenharmony_ci        if (strcmp(scanner->val[OPT_CROP_IMAGE].s, scanner->device->crop_sw_list[0]) != 0) {
1524141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %s ignored during preview\n", scanner->opt[OPT_CROP_IMAGE].name, scanner->val[OPT_CROP_IMAGE].s);
1525141cc406Sopenharmony_ci        }
1526141cc406Sopenharmony_ci        if (scanner->val[OPT_TRANSFORM_TO_SRGB].b) {
1527141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored during preview\n", scanner->opt[OPT_TRANSFORM_TO_SRGB].name, scanner->val[OPT_TRANSFORM_TO_SRGB].w);
1528141cc406Sopenharmony_ci        }
1529141cc406Sopenharmony_ci        if (scanner->val[OPT_INVERT_IMAGE].w) {
1530141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored during preview\n", scanner->opt[OPT_INVERT_IMAGE].name, scanner->val[OPT_INVERT_IMAGE].w);
1531141cc406Sopenharmony_ci        }
1532141cc406Sopenharmony_ci    } else if (strcmp(scanner->val[OPT_MODE].s,SANE_VALUE_SCAN_MODE_LINEART)==0) {
1533141cc406Sopenharmony_ci        /* Can we do any post processing in lineart? Needs testing to see what's possible */
1534141cc406Sopenharmony_ci        if (scanner->val[OPT_BIT_DEPTH].w != 1) {
1535141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in lineart mode (will use 1)\n", scanner->opt[OPT_BIT_DEPTH].name, scanner->val[OPT_BIT_DEPTH].w);
1536141cc406Sopenharmony_ci        }
1537141cc406Sopenharmony_ci        if (!scanner->val[OPT_FAST_INFRARED].b) {
1538141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in lineart mode (irrelevant)\n", scanner->opt[OPT_FAST_INFRARED].name, scanner->val[OPT_FAST_INFRARED].b);
1539141cc406Sopenharmony_ci        }
1540141cc406Sopenharmony_ci        if (!scanner->val[OPT_CORRECT_SHADING].b) {
1541141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in lineart mode (irrelevant)\n", scanner->opt[OPT_CORRECT_SHADING].name, scanner->val[OPT_CORRECT_SHADING].b);
1542141cc406Sopenharmony_ci        }
1543141cc406Sopenharmony_ci        if (!scanner->val[OPT_CORRECT_INFRARED].b) {
1544141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in lineart mode (irrelevant)\n", scanner->opt[OPT_CORRECT_INFRARED].name, scanner->val[OPT_CORRECT_INFRARED].b);
1545141cc406Sopenharmony_ci        }
1546141cc406Sopenharmony_ci        if (scanner->val[OPT_CLEAN_IMAGE].b) {
1547141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in lineart mode (irrelevant)\n", scanner->opt[OPT_CLEAN_IMAGE].name, scanner->val[OPT_CLEAN_IMAGE].b);
1548141cc406Sopenharmony_ci        }
1549141cc406Sopenharmony_ci        if (scanner->val[OPT_SMOOTH_IMAGE].w != 0) {
1550141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in lineart mode (irrelevant)\n", scanner->opt[OPT_SMOOTH_IMAGE].name, scanner->val[OPT_SMOOTH_IMAGE].w);
1551141cc406Sopenharmony_ci        }
1552141cc406Sopenharmony_ci        if (strcmp(scanner->val[OPT_CROP_IMAGE].s, scanner->device->crop_sw_list[0]) != 0) {
1553141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %s ignored in lineart mode (irrelevant)\n", scanner->opt[OPT_CROP_IMAGE].name, scanner->val[OPT_CROP_IMAGE].s);
1554141cc406Sopenharmony_ci        }
1555141cc406Sopenharmony_ci        if (scanner->val[OPT_TRANSFORM_TO_SRGB].b) {
1556141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in lineart mode (irrelevant)\n", scanner->opt[OPT_TRANSFORM_TO_SRGB].name, scanner->val[OPT_TRANSFORM_TO_SRGB].w);
1557141cc406Sopenharmony_ci        }
1558141cc406Sopenharmony_ci    } else if (strcmp(scanner->val[OPT_MODE].s,SANE_VALUE_SCAN_MODE_HALFTONE)==0) {
1559141cc406Sopenharmony_ci        /* Can we do any post processing in halftone? Needs testing to see what's possible */
1560141cc406Sopenharmony_ci        if (scanner->val[OPT_BIT_DEPTH].w != 1) {
1561141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in halftone mode (will use 1)\n", scanner->opt[OPT_BIT_DEPTH].name, scanner->val[OPT_BIT_DEPTH].w);
1562141cc406Sopenharmony_ci        }
1563141cc406Sopenharmony_ci        if (!scanner->val[OPT_FAST_INFRARED].b) {
1564141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in halftone mode (irrelevant)\n", scanner->opt[OPT_FAST_INFRARED].name, scanner->val[OPT_FAST_INFRARED].b);
1565141cc406Sopenharmony_ci        }
1566141cc406Sopenharmony_ci        if (!scanner->val[OPT_CORRECT_SHADING].b) {
1567141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in halftone mode (irrelevant)\n", scanner->opt[OPT_CORRECT_SHADING].name, scanner->val[OPT_CORRECT_SHADING].b);
1568141cc406Sopenharmony_ci        }
1569141cc406Sopenharmony_ci        if (!scanner->val[OPT_CORRECT_INFRARED].b) {
1570141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in halftone mode (irrelevant)\n", scanner->opt[OPT_CORRECT_INFRARED].name, scanner->val[OPT_CORRECT_INFRARED].b);
1571141cc406Sopenharmony_ci        }
1572141cc406Sopenharmony_ci        if (scanner->val[OPT_CLEAN_IMAGE].b) {
1573141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in halftone mode (irrelevant)\n", scanner->opt[OPT_CLEAN_IMAGE].name, scanner->val[OPT_CLEAN_IMAGE].b);
1574141cc406Sopenharmony_ci        }
1575141cc406Sopenharmony_ci        if (scanner->val[OPT_SMOOTH_IMAGE].w != 0) {
1576141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in halftone mode (irrelevant)\n", scanner->opt[OPT_SMOOTH_IMAGE].name, scanner->val[OPT_SMOOTH_IMAGE].w);
1577141cc406Sopenharmony_ci        }
1578141cc406Sopenharmony_ci        if (strcmp(scanner->val[OPT_CROP_IMAGE].s, scanner->device->crop_sw_list[0]) != 0) {
1579141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %s ignored in halftone mode (irrelevant)\n", scanner->opt[OPT_CROP_IMAGE].name, scanner->val[OPT_CROP_IMAGE].s);
1580141cc406Sopenharmony_ci        }
1581141cc406Sopenharmony_ci        if (scanner->val[OPT_TRANSFORM_TO_SRGB].b) {
1582141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in halftone mode (irrelevant)\n", scanner->opt[OPT_TRANSFORM_TO_SRGB].name, scanner->val[OPT_TRANSFORM_TO_SRGB].w);
1583141cc406Sopenharmony_ci        }
1584141cc406Sopenharmony_ci    } else if (strcmp(scanner->val[OPT_MODE].s,SANE_VALUE_SCAN_MODE_GRAY)==0) {
1585141cc406Sopenharmony_ci        /* Can we do any post processing in gray mode? */
1586141cc406Sopenharmony_ci        /* Can we obtain a single color channel in this mode? How? */
1587141cc406Sopenharmony_ci        /* Is this just RGB with luminance trasformation? */
1588141cc406Sopenharmony_ci        /* Needs testing to see what's possible */
1589141cc406Sopenharmony_ci        /* Only do 8 or 16 bit scans */
1590141cc406Sopenharmony_ci        if (scanner->val[OPT_BIT_DEPTH].w == 1) {
1591141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in gray mode (will use 8)\n", scanner->opt[OPT_BIT_DEPTH].name, scanner->val[OPT_BIT_DEPTH].w);
1592141cc406Sopenharmony_ci        }
1593141cc406Sopenharmony_ci        if (!scanner->val[OPT_FAST_INFRARED].b) {
1594141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in gray mode (irrelevant)\n", scanner->opt[OPT_FAST_INFRARED].name, scanner->val[OPT_FAST_INFRARED].b);
1595141cc406Sopenharmony_ci        }
1596141cc406Sopenharmony_ci        if (!scanner->val[OPT_CORRECT_INFRARED].b) {
1597141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in gray mode (irrelevant)\n", scanner->opt[OPT_CORRECT_INFRARED].name, scanner->val[OPT_CORRECT_INFRARED].b);
1598141cc406Sopenharmony_ci        }
1599141cc406Sopenharmony_ci        if (scanner->val[OPT_CLEAN_IMAGE].b) {
1600141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in gray mode (irrelevant)\n", scanner->opt[OPT_CLEAN_IMAGE].name, scanner->val[OPT_CLEAN_IMAGE].b);
1601141cc406Sopenharmony_ci        }
1602141cc406Sopenharmony_ci        if (scanner->val[OPT_TRANSFORM_TO_SRGB].b) {
1603141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in gray mode (irrelevant)\n", scanner->opt[OPT_TRANSFORM_TO_SRGB].name, scanner->val[OPT_TRANSFORM_TO_SRGB].w);
1604141cc406Sopenharmony_ci        }
1605141cc406Sopenharmony_ci    } else if (strcmp(scanner->val[OPT_MODE].s,SANE_VALUE_SCAN_MODE_COLOR)==0) {
1606141cc406Sopenharmony_ci        /* Some options require infrared data to be obtained, so all infrared options are relevant */
1607141cc406Sopenharmony_ci        /* Only do 8 or 16 bit scans */
1608141cc406Sopenharmony_ci        if (scanner->val[OPT_BIT_DEPTH].w == 1) {
1609141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in color mode (will use 8)\n", scanner->opt[OPT_BIT_DEPTH].name, scanner->val[OPT_BIT_DEPTH].w);
1610141cc406Sopenharmony_ci        }
1611141cc406Sopenharmony_ci    } else if (strcmp(scanner->val[OPT_MODE].s,SANE_VALUE_SCAN_MODE_RGBI)==0) {
1612141cc406Sopenharmony_ci        /* Only do 8 or 16 bit scans */
1613141cc406Sopenharmony_ci        if (scanner->val[OPT_BIT_DEPTH].w == 1) {
1614141cc406Sopenharmony_ci            DBG (DBG_info_sane, "Option %s = %d ignored in color mode (will use 8)\n", scanner->opt[OPT_BIT_DEPTH].name, scanner->val[OPT_BIT_DEPTH].w);
1615141cc406Sopenharmony_ci        }
1616141cc406Sopenharmony_ci    }
1617141cc406Sopenharmony_ci
1618141cc406Sopenharmony_ci    return 1;
1619141cc406Sopenharmony_ci}
1620141cc406Sopenharmony_ci
1621141cc406Sopenharmony_ci/**
1622141cc406Sopenharmony_ci * Print options
1623141cc406Sopenharmony_ci *
1624141cc406Sopenharmony_ci * @param scanner
1625141cc406Sopenharmony_ci */
1626141cc406Sopenharmony_civoid
1627141cc406Sopenharmony_cisanei_pieusb_print_options(struct Pieusb_Scanner *scanner)
1628141cc406Sopenharmony_ci{
1629141cc406Sopenharmony_ci    int k;
1630141cc406Sopenharmony_ci    /* List current options and values */
1631141cc406Sopenharmony_ci    DBG (DBG_info, "Num options = %d\n", scanner->val[OPT_NUM_OPTS].w);
1632141cc406Sopenharmony_ci    for (k = 1; k < scanner->val[OPT_NUM_OPTS].w; k++) {
1633141cc406Sopenharmony_ci        switch (scanner->opt[k].type) {
1634141cc406Sopenharmony_ci            case SANE_TYPE_BOOL:
1635141cc406Sopenharmony_ci                DBG(DBG_info,"  Option %d: %s = %d\n", k, scanner->opt[k].name, scanner->val[k].b);
1636141cc406Sopenharmony_ci                break;
1637141cc406Sopenharmony_ci            case SANE_TYPE_INT:
1638141cc406Sopenharmony_ci	        DBG(DBG_info,"  Option %d: %s = %d\n", k, scanner->opt[k].name, scanner->val[k].w);
1639141cc406Sopenharmony_ci                break;
1640141cc406Sopenharmony_ci            case SANE_TYPE_FIXED:
1641141cc406Sopenharmony_ci                DBG(DBG_info,"  Option %d: %s = %f\n", k, scanner->opt[k].name, SANE_UNFIX (scanner->val[k].w));
1642141cc406Sopenharmony_ci                break;
1643141cc406Sopenharmony_ci            case SANE_TYPE_STRING:
1644141cc406Sopenharmony_ci                DBG(DBG_info,"  Option %d: %s = %s\n", k, scanner->opt[k].name, scanner->val[k].s);
1645141cc406Sopenharmony_ci                break;
1646141cc406Sopenharmony_ci            case SANE_TYPE_GROUP:
1647141cc406Sopenharmony_ci                DBG(DBG_info,"  Option %d: %s = %s\n", k, scanner->opt[k].title, scanner->val[k].s);
1648141cc406Sopenharmony_ci	        break;
1649141cc406Sopenharmony_ci            default:
1650141cc406Sopenharmony_ci                DBG(DBG_info,"  Option %d: %s unknown type %d\n", k, scanner->opt[k].name, scanner->opt[k].type);
1651141cc406Sopenharmony_ci                break;
1652141cc406Sopenharmony_ci        }
1653141cc406Sopenharmony_ci    }
1654141cc406Sopenharmony_ci}
1655141cc406Sopenharmony_ci
1656141cc406Sopenharmony_ci/**
1657141cc406Sopenharmony_ci * Calculate reference values for each pixel, line means and line maxima.
1658141cc406Sopenharmony_ci * We have got 45 lines for all four colors and for each CCD pixel.
1659141cc406Sopenharmony_ci * The reference value for each pixel is the 45-line average for that
1660141cc406Sopenharmony_ci * pixel, for each color separately.
1661141cc406Sopenharmony_ci *
1662141cc406Sopenharmony_ci * @param scanner
1663141cc406Sopenharmony_ci * @param buffer
1664141cc406Sopenharmony_ci */
1665141cc406Sopenharmony_cistatic void pieusb_calculate_shading(struct Pieusb_Scanner *scanner, SANE_Byte* buffer)
1666141cc406Sopenharmony_ci{
1667141cc406Sopenharmony_ci    int k, m;
1668141cc406Sopenharmony_ci    SANE_Byte* p;
1669141cc406Sopenharmony_ci    SANE_Int ci, val;
1670141cc406Sopenharmony_ci    SANE_Int shading_width = scanner->device->shading_parameters[0].pixelsPerLine;
1671141cc406Sopenharmony_ci    SANE_Int shading_height = scanner->device->shading_parameters[0].nLines;
1672141cc406Sopenharmony_ci
1673141cc406Sopenharmony_ci    /* Initialize all to 0 */
1674141cc406Sopenharmony_ci    for (k = 0; k < SHADING_PARAMETERS_INFO_COUNT; k++) {
1675141cc406Sopenharmony_ci        scanner->shading_max[k] = 0;
1676141cc406Sopenharmony_ci        scanner->shading_mean[k] = 0;
1677141cc406Sopenharmony_ci        memset(scanner->shading_ref[k], 0, shading_width * sizeof (SANE_Int));
1678141cc406Sopenharmony_ci    }
1679141cc406Sopenharmony_ci    /* Process data from buffer */
1680141cc406Sopenharmony_ci    p = buffer;
1681141cc406Sopenharmony_ci    switch (scanner->mode.colorFormat) {
1682141cc406Sopenharmony_ci        case 0x01: /* Pixel */
1683141cc406Sopenharmony_ci            /* Process pixel by pixel */
1684141cc406Sopenharmony_ci            for (k = 0; k < shading_height; k++) {
1685141cc406Sopenharmony_ci                for (m = 0; m < shading_width; m++) {
1686141cc406Sopenharmony_ci                    for (ci = 0; ci < SHADING_PARAMETERS_INFO_COUNT; ci++) {
1687141cc406Sopenharmony_ci                        val = *(p) + *(p+1) * 256;
1688141cc406Sopenharmony_ci                        scanner->shading_ref[ci][m] += val;
1689141cc406Sopenharmony_ci                        scanner->shading_max[ci] = scanner->shading_max[ci] < val ? val : scanner->shading_max[ci];
1690141cc406Sopenharmony_ci                        p += 2;
1691141cc406Sopenharmony_ci                    }
1692141cc406Sopenharmony_ci                }
1693141cc406Sopenharmony_ci            }
1694141cc406Sopenharmony_ci            break;
1695141cc406Sopenharmony_ci        case 0x04: /* Indexed */
1696141cc406Sopenharmony_ci            /* Process each line in the sequence found in the buffer */
1697141cc406Sopenharmony_ci            for (k = 0; k < shading_height*4; k++) {
1698141cc406Sopenharmony_ci                /* Save at right color */
1699141cc406Sopenharmony_ci                switch (*p) {
1700141cc406Sopenharmony_ci                    case 'R': ci = 0; break;
1701141cc406Sopenharmony_ci                    case 'G': ci = 1; break;
1702141cc406Sopenharmony_ci                    case 'B': ci = 2; break;
1703141cc406Sopenharmony_ci                    case 'I': ci = 3; break;
1704141cc406Sopenharmony_ci                    default: ci = -1; break; /* ignore line */
1705141cc406Sopenharmony_ci                }
1706141cc406Sopenharmony_ci                /* Add scanned data to reference line and keep track of maximum */
1707141cc406Sopenharmony_ci                if (ci != -1) {
1708141cc406Sopenharmony_ci                    for (m = 0; m < shading_width; m++) {
1709141cc406Sopenharmony_ci                        val = *(p+2+2*m) + *(p+2+2*m+1) * 256;
1710141cc406Sopenharmony_ci                        scanner->shading_ref[ci][m] += val;
1711141cc406Sopenharmony_ci                        scanner->shading_max[ci] = scanner->shading_max[ci] < val ? val : scanner->shading_max[ci];
1712141cc406Sopenharmony_ci                        /* DBG(DBG_error,"%02d Shading_ref[%d][%d] = %d\n",k,ci,m,scanner->shading_ref[ci][m]); */
1713141cc406Sopenharmony_ci                    }
1714141cc406Sopenharmony_ci                }
1715141cc406Sopenharmony_ci                /* Next line */
1716141cc406Sopenharmony_ci                p += 2*shading_width+2;
1717141cc406Sopenharmony_ci            }
1718141cc406Sopenharmony_ci            break;
1719141cc406Sopenharmony_ci        default:
1720141cc406Sopenharmony_ci            DBG (DBG_error,"sane_start(): color format %d not implemented\n",scanner->mode.colorFormat);
1721141cc406Sopenharmony_ci            return;
1722141cc406Sopenharmony_ci    }
1723141cc406Sopenharmony_ci    /* Mean reference value needs division */
1724141cc406Sopenharmony_ci    for (k = 0; k < SHADING_PARAMETERS_INFO_COUNT; k++) {
1725141cc406Sopenharmony_ci        for (m = 0; m < shading_width; m++) {
1726141cc406Sopenharmony_ci            scanner->shading_ref[k][m] = lround((double)scanner->shading_ref[k][m]/shading_height);
1727141cc406Sopenharmony_ci            /* DBG(DBG_error,"Shading_ref[%d][%d] = %d\n",k,m,scanner->shading_ref[k][m]); */
1728141cc406Sopenharmony_ci        }
1729141cc406Sopenharmony_ci    }
1730141cc406Sopenharmony_ci    /* Overall means */
1731141cc406Sopenharmony_ci    for (k = 0; k < SHADING_PARAMETERS_INFO_COUNT; k++) {
1732141cc406Sopenharmony_ci        for (m=0; m<shading_width; m++) {
1733141cc406Sopenharmony_ci            scanner->shading_mean[k] += scanner->shading_ref[k][m];
1734141cc406Sopenharmony_ci        }
1735141cc406Sopenharmony_ci        scanner->shading_mean[k] = lround((double)scanner->shading_mean[k]/shading_width);
1736141cc406Sopenharmony_ci        DBG (DBG_error,"Shading_mean[%d] = %d\n",k,scanner->shading_mean[k]);
1737141cc406Sopenharmony_ci    }
1738141cc406Sopenharmony_ci
1739141cc406Sopenharmony_ci    /* Set shading data present */
1740141cc406Sopenharmony_ci    scanner->shading_data_present = SANE_TRUE;
1741141cc406Sopenharmony_ci
1742141cc406Sopenharmony_ci    /* Export shading data as TIFF */
1743141cc406Sopenharmony_ci#ifdef CAN_DO_4_CHANNEL_TIFF
1744141cc406Sopenharmony_ci    if (scanner->val[OPT_SAVE_SHADINGDATA].b) {
1745141cc406Sopenharmony_ci        struct Pieusb_Read_Buffer shading;
1746141cc406Sopenharmony_ci        SANE_Byte* lboff = buffer;
1747141cc406Sopenharmony_ci        SANE_Int bpl = shading_width*2;
1748141cc406Sopenharmony_ci        SANE_Int n;
1749141cc406Sopenharmony_ci        buffer_create(&shading, shading_width, shading_height, 0x0F, 16);
1750141cc406Sopenharmony_ci        for (n=0; n<4*shading_height; n++) {
1751141cc406Sopenharmony_ci            if (buffer_put_single_color_line(&shading, *lboff, lboff+2, bpl) == 0) {
1752141cc406Sopenharmony_ci                break;
1753141cc406Sopenharmony_ci            }
1754141cc406Sopenharmony_ci            lboff += (bpl + 2);
1755141cc406Sopenharmony_ci        }
1756141cc406Sopenharmony_ci        FILE* fs = fopen("pieusb.shading", "w");
1757141cc406Sopenharmony_ci        /* write_tiff_rgbi_header (fs, shading_width, shading_height, 16, 3600, NULL); */
1758141cc406Sopenharmony_ci        fwrite(shading.data, 1, shading.image_size_bytes, fs);
1759141cc406Sopenharmony_ci        fclose(fs);
1760141cc406Sopenharmony_ci        buffer_delete(&shading);
1761141cc406Sopenharmony_ci    }
1762141cc406Sopenharmony_ci#endif
1763141cc406Sopenharmony_ci
1764141cc406Sopenharmony_ci}
1765141cc406Sopenharmony_ci
1766141cc406Sopenharmony_ci/*
1767141cc406Sopenharmony_ci * Set frame (from scanner options)
1768141cc406Sopenharmony_ci */
1769141cc406Sopenharmony_ci
1770141cc406Sopenharmony_ciSANE_Status
1771141cc406Sopenharmony_cisanei_pieusb_set_frame_from_options(Pieusb_Scanner * scanner)
1772141cc406Sopenharmony_ci{
1773141cc406Sopenharmony_ci    double dpmm;
1774141cc406Sopenharmony_ci    struct Pieusb_Command_Status status;
1775141cc406Sopenharmony_ci
1776141cc406Sopenharmony_ci    dpmm = (double) scanner->device->maximum_resolution / MM_PER_INCH;
1777141cc406Sopenharmony_ci    scanner->frame.x0 = SANE_UNFIX(scanner->val[OPT_TL_X].w) * dpmm;
1778141cc406Sopenharmony_ci    scanner->frame.y0 = SANE_UNFIX(scanner->val[OPT_TL_Y].w) * dpmm;
1779141cc406Sopenharmony_ci    scanner->frame.x1 = SANE_UNFIX(scanner->val[OPT_BR_X].w) * dpmm;
1780141cc406Sopenharmony_ci    scanner->frame.y1 = SANE_UNFIX(scanner->val[OPT_BR_Y].w) * dpmm;
1781141cc406Sopenharmony_ci    scanner->frame.index = 0x80; /* 0x80: value from cyberview */
1782141cc406Sopenharmony_ci    sanei_pieusb_cmd_set_scan_frame (scanner->device_number, scanner->frame.index, &(scanner->frame), &status);
1783141cc406Sopenharmony_ci    DBG (DBG_info_sane, "sanei_pieusb_set_frame_from_options(): sanei_pieusb_cmd_set_scan_frame status %s\n", sane_strstatus (sanei_pieusb_convert_status (status.pieusb_status)));
1784141cc406Sopenharmony_ci    return sanei_pieusb_convert_status (status.pieusb_status);
1785141cc406Sopenharmony_ci}
1786141cc406Sopenharmony_ci
1787141cc406Sopenharmony_ci/*
1788141cc406Sopenharmony_ci * Set mode (from scanner options)
1789141cc406Sopenharmony_ci */
1790141cc406Sopenharmony_ci
1791141cc406Sopenharmony_ciSANE_Status
1792141cc406Sopenharmony_cisanei_pieusb_set_mode_from_options(Pieusb_Scanner * scanner)
1793141cc406Sopenharmony_ci{
1794141cc406Sopenharmony_ci    struct Pieusb_Command_Status status;
1795141cc406Sopenharmony_ci    const char *mode;
1796141cc406Sopenharmony_ci    SANE_Status res;
1797141cc406Sopenharmony_ci
1798141cc406Sopenharmony_ci    mode = scanner->val[OPT_MODE].s;
1799141cc406Sopenharmony_ci    if (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) {
1800141cc406Sopenharmony_ci        scanner->mode.passes = SCAN_FILTER_GREEN; /* G */
1801141cc406Sopenharmony_ci        scanner->mode.colorFormat = SCAN_COLOR_FORMAT_PIXEL;
1802141cc406Sopenharmony_ci    } else if(strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0) {
1803141cc406Sopenharmony_ci        scanner->mode.passes = SCAN_FILTER_GREEN; /* G */
1804141cc406Sopenharmony_ci        scanner->mode.colorFormat = SCAN_COLOR_FORMAT_PIXEL;
1805141cc406Sopenharmony_ci    } else if(strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) {
1806141cc406Sopenharmony_ci        scanner->mode.passes = SCAN_FILTER_GREEN; /* G=gray; unable to get R & B & I to work */
1807141cc406Sopenharmony_ci        scanner->mode.colorFormat = SCAN_COLOR_FORMAT_PIXEL;
1808141cc406Sopenharmony_ci    } else if(scanner->val[OPT_PREVIEW].b) {
1809141cc406Sopenharmony_ci        /* Catch preview here, otherwise next ifs get complicated */
1810141cc406Sopenharmony_ci        scanner->mode.passes = SCAN_ONE_PASS_COLOR;
1811141cc406Sopenharmony_ci        scanner->mode.colorFormat = SCAN_COLOR_FORMAT_INDEX; /* pixel format might be an alternative */
1812141cc406Sopenharmony_ci    } else if(strcmp (mode, SANE_VALUE_SCAN_MODE_RGBI) == 0) {
1813141cc406Sopenharmony_ci        scanner->mode.passes = SCAN_ONE_PASS_RGBI;
1814141cc406Sopenharmony_ci        scanner->mode.colorFormat = SCAN_COLOR_FORMAT_INDEX;
1815141cc406Sopenharmony_ci    } else if(strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0 && scanner->val[OPT_CLEAN_IMAGE].b) {
1816141cc406Sopenharmony_ci        scanner->mode.passes = SCAN_ONE_PASS_RGBI; /* Need infrared for cleaning */
1817141cc406Sopenharmony_ci        scanner->mode.colorFormat = SCAN_COLOR_FORMAT_INDEX;
1818141cc406Sopenharmony_ci    } else { /* SANE_VALUE_SCAN_MODE_COLOR */
1819141cc406Sopenharmony_ci        scanner->mode.passes = SCAN_ONE_PASS_COLOR;
1820141cc406Sopenharmony_ci        scanner->mode.colorFormat = SCAN_COLOR_FORMAT_INDEX; /* pixel format might be an alternative */
1821141cc406Sopenharmony_ci    }
1822141cc406Sopenharmony_ci    /* Resolution */
1823141cc406Sopenharmony_ci    if (scanner->val[OPT_PREVIEW].b) {
1824141cc406Sopenharmony_ci        scanner->mode.resolution = scanner->device->fast_preview_resolution;
1825141cc406Sopenharmony_ci        DBG (DBG_info_sane, "sanei_pieusb_set_mode_from_options(): resolution fast preview (%d)\n", scanner->mode.resolution);
1826141cc406Sopenharmony_ci    } else {
1827141cc406Sopenharmony_ci        scanner->mode.resolution = SANE_UNFIX (scanner->val[OPT_RESOLUTION].w);
1828141cc406Sopenharmony_ci        DBG (DBG_info_sane, "sanei_pieusb_set_mode_from_options(): resolution from option setting (%d)\n", scanner->mode.resolution);
1829141cc406Sopenharmony_ci    }
1830141cc406Sopenharmony_ci    /* Bit depth: exit on untested values */
1831141cc406Sopenharmony_ci    switch (scanner->val[OPT_BIT_DEPTH].w) {
1832141cc406Sopenharmony_ci        case 1: scanner->mode.colorDepth = SCAN_COLOR_DEPTH_1; break;
1833141cc406Sopenharmony_ci        case 8: scanner->mode.colorDepth = SCAN_COLOR_DEPTH_8; break;
1834141cc406Sopenharmony_ci        case 16: scanner->mode.colorDepth = SCAN_COLOR_DEPTH_16; break;
1835141cc406Sopenharmony_ci        default: /* 4, 10 & 12 */
1836141cc406Sopenharmony_ci            DBG (DBG_error, "sanei_pieusb_set_mode_from_options(): sanei_pieusb_cmd_set_scan_frame untested bit depth %d\n", scanner->val[OPT_BIT_DEPTH].w);
1837141cc406Sopenharmony_ci            return SANE_STATUS_INVAL;
1838141cc406Sopenharmony_ci    }
1839141cc406Sopenharmony_ci    scanner->mode.byteOrder = 0x01; /* 0x01 = Intel; only bit 0 used */
1840141cc406Sopenharmony_ci    scanner->mode.sharpen = scanner->val[OPT_SHARPEN].b && !scanner->val[OPT_PREVIEW].b;
1841141cc406Sopenharmony_ci    scanner->mode.skipShadingAnalysis = !scanner->val[OPT_SHADING_ANALYSIS].b;
1842141cc406Sopenharmony_ci    scanner->mode.fastInfrared = scanner->val[OPT_FAST_INFRARED].b && !scanner->val[OPT_PREVIEW].b;
1843141cc406Sopenharmony_ci    if (strcmp (scanner->val[OPT_HALFTONE_PATTERN].s, "53lpi 45d ROUND") == 0) {
1844141cc406Sopenharmony_ci        scanner->mode.halftonePattern = 0;
1845141cc406Sopenharmony_ci    } else { /*TODO: the others */
1846141cc406Sopenharmony_ci        scanner->mode.halftonePattern = 0;
1847141cc406Sopenharmony_ci    }
1848141cc406Sopenharmony_ci    scanner->mode.lineThreshold = SANE_UNFIX (scanner->val[OPT_THRESHOLD].w) / 100 * 0xFF; /* 0xFF = 100% */
1849141cc406Sopenharmony_ci    sanei_pieusb_cmd_set_mode (scanner->device_number, &(scanner->mode), &status);
1850141cc406Sopenharmony_ci    res = sanei_pieusb_convert_status(status.pieusb_status);
1851141cc406Sopenharmony_ci    if (res == SANE_STATUS_GOOD) {
1852141cc406Sopenharmony_ci      res = sanei_pieusb_wait_ready (scanner, 0);
1853141cc406Sopenharmony_ci    }
1854141cc406Sopenharmony_ci    DBG (DBG_info_sane, "sanei_pieusb_set_mode_from_options(): sanei_pieusb_cmd_set_mode status %s\n", sane_strstatus(res));
1855141cc406Sopenharmony_ci    return res;
1856141cc406Sopenharmony_ci}
1857141cc406Sopenharmony_ci
1858141cc406Sopenharmony_ci/**
1859141cc406Sopenharmony_ci * Set gains, exposure and offset, to:
1860141cc406Sopenharmony_ci * - values default (pieusb_set_default_gain_offset)
1861141cc406Sopenharmony_ci * - values set by options
1862141cc406Sopenharmony_ci * - values set by auto-calibration procedure
1863141cc406Sopenharmony_ci * - values determined from preceding preview
1864141cc406Sopenharmony_ci *
1865141cc406Sopenharmony_ci * @param scanner
1866141cc406Sopenharmony_ci * @return
1867141cc406Sopenharmony_ci */
1868141cc406Sopenharmony_ciSANE_Status
1869141cc406Sopenharmony_cisanei_pieusb_set_gain_offset(Pieusb_Scanner * scanner, const char *calibration_mode)
1870141cc406Sopenharmony_ci{
1871141cc406Sopenharmony_ci    struct Pieusb_Command_Status status;
1872141cc406Sopenharmony_ci    SANE_Status ret;
1873141cc406Sopenharmony_ci    double gain;
1874141cc406Sopenharmony_ci
1875141cc406Sopenharmony_ci    DBG (DBG_info,"sanei_pieusb_set_gain_offset(): mode = %s\n", calibration_mode);
1876141cc406Sopenharmony_ci
1877141cc406Sopenharmony_ci    if (strcmp (calibration_mode, SCAN_CALIBRATION_DEFAULT) == 0) {
1878141cc406Sopenharmony_ci        /* Default values */
1879141cc406Sopenharmony_ci        DBG(DBG_info_sane,"sanei_pieusb_set_gain_offset(): get calibration data from defaults\n");
1880141cc406Sopenharmony_ci        scanner->settings.exposureTime[0] = DEFAULT_EXPOSURE;
1881141cc406Sopenharmony_ci        scanner->settings.exposureTime[1] = DEFAULT_EXPOSURE;
1882141cc406Sopenharmony_ci        scanner->settings.exposureTime[2] = DEFAULT_EXPOSURE;
1883141cc406Sopenharmony_ci        scanner->settings.exposureTime[3] = DEFAULT_EXPOSURE;
1884141cc406Sopenharmony_ci        scanner->settings.offset[0] = DEFAULT_OFFSET;
1885141cc406Sopenharmony_ci        scanner->settings.offset[1] = DEFAULT_OFFSET;
1886141cc406Sopenharmony_ci        scanner->settings.offset[2] = DEFAULT_OFFSET;
1887141cc406Sopenharmony_ci        scanner->settings.offset[3] = DEFAULT_OFFSET;
1888141cc406Sopenharmony_ci        scanner->settings.gain[0] = DEFAULT_GAIN;
1889141cc406Sopenharmony_ci        scanner->settings.gain[1] = DEFAULT_GAIN;
1890141cc406Sopenharmony_ci        scanner->settings.gain[2] = DEFAULT_GAIN;
1891141cc406Sopenharmony_ci        scanner->settings.gain[3] = DEFAULT_GAIN;
1892141cc406Sopenharmony_ci        scanner->settings.light = DEFAULT_LIGHT;
1893141cc406Sopenharmony_ci        scanner->settings.extraEntries = DEFAULT_ADDITIONAL_ENTRIES;
1894141cc406Sopenharmony_ci        scanner->settings.doubleTimes = DEFAULT_DOUBLE_TIMES;
1895141cc406Sopenharmony_ci        status.pieusb_status = PIEUSB_STATUS_GOOD;
1896141cc406Sopenharmony_ci    } else if ((strcmp(calibration_mode, SCAN_CALIBRATION_PREVIEW) == 0)
1897141cc406Sopenharmony_ci	       && scanner->preview_done) {
1898141cc406Sopenharmony_ci        /* If no preview data available, do the auto-calibration. */
1899141cc406Sopenharmony_ci        double dg, dgi;
1900141cc406Sopenharmony_ci        DBG (DBG_info, "sanei_pieusb_set_gain_offset(): get calibration data from preview. scanner->mode.passes %d\n", scanner->mode.passes);
1901141cc406Sopenharmony_ci        switch (scanner->mode.passes) {
1902141cc406Sopenharmony_ci            case SCAN_ONE_PASS_RGBI:
1903141cc406Sopenharmony_ci                dg = 3.00;
1904141cc406Sopenharmony_ci                dgi = ((double)scanner->settings.saturationLevel[0] / 65536) / ((double)scanner->preview_upper_bound[0] / HISTOGRAM_SIZE);
1905141cc406Sopenharmony_ci                if (dgi < dg) dg = dgi;
1906141cc406Sopenharmony_ci                dgi = ((double)scanner->settings.saturationLevel[1] / 65536) / ((double)scanner->preview_upper_bound[1] / HISTOGRAM_SIZE);
1907141cc406Sopenharmony_ci                if (dgi < dg) dg = dgi;
1908141cc406Sopenharmony_ci                dgi = ((double)scanner->settings.saturationLevel[2] / 65536) / ((double)scanner->preview_upper_bound[2] / HISTOGRAM_SIZE);
1909141cc406Sopenharmony_ci                if (dgi < dg) dg = dgi;
1910141cc406Sopenharmony_ci                updateGain2(scanner, 0, dg);
1911141cc406Sopenharmony_ci                updateGain2(scanner, 1, dg);
1912141cc406Sopenharmony_ci                updateGain2(scanner, 2, dg);
1913141cc406Sopenharmony_ci	    break;
1914141cc406Sopenharmony_ci            case SCAN_ONE_PASS_COLOR:
1915141cc406Sopenharmony_ci                dg = 3.00;
1916141cc406Sopenharmony_ci                dgi = ((double)scanner->settings.saturationLevel[0] / 65536) / ((double)scanner->preview_upper_bound[0] / HISTOGRAM_SIZE);
1917141cc406Sopenharmony_ci                if (dgi < dg) dg = dgi;
1918141cc406Sopenharmony_ci                dgi = ((double)scanner->settings.saturationLevel[1] / 65536) / ((double)scanner->preview_upper_bound[1] / HISTOGRAM_SIZE);
1919141cc406Sopenharmony_ci                if (dgi < dg) dg = dgi;
1920141cc406Sopenharmony_ci                dgi = ((double)scanner->settings.saturationLevel[2] / 65536) / ((double)scanner->preview_upper_bound[2] / HISTOGRAM_SIZE);
1921141cc406Sopenharmony_ci                if (dgi < dg) dg = dgi;
1922141cc406Sopenharmony_ci                updateGain2(scanner, 0, dg);
1923141cc406Sopenharmony_ci                updateGain2(scanner, 1, dg);
1924141cc406Sopenharmony_ci                updateGain2(scanner, 2, dg);
1925141cc406Sopenharmony_ci                break;
1926141cc406Sopenharmony_ci            case SCAN_FILTER_BLUE:
1927141cc406Sopenharmony_ci                dg = 3.00;
1928141cc406Sopenharmony_ci                dgi = ((double)scanner->settings.saturationLevel[2] / 65536) / ((double)scanner->preview_upper_bound[2] / HISTOGRAM_SIZE);
1929141cc406Sopenharmony_ci                if (dgi < dg) dg = dgi;
1930141cc406Sopenharmony_ci                updateGain2(scanner, 2, dg);
1931141cc406Sopenharmony_ci                break;
1932141cc406Sopenharmony_ci            case SCAN_FILTER_GREEN:
1933141cc406Sopenharmony_ci                dg = 3.00;
1934141cc406Sopenharmony_ci                dgi = ((double)scanner->settings.saturationLevel[1] / 65536) / ((double)scanner->preview_upper_bound[1] / HISTOGRAM_SIZE);
1935141cc406Sopenharmony_ci                if (dgi < dg) dg = dgi;
1936141cc406Sopenharmony_ci                updateGain2(scanner, 1, dg);
1937141cc406Sopenharmony_ci                break;
1938141cc406Sopenharmony_ci            case SCAN_FILTER_RED:
1939141cc406Sopenharmony_ci                dg = 3.00;
1940141cc406Sopenharmony_ci                dgi = ((double)scanner->settings.saturationLevel[0] / 65536) / ((double)scanner->preview_upper_bound[0] / HISTOGRAM_SIZE);
1941141cc406Sopenharmony_ci                if (dgi < dg) dg = dgi;
1942141cc406Sopenharmony_ci                updateGain2(scanner, 0, dg);
1943141cc406Sopenharmony_ci                break;
1944141cc406Sopenharmony_ci            case SCAN_FILTER_NEUTRAL:
1945141cc406Sopenharmony_ci                break;
1946141cc406Sopenharmony_ci        }
1947141cc406Sopenharmony_ci        status.pieusb_status = PIEUSB_STATUS_GOOD;
1948141cc406Sopenharmony_ci    } else if (strcmp (calibration_mode, SCAN_CALIBRATION_OPTIONS) == 0) {
1949141cc406Sopenharmony_ci        DBG (DBG_info_sane, "sanei_pieusb_set_gain_offset(): get calibration data from options\n");
1950141cc406Sopenharmony_ci        /* Exposure times */
1951141cc406Sopenharmony_ci        scanner->settings.exposureTime[0] = scanner->val[OPT_SET_EXPOSURE_R].w;
1952141cc406Sopenharmony_ci        scanner->settings.exposureTime[1] = scanner->val[OPT_SET_EXPOSURE_G].w;
1953141cc406Sopenharmony_ci        scanner->settings.exposureTime[2] = scanner->val[OPT_SET_EXPOSURE_B].w;
1954141cc406Sopenharmony_ci        scanner->settings.exposureTime[3] = scanner->val[OPT_SET_EXPOSURE_I].w; /* Infrared */
1955141cc406Sopenharmony_ci        /* Offsets */
1956141cc406Sopenharmony_ci        scanner->settings.offset[0] = scanner->val[OPT_SET_OFFSET_R].w;
1957141cc406Sopenharmony_ci        scanner->settings.offset[1] = scanner->val[OPT_SET_OFFSET_G].w;
1958141cc406Sopenharmony_ci        scanner->settings.offset[2] = scanner->val[OPT_SET_OFFSET_B].w;
1959141cc406Sopenharmony_ci        scanner->settings.offset[3] = scanner->val[OPT_SET_OFFSET_I].w; /* Infrared */
1960141cc406Sopenharmony_ci        /* Gains */
1961141cc406Sopenharmony_ci        scanner->settings.gain[0] = scanner->val[OPT_SET_GAIN_R].w;
1962141cc406Sopenharmony_ci        scanner->settings.gain[1] = scanner->val[OPT_SET_GAIN_G].w;
1963141cc406Sopenharmony_ci        scanner->settings.gain[2] = scanner->val[OPT_SET_GAIN_B].w;
1964141cc406Sopenharmony_ci        scanner->settings.gain[3] = scanner->val[OPT_SET_GAIN_I].w; /* Infrared */
1965141cc406Sopenharmony_ci        /* Light, extra entries and doubling */
1966141cc406Sopenharmony_ci        scanner->settings.light = scanner->val[OPT_LIGHT].w;
1967141cc406Sopenharmony_ci        scanner->settings.extraEntries = DEFAULT_ADDITIONAL_ENTRIES;
1968141cc406Sopenharmony_ci        scanner->settings.doubleTimes = scanner->val[OPT_DOUBLE_TIMES].w;
1969141cc406Sopenharmony_ci        status.pieusb_status = PIEUSB_STATUS_GOOD;
1970141cc406Sopenharmony_ci    } else { /* SCAN_CALIBRATION_AUTO */
1971141cc406Sopenharmony_ci        DBG (DBG_info_sane, "sanei_pieusb_set_gain_offset(): get calibration data from scanner\n");
1972141cc406Sopenharmony_ci        sanei_pieusb_cmd_get_gain_offset (scanner->device_number, &scanner->settings, &status);
1973141cc406Sopenharmony_ci    }
1974141cc406Sopenharmony_ci    /* Check status */
1975141cc406Sopenharmony_ci    if (status.pieusb_status == PIEUSB_STATUS_DEVICE_BUSY) {
1976141cc406Sopenharmony_ci      ret = sanei_pieusb_wait_ready (scanner, 0);
1977141cc406Sopenharmony_ci      if (ret != SANE_STATUS_GOOD) {
1978141cc406Sopenharmony_ci	DBG (DBG_error,"sanei_pieusb_set_gain_offset(): not ready after sanei_pieusb_cmd_get_gain_offset(): %d\n", ret);
1979141cc406Sopenharmony_ci	return ret;
1980141cc406Sopenharmony_ci      }
1981141cc406Sopenharmony_ci    }
1982141cc406Sopenharmony_ci    else if (status.pieusb_status != PIEUSB_STATUS_GOOD) {
1983141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
1984141cc406Sopenharmony_ci    }
1985141cc406Sopenharmony_ci    /* Adjust gain */
1986141cc406Sopenharmony_ci    gain = 1.0;
1987141cc406Sopenharmony_ci    if (strcmp (scanner->val[OPT_GAIN_ADJUST].s, SCAN_GAIN_ADJUST_03) == 0) {
1988141cc406Sopenharmony_ci        gain = 0.3;
1989141cc406Sopenharmony_ci    } else if (strcmp (scanner->val[OPT_GAIN_ADJUST].s, SCAN_GAIN_ADJUST_05) == 0) {
1990141cc406Sopenharmony_ci        gain = 0.5;
1991141cc406Sopenharmony_ci    } else if (strcmp (scanner->val[OPT_GAIN_ADJUST].s, SCAN_GAIN_ADJUST_08) == 0) {
1992141cc406Sopenharmony_ci        gain = 0.8;
1993141cc406Sopenharmony_ci    } else if (strcmp (scanner->val[OPT_GAIN_ADJUST].s, SCAN_GAIN_ADJUST_10) == 0) {
1994141cc406Sopenharmony_ci        gain = 1.0;
1995141cc406Sopenharmony_ci    } else if (strcmp (scanner->val[OPT_GAIN_ADJUST].s, SCAN_GAIN_ADJUST_12) == 0) {
1996141cc406Sopenharmony_ci        gain = 1.2;
1997141cc406Sopenharmony_ci    } else if (strcmp (scanner->val[OPT_GAIN_ADJUST].s, SCAN_GAIN_ADJUST_16) == 0) {
1998141cc406Sopenharmony_ci        gain = 1.6;
1999141cc406Sopenharmony_ci    } else if (strcmp (scanner->val[OPT_GAIN_ADJUST].s, SCAN_GAIN_ADJUST_19) == 0) {
2000141cc406Sopenharmony_ci        gain = 1.9;
2001141cc406Sopenharmony_ci    } else if (strcmp (scanner->val[OPT_GAIN_ADJUST].s, SCAN_GAIN_ADJUST_24) == 0) {
2002141cc406Sopenharmony_ci        gain = 2.4;
2003141cc406Sopenharmony_ci    } else if (strcmp (scanner->val[OPT_GAIN_ADJUST].s, SCAN_GAIN_ADJUST_30) == 0) {
2004141cc406Sopenharmony_ci        gain = 3.0;
2005141cc406Sopenharmony_ci    }
2006141cc406Sopenharmony_ci    switch (scanner->mode.passes) {
2007141cc406Sopenharmony_ci        case SCAN_ONE_PASS_RGBI:
2008141cc406Sopenharmony_ci        case SCAN_ONE_PASS_COLOR:
2009141cc406Sopenharmony_ci            updateGain2 (scanner, 0, gain);
2010141cc406Sopenharmony_ci            updateGain2 (scanner, 1, gain);
2011141cc406Sopenharmony_ci            updateGain2 (scanner, 2, gain);
2012141cc406Sopenharmony_ci            /* Don't correct IR, hampers cleaning process... */
2013141cc406Sopenharmony_ci            break;
2014141cc406Sopenharmony_ci        case SCAN_FILTER_INFRARED:
2015141cc406Sopenharmony_ci            updateGain2 (scanner, 3, gain);
2016141cc406Sopenharmony_ci            break;
2017141cc406Sopenharmony_ci        case SCAN_FILTER_BLUE:
2018141cc406Sopenharmony_ci            updateGain2 (scanner, 2, gain);
2019141cc406Sopenharmony_ci            break;
2020141cc406Sopenharmony_ci        case SCAN_FILTER_GREEN:
2021141cc406Sopenharmony_ci            updateGain2 (scanner, 1, gain);
2022141cc406Sopenharmony_ci            break;
2023141cc406Sopenharmony_ci        case SCAN_FILTER_RED:
2024141cc406Sopenharmony_ci            updateGain2 (scanner, 0, gain);
2025141cc406Sopenharmony_ci            break;
2026141cc406Sopenharmony_ci        case SCAN_FILTER_NEUTRAL:
2027141cc406Sopenharmony_ci            break;
2028141cc406Sopenharmony_ci    }
2029141cc406Sopenharmony_ci    /* Now set values for gain, offset and exposure */
2030141cc406Sopenharmony_ci    sanei_pieusb_cmd_set_gain_offset (scanner->device_number, &(scanner->settings), &status);
2031141cc406Sopenharmony_ci    ret = sanei_pieusb_convert_status (status.pieusb_status);
2032141cc406Sopenharmony_ci    DBG (DBG_info_sane, "sanei_pieusb_set_gain_offset(): status %s\n", sane_strstatus (ret));
2033141cc406Sopenharmony_ci    return ret;
2034141cc406Sopenharmony_ci}
2035141cc406Sopenharmony_ci
2036141cc406Sopenharmony_ci/*
2037141cc406Sopenharmony_ci * get shading data
2038141cc406Sopenharmony_ci * must be called immediately after sanei_pieusb_set_gain_offset
2039141cc406Sopenharmony_ci */
2040141cc406Sopenharmony_ci
2041141cc406Sopenharmony_ciSANE_Status
2042141cc406Sopenharmony_cisanei_pieusb_get_shading_data(Pieusb_Scanner * scanner)
2043141cc406Sopenharmony_ci{
2044141cc406Sopenharmony_ci    struct Pieusb_Command_Status status;
2045141cc406Sopenharmony_ci    SANE_Int shading_width;
2046141cc406Sopenharmony_ci    SANE_Int shading_height;
2047141cc406Sopenharmony_ci    SANE_Byte* buffer;
2048141cc406Sopenharmony_ci    SANE_Int lines;
2049141cc406Sopenharmony_ci    SANE_Int cols;
2050141cc406Sopenharmony_ci    SANE_Int size;
2051141cc406Sopenharmony_ci    SANE_Status res = SANE_STATUS_GOOD;
2052141cc406Sopenharmony_ci
2053141cc406Sopenharmony_ci    DBG (DBG_info_sane, "sanei_pieusb_get_shading_data()\n");
2054141cc406Sopenharmony_ci    shading_width = scanner->device->shading_parameters[0].pixelsPerLine;
2055141cc406Sopenharmony_ci    shading_height = scanner->device->shading_parameters[0].nLines;
2056141cc406Sopenharmony_ci    if (shading_height < 1) {
2057141cc406Sopenharmony_ci        DBG (DBG_error, "shading_height < 1\n");
2058141cc406Sopenharmony_ci	return SANE_STATUS_INVAL;
2059141cc406Sopenharmony_ci    }
2060141cc406Sopenharmony_ci    switch (scanner->mode.colorFormat) {
2061141cc406Sopenharmony_ci        case SCAN_COLOR_FORMAT_PIXEL: /* Pixel */
2062141cc406Sopenharmony_ci            lines = shading_height * 4;
2063141cc406Sopenharmony_ci            cols = 2 * shading_width;
2064141cc406Sopenharmony_ci            break;
2065141cc406Sopenharmony_ci        case SCAN_COLOR_FORMAT_INDEX: /* Indexed */
2066141cc406Sopenharmony_ci            lines = shading_height * 4;
2067141cc406Sopenharmony_ci            cols = (2 * shading_width + 2);
2068141cc406Sopenharmony_ci            break;
2069141cc406Sopenharmony_ci        default:
2070141cc406Sopenharmony_ci            DBG (DBG_error, "sanei_pieusb_get_shading_data(): color format %d not implemented\n", scanner->mode.colorFormat);
2071141cc406Sopenharmony_ci            return SANE_STATUS_INVAL;
2072141cc406Sopenharmony_ci    }
2073141cc406Sopenharmony_ci
2074141cc406Sopenharmony_ci    size = cols * lines;
2075141cc406Sopenharmony_ci    buffer = malloc (size);
2076141cc406Sopenharmony_ci    if (buffer == NULL) {
2077141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
2078141cc406Sopenharmony_ci    }
2079141cc406Sopenharmony_ci    sanei_pieusb_cmd_get_scanned_lines (scanner->device_number, buffer, 4, cols * 4, &status);
2080141cc406Sopenharmony_ci    if (status.pieusb_status == PIEUSB_STATUS_GOOD) {
2081141cc406Sopenharmony_ci        res = sanei_pieusb_wait_ready (scanner, 0);
2082141cc406Sopenharmony_ci        if (res == SANE_STATUS_GOOD) {
2083141cc406Sopenharmony_ci            sanei_pieusb_cmd_get_scanned_lines (scanner->device_number, buffer + cols*4, lines - 4, (lines - 4) * cols, &status);
2084141cc406Sopenharmony_ci	    if (status.pieusb_status == PIEUSB_STATUS_GOOD) {
2085141cc406Sopenharmony_ci	        pieusb_calculate_shading (scanner, buffer);
2086141cc406Sopenharmony_ci	    }
2087141cc406Sopenharmony_ci	    res = sanei_pieusb_convert_status (status.pieusb_status);
2088141cc406Sopenharmony_ci	}
2089141cc406Sopenharmony_ci    }
2090141cc406Sopenharmony_ci    else {
2091141cc406Sopenharmony_ci        res = sanei_pieusb_convert_status (status.pieusb_status);
2092141cc406Sopenharmony_ci    }
2093141cc406Sopenharmony_ci    free (buffer);
2094141cc406Sopenharmony_ci    return res;
2095141cc406Sopenharmony_ci}
2096141cc406Sopenharmony_ci
2097141cc406Sopenharmony_ci/*
2098141cc406Sopenharmony_ci *
2099141cc406Sopenharmony_ci */
2100141cc406Sopenharmony_ci
2101141cc406Sopenharmony_ciSANE_Status
2102141cc406Sopenharmony_cisanei_pieusb_get_ccd_mask(Pieusb_Scanner * scanner)
2103141cc406Sopenharmony_ci{
2104141cc406Sopenharmony_ci    struct Pieusb_Command_Status status;
2105141cc406Sopenharmony_ci
2106141cc406Sopenharmony_ci    DBG(DBG_info_proc, "sanei_pieusb_get_ccd_mask()\n");
2107141cc406Sopenharmony_ci
2108141cc406Sopenharmony_ci    sanei_pieusb_cmd_get_ccd_mask(scanner->device_number, scanner->ccd_mask, scanner->ccd_mask_size, &status);
2109141cc406Sopenharmony_ci    if (status.pieusb_status == PIEUSB_STATUS_GOOD) {
2110141cc406Sopenharmony_ci      /* Save CCD mask */
2111141cc406Sopenharmony_ci      if (scanner->val[OPT_SAVE_CCDMASK].b) {
2112141cc406Sopenharmony_ci        FILE* fs = fopen ("pieusb.ccd", "w");
2113141cc406Sopenharmony_ci        fwrite (scanner->ccd_mask, 1, scanner->ccd_mask_size, fs);
2114141cc406Sopenharmony_ci        fclose (fs);
2115141cc406Sopenharmony_ci      }
2116141cc406Sopenharmony_ci    }
2117141cc406Sopenharmony_ci  return sanei_pieusb_convert_status(status.pieusb_status);
2118141cc406Sopenharmony_ci
2119141cc406Sopenharmony_ci}
2120141cc406Sopenharmony_ci
2121141cc406Sopenharmony_ci/**
2122141cc406Sopenharmony_ci * Read parameters from scanner
2123141cc406Sopenharmony_ci * and initialize SANE parameters
2124141cc406Sopenharmony_ci *
2125141cc406Sopenharmony_ci * @param scanner
2126141cc406Sopenharmony_ci * @return parameter_bytes for use in get_scan_data()
2127141cc406Sopenharmony_ci */
2128141cc406Sopenharmony_ciSANE_Status
2129141cc406Sopenharmony_cisanei_pieusb_get_parameters(Pieusb_Scanner * scanner, SANE_Int *parameter_bytes)
2130141cc406Sopenharmony_ci{
2131141cc406Sopenharmony_ci    struct Pieusb_Command_Status status;
2132141cc406Sopenharmony_ci    struct Pieusb_Scan_Parameters parameters;
2133141cc406Sopenharmony_ci    const char *mode;
2134141cc406Sopenharmony_ci
2135141cc406Sopenharmony_ci    DBG (DBG_info_proc, "sanei_pieusb_get_parameters()\n");
2136141cc406Sopenharmony_ci
2137141cc406Sopenharmony_ci    sanei_pieusb_cmd_get_parameters (scanner->device_number, &parameters, &status);
2138141cc406Sopenharmony_ci    if (status.pieusb_status != PIEUSB_STATUS_GOOD) {
2139141cc406Sopenharmony_ci        return sanei_pieusb_convert_status (status.pieusb_status);
2140141cc406Sopenharmony_ci    }
2141141cc406Sopenharmony_ci    *parameter_bytes = parameters.bytes;
2142141cc406Sopenharmony_ci    /* Use response from sanei_pieusb_cmd_get_parameters() for initialization of SANE parameters.
2143141cc406Sopenharmony_ci     * Note the weird values of the bytes-field: this is because of the colorFormat
2144141cc406Sopenharmony_ci     * setting in sanei_pieusb_cmd_set_mode(). The single-color modes all use the pixel format,
2145141cc406Sopenharmony_ci     * which makes sanei_pieusb_cmd_get_parameters() return a full color line although just
2146141cc406Sopenharmony_ci     * one color actually contains data. For the index format, the bytes field
2147141cc406Sopenharmony_ci     * gives the size of a single color line. */
2148141cc406Sopenharmony_ci    mode = scanner->val[OPT_MODE].s;
2149141cc406Sopenharmony_ci    if (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) {
2150141cc406Sopenharmony_ci        scanner->scan_parameters.format = SANE_FRAME_GRAY;
2151141cc406Sopenharmony_ci        scanner->scan_parameters.depth = 1;
2152141cc406Sopenharmony_ci        scanner->scan_parameters.bytes_per_line = parameters.bytes/3;
2153141cc406Sopenharmony_ci    } else if (strcmp (mode, SANE_VALUE_SCAN_MODE_HALFTONE) == 0) {
2154141cc406Sopenharmony_ci        scanner->scan_parameters.format = SANE_FRAME_GRAY;
2155141cc406Sopenharmony_ci        scanner->scan_parameters.depth = 1;
2156141cc406Sopenharmony_ci        scanner->scan_parameters.bytes_per_line = parameters.bytes/3;
2157141cc406Sopenharmony_ci    } else if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) {
2158141cc406Sopenharmony_ci        scanner->scan_parameters.format = SANE_FRAME_GRAY;
2159141cc406Sopenharmony_ci        scanner->scan_parameters.depth = scanner->val[OPT_BIT_DEPTH].w;
2160141cc406Sopenharmony_ci        scanner->scan_parameters.bytes_per_line = parameters.bytes/3;
2161141cc406Sopenharmony_ci    } else if (strcmp (mode, SANE_VALUE_SCAN_MODE_RGBI) == 0) {
2162141cc406Sopenharmony_ci        scanner->scan_parameters.format = SANE_FRAME_RGB; /* was: SANE_FRAME_RGBI */
2163141cc406Sopenharmony_ci        scanner->scan_parameters.depth = scanner->val[OPT_BIT_DEPTH].w;
2164141cc406Sopenharmony_ci        scanner->scan_parameters.bytes_per_line = 4*parameters.bytes;
2165141cc406Sopenharmony_ci    } else { /* SANE_VALUE_SCAN_MODE_COLOR, with and without option clean image set */
2166141cc406Sopenharmony_ci        scanner->scan_parameters.format = SANE_FRAME_RGB;
2167141cc406Sopenharmony_ci        scanner->scan_parameters.depth = scanner->val[OPT_BIT_DEPTH].w;
2168141cc406Sopenharmony_ci        scanner->scan_parameters.bytes_per_line = 3*parameters.bytes;
2169141cc406Sopenharmony_ci    }
2170141cc406Sopenharmony_ci    scanner->scan_parameters.lines = parameters.lines;
2171141cc406Sopenharmony_ci    scanner->scan_parameters.pixels_per_line = parameters.width;
2172141cc406Sopenharmony_ci    scanner->scan_parameters.last_frame = SANE_TRUE;
2173141cc406Sopenharmony_ci
2174141cc406Sopenharmony_ci    DBG (DBG_info_sane,"sanei_pieusb_get_parameters(): mode '%s'\n", mode);
2175141cc406Sopenharmony_ci    DBG (DBG_info_sane," format = %d\n", scanner->scan_parameters.format);
2176141cc406Sopenharmony_ci    DBG (DBG_info_sane," depth = %d\n", scanner->scan_parameters.depth);
2177141cc406Sopenharmony_ci    DBG (DBG_info_sane," bytes_per_line = %d\n", scanner->scan_parameters.bytes_per_line);
2178141cc406Sopenharmony_ci    DBG (DBG_info_sane," lines = %d\n", scanner->scan_parameters.lines);
2179141cc406Sopenharmony_ci    DBG (DBG_info_sane," pixels_per_line = %d\n", scanner->scan_parameters.pixels_per_line);
2180141cc406Sopenharmony_ci    DBG (DBG_info_sane," last_frame = %d\n", scanner->scan_parameters.last_frame);
2181141cc406Sopenharmony_ci
2182141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
2183141cc406Sopenharmony_ci}
2184141cc406Sopenharmony_ci
2185141cc406Sopenharmony_ciSANE_Status
2186141cc406Sopenharmony_cisanei_pieusb_get_scan_data(Pieusb_Scanner * scanner, SANE_Int parameter_bytes)
2187141cc406Sopenharmony_ci{
2188141cc406Sopenharmony_ci    struct Pieusb_Command_Status status;
2189141cc406Sopenharmony_ci    SANE_Parameters *parameters = &scanner->scan_parameters;
2190141cc406Sopenharmony_ci    SANE_Int lines_to_read, lines_remaining;
2191141cc406Sopenharmony_ci    SANE_Int ppl, bpl;
2192141cc406Sopenharmony_ci    SANE_Byte *linebuf, *lboff;
2193141cc406Sopenharmony_ci    SANE_Bool compress;
2194141cc406Sopenharmony_ci    int n, k, i;
2195141cc406Sopenharmony_ci
2196141cc406Sopenharmony_ci    switch (scanner->mode.colorFormat) {
2197141cc406Sopenharmony_ci        case SCAN_COLOR_FORMAT_PIXEL: /* Pixel */
2198141cc406Sopenharmony_ci            lines_to_read = scanner->buffer.height;
2199141cc406Sopenharmony_ci            break;
2200141cc406Sopenharmony_ci        case SCAN_COLOR_FORMAT_INDEX: /* Indexed */
2201141cc406Sopenharmony_ci            lines_to_read = scanner->buffer.colors * scanner->buffer.height;
2202141cc406Sopenharmony_ci            break;
2203141cc406Sopenharmony_ci        default:
2204141cc406Sopenharmony_ci            DBG(DBG_error, "sanei_pieusb_get_scan_data(): color format %d not implemented\n",scanner->mode.colorFormat);
2205141cc406Sopenharmony_ci            return SANE_STATUS_INVAL;
2206141cc406Sopenharmony_ci    }
2207141cc406Sopenharmony_ci    lines_remaining = lines_to_read;
2208141cc406Sopenharmony_ci    DBG (DBG_info_proc, "sanei_pieusb_get_scan_data(colorFormat %d), lines_to_read %d, bytes %d\n", scanner->mode.colorFormat, lines_to_read, parameter_bytes);
2209141cc406Sopenharmony_ci
2210141cc406Sopenharmony_ci  /*
2211141cc406Sopenharmony_ci    fdraw = open("/tmp/pieusb.raw", O_WRONLY | O_CREAT | O_TRUNC, (mode_t)0600);
2212141cc406Sopenharmony_ci    if (fdraw == -1) {
2213141cc406Sopenharmony_ci         perror("error opening raw image buffer file");
2214141cc406Sopenharmony_ci    }
2215141cc406Sopenharmony_ci*/
2216141cc406Sopenharmony_ci    while (lines_remaining > 0) {
2217141cc406Sopenharmony_ci        SANE_Int lines;
2218141cc406Sopenharmony_ci        /* Read lines */
2219141cc406Sopenharmony_ci        /* The amount of bytes_per_line varies with color format setting; only 'pixel' and 'index' implemented */
2220141cc406Sopenharmony_ci        ppl = parameters->pixels_per_line;
2221141cc406Sopenharmony_ci        switch (scanner->mode.colorFormat) {
2222141cc406Sopenharmony_ci	    case SCAN_COLOR_FORMAT_PIXEL: /* Pixel */
2223141cc406Sopenharmony_ci	        bpl = parameter_bytes;
2224141cc406Sopenharmony_ci	        break;
2225141cc406Sopenharmony_ci            case SCAN_COLOR_FORMAT_INDEX: /* Indexed */
2226141cc406Sopenharmony_ci	        bpl = parameter_bytes + 2; /* Index bytes! */
2227141cc406Sopenharmony_ci	        break;
2228141cc406Sopenharmony_ci            default:
2229141cc406Sopenharmony_ci	        DBG(DBG_error, "sanei_pieusb_get_scan_data(): color format %d not implemented\n", scanner->mode.colorFormat);
2230141cc406Sopenharmony_ci	        return SANE_STATUS_INVAL;
2231141cc406Sopenharmony_ci        }
2232141cc406Sopenharmony_ci        lines = (lines_remaining < 256) ? lines_remaining : 255;
2233141cc406Sopenharmony_ci        DBG(DBG_info_sane, "sanei_pieusb_get_scan_data(): reading lines: now %d, bytes per line = %d\n", lines, bpl);
2234141cc406Sopenharmony_ci        linebuf = malloc(lines * bpl);
2235141cc406Sopenharmony_ci        sanei_pieusb_cmd_get_scanned_lines(scanner->device_number, linebuf, lines, lines * bpl, &status);
2236141cc406Sopenharmony_ci        if (status.pieusb_status != PIEUSB_STATUS_GOOD ) {
2237141cc406Sopenharmony_ci	    /* Error, return */
2238141cc406Sopenharmony_ci	    free(linebuf);
2239141cc406Sopenharmony_ci	    return SANE_STATUS_INVAL;
2240141cc406Sopenharmony_ci	}
2241141cc406Sopenharmony_ci        /* Save raw data */
2242141cc406Sopenharmony_ci/*
2243141cc406Sopenharmony_ci        if (fdraw != -1) {
2244141cc406Sopenharmony_ci            wcnt = write(fdraw,linebuf,parameters.lines*bpl);
2245141cc406Sopenharmony_ci            DBG(DBG_info_sane,"Raw written %d\n",wcnt);
2246141cc406Sopenharmony_ci        }
2247141cc406Sopenharmony_ci*/
2248141cc406Sopenharmony_ci        /* Copy into official buffer
2249141cc406Sopenharmony_ci	 * Sometimes the scanner returns too many lines. Take care not to
2250141cc406Sopenharmony_ci	 * overflow the buffer. */
2251141cc406Sopenharmony_ci        lboff = linebuf;
2252141cc406Sopenharmony_ci        switch (scanner->mode.colorFormat) {
2253141cc406Sopenharmony_ci	    case SCAN_COLOR_FORMAT_PIXEL:
2254141cc406Sopenharmony_ci	        /* The scanner may return lines with 3 colors even though only
2255141cc406Sopenharmony_ci		 * one color is actually scanned. Detect this situation and
2256141cc406Sopenharmony_ci		 * eliminate the excess samples from the line buffer before
2257141cc406Sopenharmony_ci		 * handing it to buffer_put_full_color_line(). */
2258141cc406Sopenharmony_ci	        compress = SANE_FALSE;
2259141cc406Sopenharmony_ci	        if (scanner->buffer.colors == 1
2260141cc406Sopenharmony_ci		    && (bpl * scanner->buffer.packing_density / ppl) == (3 * scanner->buffer.packet_size_bytes)) {
2261141cc406Sopenharmony_ci		    compress = SANE_TRUE;
2262141cc406Sopenharmony_ci	        }
2263141cc406Sopenharmony_ci	        for (n = 0; n < lines; n++) {
2264141cc406Sopenharmony_ci		     if (compress) {
2265141cc406Sopenharmony_ci		         /* Move samples to fill up all unused locations */
2266141cc406Sopenharmony_ci		         int ps = scanner->buffer.packet_size_bytes;
2267141cc406Sopenharmony_ci		         for (k = 0; k < scanner->buffer.line_size_packets; k++) {
2268141cc406Sopenharmony_ci			     for (i = 0; i < ps; i++) {
2269141cc406Sopenharmony_ci			         lboff[ps*k+i] = lboff[3*ps*k+i];
2270141cc406Sopenharmony_ci			     }
2271141cc406Sopenharmony_ci			 }
2272141cc406Sopenharmony_ci		     }
2273141cc406Sopenharmony_ci		     if (sanei_pieusb_buffer_put_full_color_line(&scanner->buffer, lboff, bpl/3) == 0) {
2274141cc406Sopenharmony_ci		         /* Error, return */
2275141cc406Sopenharmony_ci		         return SANE_STATUS_INVAL;
2276141cc406Sopenharmony_ci		     }
2277141cc406Sopenharmony_ci		     lboff += bpl;
2278141cc406Sopenharmony_ci	        }
2279141cc406Sopenharmony_ci	        break;
2280141cc406Sopenharmony_ci	    case SCAN_COLOR_FORMAT_INDEX:
2281141cc406Sopenharmony_ci	        /* Indexed data */
2282141cc406Sopenharmony_ci	        for (n = 0; n < lines; n++) {
2283141cc406Sopenharmony_ci		    if (sanei_pieusb_buffer_put_single_color_line(&scanner->buffer, *lboff, lboff+2, bpl-2) == 0) {
2284141cc406Sopenharmony_ci		        /* Error, return */
2285141cc406Sopenharmony_ci		        return SANE_STATUS_INVAL;
2286141cc406Sopenharmony_ci		    }
2287141cc406Sopenharmony_ci		    lboff += bpl;
2288141cc406Sopenharmony_ci	        }
2289141cc406Sopenharmony_ci	        break;
2290141cc406Sopenharmony_ci	    default:
2291141cc406Sopenharmony_ci	        DBG(DBG_error, "sanei_pieusb_get_scan_data(): store color format %d not implemented\n", scanner->mode.colorFormat);
2292141cc406Sopenharmony_ci	        free(linebuf);
2293141cc406Sopenharmony_ci	        return SANE_STATUS_INVAL;
2294141cc406Sopenharmony_ci        }
2295141cc406Sopenharmony_ci        free(linebuf);
2296141cc406Sopenharmony_ci        lines_remaining -= lines; /* Note: excess discarded */
2297141cc406Sopenharmony_ci        DBG(DBG_info_sane, "sanei_pieusb_get_scan_data(): reading lines: remaining %d\n", lines_remaining);
2298141cc406Sopenharmony_ci    }
2299141cc406Sopenharmony_ci/*
2300141cc406Sopenharmony_ci    if (fdraw != -1) close(fdraw);
2301141cc406Sopenharmony_ci*/
2302141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
2303141cc406Sopenharmony_ci}
2304141cc406Sopenharmony_ci
2305141cc406Sopenharmony_ci/**
2306141cc406Sopenharmony_ci * Wait for scanner to get ready
2307141cc406Sopenharmony_ci *
2308141cc406Sopenharmony_ci * loop of test_ready/read_state
2309141cc406Sopenharmony_ci *
2310141cc406Sopenharmony_ci * @param scanner
2311141cc406Sopenharmony_ci * @param device_number, used if scanner == NULL
2312141cc406Sopenharmony_ci * @return SANE_Status
2313141cc406Sopenharmony_ci */
2314141cc406Sopenharmony_ci
2315141cc406Sopenharmony_ciSANE_Status
2316141cc406Sopenharmony_cisanei_pieusb_wait_ready(Pieusb_Scanner * scanner, SANE_Int device_number)
2317141cc406Sopenharmony_ci{
2318141cc406Sopenharmony_ci  struct Pieusb_Command_Status status;
2319141cc406Sopenharmony_ci  struct Pieusb_Scanner_State state;
2320141cc406Sopenharmony_ci  time_t start, elapsed;
2321141cc406Sopenharmony_ci
2322141cc406Sopenharmony_ci  DBG (DBG_info_proc, "sanei_pieusb_wait_ready()\n");
2323141cc406Sopenharmony_ci  start = time(NULL);
2324141cc406Sopenharmony_ci  if (scanner)
2325141cc406Sopenharmony_ci    device_number = scanner->device_number;
2326141cc406Sopenharmony_ci
2327141cc406Sopenharmony_ci  for(;;) {
2328141cc406Sopenharmony_ci    sanei_pieusb_cmd_test_unit_ready(device_number, &status);
2329141cc406Sopenharmony_ci    DBG (DBG_info_proc, "-> sanei_pieusb_cmd_test_unit_ready: %d\n", status.pieusb_status);
2330141cc406Sopenharmony_ci    if (status.pieusb_status == PIEUSB_STATUS_GOOD)
2331141cc406Sopenharmony_ci      break;
2332141cc406Sopenharmony_ci    if (status.pieusb_status == PIEUSB_STATUS_IO_ERROR)
2333141cc406Sopenharmony_ci      break;
2334141cc406Sopenharmony_ci    sanei_pieusb_cmd_read_state(device_number, &state, &status);
2335141cc406Sopenharmony_ci    DBG (DBG_info_proc, "-> sanei_pieusb_cmd_read_state: %d\n", status.pieusb_status);
2336141cc406Sopenharmony_ci    if (status.pieusb_status != PIEUSB_STATUS_DEVICE_BUSY)
2337141cc406Sopenharmony_ci      break;
2338141cc406Sopenharmony_ci    sleep(2);
2339141cc406Sopenharmony_ci    elapsed = time(NULL) - start;
2340141cc406Sopenharmony_ci    if (elapsed > 120) { /* 2 minute overall timeout */
2341141cc406Sopenharmony_ci      DBG (DBG_error, "scanner not ready after 2 minutes\n");
2342141cc406Sopenharmony_ci      break;
2343141cc406Sopenharmony_ci    }
2344141cc406Sopenharmony_ci    if (elapsed % 2) {
2345141cc406Sopenharmony_ci      DBG (DBG_info, "still waiting for scanner to get ready\n");
2346141cc406Sopenharmony_ci    }
2347141cc406Sopenharmony_ci  }
2348141cc406Sopenharmony_ci  return sanei_pieusb_convert_status(status.pieusb_status);
2349141cc406Sopenharmony_ci}
2350141cc406Sopenharmony_ci
2351141cc406Sopenharmony_ci
2352141cc406Sopenharmony_ciSANE_Status sanei_pieusb_analyze_preview(Pieusb_Scanner * scanner)
2353141cc406Sopenharmony_ci{
2354141cc406Sopenharmony_ci    int k, n;
2355141cc406Sopenharmony_ci    SANE_Parameters params;
2356141cc406Sopenharmony_ci    SANE_Int N;
2357141cc406Sopenharmony_ci    double *norm_histo;
2358141cc406Sopenharmony_ci    double level;
2359141cc406Sopenharmony_ci
2360141cc406Sopenharmony_ci    DBG(DBG_info, "sanei_pieusb_analyze_preview(): saving preview data\n");
2361141cc406Sopenharmony_ci
2362141cc406Sopenharmony_ci    /* Settings */
2363141cc406Sopenharmony_ci    scanner->preview_done = SANE_TRUE;
2364141cc406Sopenharmony_ci    for (k = 0; k < 4; k++) {
2365141cc406Sopenharmony_ci        scanner->preview_exposure[k] = scanner->settings.exposureTime[k];
2366141cc406Sopenharmony_ci        scanner->preview_gain[k] = scanner->settings.gain[k];
2367141cc406Sopenharmony_ci        scanner->preview_offset[k] = scanner->settings.offset[k];
2368141cc406Sopenharmony_ci    }
2369141cc406Sopenharmony_ci    /* Analyze color planes */
2370141cc406Sopenharmony_ci    N = scanner->buffer.width * scanner->buffer.height;
2371141cc406Sopenharmony_ci    params.format = SANE_FRAME_GRAY;
2372141cc406Sopenharmony_ci    params.depth = scanner->buffer.depth;
2373141cc406Sopenharmony_ci    params.pixels_per_line = scanner->buffer.width;
2374141cc406Sopenharmony_ci    params.lines = scanner->buffer.height;
2375141cc406Sopenharmony_ci    for (k = 0; k < scanner->buffer.colors; k++) {
2376141cc406Sopenharmony_ci        /* Create histogram for color k */
2377141cc406Sopenharmony_ci        sanei_ir_create_norm_histogram (&params, scanner->buffer.data + k * N, &norm_histo);
2378141cc406Sopenharmony_ci        /* Find 1% and 99% limits */
2379141cc406Sopenharmony_ci        level = 0;
2380141cc406Sopenharmony_ci        for (n =0; n < HISTOGRAM_SIZE; n++) {
2381141cc406Sopenharmony_ci
2382141cc406Sopenharmony_ci            level += norm_histo[n];
2383141cc406Sopenharmony_ci            if (level < 0.01) {
2384141cc406Sopenharmony_ci                scanner->preview_lower_bound[k] = n;
2385141cc406Sopenharmony_ci            }
2386141cc406Sopenharmony_ci            if (level < 0.99) {
2387141cc406Sopenharmony_ci                scanner->preview_upper_bound[k] = n;
2388141cc406Sopenharmony_ci            }
2389141cc406Sopenharmony_ci        }
2390141cc406Sopenharmony_ci        DBG(DBG_info,"sanei_pieusb_analyze_preview(): 1%%-99%% levels for color %d: %d - %d\n", k, scanner->preview_lower_bound[k], scanner->preview_upper_bound[k]);
2391141cc406Sopenharmony_ci    }
2392141cc406Sopenharmony_ci    /* Disable remaining color planes */
2393141cc406Sopenharmony_ci    for (k = scanner->buffer.colors; k < 4; k++) {
2394141cc406Sopenharmony_ci        scanner->preview_lower_bound[k] = 0;
2395141cc406Sopenharmony_ci        scanner->preview_upper_bound[k] = 0;
2396141cc406Sopenharmony_ci    }
2397141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
2398141cc406Sopenharmony_ci}
2399141cc406Sopenharmony_ci
2400141cc406Sopenharmony_ci
2401141cc406Sopenharmony_ci/**
2402141cc406Sopenharmony_ci * Return actual gain at given gain setting
2403141cc406Sopenharmony_ci *
2404141cc406Sopenharmony_ci * @param gain Gain setting (0 - 63)
2405141cc406Sopenharmony_ci * @return
2406141cc406Sopenharmony_ci */
2407141cc406Sopenharmony_cistatic double getGain(int gain)
2408141cc406Sopenharmony_ci{
2409141cc406Sopenharmony_ci    int k;
2410141cc406Sopenharmony_ci
2411141cc406Sopenharmony_ci    /* Actually an error, but don't be picky */
2412141cc406Sopenharmony_ci    if (gain <= 0) {
2413141cc406Sopenharmony_ci        return gains[0];
2414141cc406Sopenharmony_ci    }
2415141cc406Sopenharmony_ci    /* A gain > 63 is also an error, but don't be picky */
2416141cc406Sopenharmony_ci    if (gain >= 60) {
2417141cc406Sopenharmony_ci        return (gain-55)*(gains[12]-gains[11])/5 + gains[11];
2418141cc406Sopenharmony_ci    }
2419141cc406Sopenharmony_ci    /* Interpolate other values */
2420141cc406Sopenharmony_ci    k = gain/5; /* index of array value just below given gain */
2421141cc406Sopenharmony_ci    return (gain-5*k)*(gains[k+1]-gains[k])/5 + gains[k];
2422141cc406Sopenharmony_ci}
2423141cc406Sopenharmony_ci
2424141cc406Sopenharmony_cistatic int getGainSetting(double gain)
2425141cc406Sopenharmony_ci{
2426141cc406Sopenharmony_ci    int k, m;
2427141cc406Sopenharmony_ci
2428141cc406Sopenharmony_ci    /* Out of bounds */
2429141cc406Sopenharmony_ci    if (gain < 1.0) {
2430141cc406Sopenharmony_ci        return 0;
2431141cc406Sopenharmony_ci    }
2432141cc406Sopenharmony_ci    if (gain >= gains[12]) {
2433141cc406Sopenharmony_ci        m = 60 + lround((gain-gains[11])/(gains[12]-gains[11])*5);
2434141cc406Sopenharmony_ci        if (m > 63) m = 63;
2435141cc406Sopenharmony_ci        return m;
2436141cc406Sopenharmony_ci    }
2437141cc406Sopenharmony_ci    /* Interpolate the rest */
2438141cc406Sopenharmony_ci    m = 0;
2439141cc406Sopenharmony_ci    for (k = 0; k <= 11; k++) {
2440141cc406Sopenharmony_ci        if (gains[k] <= gain && gain < gains[k+1]) {
2441141cc406Sopenharmony_ci            m = 5*k + lround((gain-gains[k])/(gains[k+1]-gains[k])*5);
2442141cc406Sopenharmony_ci        }
2443141cc406Sopenharmony_ci    }
2444141cc406Sopenharmony_ci    return m;
2445141cc406Sopenharmony_ci}
2446141cc406Sopenharmony_ci
2447141cc406Sopenharmony_ci/**
2448141cc406Sopenharmony_ci * Modify gain and exposure times in order to make maximal use of the scan depth.
2449141cc406Sopenharmony_ci * Each color treated separately, infrared excluded.
2450141cc406Sopenharmony_ci *
2451141cc406Sopenharmony_ci * This may be too aggressive => leads to a noisy whitish border instead of the orange.
2452141cc406Sopenharmony_ci * In a couuple of tries, gain was set to values of 60 and above, which introduces
2453141cc406Sopenharmony_ci * the noise?
2454141cc406Sopenharmony_ci * The whitish border is logical since the brightest parts of the negative, the
2455141cc406Sopenharmony_ci * unexposed borders, are amplified to values near CCD saturation, which is white.
2456141cc406Sopenharmony_ci * Maybe a uniform gain increase for each color is more appropriate? Somewhere
2457141cc406Sopenharmony_ci * between 2.5 and 3 seems worthwhile trying, see updateGain2().
2458141cc406Sopenharmony_ci *
2459141cc406Sopenharmony_ci        switch (scanner->mode.passes) {
2460141cc406Sopenharmony_ci            case SCAN_ONE_PASS_RGBI:
2461141cc406Sopenharmony_ci                updateGain(scanner,0);
2462141cc406Sopenharmony_ci                updateGain(scanner,1);
2463141cc406Sopenharmony_ci                updateGain(scanner,2);
2464141cc406Sopenharmony_ci                updateGain(scanner,3);
2465141cc406Sopenharmony_ci                break;
2466141cc406Sopenharmony_ci            case SCAN_ONE_PASS_COLOR:
2467141cc406Sopenharmony_ci                updateGain(scanner,0);
2468141cc406Sopenharmony_ci                updateGain(scanner,1);
2469141cc406Sopenharmony_ci                updateGain(scanner,2);
2470141cc406Sopenharmony_ci                break;
2471141cc406Sopenharmony_ci            case SCAN_FILTER_INFRARED:
2472141cc406Sopenharmony_ci                updateGain(scanner,3);
2473141cc406Sopenharmony_ci                break;
2474141cc406Sopenharmony_ci            case SCAN_FILTER_BLUE:
2475141cc406Sopenharmony_ci                updateGain(scanner,2);
2476141cc406Sopenharmony_ci                break;
2477141cc406Sopenharmony_ci            case SCAN_FILTER_GREEN:
2478141cc406Sopenharmony_ci                updateGain(scanner,1);
2479141cc406Sopenharmony_ci                break;
2480141cc406Sopenharmony_ci            case SCAN_FILTER_RED:
2481141cc406Sopenharmony_ci                updateGain(scanner,0);
2482141cc406Sopenharmony_ci                break;
2483141cc406Sopenharmony_ci            case SCAN_FILTER_NEUTRAL:
2484141cc406Sopenharmony_ci                break;
2485141cc406Sopenharmony_ci        }
2486141cc406Sopenharmony_ci * @param scanner
2487141cc406Sopenharmony_ci */
2488141cc406Sopenharmony_ci/*
2489141cc406Sopenharmony_cistatic void updateGain(Pieusb_Scanner *scanner, int color_index)
2490141cc406Sopenharmony_ci{
2491141cc406Sopenharmony_ci    double g, dg;
2492141cc406Sopenharmony_ci
2493141cc406Sopenharmony_ci    DBG(DBG_info_sane,"updateGain(): color %d preview used G=%d Exp=%d\n", color_index, scanner->preview_gain[color_index], scanner->preview_exposure[color_index]);
2494141cc406Sopenharmony_ci    // Additional gain to obtain
2495141cc406Sopenharmony_ci    dg = ((double)scanner->settings.saturationLevel[color_index] / 65536) / ((double)scanner->preview_upper_bound[color_index] / HISTOGRAM_SIZE);
2496141cc406Sopenharmony_ci    DBG(DBG_info_sane,"updateGain(): additional gain %f\n", dg);
2497141cc406Sopenharmony_ci    // Achieve this by modifying gain and exposure
2498141cc406Sopenharmony_ci    // Gain used for preview
2499141cc406Sopenharmony_ci    g = getGain(scanner->preview_gain[color_index]);
2500141cc406Sopenharmony_ci    DBG(DBG_info_sane,"updateGain(): preview had gain %d => %f\n",scanner->preview_gain[color_index],g);
2501141cc406Sopenharmony_ci    // Look up new gain setting g*sqrt(dg)
2502141cc406Sopenharmony_ci    DBG(DBG_info_sane,"updateGain(): optimized gain * %f = %f\n",sqrt(dg),sqrt(dg)*g);
2503141cc406Sopenharmony_ci    scanner->settings.gain[color_index] = getGainSetting(g*sqrt(dg));
2504141cc406Sopenharmony_ci    DBG(DBG_info_sane,"updateGain(): optimized gain setting %d => %f\n",scanner->settings.gain[color_index],getGain(scanner->settings.gain[color_index]));
2505141cc406Sopenharmony_ci    // Exposure change is straightforward
2506141cc406Sopenharmony_ci    DBG(DBG_info_sane,"updateGain(): remains for exposure %f\n",dg/(getGain(scanner->settings.gain[color_index])/g));
2507141cc406Sopenharmony_ci    scanner->settings.exposureTime[color_index] = lround( g / getGain(scanner->settings.gain[color_index]) * dg * scanner->preview_exposure[color_index] );
2508141cc406Sopenharmony_ci    DBG(DBG_info_sane,"updateGain(): new setting G=%d Exp=%d\n", scanner->settings.gain[color_index], scanner->settings.exposureTime[color_index]);
2509141cc406Sopenharmony_ci}
2510141cc406Sopenharmony_ci*/
2511141cc406Sopenharmony_ci
2512141cc406Sopenharmony_cistatic void updateGain2(Pieusb_Scanner *scanner, int color_index, double gain_increase)
2513141cc406Sopenharmony_ci{
2514141cc406Sopenharmony_ci    double g;
2515141cc406Sopenharmony_ci
2516141cc406Sopenharmony_ci    DBG(DBG_info,"updateGain2(): color %d preview used G=%d Exp=%d\n", color_index, scanner->settings.gain[color_index], scanner->settings.exposureTime[color_index]);
2517141cc406Sopenharmony_ci    /* Additional gain to obtain */
2518141cc406Sopenharmony_ci    DBG(DBG_info,"updateGain2(): additional gain %f\n", gain_increase);
2519141cc406Sopenharmony_ci    /* Achieve this by modifying gain and exposure */
2520141cc406Sopenharmony_ci    /* Gain used for preview */
2521141cc406Sopenharmony_ci    g = getGain(scanner->settings.gain[color_index]);
2522141cc406Sopenharmony_ci    DBG(DBG_info,"updateGain2(): preview had gain %d => %f\n", scanner->settings.gain[color_index], g);
2523141cc406Sopenharmony_ci    /* Look up new gain setting g*sqrt(dg) */
2524141cc406Sopenharmony_ci    DBG(DBG_info,"updateGain2(): optimized gain * %f = %f\n", sqrt(gain_increase), sqrt(gain_increase) * g);
2525141cc406Sopenharmony_ci    scanner->settings.gain[color_index] = getGainSetting(g * sqrt(gain_increase));
2526141cc406Sopenharmony_ci    DBG(DBG_info,"updateGain2(): optimized gain setting %d => %f\n", scanner->settings.gain[color_index], getGain(scanner->settings.gain[color_index]));
2527141cc406Sopenharmony_ci    /* Exposure change is straightforward */
2528141cc406Sopenharmony_ci    DBG(DBG_info,"updateGain2(): remains for exposure %f\n", gain_increase / (getGain(scanner->settings.gain[color_index]) / g));
2529141cc406Sopenharmony_ci    scanner->settings.exposureTime[color_index] = lround( g / getGain(scanner->settings.gain[color_index]) * gain_increase * scanner->settings.exposureTime[color_index] );
2530141cc406Sopenharmony_ci    DBG(DBG_info,"updateGain2(): new setting G=%d Exp=%d\n", scanner->settings.gain[color_index], scanner->settings.exposureTime[color_index]);
2531141cc406Sopenharmony_ci}
2532