1141cc406Sopenharmony_ci/* sane - Scanner Access Now Easy.
2141cc406Sopenharmony_ci
3141cc406Sopenharmony_ci   This file implements a SANE backend for the Fujitsu fi-60F, the
4141cc406Sopenharmony_ci   ScanSnap S300/S1300, and (hopefully) other Epson-based scanners.
5141cc406Sopenharmony_ci
6141cc406Sopenharmony_ci   Copyright 2007-2022 by m. allan noah <kitno455 at gmail dot com>
7141cc406Sopenharmony_ci   Copyright 2009 by Richard Goedeken <richard at fascinationsoftware dot com>
8141cc406Sopenharmony_ci
9141cc406Sopenharmony_ci   Development funded by Microdea, Inc., TrueCheck, Inc. and Archivista, GmbH
10141cc406Sopenharmony_ci
11141cc406Sopenharmony_ci   --------------------------------------------------------------------------
12141cc406Sopenharmony_ci
13141cc406Sopenharmony_ci   This program is free software; you can redistribute it and/or
14141cc406Sopenharmony_ci   modify it under the terms of the GNU General Public License as
15141cc406Sopenharmony_ci   published by the Free Software Foundation; either version 2 of the
16141cc406Sopenharmony_ci   License, or (at your option) any later version.
17141cc406Sopenharmony_ci
18141cc406Sopenharmony_ci   This program is distributed in the hope that it will be useful, but
19141cc406Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
20141cc406Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21141cc406Sopenharmony_ci   General Public License for more details.
22141cc406Sopenharmony_ci
23141cc406Sopenharmony_ci   You should have received a copy of the GNU General Public License
24141cc406Sopenharmony_ci   along with this program.  If not, see <https://www.gnu.org/licenses/>.
25141cc406Sopenharmony_ci
26141cc406Sopenharmony_ci   As a special exception, the authors of SANE give permission for
27141cc406Sopenharmony_ci   additional uses of the libraries contained in this release of SANE.
28141cc406Sopenharmony_ci
29141cc406Sopenharmony_ci   The exception is that, if you link a SANE library with other files
30141cc406Sopenharmony_ci   to produce an executable, this does not by itself cause the
31141cc406Sopenharmony_ci   resulting executable to be covered by the GNU General Public
32141cc406Sopenharmony_ci   License.  Your use of that executable is in no way restricted on
33141cc406Sopenharmony_ci   account of linking the SANE library code into it.
34141cc406Sopenharmony_ci
35141cc406Sopenharmony_ci   This exception does not, however, invalidate any other reasons why
36141cc406Sopenharmony_ci   the executable file might be covered by the GNU General Public
37141cc406Sopenharmony_ci   License.
38141cc406Sopenharmony_ci
39141cc406Sopenharmony_ci   If you submit changes to SANE to the maintainers to be included in
40141cc406Sopenharmony_ci   a subsequent release, you agree by submitting the changes that
41141cc406Sopenharmony_ci   those changes may be distributed with this exception intact.
42141cc406Sopenharmony_ci
43141cc406Sopenharmony_ci   If you write modifications of your own for SANE, it is your choice
44141cc406Sopenharmony_ci   whether to permit this exception to apply to your modifications.
45141cc406Sopenharmony_ci   If you do not wish that, delete this exception notice.
46141cc406Sopenharmony_ci
47141cc406Sopenharmony_ci   --------------------------------------------------------------------------
48141cc406Sopenharmony_ci
49141cc406Sopenharmony_ci   The source code is divided in sections which you can easily find by
50141cc406Sopenharmony_ci   searching for the tag "@@".
51141cc406Sopenharmony_ci
52141cc406Sopenharmony_ci   Section 1 - Init & static stuff
53141cc406Sopenharmony_ci   Section 2 - sane_init, _get_devices, _open & friends
54141cc406Sopenharmony_ci   Section 3 - sane_*_option functions
55141cc406Sopenharmony_ci   Section 4 - sane_start, _get_param, _read & friends
56141cc406Sopenharmony_ci   Section 5 - sane_close functions
57141cc406Sopenharmony_ci   Section 6 - misc functions
58141cc406Sopenharmony_ci
59141cc406Sopenharmony_ci   Changes:
60141cc406Sopenharmony_ci      v0, 2007-08-08, MAN
61141cc406Sopenharmony_ci        - initial alpha release, S300 raw data only
62141cc406Sopenharmony_ci      v1, 2007-09-03, MAN
63141cc406Sopenharmony_ci        - only supports 300dpi duplex binary for S300
64141cc406Sopenharmony_ci      v2, 2007-09-05, MAN
65141cc406Sopenharmony_ci        - add resolution option (only one choice)
66141cc406Sopenharmony_ci	- add simplex option
67141cc406Sopenharmony_ci      v3, 2007-09-12, MAN
68141cc406Sopenharmony_ci        - add support for 150 dpi resolution
69141cc406Sopenharmony_ci      v4, 2007-10-03, MAN
70141cc406Sopenharmony_ci        - change binarization algo to use average of all channels
71141cc406Sopenharmony_ci      v5, 2007-10-10, MAN
72141cc406Sopenharmony_ci        - move data blocks to separate file
73141cc406Sopenharmony_ci        - add basic fi-60F support (600dpi color)
74141cc406Sopenharmony_ci      v6, 2007-11-12, MAN
75141cc406Sopenharmony_ci        - move various data vars into transfer structs
76141cc406Sopenharmony_ci        - move most of read_from_scanner to sane_read
77141cc406Sopenharmony_ci	- add single line reads to calibration code
78141cc406Sopenharmony_ci	- generate calibration buffer from above reads
79141cc406Sopenharmony_ci      v7, 2007-12-05, MAN
80141cc406Sopenharmony_ci        - split calibration into fine and coarse functions
81141cc406Sopenharmony_ci        - add S300 fine calibration code
82141cc406Sopenharmony_ci        - add S300 color and grayscale support
83141cc406Sopenharmony_ci      v8, 2007-12-06, MAN
84141cc406Sopenharmony_ci        - change sane_start to call ingest earlier
85141cc406Sopenharmony_ci        - enable SOURCE_ADF_BACK
86141cc406Sopenharmony_ci        - add if() around memcopy and better debugs in sane_read
87141cc406Sopenharmony_ci        - shorten default scan sizes from 15.4 to 11.75 inches
88141cc406Sopenharmony_ci      v9, 2007-12-17, MAN
89141cc406Sopenharmony_ci        - fi-60F 300 & 600 dpi support (150 is non-square?)
90141cc406Sopenharmony_ci        - fi-60F gray & binary support
91141cc406Sopenharmony_ci        - fi-60F improved calibration
92141cc406Sopenharmony_ci      v10, 2007-12-19, MAN (SANE v1.0.19)
93141cc406Sopenharmony_ci        - fix missing function (and memory leak)
94141cc406Sopenharmony_ci      v11 2008-02-14, MAN
95141cc406Sopenharmony_ci	- sanei_config_read has already cleaned string (#310597)
96141cc406Sopenharmony_ci      v12 2008-02-28, MAN
97141cc406Sopenharmony_ci	- cleanup double free bug with new destroy()
98141cc406Sopenharmony_ci      v13 2008-09-18, MAN
99141cc406Sopenharmony_ci	- add working page-height control
100141cc406Sopenharmony_ci	- add working brightness, contrast and threshold controls
101141cc406Sopenharmony_ci        - add disabled threshold curve and geometry controls
102141cc406Sopenharmony_ci        - move initialization code to sane_get_devices, for hotplugging
103141cc406Sopenharmony_ci      v14 2008-09-24, MAN
104141cc406Sopenharmony_ci        - support S300 on USB power
105141cc406Sopenharmony_ci        - support S300 225x200 and 600x600 scans
106141cc406Sopenharmony_ci        - support for automatic paper length detection (parm.lines = -1)
107141cc406Sopenharmony_ci      v15 2008-09-24, MAN
108141cc406Sopenharmony_ci        - expose hardware buttons/sensors as options for S300
109141cc406Sopenharmony_ci      v16 2008-10-01, MAN
110141cc406Sopenharmony_ci        - split fill_frontback_buffers_S300 into 3 functions
111141cc406Sopenharmony_ci        - enable threshold_curve option
112141cc406Sopenharmony_ci        - add 1-D dynamic binary thresholding code
113141cc406Sopenharmony_ci        - remove y-resolution option
114141cc406Sopenharmony_ci        - pad 225x200 data to 225x225
115141cc406Sopenharmony_ci      v17 2008-10-03, MAN
116141cc406Sopenharmony_ci        - increase scan height ~1/2 inch due to head offset
117141cc406Sopenharmony_ci        - change page length autodetection condition
118141cc406Sopenharmony_ci      v18 2009-01-21, MAN
119141cc406Sopenharmony_ci         - don't export private symbols
120141cc406Sopenharmony_ci      v19 2009-08-31, RG
121141cc406Sopenharmony_ci         - rewritten calibration routines
122141cc406Sopenharmony_ci      v20 2010-02-09, MAN (SANE 1.0.21 to 1.0.24)
123141cc406Sopenharmony_ci         - cleanup #include lines & copyright
124141cc406Sopenharmony_ci         - add S1300
125141cc406Sopenharmony_ci      v21 2011-04-15, MAN
126141cc406Sopenharmony_ci         - unreleased attempt at S1100 support
127141cc406Sopenharmony_ci      v22 2014-05-15, MAN/Hiroshi Miura
128141cc406Sopenharmony_ci         - port some S1100 changes from v21
129141cc406Sopenharmony_ci         - add paper size support
130141cc406Sopenharmony_ci      v23 2014-05-20, MAN
131141cc406Sopenharmony_ci         - add S1300i support
132141cc406Sopenharmony_ci         - fix buffer overruns in read_from_scanner
133141cc406Sopenharmony_ci         - set default page width
134141cc406Sopenharmony_ci         - simplified the 225x200 resolution code
135141cc406Sopenharmony_ci      v24 2014-06-01, MAN
136141cc406Sopenharmony_ci         - enable fine calibration for S1300i 225 & 300 dpi, and S300 150 dpi
137141cc406Sopenharmony_ci      v25 2014-06-04, MAN
138141cc406Sopenharmony_ci         - initial support for fi-65F
139141cc406Sopenharmony_ci         - initial support for S1100
140141cc406Sopenharmony_ci      v26 2014-06-28, MAN
141141cc406Sopenharmony_ci         - add resolution scaling
142141cc406Sopenharmony_ci         - fix 150 dpi settings for fi-60F and fi-65F
143141cc406Sopenharmony_ci         - make adf_height_padding variable
144141cc406Sopenharmony_ci         - make white_factor variable
145141cc406Sopenharmony_ci      v27 2015-01-24, MAN
146141cc406Sopenharmony_ci         - don't override br_x and br_y
147141cc406Sopenharmony_ci         - call change_params after changing page_width
148141cc406Sopenharmony_ci      v28 2015-03-23, MAN
149141cc406Sopenharmony_ci         - call get_hardware_status before starting scan
150141cc406Sopenharmony_ci      v29 2017-03-18, MAN
151141cc406Sopenharmony_ci         - fix infinite loop when scaling in Y direction
152141cc406Sopenharmony_ci      v30 2017-03-21, MAN
153141cc406Sopenharmony_ci         - fix image truncation when using 150 DPI in Y direction
154141cc406Sopenharmony_ci         - add 200 and 400 DPI Y direction support for fi-60F/65F
155141cc406Sopenharmony_ci      v31 2017-04-09, MAN
156141cc406Sopenharmony_ci         - hardware gray support for fi-60F/65F (disabled pending calibration)
157141cc406Sopenharmony_ci         - merge fi-60F/65F settings
158141cc406Sopenharmony_ci      v32 2022-11-15, MAN
159141cc406Sopenharmony_ci         - fix hanging scan when using source = ADF Back (fixes #601)
160141cc406Sopenharmony_ci      v33 2022-11-17, MAN
161141cc406Sopenharmony_ci         - S1300i: fix color plane offset at 225 and 330 dpi (fixes #538)
162141cc406Sopenharmony_ci
163141cc406Sopenharmony_ci   SANE FLOW DIAGRAM
164141cc406Sopenharmony_ci
165141cc406Sopenharmony_ci   - sane_init() : initialize backend
166141cc406Sopenharmony_ci   . - sane_get_devices() : query list of scanner devices
167141cc406Sopenharmony_ci   . - sane_open() : open a particular scanner device
168141cc406Sopenharmony_ci   . . - sane_set_io_mode : set blocking mode
169141cc406Sopenharmony_ci   . . - sane_get_select_fd : get scanner fd
170141cc406Sopenharmony_ci   . .
171141cc406Sopenharmony_ci   . . - sane_get_option_descriptor() : get option information
172141cc406Sopenharmony_ci   . . - sane_control_option() : change option values
173141cc406Sopenharmony_ci   . . - sane_get_parameters() : returns estimated scan parameters
174141cc406Sopenharmony_ci   . . - (repeat previous 3 functions)
175141cc406Sopenharmony_ci   . .
176141cc406Sopenharmony_ci   . . - sane_start() : start image acquisition
177141cc406Sopenharmony_ci   . .   - sane_get_parameters() : returns actual scan parameters
178141cc406Sopenharmony_ci   . .   - sane_read() : read image data (from pipe)
179141cc406Sopenharmony_ci   . . (sane_read called multiple times; after sane_read returns EOF,
180141cc406Sopenharmony_ci   . . loop may continue with sane_start which may return a 2nd page
181141cc406Sopenharmony_ci   . . when doing duplex scans, or load the next page from the ADF)
182141cc406Sopenharmony_ci   . .
183141cc406Sopenharmony_ci   . . - sane_cancel() : cancel operation
184141cc406Sopenharmony_ci   . - sane_close() : close opened scanner device
185141cc406Sopenharmony_ci   - sane_exit() : terminate use of backend
186141cc406Sopenharmony_ci
187141cc406Sopenharmony_ci*/
188141cc406Sopenharmony_ci
189141cc406Sopenharmony_ci/*
190141cc406Sopenharmony_ci * @@ Section 1 - Init
191141cc406Sopenharmony_ci */
192141cc406Sopenharmony_ci
193141cc406Sopenharmony_ci#include "../include/sane/config.h"
194141cc406Sopenharmony_ci
195141cc406Sopenharmony_ci#include <string.h> /*memcpy...*/
196141cc406Sopenharmony_ci#include <ctype.h> /*isspace*/
197141cc406Sopenharmony_ci#include <math.h> /*tan*/
198141cc406Sopenharmony_ci#include <unistd.h> /*usleep*/
199141cc406Sopenharmony_ci#include <time.h> /*time*/
200141cc406Sopenharmony_ci
201141cc406Sopenharmony_ci#include "../include/sane/sanei_backend.h"
202141cc406Sopenharmony_ci#include "../include/sane/sanei_usb.h"
203141cc406Sopenharmony_ci#include "../include/sane/saneopts.h"
204141cc406Sopenharmony_ci#include "../include/sane/sanei_config.h"
205141cc406Sopenharmony_ci
206141cc406Sopenharmony_ci#include "epjitsu.h"
207141cc406Sopenharmony_ci#include "epjitsu-cmd.h"
208141cc406Sopenharmony_ci
209141cc406Sopenharmony_ci#define DEBUG 1
210141cc406Sopenharmony_ci#define BUILD 33
211141cc406Sopenharmony_ci
212141cc406Sopenharmony_ci#ifndef MIN
213141cc406Sopenharmony_ci  #define MIN(a,b) ((a) < (b) ? (a) : (b))
214141cc406Sopenharmony_ci#endif
215141cc406Sopenharmony_ci#ifndef MAX
216141cc406Sopenharmony_ci  #define MAX(a,b) ((a) > (b) ? (a) : (b))
217141cc406Sopenharmony_ci#endif
218141cc406Sopenharmony_ci#ifndef MAX3
219141cc406Sopenharmony_ci  #define MAX3(a,b,c) ((a) > (b) ? ((a) > (c) ? a : c) : ((b) > (c) ? b : c))
220141cc406Sopenharmony_ci#endif
221141cc406Sopenharmony_ci
222141cc406Sopenharmony_ciunsigned char global_firmware_filename[PATH_MAX];
223141cc406Sopenharmony_ci
224141cc406Sopenharmony_ci/* values for SANE_DEBUG_EPJITSU env var:
225141cc406Sopenharmony_ci - errors           5
226141cc406Sopenharmony_ci - function trace  10
227141cc406Sopenharmony_ci - function detail 15
228141cc406Sopenharmony_ci - get/setopt cmds 20
229141cc406Sopenharmony_ci - usb cmd trace   25
230141cc406Sopenharmony_ci - usb cmd detail  30
231141cc406Sopenharmony_ci - useless noise   35
232141cc406Sopenharmony_ci*/
233141cc406Sopenharmony_ci
234141cc406Sopenharmony_ci/* Calibration settings */
235141cc406Sopenharmony_ci#define COARSE_OFFSET_TARGET   15
236141cc406Sopenharmony_cistatic int coarse_gain_min[3] = { 88, 88, 88 };    /* front, back, FI-60F 3rd plane */
237141cc406Sopenharmony_cistatic int coarse_gain_max[3] = { 92, 92, 92 };
238141cc406Sopenharmony_cistatic int fine_gain_target[3] = {185, 150, 170};  /* front, back, FI-60F is this ok? */
239141cc406Sopenharmony_ci
240141cc406Sopenharmony_ci/* ------------------------------------------------------------------------- */
241141cc406Sopenharmony_ci#define STRING_FLATBED SANE_I18N("Flatbed")
242141cc406Sopenharmony_ci#define STRING_ADFFRONT SANE_I18N("ADF Front")
243141cc406Sopenharmony_ci#define STRING_ADFBACK SANE_I18N("ADF Back")
244141cc406Sopenharmony_ci#define STRING_ADFDUPLEX SANE_I18N("ADF Duplex")
245141cc406Sopenharmony_ci
246141cc406Sopenharmony_ci#define STRING_LINEART SANE_VALUE_SCAN_MODE_LINEART
247141cc406Sopenharmony_ci#define STRING_GRAYSCALE SANE_VALUE_SCAN_MODE_GRAY
248141cc406Sopenharmony_ci#define STRING_COLOR SANE_VALUE_SCAN_MODE_COLOR
249141cc406Sopenharmony_ci
250141cc406Sopenharmony_ci/*
251141cc406Sopenharmony_ci * used by attach* and sane_get_devices
252141cc406Sopenharmony_ci * a ptr to a null term array of ptrs to SANE_Device structs
253141cc406Sopenharmony_ci * a ptr to a single-linked list of scanner structs
254141cc406Sopenharmony_ci */
255141cc406Sopenharmony_cistatic const SANE_Device **sane_devArray = NULL;
256141cc406Sopenharmony_cistatic struct scanner *scanner_devList = NULL;
257141cc406Sopenharmony_ci
258141cc406Sopenharmony_ci/*
259141cc406Sopenharmony_ci * @@ Section 2 - SANE & scanner init code
260141cc406Sopenharmony_ci */
261141cc406Sopenharmony_ci
262141cc406Sopenharmony_ci/*
263141cc406Sopenharmony_ci * Called by SANE initially.
264141cc406Sopenharmony_ci *
265141cc406Sopenharmony_ci * From the SANE spec:
266141cc406Sopenharmony_ci * This function must be called before any other SANE function can be
267141cc406Sopenharmony_ci * called. The behavior of a SANE backend is undefined if this
268141cc406Sopenharmony_ci * function is not called first. The version code of the backend is
269141cc406Sopenharmony_ci * returned in the value pointed to by version_code. If that pointer
270141cc406Sopenharmony_ci * is NULL, no version code is returned. Argument authorize is either
271141cc406Sopenharmony_ci * a pointer to a function that is invoked when the backend requires
272141cc406Sopenharmony_ci * authentication for a specific resource or NULL if the frontend does
273141cc406Sopenharmony_ci * not support authentication.
274141cc406Sopenharmony_ci */
275141cc406Sopenharmony_ciSANE_Status
276141cc406Sopenharmony_cisane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
277141cc406Sopenharmony_ci{
278141cc406Sopenharmony_ci    (void) authorize;           /* get rid of compiler warning */
279141cc406Sopenharmony_ci
280141cc406Sopenharmony_ci    DBG_INIT ();
281141cc406Sopenharmony_ci    DBG (10, "sane_init: start\n");
282141cc406Sopenharmony_ci
283141cc406Sopenharmony_ci    if (version_code)
284141cc406Sopenharmony_ci      *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD);
285141cc406Sopenharmony_ci
286141cc406Sopenharmony_ci    DBG (5, "sane_init: epjitsu backend %d.%d.%d, from %s\n",
287141cc406Sopenharmony_ci      SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, BUILD, PACKAGE_STRING);
288141cc406Sopenharmony_ci
289141cc406Sopenharmony_ci    DBG (10, "sane_init: finish\n");
290141cc406Sopenharmony_ci
291141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
292141cc406Sopenharmony_ci}
293141cc406Sopenharmony_ci
294141cc406Sopenharmony_ci/*
295141cc406Sopenharmony_ci * Called by SANE to find out about supported devices.
296141cc406Sopenharmony_ci *
297141cc406Sopenharmony_ci * From the SANE spec:
298141cc406Sopenharmony_ci * This function can be used to query the list of devices that are
299141cc406Sopenharmony_ci * available. If the function executes successfully, it stores a
300141cc406Sopenharmony_ci * pointer to a NULL terminated array of pointers to SANE_Device
301141cc406Sopenharmony_ci * structures in *device_list. The returned list is guaranteed to
302141cc406Sopenharmony_ci * remain unchanged and valid until (a) another call to this function
303141cc406Sopenharmony_ci * is performed or (b) a call to sane_exit() is performed. This
304141cc406Sopenharmony_ci * function can be called repeatedly to detect when new devices become
305141cc406Sopenharmony_ci * available. If argument local_only is true, only local devices are
306141cc406Sopenharmony_ci * returned (devices directly attached to the machine that SANE is
307141cc406Sopenharmony_ci * running on). If it is false, the device list includes all remote
308141cc406Sopenharmony_ci * devices that are accessible to the SANE library.
309141cc406Sopenharmony_ci *
310141cc406Sopenharmony_ci * SANE does not require that this function is called before a
311141cc406Sopenharmony_ci * sane_open() call is performed. A device name may be specified
312141cc406Sopenharmony_ci * explicitly by a user which would make it unnecessary and
313141cc406Sopenharmony_ci * undesirable to call this function first.
314141cc406Sopenharmony_ci *
315141cc406Sopenharmony_ci * Read the config file, find scanners with help from sanei_*
316141cc406Sopenharmony_ci * store in global device structs
317141cc406Sopenharmony_ci */
318141cc406Sopenharmony_ciSANE_Status
319141cc406Sopenharmony_cisane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
320141cc406Sopenharmony_ci{
321141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
322141cc406Sopenharmony_ci    struct scanner * s;
323141cc406Sopenharmony_ci    struct scanner * prev = NULL;
324141cc406Sopenharmony_ci    char line[PATH_MAX];
325141cc406Sopenharmony_ci    const char *lp;
326141cc406Sopenharmony_ci    FILE *fp;
327141cc406Sopenharmony_ci    int num_devices=0;
328141cc406Sopenharmony_ci    int i=0;
329141cc406Sopenharmony_ci
330141cc406Sopenharmony_ci    (void) local_only;          /* get rid of compiler warning */
331141cc406Sopenharmony_ci
332141cc406Sopenharmony_ci    DBG (10, "sane_get_devices: start\n");
333141cc406Sopenharmony_ci
334141cc406Sopenharmony_ci    /* mark all existing scanners as missing, attach_one will remove mark */
335141cc406Sopenharmony_ci    for (s = scanner_devList; s; s = s->next) {
336141cc406Sopenharmony_ci      s->missing = 1;
337141cc406Sopenharmony_ci    }
338141cc406Sopenharmony_ci
339141cc406Sopenharmony_ci    sanei_usb_init();
340141cc406Sopenharmony_ci
341141cc406Sopenharmony_ci    fp = sanei_config_open (CONFIG_FILE);
342141cc406Sopenharmony_ci
343141cc406Sopenharmony_ci    if (fp) {
344141cc406Sopenharmony_ci
345141cc406Sopenharmony_ci        DBG (15, "sane_get_devices: reading config file %s\n", CONFIG_FILE);
346141cc406Sopenharmony_ci
347141cc406Sopenharmony_ci        while (sanei_config_read (line, PATH_MAX, fp)) {
348141cc406Sopenharmony_ci
349141cc406Sopenharmony_ci            lp = line;
350141cc406Sopenharmony_ci
351141cc406Sopenharmony_ci            /* ignore comments */
352141cc406Sopenharmony_ci            if (*lp == '#')
353141cc406Sopenharmony_ci                continue;
354141cc406Sopenharmony_ci
355141cc406Sopenharmony_ci            /* skip empty lines */
356141cc406Sopenharmony_ci            if (*lp == 0)
357141cc406Sopenharmony_ci                continue;
358141cc406Sopenharmony_ci
359141cc406Sopenharmony_ci            if ((strncmp ("firmware", lp, 8) == 0) && isspace (lp[8])) {
360141cc406Sopenharmony_ci                size_t firmware_len;
361141cc406Sopenharmony_ci
362141cc406Sopenharmony_ci                lp += 8;
363141cc406Sopenharmony_ci                lp = sanei_config_skip_whitespace (lp);
364141cc406Sopenharmony_ci                DBG (15, "sane_get_devices: firmware '%s'\n", lp);
365141cc406Sopenharmony_ci
366141cc406Sopenharmony_ci                firmware_len = strlen(lp);
367141cc406Sopenharmony_ci                if (firmware_len > sizeof(global_firmware_filename) - 1)
368141cc406Sopenharmony_ci                  {
369141cc406Sopenharmony_ci                    DBG (5, "sane_get_devices: firmware file too long. ignoring '%s'\n", lp);
370141cc406Sopenharmony_ci                  }
371141cc406Sopenharmony_ci                else
372141cc406Sopenharmony_ci                  {
373141cc406Sopenharmony_ci                    strcpy((char *)global_firmware_filename, lp);
374141cc406Sopenharmony_ci                  }
375141cc406Sopenharmony_ci            }
376141cc406Sopenharmony_ci            else if ((strncmp ("usb", lp, 3) == 0) && isspace (lp[3])) {
377141cc406Sopenharmony_ci                DBG (15, "sane_get_devices: looking for '%s'\n", lp);
378141cc406Sopenharmony_ci                sanei_usb_attach_matching_devices(lp, attach_one);
379141cc406Sopenharmony_ci            }
380141cc406Sopenharmony_ci            else{
381141cc406Sopenharmony_ci                DBG (5, "sane_get_devices: config line \"%s\" ignored.\n", lp);
382141cc406Sopenharmony_ci            }
383141cc406Sopenharmony_ci        }
384141cc406Sopenharmony_ci        fclose (fp);
385141cc406Sopenharmony_ci    }
386141cc406Sopenharmony_ci
387141cc406Sopenharmony_ci    else {
388141cc406Sopenharmony_ci        DBG (5, "sane_get_devices: no config file '%s'!\n",
389141cc406Sopenharmony_ci          CONFIG_FILE);
390141cc406Sopenharmony_ci    }
391141cc406Sopenharmony_ci
392141cc406Sopenharmony_ci    /*delete missing scanners from list*/
393141cc406Sopenharmony_ci    for (s = scanner_devList; s;) {
394141cc406Sopenharmony_ci      if(s->missing){
395141cc406Sopenharmony_ci        DBG (5, "sane_get_devices: missing scanner %s\n",s->sane.name);
396141cc406Sopenharmony_ci
397141cc406Sopenharmony_ci        /*splice s out of list by changing pointer in prev to next*/
398141cc406Sopenharmony_ci        if(prev){
399141cc406Sopenharmony_ci          prev->next = s->next;
400141cc406Sopenharmony_ci          free(s);
401141cc406Sopenharmony_ci          s=prev->next;
402141cc406Sopenharmony_ci        }
403141cc406Sopenharmony_ci        /*remove s from head of list, using prev to cache it*/
404141cc406Sopenharmony_ci        else{
405141cc406Sopenharmony_ci          prev = s;
406141cc406Sopenharmony_ci          s = s->next;
407141cc406Sopenharmony_ci          free(prev);
408141cc406Sopenharmony_ci          prev=NULL;
409141cc406Sopenharmony_ci
410141cc406Sopenharmony_ci          /*reset head to next s*/
411141cc406Sopenharmony_ci          scanner_devList = s;
412141cc406Sopenharmony_ci        }
413141cc406Sopenharmony_ci      }
414141cc406Sopenharmony_ci      else{
415141cc406Sopenharmony_ci        prev = s;
416141cc406Sopenharmony_ci        s=prev->next;
417141cc406Sopenharmony_ci      }
418141cc406Sopenharmony_ci    }
419141cc406Sopenharmony_ci
420141cc406Sopenharmony_ci    for (s = scanner_devList; s; s=s->next) {
421141cc406Sopenharmony_ci        DBG (15, "sane_get_devices: found scanner %s\n",s->sane.name);
422141cc406Sopenharmony_ci        num_devices++;
423141cc406Sopenharmony_ci    }
424141cc406Sopenharmony_ci
425141cc406Sopenharmony_ci    DBG (15, "sane_get_devices: found %d scanner(s)\n",num_devices);
426141cc406Sopenharmony_ci
427141cc406Sopenharmony_ci    if (sane_devArray)
428141cc406Sopenharmony_ci      free (sane_devArray);
429141cc406Sopenharmony_ci
430141cc406Sopenharmony_ci    sane_devArray = calloc (num_devices + 1, sizeof (SANE_Device*));
431141cc406Sopenharmony_ci    if (!sane_devArray)
432141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
433141cc406Sopenharmony_ci
434141cc406Sopenharmony_ci    for (s = scanner_devList; s; s=s->next) {
435141cc406Sopenharmony_ci        sane_devArray[i++] = (SANE_Device *)&s->sane;
436141cc406Sopenharmony_ci    }
437141cc406Sopenharmony_ci    sane_devArray[i] = 0;
438141cc406Sopenharmony_ci
439141cc406Sopenharmony_ci    if(device_list){
440141cc406Sopenharmony_ci        *device_list = sane_devArray;
441141cc406Sopenharmony_ci    }
442141cc406Sopenharmony_ci
443141cc406Sopenharmony_ci    DBG (10, "sane_get_devices: finish\n");
444141cc406Sopenharmony_ci
445141cc406Sopenharmony_ci    return ret;
446141cc406Sopenharmony_ci}
447141cc406Sopenharmony_ci
448141cc406Sopenharmony_ci/* callback used by sane_init
449141cc406Sopenharmony_ci * build the scanner struct and link to global list
450141cc406Sopenharmony_ci * unless struct is already loaded, then pretend
451141cc406Sopenharmony_ci */
452141cc406Sopenharmony_cistatic SANE_Status
453141cc406Sopenharmony_ciattach_one (const char *name)
454141cc406Sopenharmony_ci{
455141cc406Sopenharmony_ci    struct scanner *s;
456141cc406Sopenharmony_ci    int ret, i;
457141cc406Sopenharmony_ci
458141cc406Sopenharmony_ci    DBG (10, "attach_one: start '%s'\n", name);
459141cc406Sopenharmony_ci
460141cc406Sopenharmony_ci    for (s = scanner_devList; s; s = s->next) {
461141cc406Sopenharmony_ci        if (strcmp (s->sane.name, name) == 0) {
462141cc406Sopenharmony_ci            DBG (10, "attach_one: already attached!\n");
463141cc406Sopenharmony_ci            s->missing = 0;
464141cc406Sopenharmony_ci            return SANE_STATUS_GOOD;
465141cc406Sopenharmony_ci        }
466141cc406Sopenharmony_ci    }
467141cc406Sopenharmony_ci
468141cc406Sopenharmony_ci    /* build a scanner struct to hold it */
469141cc406Sopenharmony_ci    DBG (15, "attach_one: init struct\n");
470141cc406Sopenharmony_ci
471141cc406Sopenharmony_ci    if ((s = calloc (sizeof (*s), 1)) == NULL)
472141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
473141cc406Sopenharmony_ci
474141cc406Sopenharmony_ci    /* copy the device name */
475141cc406Sopenharmony_ci    s->sane.name = strdup (name);
476141cc406Sopenharmony_ci    if (!s->sane.name){
477141cc406Sopenharmony_ci        destroy(s);
478141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
479141cc406Sopenharmony_ci    }
480141cc406Sopenharmony_ci
481141cc406Sopenharmony_ci    /* connect the fd */
482141cc406Sopenharmony_ci    DBG (15, "attach_one: connect fd\n");
483141cc406Sopenharmony_ci
484141cc406Sopenharmony_ci    s->fd = -1;
485141cc406Sopenharmony_ci    ret = connect_fd(s);
486141cc406Sopenharmony_ci    if(ret != SANE_STATUS_GOOD){
487141cc406Sopenharmony_ci        destroy(s);
488141cc406Sopenharmony_ci        return ret;
489141cc406Sopenharmony_ci    }
490141cc406Sopenharmony_ci
491141cc406Sopenharmony_ci    /* load the firmware file into scanner */
492141cc406Sopenharmony_ci    ret = load_fw(s);
493141cc406Sopenharmony_ci    if (ret != SANE_STATUS_GOOD) {
494141cc406Sopenharmony_ci        destroy(s);
495141cc406Sopenharmony_ci        DBG (5, "attach_one: firmware load failed\n");
496141cc406Sopenharmony_ci        return ret;
497141cc406Sopenharmony_ci    }
498141cc406Sopenharmony_ci
499141cc406Sopenharmony_ci    /* Now query the device to load its vendor/model/version */
500141cc406Sopenharmony_ci    ret = get_ident(s);
501141cc406Sopenharmony_ci    if (ret != SANE_STATUS_GOOD) {
502141cc406Sopenharmony_ci        destroy(s);
503141cc406Sopenharmony_ci        DBG (5, "attach_one: identify failed\n");
504141cc406Sopenharmony_ci        return ret;
505141cc406Sopenharmony_ci    }
506141cc406Sopenharmony_ci
507141cc406Sopenharmony_ci    DBG (15, "attach_one: Found %s scanner %s at %s\n",
508141cc406Sopenharmony_ci      s->sane.vendor, s->sane.model, s->sane.name);
509141cc406Sopenharmony_ci
510141cc406Sopenharmony_ci    if (strstr (s->sane.model, "S1300i")){
511141cc406Sopenharmony_ci        unsigned char stat;
512141cc406Sopenharmony_ci
513141cc406Sopenharmony_ci        DBG (15, "attach_one: Found S1300i\n");
514141cc406Sopenharmony_ci
515141cc406Sopenharmony_ci        stat = get_stat(s);
516141cc406Sopenharmony_ci        if(stat & 0x01){
517141cc406Sopenharmony_ci          DBG (5, "attach_one: on USB power?\n");
518141cc406Sopenharmony_ci          s->usb_power=1;
519141cc406Sopenharmony_ci        }
520141cc406Sopenharmony_ci
521141cc406Sopenharmony_ci        s->model = MODEL_S1300i;
522141cc406Sopenharmony_ci
523141cc406Sopenharmony_ci        s->has_adf = 1;
524141cc406Sopenharmony_ci        s->has_adf_duplex = 1;
525141cc406Sopenharmony_ci        s->min_res = 50;
526141cc406Sopenharmony_ci        s->max_res = 600;
527141cc406Sopenharmony_ci        s->adf_height_padding = 600;
528141cc406Sopenharmony_ci        /* Blue, Red, Green */
529141cc406Sopenharmony_ci        s->white_factor[0] = 1.0;
530141cc406Sopenharmony_ci        s->white_factor[1] = 0.93;
531141cc406Sopenharmony_ci        s->white_factor[2] = 0.98;
532141cc406Sopenharmony_ci
533141cc406Sopenharmony_ci        s->source = SOURCE_ADF_FRONT;
534141cc406Sopenharmony_ci        s->mode = MODE_LINEART;
535141cc406Sopenharmony_ci        s->resolution = 300;
536141cc406Sopenharmony_ci        s->page_height = 11.5 * 1200;
537141cc406Sopenharmony_ci        s->page_width  = 8.5 * 1200;
538141cc406Sopenharmony_ci
539141cc406Sopenharmony_ci        s->threshold = 120;
540141cc406Sopenharmony_ci        s->threshold_curve = 55;
541141cc406Sopenharmony_ci    }
542141cc406Sopenharmony_ci    else if (strstr (s->sane.model, "S300") || strstr (s->sane.model, "S1300")){
543141cc406Sopenharmony_ci        unsigned char stat;
544141cc406Sopenharmony_ci
545141cc406Sopenharmony_ci        DBG (15, "attach_one: Found S300/S1300\n");
546141cc406Sopenharmony_ci
547141cc406Sopenharmony_ci        stat = get_stat(s);
548141cc406Sopenharmony_ci        if(stat & 0x01){
549141cc406Sopenharmony_ci          DBG (5, "attach_one: on USB power?\n");
550141cc406Sopenharmony_ci          s->usb_power=1;
551141cc406Sopenharmony_ci        }
552141cc406Sopenharmony_ci
553141cc406Sopenharmony_ci        s->model = MODEL_S300;
554141cc406Sopenharmony_ci
555141cc406Sopenharmony_ci        s->has_adf = 1;
556141cc406Sopenharmony_ci        s->has_adf_duplex = 1;
557141cc406Sopenharmony_ci        s->min_res = 50;
558141cc406Sopenharmony_ci        s->max_res = 600;
559141cc406Sopenharmony_ci        s->adf_height_padding = 600;
560141cc406Sopenharmony_ci        /* Blue, Red, Green */
561141cc406Sopenharmony_ci        s->white_factor[0] = 1.0;
562141cc406Sopenharmony_ci        s->white_factor[1] = 0.93;
563141cc406Sopenharmony_ci        s->white_factor[2] = 0.98;
564141cc406Sopenharmony_ci
565141cc406Sopenharmony_ci        s->source = SOURCE_ADF_FRONT;
566141cc406Sopenharmony_ci        s->mode = MODE_LINEART;
567141cc406Sopenharmony_ci        s->resolution = 300;
568141cc406Sopenharmony_ci        s->page_height = 11.5 * 1200;
569141cc406Sopenharmony_ci        s->page_width  = 8.5 * 1200;
570141cc406Sopenharmony_ci
571141cc406Sopenharmony_ci        s->threshold = 120;
572141cc406Sopenharmony_ci        s->threshold_curve = 55;
573141cc406Sopenharmony_ci    }
574141cc406Sopenharmony_ci    else if (strstr (s->sane.model, "S1100")){
575141cc406Sopenharmony_ci        DBG (15, "attach_one: Found S1100\n");
576141cc406Sopenharmony_ci        s->model = MODEL_S1100;
577141cc406Sopenharmony_ci
578141cc406Sopenharmony_ci        s->usb_power = 1;
579141cc406Sopenharmony_ci        s->has_adf = 1;
580141cc406Sopenharmony_ci        s->has_adf_duplex = 0;
581141cc406Sopenharmony_ci        s->min_res = 50;
582141cc406Sopenharmony_ci        s->max_res = 600;
583141cc406Sopenharmony_ci        s->adf_height_padding = 450;
584141cc406Sopenharmony_ci        /* Blue, Red, Green */
585141cc406Sopenharmony_ci        s->white_factor[0] = 0.95;
586141cc406Sopenharmony_ci        s->white_factor[1] = 1.0;
587141cc406Sopenharmony_ci        s->white_factor[2] = 1.0;
588141cc406Sopenharmony_ci
589141cc406Sopenharmony_ci        s->source = SOURCE_ADF_FRONT;
590141cc406Sopenharmony_ci        s->mode = MODE_LINEART;
591141cc406Sopenharmony_ci        s->resolution = 300;
592141cc406Sopenharmony_ci        s->page_height = 11.5 * 1200;
593141cc406Sopenharmony_ci        s->page_width  = 8.5 * 1200;
594141cc406Sopenharmony_ci
595141cc406Sopenharmony_ci        s->threshold = 120;
596141cc406Sopenharmony_ci        s->threshold_curve = 55;
597141cc406Sopenharmony_ci    }
598141cc406Sopenharmony_ci    else if (strstr (s->sane.model, "fi-60F")){
599141cc406Sopenharmony_ci        DBG (15, "attach_one: Found fi-60F\n");
600141cc406Sopenharmony_ci
601141cc406Sopenharmony_ci        s->model = MODEL_FI60F;
602141cc406Sopenharmony_ci
603141cc406Sopenharmony_ci        s->has_fb = 1;
604141cc406Sopenharmony_ci        s->min_res = 50;
605141cc406Sopenharmony_ci        s->max_res = 600;
606141cc406Sopenharmony_ci        /* Blue, Red, Green */
607141cc406Sopenharmony_ci        s->white_factor[0] = 1.0;
608141cc406Sopenharmony_ci        s->white_factor[1] = 0.93;
609141cc406Sopenharmony_ci        s->white_factor[2] = 0.98;
610141cc406Sopenharmony_ci
611141cc406Sopenharmony_ci        s->source = SOURCE_FLATBED;
612141cc406Sopenharmony_ci        s->mode = MODE_COLOR;
613141cc406Sopenharmony_ci        s->resolution = 300;
614141cc406Sopenharmony_ci        s->page_height = 5.83 * 1200;
615141cc406Sopenharmony_ci        s->page_width  = 4.1 * 1200;
616141cc406Sopenharmony_ci
617141cc406Sopenharmony_ci        s->threshold = 120;
618141cc406Sopenharmony_ci        s->threshold_curve = 55;
619141cc406Sopenharmony_ci    }
620141cc406Sopenharmony_ci
621141cc406Sopenharmony_ci    else if (strstr (s->sane.model, "fi-65F")){
622141cc406Sopenharmony_ci        DBG (15, "attach_one: Found fi-65F\n");
623141cc406Sopenharmony_ci
624141cc406Sopenharmony_ci        s->model = MODEL_FI65F;
625141cc406Sopenharmony_ci
626141cc406Sopenharmony_ci        s->has_fb = 1;
627141cc406Sopenharmony_ci        s->min_res = 50;
628141cc406Sopenharmony_ci        s->max_res = 600;
629141cc406Sopenharmony_ci        /* Blue, Red, Green */
630141cc406Sopenharmony_ci        s->white_factor[0] = 1.0;
631141cc406Sopenharmony_ci        s->white_factor[1] = 0.93;
632141cc406Sopenharmony_ci        s->white_factor[2] = 0.98;
633141cc406Sopenharmony_ci
634141cc406Sopenharmony_ci        s->source = SOURCE_FLATBED;
635141cc406Sopenharmony_ci        s->mode = MODE_COLOR;
636141cc406Sopenharmony_ci        s->resolution = 300;
637141cc406Sopenharmony_ci        s->page_height = 5.83 * 1200;
638141cc406Sopenharmony_ci        s->page_width  = 4.1 * 1200;
639141cc406Sopenharmony_ci
640141cc406Sopenharmony_ci        s->threshold = 120;
641141cc406Sopenharmony_ci        s->threshold_curve = 55;
642141cc406Sopenharmony_ci    }
643141cc406Sopenharmony_ci    else{
644141cc406Sopenharmony_ci        DBG (15, "attach_one: Found other\n");
645141cc406Sopenharmony_ci    }
646141cc406Sopenharmony_ci
647141cc406Sopenharmony_ci    /* set SANE option 'values' to good defaults */
648141cc406Sopenharmony_ci    DBG (15, "attach_one: init options\n");
649141cc406Sopenharmony_ci
650141cc406Sopenharmony_ci    /* go ahead and setup the first opt, because
651141cc406Sopenharmony_ci     * frontend may call control_option on it
652141cc406Sopenharmony_ci     * before calling get_option_descriptor
653141cc406Sopenharmony_ci     */
654141cc406Sopenharmony_ci    memset (s->opt, 0, sizeof (s->opt));
655141cc406Sopenharmony_ci    for (i = 0; i < NUM_OPTIONS; ++i) {
656141cc406Sopenharmony_ci        s->opt[i].name = "filler";
657141cc406Sopenharmony_ci        s->opt[i].size = sizeof (SANE_Word);
658141cc406Sopenharmony_ci        s->opt[i].cap = SANE_CAP_INACTIVE;
659141cc406Sopenharmony_ci    }
660141cc406Sopenharmony_ci
661141cc406Sopenharmony_ci    s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
662141cc406Sopenharmony_ci    s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
663141cc406Sopenharmony_ci    s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
664141cc406Sopenharmony_ci    s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
665141cc406Sopenharmony_ci
666141cc406Sopenharmony_ci    DBG (15, "attach_one: init settings\n");
667141cc406Sopenharmony_ci    ret = change_params(s);
668141cc406Sopenharmony_ci
669141cc406Sopenharmony_ci    /* we close the connection, so that another backend can talk to scanner */
670141cc406Sopenharmony_ci    disconnect_fd(s);
671141cc406Sopenharmony_ci
672141cc406Sopenharmony_ci    s->next = scanner_devList;
673141cc406Sopenharmony_ci    scanner_devList = s;
674141cc406Sopenharmony_ci
675141cc406Sopenharmony_ci    DBG (10, "attach_one: finish\n");
676141cc406Sopenharmony_ci
677141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
678141cc406Sopenharmony_ci}
679141cc406Sopenharmony_ci
680141cc406Sopenharmony_ci/*
681141cc406Sopenharmony_ci * connect the fd in the scanner struct
682141cc406Sopenharmony_ci */
683141cc406Sopenharmony_cistatic SANE_Status
684141cc406Sopenharmony_ciconnect_fd (struct scanner *s)
685141cc406Sopenharmony_ci{
686141cc406Sopenharmony_ci    SANE_Status ret;
687141cc406Sopenharmony_ci
688141cc406Sopenharmony_ci    DBG (10, "connect_fd: start\n");
689141cc406Sopenharmony_ci
690141cc406Sopenharmony_ci    if(s->fd > -1){
691141cc406Sopenharmony_ci        DBG (5, "connect_fd: already open\n");
692141cc406Sopenharmony_ci        ret = SANE_STATUS_GOOD;
693141cc406Sopenharmony_ci    }
694141cc406Sopenharmony_ci    else {
695141cc406Sopenharmony_ci        DBG (15, "connect_fd: opening USB device\n");
696141cc406Sopenharmony_ci        ret = sanei_usb_open (s->sane.name, &(s->fd));
697141cc406Sopenharmony_ci    }
698141cc406Sopenharmony_ci
699141cc406Sopenharmony_ci    if(ret != SANE_STATUS_GOOD){
700141cc406Sopenharmony_ci        DBG (5, "connect_fd: could not open device: %d\n", ret);
701141cc406Sopenharmony_ci    }
702141cc406Sopenharmony_ci
703141cc406Sopenharmony_ci    DBG (10, "connect_fd: finish\n");
704141cc406Sopenharmony_ci
705141cc406Sopenharmony_ci    return ret;
706141cc406Sopenharmony_ci}
707141cc406Sopenharmony_ci
708141cc406Sopenharmony_ci/*
709141cc406Sopenharmony_ci * try to load fw into scanner
710141cc406Sopenharmony_ci */
711141cc406Sopenharmony_cistatic SANE_Status
712141cc406Sopenharmony_ciload_fw (struct scanner *s)
713141cc406Sopenharmony_ci{
714141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
715141cc406Sopenharmony_ci    int file, i;
716141cc406Sopenharmony_ci    int len = 0;
717141cc406Sopenharmony_ci    unsigned char * buf;
718141cc406Sopenharmony_ci
719141cc406Sopenharmony_ci    unsigned char cmd[4];
720141cc406Sopenharmony_ci    size_t cmdLen;
721141cc406Sopenharmony_ci    unsigned char stat[2];
722141cc406Sopenharmony_ci    size_t statLen;
723141cc406Sopenharmony_ci
724141cc406Sopenharmony_ci    DBG (10, "load_fw: start\n");
725141cc406Sopenharmony_ci
726141cc406Sopenharmony_ci    /*check status*/
727141cc406Sopenharmony_ci    /*reuse stat buffer*/
728141cc406Sopenharmony_ci    stat[0] = get_stat(s);
729141cc406Sopenharmony_ci
730141cc406Sopenharmony_ci    if(stat[0] & 0x10){
731141cc406Sopenharmony_ci        DBG (5, "load_fw: firmware already loaded?\n");
732141cc406Sopenharmony_ci        return SANE_STATUS_GOOD;
733141cc406Sopenharmony_ci    }
734141cc406Sopenharmony_ci
735141cc406Sopenharmony_ci    if(!global_firmware_filename[0]){
736141cc406Sopenharmony_ci        DBG (5, "load_fw: missing filename\n");
737141cc406Sopenharmony_ci        return SANE_STATUS_NO_DOCS;
738141cc406Sopenharmony_ci    }
739141cc406Sopenharmony_ci
740141cc406Sopenharmony_ci    file = open((char *)global_firmware_filename,O_RDONLY);
741141cc406Sopenharmony_ci    if(!file){
742141cc406Sopenharmony_ci        DBG (5, "load_fw: failed to open file %s\n",global_firmware_filename);
743141cc406Sopenharmony_ci        return SANE_STATUS_NO_DOCS;
744141cc406Sopenharmony_ci    }
745141cc406Sopenharmony_ci
746141cc406Sopenharmony_ci    /* skip first 256 (=0x100) bytes */
747141cc406Sopenharmony_ci    if(lseek(file,0x100,SEEK_SET) != 0x100){
748141cc406Sopenharmony_ci        DBG (5, "load_fw: failed to lseek file %s\n",global_firmware_filename);
749141cc406Sopenharmony_ci	close(file);
750141cc406Sopenharmony_ci        return SANE_STATUS_NO_DOCS;
751141cc406Sopenharmony_ci    }
752141cc406Sopenharmony_ci
753141cc406Sopenharmony_ci    buf = malloc(FIRMWARE_LENGTH);
754141cc406Sopenharmony_ci    if(!buf){
755141cc406Sopenharmony_ci        DBG (5, "load_fw: failed to alloc mem\n");
756141cc406Sopenharmony_ci	close(file);
757141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
758141cc406Sopenharmony_ci    }
759141cc406Sopenharmony_ci
760141cc406Sopenharmony_ci    len = read(file,buf,FIRMWARE_LENGTH);
761141cc406Sopenharmony_ci    close(file);
762141cc406Sopenharmony_ci
763141cc406Sopenharmony_ci    if(len != FIRMWARE_LENGTH){
764141cc406Sopenharmony_ci        DBG (5, "load_fw: firmware file %s wrong length\n",
765141cc406Sopenharmony_ci          global_firmware_filename);
766141cc406Sopenharmony_ci        free(buf);
767141cc406Sopenharmony_ci        return SANE_STATUS_NO_DOCS;
768141cc406Sopenharmony_ci    }
769141cc406Sopenharmony_ci
770141cc406Sopenharmony_ci    DBG (15, "load_fw: read firmware file %s ok\n", global_firmware_filename);
771141cc406Sopenharmony_ci
772141cc406Sopenharmony_ci    /* firmware upload is in three commands */
773141cc406Sopenharmony_ci
774141cc406Sopenharmony_ci    /*start/status*/
775141cc406Sopenharmony_ci    cmd[0] = 0x1b;
776141cc406Sopenharmony_ci    cmd[1] = 0x06;
777141cc406Sopenharmony_ci    cmdLen = 2;
778141cc406Sopenharmony_ci    statLen = 1;
779141cc406Sopenharmony_ci
780141cc406Sopenharmony_ci    ret = do_cmd(
781141cc406Sopenharmony_ci      s, 0,
782141cc406Sopenharmony_ci      cmd, cmdLen,
783141cc406Sopenharmony_ci      NULL, 0,
784141cc406Sopenharmony_ci      stat, &statLen
785141cc406Sopenharmony_ci    );
786141cc406Sopenharmony_ci    if(ret){
787141cc406Sopenharmony_ci        DBG (5, "load_fw: error on cmd 1\n");
788141cc406Sopenharmony_ci        free(buf);
789141cc406Sopenharmony_ci        return ret;
790141cc406Sopenharmony_ci    }
791141cc406Sopenharmony_ci    if(stat[0] != 6){
792141cc406Sopenharmony_ci        DBG (5, "load_fw: bad stat on cmd 1\n");
793141cc406Sopenharmony_ci        free(buf);
794141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
795141cc406Sopenharmony_ci    }
796141cc406Sopenharmony_ci
797141cc406Sopenharmony_ci    /*length/data*/
798141cc406Sopenharmony_ci    cmd[0] = 0x01;
799141cc406Sopenharmony_ci    cmd[1] = 0x00;
800141cc406Sopenharmony_ci    cmd[2] = 0x01;
801141cc406Sopenharmony_ci    cmd[3] = 0x00;
802141cc406Sopenharmony_ci    cmdLen = 4;
803141cc406Sopenharmony_ci
804141cc406Sopenharmony_ci    ret = do_cmd(
805141cc406Sopenharmony_ci      s, 0,
806141cc406Sopenharmony_ci      cmd, cmdLen,
807141cc406Sopenharmony_ci      buf, FIRMWARE_LENGTH,
808141cc406Sopenharmony_ci      NULL, 0
809141cc406Sopenharmony_ci    );
810141cc406Sopenharmony_ci    if(ret){
811141cc406Sopenharmony_ci        DBG (5, "load_fw: error on cmd 2\n");
812141cc406Sopenharmony_ci        free(buf);
813141cc406Sopenharmony_ci        return ret;
814141cc406Sopenharmony_ci    }
815141cc406Sopenharmony_ci
816141cc406Sopenharmony_ci    /*checksum/status*/
817141cc406Sopenharmony_ci    cmd[0] = 0;
818141cc406Sopenharmony_ci    for(i=0;i<FIRMWARE_LENGTH;i++){
819141cc406Sopenharmony_ci        cmd[0] += buf[i];
820141cc406Sopenharmony_ci    }
821141cc406Sopenharmony_ci    free(buf);
822141cc406Sopenharmony_ci
823141cc406Sopenharmony_ci    cmdLen = 1;
824141cc406Sopenharmony_ci    statLen = 1;
825141cc406Sopenharmony_ci
826141cc406Sopenharmony_ci    ret = do_cmd(
827141cc406Sopenharmony_ci      s, 0,
828141cc406Sopenharmony_ci      cmd, cmdLen,
829141cc406Sopenharmony_ci      NULL, 0,
830141cc406Sopenharmony_ci      stat, &statLen
831141cc406Sopenharmony_ci    );
832141cc406Sopenharmony_ci    if(ret){
833141cc406Sopenharmony_ci        DBG (5, "load_fw: error on cmd 3\n");
834141cc406Sopenharmony_ci        return ret;
835141cc406Sopenharmony_ci    }
836141cc406Sopenharmony_ci    if(stat[0] != 6){
837141cc406Sopenharmony_ci        DBG (5, "load_fw: bad stat on cmd 3\n");
838141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
839141cc406Sopenharmony_ci    }
840141cc406Sopenharmony_ci
841141cc406Sopenharmony_ci    /*reinit*/
842141cc406Sopenharmony_ci    cmd[0] = 0x1b;
843141cc406Sopenharmony_ci    cmd[1] = 0x16;
844141cc406Sopenharmony_ci    cmdLen = 2;
845141cc406Sopenharmony_ci    statLen = 1;
846141cc406Sopenharmony_ci
847141cc406Sopenharmony_ci    ret = do_cmd(
848141cc406Sopenharmony_ci      s, 0,
849141cc406Sopenharmony_ci      cmd, cmdLen,
850141cc406Sopenharmony_ci      NULL, 0,
851141cc406Sopenharmony_ci      stat, &statLen
852141cc406Sopenharmony_ci    );
853141cc406Sopenharmony_ci    if(ret){
854141cc406Sopenharmony_ci        DBG (5, "load_fw: error reinit cmd\n");
855141cc406Sopenharmony_ci        return ret;
856141cc406Sopenharmony_ci    }
857141cc406Sopenharmony_ci    if(stat[0] != 6){
858141cc406Sopenharmony_ci        DBG (5, "load_fw: reinit cmd bad status?\n");
859141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
860141cc406Sopenharmony_ci    }
861141cc406Sopenharmony_ci
862141cc406Sopenharmony_ci    cmd[0] = 0x80;
863141cc406Sopenharmony_ci    cmdLen = 1;
864141cc406Sopenharmony_ci    statLen = 1;
865141cc406Sopenharmony_ci
866141cc406Sopenharmony_ci    ret = do_cmd(
867141cc406Sopenharmony_ci      s, 0,
868141cc406Sopenharmony_ci      cmd, cmdLen,
869141cc406Sopenharmony_ci      NULL, 0,
870141cc406Sopenharmony_ci      stat, &statLen
871141cc406Sopenharmony_ci    );
872141cc406Sopenharmony_ci    if(ret){
873141cc406Sopenharmony_ci        DBG (5, "load_fw: error reinit payload\n");
874141cc406Sopenharmony_ci        return ret;
875141cc406Sopenharmony_ci    }
876141cc406Sopenharmony_ci    if(stat[0] != 6){
877141cc406Sopenharmony_ci        DBG (5, "load_fw: reinit payload bad status?\n");
878141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
879141cc406Sopenharmony_ci    }
880141cc406Sopenharmony_ci
881141cc406Sopenharmony_ci    /*reuse stat buffer*/
882141cc406Sopenharmony_ci    stat[0] = get_stat(s);
883141cc406Sopenharmony_ci
884141cc406Sopenharmony_ci    if(!(stat[0] & 0x10)){
885141cc406Sopenharmony_ci        DBG (5, "load_fw: firmware not loaded? %#x\n",stat[0]);
886141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
887141cc406Sopenharmony_ci    }
888141cc406Sopenharmony_ci
889141cc406Sopenharmony_ci    return ret;
890141cc406Sopenharmony_ci}
891141cc406Sopenharmony_ci
892141cc406Sopenharmony_ci/*
893141cc406Sopenharmony_ci * get status from scanner
894141cc406Sopenharmony_ci */
895141cc406Sopenharmony_cistatic unsigned char
896141cc406Sopenharmony_ciget_stat(struct scanner *s)
897141cc406Sopenharmony_ci{
898141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
899141cc406Sopenharmony_ci
900141cc406Sopenharmony_ci    unsigned char cmd[2];
901141cc406Sopenharmony_ci    size_t cmdLen;
902141cc406Sopenharmony_ci    unsigned char stat[2];
903141cc406Sopenharmony_ci    size_t statLen;
904141cc406Sopenharmony_ci
905141cc406Sopenharmony_ci    DBG (10, "get_stat: start\n");
906141cc406Sopenharmony_ci
907141cc406Sopenharmony_ci    /*check status*/
908141cc406Sopenharmony_ci    cmd[0] = 0x1b;
909141cc406Sopenharmony_ci    cmd[1] = 0x03;
910141cc406Sopenharmony_ci    cmdLen = 2;
911141cc406Sopenharmony_ci    statLen = 2;
912141cc406Sopenharmony_ci
913141cc406Sopenharmony_ci    ret = do_cmd(
914141cc406Sopenharmony_ci      s, 0,
915141cc406Sopenharmony_ci      cmd, cmdLen,
916141cc406Sopenharmony_ci      NULL, 0,
917141cc406Sopenharmony_ci      stat, &statLen
918141cc406Sopenharmony_ci    );
919141cc406Sopenharmony_ci    if(ret){
920141cc406Sopenharmony_ci        DBG (5, "get_stat: error checking status\n");
921141cc406Sopenharmony_ci        return 0;
922141cc406Sopenharmony_ci    }
923141cc406Sopenharmony_ci
924141cc406Sopenharmony_ci    return stat[0];
925141cc406Sopenharmony_ci}
926141cc406Sopenharmony_ci
927141cc406Sopenharmony_ci/*
928141cc406Sopenharmony_ci * get scanner identification
929141cc406Sopenharmony_ci */
930141cc406Sopenharmony_ci
931141cc406Sopenharmony_cistatic SANE_Status
932141cc406Sopenharmony_ciget_ident(struct scanner *s)
933141cc406Sopenharmony_ci{
934141cc406Sopenharmony_ci    int i;
935141cc406Sopenharmony_ci    SANE_Status ret;
936141cc406Sopenharmony_ci
937141cc406Sopenharmony_ci    unsigned char cmd[] = {0x1b,0x13};
938141cc406Sopenharmony_ci    size_t cmdLen = 2;
939141cc406Sopenharmony_ci    unsigned char in[0x20];
940141cc406Sopenharmony_ci    size_t inLen = sizeof(in);
941141cc406Sopenharmony_ci
942141cc406Sopenharmony_ci    DBG (10, "get_ident: start\n");
943141cc406Sopenharmony_ci
944141cc406Sopenharmony_ci    ret = do_cmd (
945141cc406Sopenharmony_ci      s, 0,
946141cc406Sopenharmony_ci      cmd, cmdLen,
947141cc406Sopenharmony_ci      NULL, 0,
948141cc406Sopenharmony_ci      in, &inLen
949141cc406Sopenharmony_ci    );
950141cc406Sopenharmony_ci
951141cc406Sopenharmony_ci    if (ret != SANE_STATUS_GOOD){
952141cc406Sopenharmony_ci      return ret;
953141cc406Sopenharmony_ci    }
954141cc406Sopenharmony_ci
955141cc406Sopenharmony_ci    /*hmm, similar to scsi?*/
956141cc406Sopenharmony_ci    for (i = 7; (in[i] == ' ' || in[i] == 0xff) && i >= 0; i--){
957141cc406Sopenharmony_ci        in[i] = 0;
958141cc406Sopenharmony_ci    }
959141cc406Sopenharmony_ci    s->sane.vendor = strndup((char *)in, 8);
960141cc406Sopenharmony_ci
961141cc406Sopenharmony_ci    for (i = 23; (in[i] == ' ' || in[i] == 0xff) && i >= 8; i--){
962141cc406Sopenharmony_ci        in[i] = 0;
963141cc406Sopenharmony_ci    }
964141cc406Sopenharmony_ci    s->sane.model= strndup((char *)in+8, 24);
965141cc406Sopenharmony_ci
966141cc406Sopenharmony_ci    s->sane.type = "scanner";
967141cc406Sopenharmony_ci
968141cc406Sopenharmony_ci    DBG (10, "get_ident: finish\n");
969141cc406Sopenharmony_ci    return ret;
970141cc406Sopenharmony_ci}
971141cc406Sopenharmony_ci
972141cc406Sopenharmony_ci/*
973141cc406Sopenharmony_ci * From the SANE spec:
974141cc406Sopenharmony_ci * This function is used to establish a connection to a particular
975141cc406Sopenharmony_ci * device. The name of the device to be opened is passed in argument
976141cc406Sopenharmony_ci * name. If the call completes successfully, a handle for the device
977141cc406Sopenharmony_ci * is returned in *h. As a special case, specifying a zero-length
978141cc406Sopenharmony_ci * string as the device requests opening the first available device
979141cc406Sopenharmony_ci * (if there is such a device).
980141cc406Sopenharmony_ci */
981141cc406Sopenharmony_ciSANE_Status
982141cc406Sopenharmony_cisane_open (SANE_String_Const name, SANE_Handle * handle)
983141cc406Sopenharmony_ci{
984141cc406Sopenharmony_ci    struct scanner *dev = NULL;
985141cc406Sopenharmony_ci    struct scanner *s = NULL;
986141cc406Sopenharmony_ci    SANE_Status ret;
987141cc406Sopenharmony_ci
988141cc406Sopenharmony_ci    DBG (10, "sane_open: start\n");
989141cc406Sopenharmony_ci
990141cc406Sopenharmony_ci    if(scanner_devList){
991141cc406Sopenharmony_ci      DBG (15, "sane_open: searching currently attached scanners\n");
992141cc406Sopenharmony_ci    }
993141cc406Sopenharmony_ci    else{
994141cc406Sopenharmony_ci      DBG (15, "sane_open: no scanners currently attached, attaching\n");
995141cc406Sopenharmony_ci
996141cc406Sopenharmony_ci      ret = sane_get_devices(NULL,0);
997141cc406Sopenharmony_ci      if(ret != SANE_STATUS_GOOD){
998141cc406Sopenharmony_ci        return ret;
999141cc406Sopenharmony_ci      }
1000141cc406Sopenharmony_ci    }
1001141cc406Sopenharmony_ci
1002141cc406Sopenharmony_ci    if(name[0] == 0){
1003141cc406Sopenharmony_ci        DBG (15, "sane_open: no device requested, using default\n");
1004141cc406Sopenharmony_ci        s = scanner_devList;
1005141cc406Sopenharmony_ci    }
1006141cc406Sopenharmony_ci    else{
1007141cc406Sopenharmony_ci        DBG (15, "sane_open: device %s requested, attaching\n", name);
1008141cc406Sopenharmony_ci
1009141cc406Sopenharmony_ci        for (dev = scanner_devList; dev; dev = dev->next) {
1010141cc406Sopenharmony_ci            if (strcmp (dev->sane.name, name) == 0) {
1011141cc406Sopenharmony_ci                s = dev;
1012141cc406Sopenharmony_ci                break;
1013141cc406Sopenharmony_ci            }
1014141cc406Sopenharmony_ci        }
1015141cc406Sopenharmony_ci    }
1016141cc406Sopenharmony_ci
1017141cc406Sopenharmony_ci    if (!s) {
1018141cc406Sopenharmony_ci        DBG (5, "sane_open: no device found\n");
1019141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
1020141cc406Sopenharmony_ci    }
1021141cc406Sopenharmony_ci
1022141cc406Sopenharmony_ci    DBG (15, "sane_open: device %s found\n", s->sane.name);
1023141cc406Sopenharmony_ci
1024141cc406Sopenharmony_ci    *handle = s;
1025141cc406Sopenharmony_ci
1026141cc406Sopenharmony_ci    /* connect the fd so we can talk to scanner */
1027141cc406Sopenharmony_ci    ret = connect_fd(s);
1028141cc406Sopenharmony_ci    if(ret != SANE_STATUS_GOOD){
1029141cc406Sopenharmony_ci        return ret;
1030141cc406Sopenharmony_ci    }
1031141cc406Sopenharmony_ci
1032141cc406Sopenharmony_ci    DBG (10, "sane_open: finish\n");
1033141cc406Sopenharmony_ci
1034141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
1035141cc406Sopenharmony_ci}
1036141cc406Sopenharmony_ci
1037141cc406Sopenharmony_ci/*
1038141cc406Sopenharmony_ci * @@ Section 3 - SANE Options functions
1039141cc406Sopenharmony_ci */
1040141cc406Sopenharmony_ci
1041141cc406Sopenharmony_ci/*
1042141cc406Sopenharmony_ci * Returns the options we know.
1043141cc406Sopenharmony_ci *
1044141cc406Sopenharmony_ci * From the SANE spec:
1045141cc406Sopenharmony_ci * This function is used to access option descriptors. The function
1046141cc406Sopenharmony_ci * returns the option descriptor for option number n of the device
1047141cc406Sopenharmony_ci * represented by handle h. Option number 0 is guaranteed to be a
1048141cc406Sopenharmony_ci * valid option. Its value is an integer that specifies the number of
1049141cc406Sopenharmony_ci * options that are available for device handle h (the count includes
1050141cc406Sopenharmony_ci * option 0). If n is not a valid option index, the function returns
1051141cc406Sopenharmony_ci * NULL. The returned option descriptor is guaranteed to remain valid
1052141cc406Sopenharmony_ci * (and at the returned address) until the device is closed.
1053141cc406Sopenharmony_ci */
1054141cc406Sopenharmony_ciconst SANE_Option_Descriptor *
1055141cc406Sopenharmony_cisane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
1056141cc406Sopenharmony_ci{
1057141cc406Sopenharmony_ci  struct scanner *s = handle;
1058141cc406Sopenharmony_ci  int i;
1059141cc406Sopenharmony_ci  SANE_Option_Descriptor *opt = &s->opt[option];
1060141cc406Sopenharmony_ci
1061141cc406Sopenharmony_ci  DBG (20, "sane_get_option_descriptor: %d\n", option);
1062141cc406Sopenharmony_ci
1063141cc406Sopenharmony_ci  if ((unsigned) option >= NUM_OPTIONS)
1064141cc406Sopenharmony_ci    return NULL;
1065141cc406Sopenharmony_ci
1066141cc406Sopenharmony_ci  /* "Mode" group -------------------------------------------------------- */
1067141cc406Sopenharmony_ci  if(option==OPT_MODE_GROUP){
1068141cc406Sopenharmony_ci    opt->title = "Scan Mode";
1069141cc406Sopenharmony_ci    opt->desc = "";
1070141cc406Sopenharmony_ci    opt->type = SANE_TYPE_GROUP;
1071141cc406Sopenharmony_ci    opt->constraint_type = SANE_CONSTRAINT_NONE;
1072141cc406Sopenharmony_ci  }
1073141cc406Sopenharmony_ci
1074141cc406Sopenharmony_ci  /* source */
1075141cc406Sopenharmony_ci  else if(option==OPT_SOURCE){
1076141cc406Sopenharmony_ci    i=0;
1077141cc406Sopenharmony_ci    if(s->has_fb){
1078141cc406Sopenharmony_ci      s->source_list[i++]=STRING_FLATBED;
1079141cc406Sopenharmony_ci    }
1080141cc406Sopenharmony_ci    if(s->has_adf){
1081141cc406Sopenharmony_ci      s->source_list[i++]=STRING_ADFFRONT;
1082141cc406Sopenharmony_ci      if(s->has_adf_duplex){
1083141cc406Sopenharmony_ci        s->source_list[i++]=STRING_ADFBACK;
1084141cc406Sopenharmony_ci        s->source_list[i++]=STRING_ADFDUPLEX;
1085141cc406Sopenharmony_ci      }
1086141cc406Sopenharmony_ci    }
1087141cc406Sopenharmony_ci    s->source_list[i]=NULL;
1088141cc406Sopenharmony_ci
1089141cc406Sopenharmony_ci    opt->name = SANE_NAME_SCAN_SOURCE;
1090141cc406Sopenharmony_ci    opt->title = SANE_TITLE_SCAN_SOURCE;
1091141cc406Sopenharmony_ci    opt->desc = SANE_DESC_SCAN_SOURCE;
1092141cc406Sopenharmony_ci    opt->type = SANE_TYPE_STRING;
1093141cc406Sopenharmony_ci    opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
1094141cc406Sopenharmony_ci    opt->constraint.string_list = s->source_list;
1095141cc406Sopenharmony_ci    opt->size = maxStringSize (opt->constraint.string_list);
1096141cc406Sopenharmony_ci    if(i > 1){
1097141cc406Sopenharmony_ci      opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
1098141cc406Sopenharmony_ci    }
1099141cc406Sopenharmony_ci  }
1100141cc406Sopenharmony_ci
1101141cc406Sopenharmony_ci  /* scan mode */
1102141cc406Sopenharmony_ci  else if(option==OPT_MODE){
1103141cc406Sopenharmony_ci    i=0;
1104141cc406Sopenharmony_ci    s->mode_list[i++]=STRING_LINEART;
1105141cc406Sopenharmony_ci    s->mode_list[i++]=STRING_GRAYSCALE;
1106141cc406Sopenharmony_ci    s->mode_list[i++]=STRING_COLOR;
1107141cc406Sopenharmony_ci    s->mode_list[i]=NULL;
1108141cc406Sopenharmony_ci
1109141cc406Sopenharmony_ci    opt->name = SANE_NAME_SCAN_MODE;
1110141cc406Sopenharmony_ci    opt->title = SANE_TITLE_SCAN_MODE;
1111141cc406Sopenharmony_ci    opt->desc = SANE_DESC_SCAN_MODE;
1112141cc406Sopenharmony_ci    opt->type = SANE_TYPE_STRING;
1113141cc406Sopenharmony_ci    opt->constraint_type = SANE_CONSTRAINT_STRING_LIST;
1114141cc406Sopenharmony_ci    opt->constraint.string_list = s->mode_list;
1115141cc406Sopenharmony_ci    opt->size = maxStringSize (opt->constraint.string_list);
1116141cc406Sopenharmony_ci    if(i > 1){
1117141cc406Sopenharmony_ci      opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
1118141cc406Sopenharmony_ci    }
1119141cc406Sopenharmony_ci  }
1120141cc406Sopenharmony_ci
1121141cc406Sopenharmony_ci  else if(option==OPT_RES){
1122141cc406Sopenharmony_ci    opt->name = SANE_NAME_SCAN_RESOLUTION;
1123141cc406Sopenharmony_ci    opt->title = SANE_TITLE_SCAN_RESOLUTION;
1124141cc406Sopenharmony_ci    opt->desc = SANE_DESC_SCAN_RESOLUTION;
1125141cc406Sopenharmony_ci    opt->type = SANE_TYPE_INT;
1126141cc406Sopenharmony_ci    opt->unit = SANE_UNIT_DPI;
1127141cc406Sopenharmony_ci    opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
1128141cc406Sopenharmony_ci
1129141cc406Sopenharmony_ci    s->res_range.min = s->min_res;
1130141cc406Sopenharmony_ci    s->res_range.max = s->max_res;
1131141cc406Sopenharmony_ci    s->res_range.quant = 1;
1132141cc406Sopenharmony_ci    opt->constraint_type = SANE_CONSTRAINT_RANGE;
1133141cc406Sopenharmony_ci    opt->constraint.range = &s->res_range;
1134141cc406Sopenharmony_ci  }
1135141cc406Sopenharmony_ci
1136141cc406Sopenharmony_ci  /* "Geometry" group ---------------------------------------------------- */
1137141cc406Sopenharmony_ci  if(option==OPT_GEOMETRY_GROUP){
1138141cc406Sopenharmony_ci    opt->name = SANE_NAME_GEOMETRY;
1139141cc406Sopenharmony_ci    opt->title = SANE_TITLE_GEOMETRY;
1140141cc406Sopenharmony_ci    opt->desc = SANE_DESC_GEOMETRY;
1141141cc406Sopenharmony_ci    opt->type = SANE_TYPE_GROUP;
1142141cc406Sopenharmony_ci    opt->constraint_type = SANE_CONSTRAINT_NONE;
1143141cc406Sopenharmony_ci  }
1144141cc406Sopenharmony_ci
1145141cc406Sopenharmony_ci  /* top-left x */
1146141cc406Sopenharmony_ci  if(option==OPT_TL_X){
1147141cc406Sopenharmony_ci    /* values stored in 1200 dpi units */
1148141cc406Sopenharmony_ci    /* must be converted to MM for sane */
1149141cc406Sopenharmony_ci    s->tl_x_range.min = SCANNER_UNIT_TO_FIXED_MM(0);
1150141cc406Sopenharmony_ci    s->tl_x_range.max = SCANNER_UNIT_TO_FIXED_MM(MAX(0, get_page_width(s)-s->min_x));
1151141cc406Sopenharmony_ci    s->tl_x_range.quant = MM_PER_UNIT_FIX;
1152141cc406Sopenharmony_ci
1153141cc406Sopenharmony_ci    opt->name = SANE_NAME_SCAN_TL_X;
1154141cc406Sopenharmony_ci    opt->title = SANE_TITLE_SCAN_TL_X;
1155141cc406Sopenharmony_ci    opt->desc = SANE_DESC_SCAN_TL_X;
1156141cc406Sopenharmony_ci    opt->type = SANE_TYPE_FIXED;
1157141cc406Sopenharmony_ci    opt->unit = SANE_UNIT_MM;
1158141cc406Sopenharmony_ci    opt->constraint_type = SANE_CONSTRAINT_RANGE;
1159141cc406Sopenharmony_ci    opt->constraint.range = &(s->tl_x_range);
1160141cc406Sopenharmony_ci    opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
1161141cc406Sopenharmony_ci    opt->cap = SANE_CAP_INACTIVE;
1162141cc406Sopenharmony_ci  }
1163141cc406Sopenharmony_ci
1164141cc406Sopenharmony_ci  /* top-left y */
1165141cc406Sopenharmony_ci  if(option==OPT_TL_Y){
1166141cc406Sopenharmony_ci    /* values stored in 1200 dpi units */
1167141cc406Sopenharmony_ci    /* must be converted to MM for sane */
1168141cc406Sopenharmony_ci    s->tl_y_range.min = SCANNER_UNIT_TO_FIXED_MM(0);
1169141cc406Sopenharmony_ci    s->tl_y_range.max = SCANNER_UNIT_TO_FIXED_MM(MAX(0, get_page_height(s)-s->min_y));
1170141cc406Sopenharmony_ci    s->tl_y_range.quant = MM_PER_UNIT_FIX;
1171141cc406Sopenharmony_ci
1172141cc406Sopenharmony_ci    opt->name = SANE_NAME_SCAN_TL_Y;
1173141cc406Sopenharmony_ci    opt->title = SANE_TITLE_SCAN_TL_Y;
1174141cc406Sopenharmony_ci    opt->desc = SANE_DESC_SCAN_TL_Y;
1175141cc406Sopenharmony_ci    opt->type = SANE_TYPE_FIXED;
1176141cc406Sopenharmony_ci    opt->unit = SANE_UNIT_MM;
1177141cc406Sopenharmony_ci    opt->constraint_type = SANE_CONSTRAINT_RANGE;
1178141cc406Sopenharmony_ci    opt->constraint.range = &(s->tl_y_range);
1179141cc406Sopenharmony_ci    opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
1180141cc406Sopenharmony_ci  }
1181141cc406Sopenharmony_ci
1182141cc406Sopenharmony_ci  /* bottom-right x */
1183141cc406Sopenharmony_ci  if(option==OPT_BR_X){
1184141cc406Sopenharmony_ci    /* values stored in 1200 dpi units */
1185141cc406Sopenharmony_ci    /* must be converted to MM for sane */
1186141cc406Sopenharmony_ci    s->br_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_x);
1187141cc406Sopenharmony_ci    s->br_x_range.max = SCANNER_UNIT_TO_FIXED_MM(MAX(s->min_x, get_page_width(s)));
1188141cc406Sopenharmony_ci    s->br_x_range.quant = MM_PER_UNIT_FIX;
1189141cc406Sopenharmony_ci
1190141cc406Sopenharmony_ci    opt->name = SANE_NAME_SCAN_BR_X;
1191141cc406Sopenharmony_ci    opt->title = SANE_TITLE_SCAN_BR_X;
1192141cc406Sopenharmony_ci    opt->desc = SANE_DESC_SCAN_BR_X;
1193141cc406Sopenharmony_ci    opt->type = SANE_TYPE_FIXED;
1194141cc406Sopenharmony_ci    opt->unit = SANE_UNIT_MM;
1195141cc406Sopenharmony_ci    opt->constraint_type = SANE_CONSTRAINT_RANGE;
1196141cc406Sopenharmony_ci    opt->constraint.range = &(s->br_x_range);
1197141cc406Sopenharmony_ci    opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
1198141cc406Sopenharmony_ci    opt->cap = SANE_CAP_INACTIVE;
1199141cc406Sopenharmony_ci  }
1200141cc406Sopenharmony_ci
1201141cc406Sopenharmony_ci  /* bottom-right y */
1202141cc406Sopenharmony_ci  if(option==OPT_BR_Y){
1203141cc406Sopenharmony_ci    /* values stored in 1200 dpi units */
1204141cc406Sopenharmony_ci    /* must be converted to MM for sane */
1205141cc406Sopenharmony_ci    s->br_y_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_y);
1206141cc406Sopenharmony_ci    s->br_y_range.max = SCANNER_UNIT_TO_FIXED_MM(MAX(s->min_y, get_page_height(s)));
1207141cc406Sopenharmony_ci    s->br_y_range.quant = MM_PER_UNIT_FIX;
1208141cc406Sopenharmony_ci
1209141cc406Sopenharmony_ci    opt->name = SANE_NAME_SCAN_BR_Y;
1210141cc406Sopenharmony_ci    opt->title = SANE_TITLE_SCAN_BR_Y;
1211141cc406Sopenharmony_ci    opt->desc = SANE_DESC_SCAN_BR_Y;
1212141cc406Sopenharmony_ci    opt->type = SANE_TYPE_FIXED;
1213141cc406Sopenharmony_ci    opt->unit = SANE_UNIT_MM;
1214141cc406Sopenharmony_ci    opt->constraint_type = SANE_CONSTRAINT_RANGE;
1215141cc406Sopenharmony_ci    opt->constraint.range = &(s->br_y_range);
1216141cc406Sopenharmony_ci    opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
1217141cc406Sopenharmony_ci    opt->cap = SANE_CAP_INACTIVE;
1218141cc406Sopenharmony_ci  }
1219141cc406Sopenharmony_ci
1220141cc406Sopenharmony_ci  /* page width */
1221141cc406Sopenharmony_ci  if(option==OPT_PAGE_WIDTH){
1222141cc406Sopenharmony_ci    /* values stored in 1200 dpi units */
1223141cc406Sopenharmony_ci    /* must be converted to MM for sane */
1224141cc406Sopenharmony_ci    s->paper_x_range.min = SCANNER_UNIT_TO_FIXED_MM(s->min_x);
1225141cc406Sopenharmony_ci    s->paper_x_range.max = SCANNER_UNIT_TO_FIXED_MM(s->max_x);
1226141cc406Sopenharmony_ci    s->paper_x_range.quant = MM_PER_UNIT_FIX;
1227141cc406Sopenharmony_ci
1228141cc406Sopenharmony_ci    opt->name = SANE_NAME_PAGE_WIDTH;
1229141cc406Sopenharmony_ci    opt->title = SANE_TITLE_PAGE_WIDTH;
1230141cc406Sopenharmony_ci    opt->desc = SANE_DESC_PAGE_WIDTH;
1231141cc406Sopenharmony_ci    opt->type = SANE_TYPE_FIXED;
1232141cc406Sopenharmony_ci    opt->unit = SANE_UNIT_MM;
1233141cc406Sopenharmony_ci    opt->constraint_type = SANE_CONSTRAINT_RANGE;
1234141cc406Sopenharmony_ci    opt->constraint.range = &s->paper_x_range;
1235141cc406Sopenharmony_ci
1236141cc406Sopenharmony_ci    if(s->has_adf){
1237141cc406Sopenharmony_ci      opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
1238141cc406Sopenharmony_ci      if(s->source == SOURCE_FLATBED){
1239141cc406Sopenharmony_ci        opt->cap |= SANE_CAP_INACTIVE;
1240141cc406Sopenharmony_ci      }
1241141cc406Sopenharmony_ci    }
1242141cc406Sopenharmony_ci    else{
1243141cc406Sopenharmony_ci      opt->cap = SANE_CAP_INACTIVE;
1244141cc406Sopenharmony_ci    }
1245141cc406Sopenharmony_ci  }
1246141cc406Sopenharmony_ci
1247141cc406Sopenharmony_ci  /* page height */
1248141cc406Sopenharmony_ci  if(option==OPT_PAGE_HEIGHT){
1249141cc406Sopenharmony_ci    /* values stored in 1200 dpi units */
1250141cc406Sopenharmony_ci    /* must be converted to MM for sane */
1251141cc406Sopenharmony_ci    s->paper_y_range.min = SCANNER_UNIT_TO_FIXED_MM(0);
1252141cc406Sopenharmony_ci    s->paper_y_range.max = SCANNER_UNIT_TO_FIXED_MM(s->max_y);
1253141cc406Sopenharmony_ci    s->paper_y_range.quant = MM_PER_UNIT_FIX;
1254141cc406Sopenharmony_ci
1255141cc406Sopenharmony_ci    opt->name = SANE_NAME_PAGE_HEIGHT;
1256141cc406Sopenharmony_ci    opt->title = SANE_TITLE_PAGE_HEIGHT;
1257141cc406Sopenharmony_ci    opt->desc = "Specifies the height of the media, 0 will auto-detect.";
1258141cc406Sopenharmony_ci    opt->type = SANE_TYPE_FIXED;
1259141cc406Sopenharmony_ci    opt->unit = SANE_UNIT_MM;
1260141cc406Sopenharmony_ci    opt->constraint_type = SANE_CONSTRAINT_RANGE;
1261141cc406Sopenharmony_ci    opt->constraint.range = &s->paper_y_range;
1262141cc406Sopenharmony_ci
1263141cc406Sopenharmony_ci    if(s->has_adf){
1264141cc406Sopenharmony_ci      opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
1265141cc406Sopenharmony_ci      if(s->source == SOURCE_FLATBED){
1266141cc406Sopenharmony_ci        opt->cap |= SANE_CAP_INACTIVE;
1267141cc406Sopenharmony_ci      }
1268141cc406Sopenharmony_ci    }
1269141cc406Sopenharmony_ci    else{
1270141cc406Sopenharmony_ci      opt->cap = SANE_CAP_INACTIVE;
1271141cc406Sopenharmony_ci    }
1272141cc406Sopenharmony_ci  }
1273141cc406Sopenharmony_ci
1274141cc406Sopenharmony_ci  /* "Enhancement" group ------------------------------------------------- */
1275141cc406Sopenharmony_ci  if(option==OPT_ENHANCEMENT_GROUP){
1276141cc406Sopenharmony_ci    opt->name = SANE_NAME_ENHANCEMENT;
1277141cc406Sopenharmony_ci    opt->title = SANE_TITLE_ENHANCEMENT;
1278141cc406Sopenharmony_ci    opt->desc = SANE_DESC_ENHANCEMENT;
1279141cc406Sopenharmony_ci    opt->type = SANE_TYPE_GROUP;
1280141cc406Sopenharmony_ci    opt->constraint_type = SANE_CONSTRAINT_NONE;
1281141cc406Sopenharmony_ci  }
1282141cc406Sopenharmony_ci
1283141cc406Sopenharmony_ci  /* brightness */
1284141cc406Sopenharmony_ci  if(option==OPT_BRIGHTNESS){
1285141cc406Sopenharmony_ci    opt->name = SANE_NAME_BRIGHTNESS;
1286141cc406Sopenharmony_ci    opt->title = SANE_TITLE_BRIGHTNESS;
1287141cc406Sopenharmony_ci    opt->desc = SANE_DESC_BRIGHTNESS;
1288141cc406Sopenharmony_ci    opt->type = SANE_TYPE_INT;
1289141cc406Sopenharmony_ci    opt->unit = SANE_UNIT_NONE;
1290141cc406Sopenharmony_ci
1291141cc406Sopenharmony_ci    opt->constraint_type = SANE_CONSTRAINT_RANGE;
1292141cc406Sopenharmony_ci    opt->constraint.range = &s->brightness_range;
1293141cc406Sopenharmony_ci    s->brightness_range.quant=1;
1294141cc406Sopenharmony_ci    s->brightness_range.min=-127;
1295141cc406Sopenharmony_ci    s->brightness_range.max=127;
1296141cc406Sopenharmony_ci
1297141cc406Sopenharmony_ci    opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
1298141cc406Sopenharmony_ci  }
1299141cc406Sopenharmony_ci
1300141cc406Sopenharmony_ci  /* contrast */
1301141cc406Sopenharmony_ci  if(option==OPT_CONTRAST){
1302141cc406Sopenharmony_ci    opt->name = SANE_NAME_CONTRAST;
1303141cc406Sopenharmony_ci    opt->title = SANE_TITLE_CONTRAST;
1304141cc406Sopenharmony_ci    opt->desc = SANE_DESC_CONTRAST;
1305141cc406Sopenharmony_ci    opt->type = SANE_TYPE_INT;
1306141cc406Sopenharmony_ci    opt->unit = SANE_UNIT_NONE;
1307141cc406Sopenharmony_ci
1308141cc406Sopenharmony_ci    opt->constraint_type = SANE_CONSTRAINT_RANGE;
1309141cc406Sopenharmony_ci    opt->constraint.range = &s->contrast_range;
1310141cc406Sopenharmony_ci    s->contrast_range.quant=1;
1311141cc406Sopenharmony_ci    s->contrast_range.min=-127;
1312141cc406Sopenharmony_ci    s->contrast_range.max=127;
1313141cc406Sopenharmony_ci
1314141cc406Sopenharmony_ci    opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
1315141cc406Sopenharmony_ci  }
1316141cc406Sopenharmony_ci
1317141cc406Sopenharmony_ci  /* gamma */
1318141cc406Sopenharmony_ci  if(option==OPT_GAMMA){
1319141cc406Sopenharmony_ci    opt->name = "gamma";
1320141cc406Sopenharmony_ci    opt->title = "Gamma function exponent";
1321141cc406Sopenharmony_ci    opt->desc = "Changes intensity of midtones";
1322141cc406Sopenharmony_ci    opt->type = SANE_TYPE_FIXED;
1323141cc406Sopenharmony_ci    opt->unit = SANE_UNIT_NONE;
1324141cc406Sopenharmony_ci
1325141cc406Sopenharmony_ci    opt->constraint_type = SANE_CONSTRAINT_RANGE;
1326141cc406Sopenharmony_ci    opt->constraint.range = &s->gamma_range;
1327141cc406Sopenharmony_ci
1328141cc406Sopenharmony_ci    /* value ranges from .3 to 5, should be log scale? */
1329141cc406Sopenharmony_ci    s->gamma_range.quant=SANE_FIX(0.01);
1330141cc406Sopenharmony_ci    s->gamma_range.min=SANE_FIX(0.3);
1331141cc406Sopenharmony_ci    s->gamma_range.max=SANE_FIX(5);
1332141cc406Sopenharmony_ci
1333141cc406Sopenharmony_ci    /*if (s->num_download_gamma){
1334141cc406Sopenharmony_ci      opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
1335141cc406Sopenharmony_ci    }*/
1336141cc406Sopenharmony_ci
1337141cc406Sopenharmony_ci    opt->cap = SANE_CAP_INACTIVE;
1338141cc406Sopenharmony_ci  }
1339141cc406Sopenharmony_ci
1340141cc406Sopenharmony_ci  /*threshold*/
1341141cc406Sopenharmony_ci  if(option==OPT_THRESHOLD){
1342141cc406Sopenharmony_ci    opt->name = SANE_NAME_THRESHOLD;
1343141cc406Sopenharmony_ci    opt->title = SANE_TITLE_THRESHOLD;
1344141cc406Sopenharmony_ci    opt->desc = SANE_DESC_THRESHOLD;
1345141cc406Sopenharmony_ci    opt->type = SANE_TYPE_INT;
1346141cc406Sopenharmony_ci    opt->unit = SANE_UNIT_NONE;
1347141cc406Sopenharmony_ci    opt->constraint_type = SANE_CONSTRAINT_RANGE;
1348141cc406Sopenharmony_ci    opt->constraint.range = &s->threshold_range;
1349141cc406Sopenharmony_ci    s->threshold_range.min=0;
1350141cc406Sopenharmony_ci    s->threshold_range.max=255;
1351141cc406Sopenharmony_ci    s->threshold_range.quant=1;
1352141cc406Sopenharmony_ci
1353141cc406Sopenharmony_ci    opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
1354141cc406Sopenharmony_ci    if(s->mode != MODE_LINEART){
1355141cc406Sopenharmony_ci        opt->cap |= SANE_CAP_INACTIVE;
1356141cc406Sopenharmony_ci    }
1357141cc406Sopenharmony_ci  }
1358141cc406Sopenharmony_ci
1359141cc406Sopenharmony_ci  if(option==OPT_THRESHOLD_CURVE){
1360141cc406Sopenharmony_ci    opt->name = "threshold-curve";
1361141cc406Sopenharmony_ci    opt->title = "Threshold curve";
1362141cc406Sopenharmony_ci    opt->desc = "Dynamic threshold curve, from light to dark, normally 50-65";
1363141cc406Sopenharmony_ci    opt->type = SANE_TYPE_INT;
1364141cc406Sopenharmony_ci    opt->unit = SANE_UNIT_NONE;
1365141cc406Sopenharmony_ci
1366141cc406Sopenharmony_ci    opt->constraint_type = SANE_CONSTRAINT_RANGE;
1367141cc406Sopenharmony_ci    opt->constraint.range = &s->threshold_curve_range;
1368141cc406Sopenharmony_ci    s->threshold_curve_range.min=0;
1369141cc406Sopenharmony_ci    s->threshold_curve_range.max=127;
1370141cc406Sopenharmony_ci    s->threshold_curve_range.quant=1;
1371141cc406Sopenharmony_ci
1372141cc406Sopenharmony_ci    opt->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
1373141cc406Sopenharmony_ci    if(s->mode != MODE_LINEART){
1374141cc406Sopenharmony_ci        opt->cap |= SANE_CAP_INACTIVE;
1375141cc406Sopenharmony_ci    }
1376141cc406Sopenharmony_ci  }
1377141cc406Sopenharmony_ci
1378141cc406Sopenharmony_ci  /* "Sensor" group ------------------------------------------------------ */
1379141cc406Sopenharmony_ci  if(option==OPT_SENSOR_GROUP){
1380141cc406Sopenharmony_ci    opt->name = SANE_NAME_SENSORS;
1381141cc406Sopenharmony_ci    opt->title = SANE_TITLE_SENSORS;
1382141cc406Sopenharmony_ci    opt->desc = SANE_DESC_SENSORS;
1383141cc406Sopenharmony_ci    opt->type = SANE_TYPE_GROUP;
1384141cc406Sopenharmony_ci    opt->constraint_type = SANE_CONSTRAINT_NONE;
1385141cc406Sopenharmony_ci
1386141cc406Sopenharmony_ci    /*flaming hack to get scanimage to hide group*/
1387141cc406Sopenharmony_ci    if (!s->has_adf)
1388141cc406Sopenharmony_ci      opt->type = SANE_TYPE_BOOL;
1389141cc406Sopenharmony_ci  }
1390141cc406Sopenharmony_ci
1391141cc406Sopenharmony_ci  if(option==OPT_SCAN_SW){
1392141cc406Sopenharmony_ci    opt->name = SANE_NAME_SCAN;
1393141cc406Sopenharmony_ci    opt->title = SANE_TITLE_SCAN;
1394141cc406Sopenharmony_ci    opt->desc = SANE_DESC_SCAN;
1395141cc406Sopenharmony_ci    opt->type = SANE_TYPE_BOOL;
1396141cc406Sopenharmony_ci    opt->unit = SANE_UNIT_NONE;
1397141cc406Sopenharmony_ci    if (s->has_adf)
1398141cc406Sopenharmony_ci      opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
1399141cc406Sopenharmony_ci    else
1400141cc406Sopenharmony_ci      opt->cap = SANE_CAP_INACTIVE;
1401141cc406Sopenharmony_ci  }
1402141cc406Sopenharmony_ci
1403141cc406Sopenharmony_ci  if(option==OPT_HOPPER){
1404141cc406Sopenharmony_ci    opt->name = SANE_NAME_PAGE_LOADED;
1405141cc406Sopenharmony_ci    opt->title = SANE_TITLE_PAGE_LOADED;
1406141cc406Sopenharmony_ci    opt->desc = SANE_DESC_PAGE_LOADED;
1407141cc406Sopenharmony_ci    opt->type = SANE_TYPE_BOOL;
1408141cc406Sopenharmony_ci    opt->unit = SANE_UNIT_NONE;
1409141cc406Sopenharmony_ci    if (s->has_adf)
1410141cc406Sopenharmony_ci      opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
1411141cc406Sopenharmony_ci    else
1412141cc406Sopenharmony_ci      opt->cap = SANE_CAP_INACTIVE;
1413141cc406Sopenharmony_ci  }
1414141cc406Sopenharmony_ci
1415141cc406Sopenharmony_ci  if(option==OPT_TOP){
1416141cc406Sopenharmony_ci    opt->name = "top-edge";
1417141cc406Sopenharmony_ci    opt->title = "Top edge";
1418141cc406Sopenharmony_ci    opt->desc = "Paper is pulled partly into adf";
1419141cc406Sopenharmony_ci    opt->type = SANE_TYPE_BOOL;
1420141cc406Sopenharmony_ci    opt->unit = SANE_UNIT_NONE;
1421141cc406Sopenharmony_ci    if (s->has_adf)
1422141cc406Sopenharmony_ci      opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
1423141cc406Sopenharmony_ci    else
1424141cc406Sopenharmony_ci      opt->cap = SANE_CAP_INACTIVE;
1425141cc406Sopenharmony_ci  }
1426141cc406Sopenharmony_ci
1427141cc406Sopenharmony_ci  if(option==OPT_ADF_OPEN){
1428141cc406Sopenharmony_ci    opt->name = SANE_NAME_COVER_OPEN;
1429141cc406Sopenharmony_ci    opt->title = SANE_TITLE_COVER_OPEN;
1430141cc406Sopenharmony_ci    opt->desc = SANE_DESC_COVER_OPEN;
1431141cc406Sopenharmony_ci    opt->type = SANE_TYPE_BOOL;
1432141cc406Sopenharmony_ci    opt->unit = SANE_UNIT_NONE;
1433141cc406Sopenharmony_ci    if (s->has_adf)
1434141cc406Sopenharmony_ci      opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
1435141cc406Sopenharmony_ci    else
1436141cc406Sopenharmony_ci      opt->cap = SANE_CAP_INACTIVE;
1437141cc406Sopenharmony_ci  }
1438141cc406Sopenharmony_ci
1439141cc406Sopenharmony_ci  if(option==OPT_SLEEP){
1440141cc406Sopenharmony_ci    opt->name = "power-save";
1441141cc406Sopenharmony_ci    opt->title = "Power saving";
1442141cc406Sopenharmony_ci    opt->desc = "Scanner in power saving mode";
1443141cc406Sopenharmony_ci    opt->type = SANE_TYPE_BOOL;
1444141cc406Sopenharmony_ci    opt->unit = SANE_UNIT_NONE;
1445141cc406Sopenharmony_ci    if (s->has_adf)
1446141cc406Sopenharmony_ci      opt->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
1447141cc406Sopenharmony_ci    else
1448141cc406Sopenharmony_ci      opt->cap = SANE_CAP_INACTIVE;
1449141cc406Sopenharmony_ci  }
1450141cc406Sopenharmony_ci
1451141cc406Sopenharmony_ci  return opt;
1452141cc406Sopenharmony_ci}
1453141cc406Sopenharmony_ci
1454141cc406Sopenharmony_ci/**
1455141cc406Sopenharmony_ci * Gets or sets an option value.
1456141cc406Sopenharmony_ci *
1457141cc406Sopenharmony_ci * From the SANE spec:
1458141cc406Sopenharmony_ci * This function is used to set or inquire the current value of option
1459141cc406Sopenharmony_ci * number n of the device represented by handle h. The manner in which
1460141cc406Sopenharmony_ci * the option is controlled is specified by parameter action. The
1461141cc406Sopenharmony_ci * possible values of this parameter are described in more detail
1462141cc406Sopenharmony_ci * below.  The value of the option is passed through argument val. It
1463141cc406Sopenharmony_ci * is a pointer to the memory that holds the option value. The memory
1464141cc406Sopenharmony_ci * area pointed to by v must be big enough to hold the entire option
1465141cc406Sopenharmony_ci * value (determined by member size in the corresponding option
1466141cc406Sopenharmony_ci * descriptor).
1467141cc406Sopenharmony_ci *
1468141cc406Sopenharmony_ci * The only exception to this rule is that when setting the value of a
1469141cc406Sopenharmony_ci * string option, the string pointed to by argument v may be shorter
1470141cc406Sopenharmony_ci * since the backend will stop reading the option value upon
1471141cc406Sopenharmony_ci * encountering the first NUL terminator in the string. If argument i
1472141cc406Sopenharmony_ci * is not NULL, the value of *i will be set to provide details on how
1473141cc406Sopenharmony_ci * well the request has been met.
1474141cc406Sopenharmony_ci */
1475141cc406Sopenharmony_ciSANE_Status
1476141cc406Sopenharmony_cisane_control_option (SANE_Handle handle, SANE_Int option,
1477141cc406Sopenharmony_ci                     SANE_Action action, void *val, SANE_Int * info)
1478141cc406Sopenharmony_ci{
1479141cc406Sopenharmony_ci  struct scanner *s = (struct scanner *) handle;
1480141cc406Sopenharmony_ci  SANE_Int dummy = 0;
1481141cc406Sopenharmony_ci
1482141cc406Sopenharmony_ci  /* Make sure that all those statements involving *info cannot break (better
1483141cc406Sopenharmony_ci   * than having to do "if (info) ..." everywhere!)
1484141cc406Sopenharmony_ci   */
1485141cc406Sopenharmony_ci  if (info == 0)
1486141cc406Sopenharmony_ci    info = &dummy;
1487141cc406Sopenharmony_ci
1488141cc406Sopenharmony_ci  if (option >= NUM_OPTIONS) {
1489141cc406Sopenharmony_ci    DBG (5, "sane_control_option: %d too big\n", option);
1490141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
1491141cc406Sopenharmony_ci  }
1492141cc406Sopenharmony_ci
1493141cc406Sopenharmony_ci  if (!SANE_OPTION_IS_ACTIVE (s->opt[option].cap)) {
1494141cc406Sopenharmony_ci    DBG (5, "sane_control_option: %d inactive\n", option);
1495141cc406Sopenharmony_ci    return SANE_STATUS_INVAL;
1496141cc406Sopenharmony_ci  }
1497141cc406Sopenharmony_ci
1498141cc406Sopenharmony_ci  /*
1499141cc406Sopenharmony_ci   * SANE_ACTION_GET_VALUE: We have to find out the current setting and
1500141cc406Sopenharmony_ci   * return it in a human-readable form (often, text).
1501141cc406Sopenharmony_ci   */
1502141cc406Sopenharmony_ci  if (action == SANE_ACTION_GET_VALUE) {
1503141cc406Sopenharmony_ci      SANE_Word * val_p = (SANE_Word *) val;
1504141cc406Sopenharmony_ci
1505141cc406Sopenharmony_ci      DBG (20, "sane_control_option: get value for '%s' (%d)\n", s->opt[option].name,option);
1506141cc406Sopenharmony_ci
1507141cc406Sopenharmony_ci      switch (option) {
1508141cc406Sopenharmony_ci
1509141cc406Sopenharmony_ci        case OPT_NUM_OPTS:
1510141cc406Sopenharmony_ci          *val_p = NUM_OPTIONS;
1511141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1512141cc406Sopenharmony_ci
1513141cc406Sopenharmony_ci        case OPT_SOURCE:
1514141cc406Sopenharmony_ci          if(s->source == SOURCE_FLATBED){
1515141cc406Sopenharmony_ci            strcpy (val, STRING_FLATBED);
1516141cc406Sopenharmony_ci          }
1517141cc406Sopenharmony_ci          else if(s->source == SOURCE_ADF_FRONT){
1518141cc406Sopenharmony_ci            strcpy (val, STRING_ADFFRONT);
1519141cc406Sopenharmony_ci          }
1520141cc406Sopenharmony_ci          else if(s->source == SOURCE_ADF_BACK){
1521141cc406Sopenharmony_ci            strcpy (val, STRING_ADFBACK);
1522141cc406Sopenharmony_ci          }
1523141cc406Sopenharmony_ci          else if(s->source == SOURCE_ADF_DUPLEX){
1524141cc406Sopenharmony_ci            strcpy (val, STRING_ADFDUPLEX);
1525141cc406Sopenharmony_ci          }
1526141cc406Sopenharmony_ci          else{
1527141cc406Sopenharmony_ci            DBG(5,"missing option val for source\n");
1528141cc406Sopenharmony_ci          }
1529141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1530141cc406Sopenharmony_ci
1531141cc406Sopenharmony_ci        case OPT_MODE:
1532141cc406Sopenharmony_ci          if(s->mode == MODE_LINEART){
1533141cc406Sopenharmony_ci            strcpy (val, STRING_LINEART);
1534141cc406Sopenharmony_ci          }
1535141cc406Sopenharmony_ci          else if(s->mode == MODE_GRAYSCALE){
1536141cc406Sopenharmony_ci            strcpy (val, STRING_GRAYSCALE);
1537141cc406Sopenharmony_ci          }
1538141cc406Sopenharmony_ci          else if(s->mode == MODE_COLOR){
1539141cc406Sopenharmony_ci            strcpy (val, STRING_COLOR);
1540141cc406Sopenharmony_ci          }
1541141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1542141cc406Sopenharmony_ci
1543141cc406Sopenharmony_ci        case OPT_RES:
1544141cc406Sopenharmony_ci          *val_p = s->resolution;
1545141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1546141cc406Sopenharmony_ci
1547141cc406Sopenharmony_ci        case OPT_TL_X:
1548141cc406Sopenharmony_ci          *val_p = SCANNER_UNIT_TO_FIXED_MM(s->tl_x);
1549141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1550141cc406Sopenharmony_ci
1551141cc406Sopenharmony_ci        case OPT_TL_Y:
1552141cc406Sopenharmony_ci          *val_p = SCANNER_UNIT_TO_FIXED_MM(s->tl_y);
1553141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1554141cc406Sopenharmony_ci
1555141cc406Sopenharmony_ci        case OPT_BR_X:
1556141cc406Sopenharmony_ci          *val_p = SCANNER_UNIT_TO_FIXED_MM(s->br_x);
1557141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1558141cc406Sopenharmony_ci
1559141cc406Sopenharmony_ci        case OPT_BR_Y:
1560141cc406Sopenharmony_ci          *val_p = SCANNER_UNIT_TO_FIXED_MM(s->br_y);
1561141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1562141cc406Sopenharmony_ci
1563141cc406Sopenharmony_ci        case OPT_PAGE_WIDTH:
1564141cc406Sopenharmony_ci          *val_p = SCANNER_UNIT_TO_FIXED_MM(s->page_width);
1565141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1566141cc406Sopenharmony_ci
1567141cc406Sopenharmony_ci        case OPT_PAGE_HEIGHT:
1568141cc406Sopenharmony_ci          *val_p = SCANNER_UNIT_TO_FIXED_MM(s->page_height);
1569141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1570141cc406Sopenharmony_ci
1571141cc406Sopenharmony_ci        case OPT_BRIGHTNESS:
1572141cc406Sopenharmony_ci          *val_p = s->brightness;
1573141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1574141cc406Sopenharmony_ci
1575141cc406Sopenharmony_ci        case OPT_CONTRAST:
1576141cc406Sopenharmony_ci          *val_p = s->contrast;
1577141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1578141cc406Sopenharmony_ci
1579141cc406Sopenharmony_ci        case OPT_GAMMA:
1580141cc406Sopenharmony_ci          *val_p = SANE_FIX(s->gamma);
1581141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1582141cc406Sopenharmony_ci
1583141cc406Sopenharmony_ci        case OPT_THRESHOLD:
1584141cc406Sopenharmony_ci          *val_p = s->threshold;
1585141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1586141cc406Sopenharmony_ci
1587141cc406Sopenharmony_ci        case OPT_THRESHOLD_CURVE:
1588141cc406Sopenharmony_ci          *val_p = s->threshold_curve;
1589141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1590141cc406Sopenharmony_ci
1591141cc406Sopenharmony_ci        /* Sensor Group */
1592141cc406Sopenharmony_ci        case OPT_SCAN_SW:
1593141cc406Sopenharmony_ci          get_hardware_status(s);
1594141cc406Sopenharmony_ci          *val_p = s->hw_scan_sw;
1595141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1596141cc406Sopenharmony_ci
1597141cc406Sopenharmony_ci        case OPT_HOPPER:
1598141cc406Sopenharmony_ci          get_hardware_status(s);
1599141cc406Sopenharmony_ci          *val_p = s->hw_hopper;
1600141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1601141cc406Sopenharmony_ci
1602141cc406Sopenharmony_ci        case OPT_TOP:
1603141cc406Sopenharmony_ci          get_hardware_status(s);
1604141cc406Sopenharmony_ci          *val_p = s->hw_top;
1605141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1606141cc406Sopenharmony_ci
1607141cc406Sopenharmony_ci        case OPT_ADF_OPEN:
1608141cc406Sopenharmony_ci          get_hardware_status(s);
1609141cc406Sopenharmony_ci          *val_p = s->hw_adf_open;
1610141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1611141cc406Sopenharmony_ci
1612141cc406Sopenharmony_ci        case OPT_SLEEP:
1613141cc406Sopenharmony_ci          get_hardware_status(s);
1614141cc406Sopenharmony_ci          *val_p = s->hw_sleep;
1615141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1616141cc406Sopenharmony_ci      }
1617141cc406Sopenharmony_ci  }
1618141cc406Sopenharmony_ci  else if (action == SANE_ACTION_SET_VALUE) {
1619141cc406Sopenharmony_ci      int tmp;
1620141cc406Sopenharmony_ci      SANE_Word val_c;
1621141cc406Sopenharmony_ci      SANE_Status status;
1622141cc406Sopenharmony_ci
1623141cc406Sopenharmony_ci      DBG (20, "sane_control_option: set value for '%s' (%d)\n", s->opt[option].name,option);
1624141cc406Sopenharmony_ci
1625141cc406Sopenharmony_ci      if ( s->started ) {
1626141cc406Sopenharmony_ci        DBG (5, "sane_control_option: can't set, device busy\n");
1627141cc406Sopenharmony_ci        return SANE_STATUS_DEVICE_BUSY;
1628141cc406Sopenharmony_ci      }
1629141cc406Sopenharmony_ci
1630141cc406Sopenharmony_ci      if (!SANE_OPTION_IS_SETTABLE (s->opt[option].cap)) {
1631141cc406Sopenharmony_ci        DBG (5, "sane_control_option: not settable\n");
1632141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
1633141cc406Sopenharmony_ci      }
1634141cc406Sopenharmony_ci
1635141cc406Sopenharmony_ci      status = sanei_constrain_value (s->opt + option, val, info);
1636141cc406Sopenharmony_ci      if (status != SANE_STATUS_GOOD) {
1637141cc406Sopenharmony_ci        DBG (5, "sane_control_option: bad value\n");
1638141cc406Sopenharmony_ci        return status;
1639141cc406Sopenharmony_ci      }
1640141cc406Sopenharmony_ci
1641141cc406Sopenharmony_ci      /* may have been changed by constraints, so don't copy until now */
1642141cc406Sopenharmony_ci      val_c = *(SANE_Word *)val;
1643141cc406Sopenharmony_ci
1644141cc406Sopenharmony_ci      /*
1645141cc406Sopenharmony_ci       * Note - for those options which can assume one of a list of
1646141cc406Sopenharmony_ci       * valid values, we can safely assume that they will have
1647141cc406Sopenharmony_ci       * exactly one of those values because that's what
1648141cc406Sopenharmony_ci       * sanei_constrain_value does. Hence no "else: invalid" branches
1649141cc406Sopenharmony_ci       * below.
1650141cc406Sopenharmony_ci       */
1651141cc406Sopenharmony_ci      switch (option) {
1652141cc406Sopenharmony_ci
1653141cc406Sopenharmony_ci        /* Mode Group */
1654141cc406Sopenharmony_ci        case OPT_SOURCE:
1655141cc406Sopenharmony_ci          if (!strcmp (val, STRING_ADFFRONT)) {
1656141cc406Sopenharmony_ci            tmp = SOURCE_ADF_FRONT;
1657141cc406Sopenharmony_ci          }
1658141cc406Sopenharmony_ci          else if (!strcmp (val, STRING_ADFBACK)) {
1659141cc406Sopenharmony_ci            tmp = SOURCE_ADF_BACK;
1660141cc406Sopenharmony_ci          }
1661141cc406Sopenharmony_ci          else if (!strcmp (val, STRING_ADFDUPLEX)) {
1662141cc406Sopenharmony_ci            tmp = SOURCE_ADF_DUPLEX;
1663141cc406Sopenharmony_ci          }
1664141cc406Sopenharmony_ci          else{
1665141cc406Sopenharmony_ci            tmp = SOURCE_FLATBED;
1666141cc406Sopenharmony_ci          }
1667141cc406Sopenharmony_ci
1668141cc406Sopenharmony_ci          if (s->source == tmp)
1669141cc406Sopenharmony_ci              return SANE_STATUS_GOOD;
1670141cc406Sopenharmony_ci
1671141cc406Sopenharmony_ci          s->source = tmp;
1672141cc406Sopenharmony_ci          *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
1673141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1674141cc406Sopenharmony_ci
1675141cc406Sopenharmony_ci        case OPT_MODE:
1676141cc406Sopenharmony_ci          if (!strcmp (val, STRING_LINEART)) {
1677141cc406Sopenharmony_ci            tmp = MODE_LINEART;
1678141cc406Sopenharmony_ci          }
1679141cc406Sopenharmony_ci          else if (!strcmp (val, STRING_GRAYSCALE)) {
1680141cc406Sopenharmony_ci            tmp = MODE_GRAYSCALE;
1681141cc406Sopenharmony_ci          }
1682141cc406Sopenharmony_ci          else{
1683141cc406Sopenharmony_ci            tmp = MODE_COLOR;
1684141cc406Sopenharmony_ci          }
1685141cc406Sopenharmony_ci
1686141cc406Sopenharmony_ci          if (tmp == s->mode)
1687141cc406Sopenharmony_ci              return SANE_STATUS_GOOD;
1688141cc406Sopenharmony_ci
1689141cc406Sopenharmony_ci          s->mode = tmp;
1690141cc406Sopenharmony_ci          *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
1691141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1692141cc406Sopenharmony_ci
1693141cc406Sopenharmony_ci        case OPT_RES:
1694141cc406Sopenharmony_ci
1695141cc406Sopenharmony_ci          if (s->resolution == val_c)
1696141cc406Sopenharmony_ci              return SANE_STATUS_GOOD;
1697141cc406Sopenharmony_ci
1698141cc406Sopenharmony_ci          s->resolution = val_c;
1699141cc406Sopenharmony_ci
1700141cc406Sopenharmony_ci          *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
1701141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1702141cc406Sopenharmony_ci
1703141cc406Sopenharmony_ci        /* Geometry Group */
1704141cc406Sopenharmony_ci        case OPT_TL_X:
1705141cc406Sopenharmony_ci          if (s->tl_x == FIXED_MM_TO_SCANNER_UNIT(val_c))
1706141cc406Sopenharmony_ci              return SANE_STATUS_GOOD;
1707141cc406Sopenharmony_ci
1708141cc406Sopenharmony_ci          s->tl_x = FIXED_MM_TO_SCANNER_UNIT(val_c);
1709141cc406Sopenharmony_ci
1710141cc406Sopenharmony_ci          *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
1711141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1712141cc406Sopenharmony_ci
1713141cc406Sopenharmony_ci        case OPT_TL_Y:
1714141cc406Sopenharmony_ci          if (s->tl_y == FIXED_MM_TO_SCANNER_UNIT(val_c))
1715141cc406Sopenharmony_ci              return SANE_STATUS_GOOD;
1716141cc406Sopenharmony_ci
1717141cc406Sopenharmony_ci          s->tl_y = FIXED_MM_TO_SCANNER_UNIT(val_c);
1718141cc406Sopenharmony_ci
1719141cc406Sopenharmony_ci          *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
1720141cc406Sopenharmony_ci          return change_params(s);
1721141cc406Sopenharmony_ci
1722141cc406Sopenharmony_ci        case OPT_BR_X:
1723141cc406Sopenharmony_ci          if (s->br_x == FIXED_MM_TO_SCANNER_UNIT(val_c))
1724141cc406Sopenharmony_ci              return SANE_STATUS_GOOD;
1725141cc406Sopenharmony_ci
1726141cc406Sopenharmony_ci          s->br_x = FIXED_MM_TO_SCANNER_UNIT(val_c);
1727141cc406Sopenharmony_ci
1728141cc406Sopenharmony_ci          *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
1729141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1730141cc406Sopenharmony_ci
1731141cc406Sopenharmony_ci        case OPT_BR_Y:
1732141cc406Sopenharmony_ci          if (s->br_y == FIXED_MM_TO_SCANNER_UNIT(val_c))
1733141cc406Sopenharmony_ci              return SANE_STATUS_GOOD;
1734141cc406Sopenharmony_ci
1735141cc406Sopenharmony_ci          s->br_y = FIXED_MM_TO_SCANNER_UNIT(val_c);
1736141cc406Sopenharmony_ci
1737141cc406Sopenharmony_ci          *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
1738141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1739141cc406Sopenharmony_ci
1740141cc406Sopenharmony_ci        case OPT_PAGE_WIDTH:
1741141cc406Sopenharmony_ci          if (s->page_width == FIXED_MM_TO_SCANNER_UNIT(val_c))
1742141cc406Sopenharmony_ci              return SANE_STATUS_GOOD;
1743141cc406Sopenharmony_ci
1744141cc406Sopenharmony_ci          s->page_width = FIXED_MM_TO_SCANNER_UNIT(val_c);
1745141cc406Sopenharmony_ci          *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
1746141cc406Sopenharmony_ci          return change_params(s);
1747141cc406Sopenharmony_ci
1748141cc406Sopenharmony_ci        case OPT_PAGE_HEIGHT:
1749141cc406Sopenharmony_ci          if (s->page_height == FIXED_MM_TO_SCANNER_UNIT(val_c))
1750141cc406Sopenharmony_ci              return SANE_STATUS_GOOD;
1751141cc406Sopenharmony_ci
1752141cc406Sopenharmony_ci          s->page_height = FIXED_MM_TO_SCANNER_UNIT(val_c);
1753141cc406Sopenharmony_ci          *info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
1754141cc406Sopenharmony_ci          return change_params(s);
1755141cc406Sopenharmony_ci
1756141cc406Sopenharmony_ci        /* Enhancement Group */
1757141cc406Sopenharmony_ci        case OPT_BRIGHTNESS:
1758141cc406Sopenharmony_ci          s->brightness = val_c;
1759141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1760141cc406Sopenharmony_ci
1761141cc406Sopenharmony_ci        case OPT_CONTRAST:
1762141cc406Sopenharmony_ci          s->contrast = val_c;
1763141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1764141cc406Sopenharmony_ci
1765141cc406Sopenharmony_ci        case OPT_GAMMA:
1766141cc406Sopenharmony_ci          s->gamma = SANE_UNFIX(val_c);
1767141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1768141cc406Sopenharmony_ci
1769141cc406Sopenharmony_ci        case OPT_THRESHOLD:
1770141cc406Sopenharmony_ci          s->threshold = val_c;
1771141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1772141cc406Sopenharmony_ci
1773141cc406Sopenharmony_ci        case OPT_THRESHOLD_CURVE:
1774141cc406Sopenharmony_ci          s->threshold_curve = val_c;
1775141cc406Sopenharmony_ci          return SANE_STATUS_GOOD;
1776141cc406Sopenharmony_ci
1777141cc406Sopenharmony_ci      }                       /* switch */
1778141cc406Sopenharmony_ci  }                           /* else */
1779141cc406Sopenharmony_ci
1780141cc406Sopenharmony_ci  return SANE_STATUS_INVAL;
1781141cc406Sopenharmony_ci}
1782141cc406Sopenharmony_ci
1783141cc406Sopenharmony_ci/* use height and width to initialize rest of transfer vals */
1784141cc406Sopenharmony_cistatic void
1785141cc406Sopenharmony_ciupdate_transfer_totals(struct transfer * t)
1786141cc406Sopenharmony_ci{
1787141cc406Sopenharmony_ci    if (t->image == NULL) return;
1788141cc406Sopenharmony_ci
1789141cc406Sopenharmony_ci    t->total_bytes = t->line_stride * t->image->height;
1790141cc406Sopenharmony_ci    t->rx_bytes = 0;
1791141cc406Sopenharmony_ci    t->done = 0;
1792141cc406Sopenharmony_ci}
1793141cc406Sopenharmony_ci
1794141cc406Sopenharmony_ci/* each model has various settings that differ based on X resolution */
1795141cc406Sopenharmony_ci/* we hard-code the list (determined from usb snoops) here */
1796141cc406Sopenharmony_cistruct model_res {
1797141cc406Sopenharmony_ci  int model;
1798141cc406Sopenharmony_ci  int mode;
1799141cc406Sopenharmony_ci  int x_res;
1800141cc406Sopenharmony_ci  int y_res;
1801141cc406Sopenharmony_ci  int usb_power;
1802141cc406Sopenharmony_ci
1803141cc406Sopenharmony_ci  int max_x;
1804141cc406Sopenharmony_ci  int min_x;
1805141cc406Sopenharmony_ci  int max_y;
1806141cc406Sopenharmony_ci  int min_y;
1807141cc406Sopenharmony_ci
1808141cc406Sopenharmony_ci  int line_stride;   /* byte width of 1 raw side, with padding */
1809141cc406Sopenharmony_ci  int plane_stride;  /* byte width of 1 raw color plane, with padding */
1810141cc406Sopenharmony_ci  int plane_width;   /* byte width of 1 raw color plane, without padding */
1811141cc406Sopenharmony_ci
1812141cc406Sopenharmony_ci  int block_height;
1813141cc406Sopenharmony_ci
1814141cc406Sopenharmony_ci  int cal_line_stride;
1815141cc406Sopenharmony_ci  int cal_plane_stride;
1816141cc406Sopenharmony_ci  int cal_plane_width;
1817141cc406Sopenharmony_ci
1818141cc406Sopenharmony_ci  unsigned char * sw_coarsecal;
1819141cc406Sopenharmony_ci  unsigned char * sw_finecal;
1820141cc406Sopenharmony_ci  unsigned char * sw_sendcal;
1821141cc406Sopenharmony_ci
1822141cc406Sopenharmony_ci  unsigned char * head_cal1;
1823141cc406Sopenharmony_ci  unsigned char * head_cal2;
1824141cc406Sopenharmony_ci  unsigned char * sw_scan;
1825141cc406Sopenharmony_ci
1826141cc406Sopenharmony_ci};
1827141cc406Sopenharmony_ci
1828141cc406Sopenharmony_cistatic struct model_res settings[] = {
1829141cc406Sopenharmony_ci
1830141cc406Sopenharmony_ci /*S300 AC*/
1831141cc406Sopenharmony_ci/* model       mode        xres yres  u   mxx mnx   mxy mny   lin_s   pln_s pln_w  bh     cls     cps   cpw */
1832141cc406Sopenharmony_ci { MODEL_S300, MODE_COLOR,  150, 150, 0, 1296, 32, 2662, 32, 4256*3, 1480*3, 1296, 41, 8512*3, 2960*3, 2592,
1833141cc406Sopenharmony_ci   setWindowCoarseCal_S300_150, setWindowFineCal_S300_150,
1834141cc406Sopenharmony_ci   setWindowSendCal_S300_150, sendCal1Header_S300_150,
1835141cc406Sopenharmony_ci   sendCal2Header_S300_150, setWindowScan_S300_150 },
1836141cc406Sopenharmony_ci
1837141cc406Sopenharmony_ci { MODEL_S300, MODE_COLOR,  225, 200, 0, 1944, 32, 3993, 32, 6144*3, 2100*3, 1944, 28, 8192*3, 2800*3, 2592,
1838141cc406Sopenharmony_ci   setWindowCoarseCal_S300_225, setWindowFineCal_S300_225,
1839141cc406Sopenharmony_ci   setWindowSendCal_S300_225, sendCal1Header_S300_225,
1840141cc406Sopenharmony_ci   sendCal2Header_S300_225, setWindowScan_S300_225 },
1841141cc406Sopenharmony_ci
1842141cc406Sopenharmony_ci { MODEL_S300, MODE_COLOR,  300, 300, 0, 2592, 32, 5324, 32, 8192*3, 2800*3, 2592, 21, 8192*3, 2800*3, 2592,
1843141cc406Sopenharmony_ci   setWindowCoarseCal_S300_300, setWindowFineCal_S300_300,
1844141cc406Sopenharmony_ci   setWindowSendCal_S300_300, sendCal1Header_S300_300,
1845141cc406Sopenharmony_ci   sendCal2Header_S300_300, setWindowScan_S300_300 },
1846141cc406Sopenharmony_ci
1847141cc406Sopenharmony_ci { MODEL_S300, MODE_COLOR,  600, 600, 0, 5184, 32, 10648, 32, 16064*3, 5440*3, 5184, 10, 16064*3, 5440*3, 5184,
1848141cc406Sopenharmony_ci   setWindowCoarseCal_S300_600, setWindowFineCal_S300_600,
1849141cc406Sopenharmony_ci   setWindowSendCal_S300_600, sendCal1Header_S300_600,
1850141cc406Sopenharmony_ci   sendCal2Header_S300_600, setWindowScan_S300_600 },
1851141cc406Sopenharmony_ci
1852141cc406Sopenharmony_ci /*S300 USB*/
1853141cc406Sopenharmony_ci/* model       mode        xres yres  u   mxx mnx   mxy mny   lin_s   pln_s pln_w  bh      cls     cps   cpw */
1854141cc406Sopenharmony_ci { MODEL_S300, MODE_COLOR,  150, 150, 1, 1296, 32, 2662, 32, 7216*3, 2960*3, 1296, 24, 14432*3, 5920*3, 2592,
1855141cc406Sopenharmony_ci   setWindowCoarseCal_S300_150_U, setWindowFineCal_S300_150_U,
1856141cc406Sopenharmony_ci   setWindowSendCal_S300_150_U, sendCal1Header_S300_150_U,
1857141cc406Sopenharmony_ci   sendCal2Header_S300_150_U, setWindowScan_S300_150_U },
1858141cc406Sopenharmony_ci
1859141cc406Sopenharmony_ci { MODEL_S300, MODE_COLOR,  225, 200, 1, 1944, 32, 3993, 32, 10584*3, 4320*3, 1944, 16, 14112*3, 5760*3, 2592,
1860141cc406Sopenharmony_ci   setWindowCoarseCal_S300_225_U, setWindowFineCal_S300_225_U,
1861141cc406Sopenharmony_ci   setWindowSendCal_S300_225_U, sendCal1Header_S300_225_U,
1862141cc406Sopenharmony_ci   sendCal2Header_S300_225_U, setWindowScan_S300_225_U },
1863141cc406Sopenharmony_ci
1864141cc406Sopenharmony_ci { MODEL_S300, MODE_COLOR,  300, 300, 1, 2592, 32, 5324, 32, 15872*3, 6640*3, 2592, 11, 15872*3, 6640*3, 2592,
1865141cc406Sopenharmony_ci   setWindowCoarseCal_S300_300_U, setWindowFineCal_S300_300_U,
1866141cc406Sopenharmony_ci   setWindowSendCal_S300_300_U, sendCal1Header_S300_300_U,
1867141cc406Sopenharmony_ci   sendCal2Header_S300_300_U, setWindowScan_S300_300_U },
1868141cc406Sopenharmony_ci
1869141cc406Sopenharmony_ci { MODEL_S300, MODE_COLOR,  600, 600, 1, 5184, 32, 10648, 32, 16064*3, 5440*3, 5184, 10, 16064*3, 5440*3, 5184,
1870141cc406Sopenharmony_ci   setWindowCoarseCal_S300_600, setWindowFineCal_S300_600,
1871141cc406Sopenharmony_ci   setWindowSendCal_S300_600, sendCal1Header_S300_600,
1872141cc406Sopenharmony_ci   sendCal2Header_S300_600, setWindowScan_S300_600 },
1873141cc406Sopenharmony_ci
1874141cc406Sopenharmony_ci /*S1300i AC*/
1875141cc406Sopenharmony_ci/* model         mode        xres yres  u   mxx mnx   mxy mny lin_s   pln_s   pln_w   bh      cls     cps   cpw */
1876141cc406Sopenharmony_ci { MODEL_S1300i, MODE_COLOR,  150, 150, 0, 1296, 32, 2662, 32, 4016*3, 1360*3, 1296,  43, 8032*3,  2720*3, 2592,
1877141cc406Sopenharmony_ci   setWindowCoarseCal_S1300i_150, setWindowFineCal_S1300i_150,
1878141cc406Sopenharmony_ci   setWindowSendCal_S1300i_150, sendCal1Header_S1300i_150,
1879141cc406Sopenharmony_ci   sendCal2Header_S1300i_150, setWindowScan_S1300i_150 },
1880141cc406Sopenharmony_ci
1881141cc406Sopenharmony_ci { MODEL_S1300i, MODE_COLOR,  225, 200, 0, 1944, 32, 3993, 32, 6072*3, 2063*3, 1944,  28, 8096*3,  2752*3, 2592,
1882141cc406Sopenharmony_ci   setWindowCoarseCal_S1300i_225, setWindowFineCal_S1300i_225,
1883141cc406Sopenharmony_ci   setWindowSendCal_S1300i_225, sendCal1Header_S1300i_225,
1884141cc406Sopenharmony_ci   sendCal2Header_S1300i_225, setWindowScan_S1300i_225 },
1885141cc406Sopenharmony_ci
1886141cc406Sopenharmony_ci { MODEL_S1300i, MODE_COLOR,  300, 300, 0, 2592, 32, 5324, 32, 8096*3, 2751*3, 2592,  21, 8096*3,  2752*3, 2592,
1887141cc406Sopenharmony_ci   setWindowCoarseCal_S1300i_300, setWindowFineCal_S1300i_300,
1888141cc406Sopenharmony_ci   setWindowSendCal_S1300i_300, sendCal1Header_S1300i_300,
1889141cc406Sopenharmony_ci   sendCal2Header_S1300i_300, setWindowScan_S1300i_300 },
1890141cc406Sopenharmony_ci
1891141cc406Sopenharmony_ci /*NOTE: S1300i uses S300 data blocks for remainder*/
1892141cc406Sopenharmony_ci { MODEL_S1300i, MODE_COLOR,  600, 600, 0, 5184, 32, 10648, 32, 16064*3, 5440*3, 5184, 10, 16064*3, 5440*3, 5184,
1893141cc406Sopenharmony_ci   setWindowCoarseCal_S300_600, setWindowFineCal_S300_600,
1894141cc406Sopenharmony_ci   setWindowSendCal_S300_600, sendCal1Header_S300_600,
1895141cc406Sopenharmony_ci   sendCal2Header_S300_600, setWindowScan_S300_600 },
1896141cc406Sopenharmony_ci
1897141cc406Sopenharmony_ci /*S1300i USB*/
1898141cc406Sopenharmony_ci/* model         mode        xres yres  u   mxx mnx    mxy mny   lin_s    pln_s pln_w  bh      cls     cps   cpw */
1899141cc406Sopenharmony_ci { MODEL_S1300i, MODE_COLOR,  150, 150, 1, 1296, 32,  2662, 32, 7216*3,  2960*3, 1296, 24, 14432*3, 5920*3, 2592,
1900141cc406Sopenharmony_ci   setWindowCoarseCal_S300_150_U, setWindowFineCal_S300_150_U,
1901141cc406Sopenharmony_ci   setWindowSendCal_S300_150_U, sendCal1Header_S1300i_USB,
1902141cc406Sopenharmony_ci   sendCal2Header_S1300i_USB, setWindowScan_S300_150_U },
1903141cc406Sopenharmony_ci
1904141cc406Sopenharmony_ci { MODEL_S1300i, MODE_COLOR,  225, 200, 1, 1944, 32,  3993, 32, 10584*3, 4320*3, 1944, 16, 14112*3, 5760*3, 2592,
1905141cc406Sopenharmony_ci   setWindowCoarseCal_S300_225_U, setWindowFineCal_S300_225_U,
1906141cc406Sopenharmony_ci   setWindowSendCal_S300_225_U, sendCal1Header_S1300i_USB,
1907141cc406Sopenharmony_ci   sendCal2Header_S1300i_USB, setWindowScan_S300_225_U },
1908141cc406Sopenharmony_ci
1909141cc406Sopenharmony_ci { MODEL_S1300i, MODE_COLOR,  300, 300, 1, 2592, 32,  5324, 32, 15872*3, 6640*3, 2592, 11, 15872*3, 6640*3, 2592,
1910141cc406Sopenharmony_ci   setWindowCoarseCal_S300_300_U, setWindowFineCal_S300_300_U,
1911141cc406Sopenharmony_ci   setWindowSendCal_S300_300_U, sendCal1Header_S1300i_USB,
1912141cc406Sopenharmony_ci   sendCal2Header_S1300i_USB, setWindowScan_S300_300_U },
1913141cc406Sopenharmony_ci
1914141cc406Sopenharmony_ci { MODEL_S1300i, MODE_COLOR,  600, 600, 1, 5184, 32, 10648, 32, 16064*3, 5440*3, 5184, 10, 16064*3, 5440*3, 5184,
1915141cc406Sopenharmony_ci   setWindowCoarseCal_S300_600, setWindowFineCal_S300_600,
1916141cc406Sopenharmony_ci   setWindowSendCal_S300_600, sendCal1Header_S1300i_USB,
1917141cc406Sopenharmony_ci   sendCal2Header_S1300i_USB, setWindowScan_S300_600 },
1918141cc406Sopenharmony_ci
1919141cc406Sopenharmony_ci /*fi-60F/65F GRAY */
1920141cc406Sopenharmony_ci/* model          mode         xres yres  u   mxx mnx   mxy mny   lin_s   pln_s pln_w   bh     cls    cps  cpw */
1921141cc406Sopenharmony_ci/* disabled until calibration code supports grayscale
1922141cc406Sopenharmony_ci { MODEL_FI60F | MODEL_FI65F, MODE_GRAYSCALE, 300, 300, 0, 1296, 32, 1749, 32,   1440,    480,  432, 364, 2400*3, 958*3, 432,
1923141cc406Sopenharmony_ci   setWindowCoarseCal_FI60F_300, setWindowFineCal_FI60F_300,
1924141cc406Sopenharmony_ci   setWindowSendCal_FI60F_300, sendCal1Header_FI60F_300,
1925141cc406Sopenharmony_ci   sendCal2Header_FI60F_300, setWindowScan_FI60F_300_g },
1926141cc406Sopenharmony_ci
1927141cc406Sopenharmony_ci { MODEL_FI60F | MODEL_FI65F, MODE_GRAYSCALE, 600, 400, 0, 2592, 32, 2332, 32,   2592,    864,  864, 202, 2848*3, 978*3, 864,
1928141cc406Sopenharmony_ci   setWindowCoarseCal_FI60F_600, setWindowFineCal_FI60F_600,
1929141cc406Sopenharmony_ci   setWindowSendCal_FI60F_600, sendCal1Header_FI60F_600,
1930141cc406Sopenharmony_ci   sendCal2Header_FI60F_600, setWindowScan_FI60F_400_g },
1931141cc406Sopenharmony_ci
1932141cc406Sopenharmony_ci { MODEL_FI60F | MODEL_FI65F, MODE_GRAYSCALE, 600, 600, 0, 2592, 32, 3498, 32,   2592,    864,  864, 202, 2848*3, 978*3, 864,
1933141cc406Sopenharmony_ci   setWindowCoarseCal_FI60F_600, setWindowFineCal_FI60F_600,
1934141cc406Sopenharmony_ci   setWindowSendCal_FI60F_600, sendCal1Header_FI60F_600,
1935141cc406Sopenharmony_ci   sendCal2Header_FI60F_600, setWindowScan_FI60F_600_g },
1936141cc406Sopenharmony_ci*/
1937141cc406Sopenharmony_ci
1938141cc406Sopenharmony_ci /*fi-60F/65F*/
1939141cc406Sopenharmony_ci/* model                      mode       xres yres  u   mxx mnx   mxy mny   lin_s   pln_s pln_w   bh     cls    cps  cpw */
1940141cc406Sopenharmony_ci { MODEL_FI60F | MODEL_FI65F, MODE_COLOR, 300, 150, 0, 1296, 32,  875, 32, 2400*3,  958*3,  432,  72, 2400*3, 958*3, 432,
1941141cc406Sopenharmony_ci   setWindowCoarseCal_FI60F_300, setWindowFineCal_FI60F_300,
1942141cc406Sopenharmony_ci   setWindowSendCal_FI60F_300, sendCal1Header_FI60F_300,
1943141cc406Sopenharmony_ci   sendCal2Header_FI60F_300, setWindowScan_FI60F_150 },
1944141cc406Sopenharmony_ci
1945141cc406Sopenharmony_ci { MODEL_FI60F | MODEL_FI65F, MODE_COLOR, 300, 200, 0, 1296, 32, 1166, 32, 2400*3,  958*3,  432,  72, 2400*3, 958*3, 432,
1946141cc406Sopenharmony_ci   setWindowCoarseCal_FI60F_300, setWindowFineCal_FI60F_300,
1947141cc406Sopenharmony_ci   setWindowSendCal_FI60F_300, sendCal1Header_FI60F_300,
1948141cc406Sopenharmony_ci   sendCal2Header_FI60F_300, setWindowScan_FI60F_200 },
1949141cc406Sopenharmony_ci
1950141cc406Sopenharmony_ci { MODEL_FI60F | MODEL_FI65F, MODE_COLOR, 300, 300, 0, 1296, 32, 1749, 32, 2400*3,  958*3,  432,  72, 2400*3, 958*3, 432,
1951141cc406Sopenharmony_ci   setWindowCoarseCal_FI60F_300, setWindowFineCal_FI60F_300,
1952141cc406Sopenharmony_ci   setWindowSendCal_FI60F_300, sendCal1Header_FI60F_300,
1953141cc406Sopenharmony_ci   sendCal2Header_FI60F_300, setWindowScan_FI60F_300 },
1954141cc406Sopenharmony_ci
1955141cc406Sopenharmony_ci { MODEL_FI60F | MODEL_FI65F, MODE_COLOR, 600, 400, 0, 2592, 32, 2332, 32, 2848*3,  978*3,  864,  61, 2848*3, 978*3, 864,
1956141cc406Sopenharmony_ci   setWindowCoarseCal_FI60F_600, setWindowFineCal_FI60F_600,
1957141cc406Sopenharmony_ci   setWindowSendCal_FI60F_600, sendCal1Header_FI60F_600,
1958141cc406Sopenharmony_ci   sendCal2Header_FI60F_600, setWindowScan_FI60F_400 },
1959141cc406Sopenharmony_ci
1960141cc406Sopenharmony_ci { MODEL_FI60F | MODEL_FI65F, MODE_COLOR, 600, 600, 0, 2592, 32, 3498, 32, 2848*3,  978*3,  864,  61, 2848*3, 978*3, 864,
1961141cc406Sopenharmony_ci   setWindowCoarseCal_FI60F_600, setWindowFineCal_FI60F_600,
1962141cc406Sopenharmony_ci   setWindowSendCal_FI60F_600, sendCal1Header_FI60F_600,
1963141cc406Sopenharmony_ci   sendCal2Header_FI60F_600, setWindowScan_FI60F_600 },
1964141cc406Sopenharmony_ci
1965141cc406Sopenharmony_ci /*S1100 USB*/
1966141cc406Sopenharmony_ci/* model        mode        xres yres  u   mxx mnx    mxy mny  lin_s pln_s pln_w  bh   cls   cps   cpw */
1967141cc406Sopenharmony_ci { MODEL_S1100, MODE_COLOR,  300, 300, 1, 2592, 32,  5324, 32,  8912, 3160, 2592, 58, 8912, 3160, 2592,
1968141cc406Sopenharmony_ci   setWindowCoarseCal_S1100_300_U, setWindowFineCal_S1100_300_U,
1969141cc406Sopenharmony_ci   setWindowSendCal_S1100_300_U, sendCal1Header_S1100_300_U,
1970141cc406Sopenharmony_ci   sendCal2Header_S1100_300_U, setWindowScan_S1100_300_U },
1971141cc406Sopenharmony_ci
1972141cc406Sopenharmony_ci { MODEL_S1100, MODE_COLOR,  600, 600, 1, 5184, 32, 10648, 32, 15904, 5360, 5184, 32, 15904, 5360, 5184,
1973141cc406Sopenharmony_ci   setWindowCoarseCal_S1100_600_U, setWindowFineCal_S1100_600_U,
1974141cc406Sopenharmony_ci   setWindowSendCal_S1100_600_U, sendCal1Header_S1100_600_U,
1975141cc406Sopenharmony_ci   sendCal2Header_S1100_600_U, setWindowScan_S1100_600_U },
1976141cc406Sopenharmony_ci
1977141cc406Sopenharmony_ci { MODEL_NONE,           0,    0,   0, 0,    0,  0,     0,  0,     0,    0,    0,  0,     0,    0,    0,
1978141cc406Sopenharmony_ci   NULL, NULL, NULL, NULL, NULL, NULL },
1979141cc406Sopenharmony_ci
1980141cc406Sopenharmony_ci};
1981141cc406Sopenharmony_ci
1982141cc406Sopenharmony_ci/*
1983141cc406Sopenharmony_ci * clean up scanner struct vals when user changes mode, res, etc
1984141cc406Sopenharmony_ci */
1985141cc406Sopenharmony_cistatic SANE_Status
1986141cc406Sopenharmony_cichange_params(struct scanner *s)
1987141cc406Sopenharmony_ci{
1988141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
1989141cc406Sopenharmony_ci
1990141cc406Sopenharmony_ci    int img_heads, img_pages, width;
1991141cc406Sopenharmony_ci    int i=0;
1992141cc406Sopenharmony_ci
1993141cc406Sopenharmony_ci    DBG (10, "change_params: start\n");
1994141cc406Sopenharmony_ci
1995141cc406Sopenharmony_ci    do {
1996141cc406Sopenharmony_ci      if(settings[i].model & s->model
1997141cc406Sopenharmony_ci        && settings[i].mode <= s->mode
1998141cc406Sopenharmony_ci        && settings[i].x_res >= s->resolution
1999141cc406Sopenharmony_ci        && settings[i].y_res >= s->resolution
2000141cc406Sopenharmony_ci        && settings[i].usb_power == s->usb_power
2001141cc406Sopenharmony_ci      ){
2002141cc406Sopenharmony_ci          break;
2003141cc406Sopenharmony_ci      }
2004141cc406Sopenharmony_ci      i++;
2005141cc406Sopenharmony_ci    } while (settings[i].model);
2006141cc406Sopenharmony_ci
2007141cc406Sopenharmony_ci    if (!settings[i].model){
2008141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
2009141cc406Sopenharmony_ci    }
2010141cc406Sopenharmony_ci
2011141cc406Sopenharmony_ci    /*1200 dpi*/
2012141cc406Sopenharmony_ci    s->max_x = PIX_TO_SCANNER_UNIT( settings[i].max_x, settings[i].x_res );
2013141cc406Sopenharmony_ci    s->min_x = PIX_TO_SCANNER_UNIT( settings[i].min_x, settings[i].x_res );
2014141cc406Sopenharmony_ci    s->max_y = PIX_TO_SCANNER_UNIT( settings[i].max_y, settings[i].y_res );
2015141cc406Sopenharmony_ci    s->min_y = PIX_TO_SCANNER_UNIT( settings[i].min_y, settings[i].y_res );
2016141cc406Sopenharmony_ci
2017141cc406Sopenharmony_ci    /*current dpi*/
2018141cc406Sopenharmony_ci    s->setWindowCoarseCal = settings[i].sw_coarsecal;
2019141cc406Sopenharmony_ci    s->setWindowCoarseCalLen = SET_WINDOW_LEN;
2020141cc406Sopenharmony_ci
2021141cc406Sopenharmony_ci    s->setWindowFineCal = settings[i].sw_finecal;
2022141cc406Sopenharmony_ci    s->setWindowFineCalLen = SET_WINDOW_LEN;
2023141cc406Sopenharmony_ci
2024141cc406Sopenharmony_ci    s->setWindowSendCal = settings[i].sw_sendcal;
2025141cc406Sopenharmony_ci    s->setWindowSendCalLen = SET_WINDOW_LEN;
2026141cc406Sopenharmony_ci
2027141cc406Sopenharmony_ci    s->sendCal1Header = settings[i].head_cal1;
2028141cc406Sopenharmony_ci    s->sendCal1HeaderLen = 14;
2029141cc406Sopenharmony_ci
2030141cc406Sopenharmony_ci    s->sendCal2Header = settings[i].head_cal2;
2031141cc406Sopenharmony_ci    s->sendCal2HeaderLen = 7;
2032141cc406Sopenharmony_ci
2033141cc406Sopenharmony_ci    s->setWindowScan = settings[i].sw_scan;
2034141cc406Sopenharmony_ci    s->setWindowScanLen = SET_WINDOW_LEN;
2035141cc406Sopenharmony_ci
2036141cc406Sopenharmony_ci    if (s->model == MODEL_S300 || s->model == MODEL_S1300i)
2037141cc406Sopenharmony_ci    {
2038141cc406Sopenharmony_ci        img_heads = 1; /* image width is the same as the plane width on the S300 */
2039141cc406Sopenharmony_ci        img_pages = 2;
2040141cc406Sopenharmony_ci    }
2041141cc406Sopenharmony_ci    else if (s->model == MODEL_S1100)
2042141cc406Sopenharmony_ci    {
2043141cc406Sopenharmony_ci        img_heads = 1; /* image width is the same as the plane width on the S1000 */
2044141cc406Sopenharmony_ci        img_pages = 1;
2045141cc406Sopenharmony_ci    }
2046141cc406Sopenharmony_ci    else /* MODEL_FI60F or MODEL_FI65F */
2047141cc406Sopenharmony_ci    {
2048141cc406Sopenharmony_ci        img_heads = 3; /* image width is 3* the plane width on the FI-60F */
2049141cc406Sopenharmony_ci        img_pages = 1;
2050141cc406Sopenharmony_ci    }
2051141cc406Sopenharmony_ci
2052141cc406Sopenharmony_ci    /* height */
2053141cc406Sopenharmony_ci    if (s->tl_y > s->max_y - s->min_y)
2054141cc406Sopenharmony_ci       s->tl_y = s->max_y - s->min_y - s->adf_height_padding;
2055141cc406Sopenharmony_ci    s->page_height = MIN(s->page_height, s->max_y - s->adf_height_padding - s->tl_y);
2056141cc406Sopenharmony_ci    if (s->page_height > 0)
2057141cc406Sopenharmony_ci       s->page_height = MAX(s->page_height, s->min_y);
2058141cc406Sopenharmony_ci    if (s->tl_y + s->page_height > s->max_y)
2059141cc406Sopenharmony_ci       s->tl_y = s->max_y - s->adf_height_padding - s->page_height;
2060141cc406Sopenharmony_ci    s->tl_y = MAX(s->tl_y, 0);
2061141cc406Sopenharmony_ci
2062141cc406Sopenharmony_ci    if (s->page_height > 0) {
2063141cc406Sopenharmony_ci        s->br_y = s->tl_y + s->page_height;
2064141cc406Sopenharmony_ci    }
2065141cc406Sopenharmony_ci    else {
2066141cc406Sopenharmony_ci        s->br_y = s->max_y;
2067141cc406Sopenharmony_ci    }
2068141cc406Sopenharmony_ci
2069141cc406Sopenharmony_ci    /*width*/
2070141cc406Sopenharmony_ci    s->page_width = MIN(s->page_width, s->max_x);
2071141cc406Sopenharmony_ci    s->page_width = MAX(s->page_width, s->min_x);
2072141cc406Sopenharmony_ci
2073141cc406Sopenharmony_ci    s->tl_x = (s->max_x - s->page_width)/2;
2074141cc406Sopenharmony_ci    s->br_x = (s->max_x + s->page_width)/2;
2075141cc406Sopenharmony_ci
2076141cc406Sopenharmony_ci    /*=============================================================*/
2077141cc406Sopenharmony_ci    /* set up the calibration scan structs */
2078141cc406Sopenharmony_ci    /* generally full width, short height, full resolution */
2079141cc406Sopenharmony_ci    s->cal_image.line_stride = settings[i].cal_line_stride;
2080141cc406Sopenharmony_ci    s->cal_image.plane_stride = settings[i].cal_plane_stride;
2081141cc406Sopenharmony_ci    s->cal_image.plane_width = settings[i].cal_plane_width;
2082141cc406Sopenharmony_ci    s->cal_image.mode = MODE_COLOR;
2083141cc406Sopenharmony_ci    s->cal_image.x_res = settings[i].x_res;
2084141cc406Sopenharmony_ci    s->cal_image.y_res = settings[i].y_res;
2085141cc406Sopenharmony_ci    s->cal_image.raw_data = NULL;
2086141cc406Sopenharmony_ci    s->cal_image.image = NULL;
2087141cc406Sopenharmony_ci
2088141cc406Sopenharmony_ci    /* width is the same, but there are 2 bytes per pixel component */
2089141cc406Sopenharmony_ci    s->cal_data.line_stride = settings[i].cal_line_stride * 2;
2090141cc406Sopenharmony_ci    s->cal_data.plane_stride = settings[i].cal_plane_stride * 2;
2091141cc406Sopenharmony_ci    s->cal_data.plane_width = settings[i].cal_plane_width;
2092141cc406Sopenharmony_ci    s->cal_data.mode = MODE_COLOR;
2093141cc406Sopenharmony_ci    s->cal_data.x_res = settings[i].x_res;
2094141cc406Sopenharmony_ci    s->cal_data.y_res = settings[i].y_res;
2095141cc406Sopenharmony_ci    s->cal_data.raw_data = NULL;
2096141cc406Sopenharmony_ci    s->cal_data.image = &s->sendcal;
2097141cc406Sopenharmony_ci
2098141cc406Sopenharmony_ci    /*=============================================================*/
2099141cc406Sopenharmony_ci    /* set up the calibration image blocks */
2100141cc406Sopenharmony_ci    width = s->cal_image.plane_width * img_heads;
2101141cc406Sopenharmony_ci    s->coarsecal.width_pix = s->darkcal.width_pix = s->lightcal.width_pix = width;
2102141cc406Sopenharmony_ci    s->coarsecal.width_bytes = s->darkcal.width_bytes = s->lightcal.width_bytes = width * 3;
2103141cc406Sopenharmony_ci    s->coarsecal.height = 1;
2104141cc406Sopenharmony_ci    s->coarsecal.mode = MODE_COLOR;
2105141cc406Sopenharmony_ci    s->coarsecal.x_res = s->darkcal.x_res = s->lightcal.x_res = settings[i].x_res;
2106141cc406Sopenharmony_ci    s->coarsecal.y_res = s->darkcal.y_res = s->lightcal.y_res = settings[i].y_res;
2107141cc406Sopenharmony_ci    s->darkcal.height = s->lightcal.height = 16;
2108141cc406Sopenharmony_ci    s->coarsecal.pages = s->darkcal.pages = s->lightcal.pages = img_pages;
2109141cc406Sopenharmony_ci    s->coarsecal.buffer = s->darkcal.buffer = s->lightcal.buffer = NULL;
2110141cc406Sopenharmony_ci
2111141cc406Sopenharmony_ci    /* set up the calibration data block */
2112141cc406Sopenharmony_ci    width = s->cal_data.plane_width * img_heads;
2113141cc406Sopenharmony_ci    s->sendcal.width_pix = width;
2114141cc406Sopenharmony_ci    s->sendcal.width_bytes = width * 6;  /* 2 bytes of cal data per pixel component */
2115141cc406Sopenharmony_ci    s->sendcal.height = 1;
2116141cc406Sopenharmony_ci    s->sendcal.mode = MODE_COLOR;
2117141cc406Sopenharmony_ci    s->sendcal.x_res = settings[i].x_res;
2118141cc406Sopenharmony_ci    s->sendcal.y_res = settings[i].y_res;
2119141cc406Sopenharmony_ci    s->sendcal.pages = img_pages;
2120141cc406Sopenharmony_ci    s->sendcal.buffer = NULL;
2121141cc406Sopenharmony_ci
2122141cc406Sopenharmony_ci    /*=============================================================*/
2123141cc406Sopenharmony_ci    /* set up the fullscan parameters */
2124141cc406Sopenharmony_ci    /* this is bookkeeping for what we actually pull from the scanner */
2125141cc406Sopenharmony_ci    /* note that this has no image, just dimensions and counters */
2126141cc406Sopenharmony_ci    s->fullscan.width_bytes = settings[i].line_stride;
2127141cc406Sopenharmony_ci    s->fullscan.mode = settings[i].mode;
2128141cc406Sopenharmony_ci    s->fullscan.x_res = settings[i].x_res;
2129141cc406Sopenharmony_ci    s->fullscan.y_res = settings[i].y_res;
2130141cc406Sopenharmony_ci    if(s->source == SOURCE_FLATBED || !s->page_height)
2131141cc406Sopenharmony_ci    {
2132141cc406Sopenharmony_ci      /* flatbed and adf in autodetect always ask for all*/
2133141cc406Sopenharmony_ci      s->fullscan.height = SCANNER_UNIT_TO_PIX(s->max_y, s->fullscan.y_res);
2134141cc406Sopenharmony_ci    }
2135141cc406Sopenharmony_ci    else
2136141cc406Sopenharmony_ci    {
2137141cc406Sopenharmony_ci      /* adf with specified paper size requires padding on top of page_height (~1/2in) */
2138141cc406Sopenharmony_ci      s->fullscan.height = SCANNER_UNIT_TO_PIX((s->page_height + s->tl_y + s->adf_height_padding), s->fullscan.y_res);
2139141cc406Sopenharmony_ci    }
2140141cc406Sopenharmony_ci
2141141cc406Sopenharmony_ci    /*=============================================================*/
2142141cc406Sopenharmony_ci    /* set up the input block raw struct */
2143141cc406Sopenharmony_ci    /* this holds up to 512k of raw scan data */
2144141cc406Sopenharmony_ci    s->block_xfr.line_stride = settings[i].line_stride;
2145141cc406Sopenharmony_ci    s->block_xfr.plane_stride = settings[i].plane_stride;
2146141cc406Sopenharmony_ci    s->block_xfr.plane_width = settings[i].plane_width;
2147141cc406Sopenharmony_ci    s->block_xfr.mode = settings[i].mode;
2148141cc406Sopenharmony_ci    s->block_xfr.x_res = settings[i].x_res;
2149141cc406Sopenharmony_ci    s->block_xfr.y_res = settings[i].y_res;
2150141cc406Sopenharmony_ci    s->block_xfr.raw_data = NULL;
2151141cc406Sopenharmony_ci    s->block_xfr.image = &s->block_img;
2152141cc406Sopenharmony_ci
2153141cc406Sopenharmony_ci    /* set up the input block image struct */
2154141cc406Sopenharmony_ci    /* note that this is the same width/x_res as the final output image */
2155141cc406Sopenharmony_ci    /* but the mode, height and y_res are the same as block_xfr */
2156141cc406Sopenharmony_ci    width = (settings[i].max_x * s->resolution / settings[i].x_res);
2157141cc406Sopenharmony_ci    s->block_img.width_pix = width;
2158141cc406Sopenharmony_ci    s->block_img.width_bytes = width * (settings[i].mode == MODE_COLOR ? 3 : 1);
2159141cc406Sopenharmony_ci    s->block_img.height = settings[i].block_height;
2160141cc406Sopenharmony_ci    s->block_img.mode = settings[i].mode;
2161141cc406Sopenharmony_ci    s->block_img.x_res = s->resolution;
2162141cc406Sopenharmony_ci    s->block_img.y_res = settings[i].y_res;
2163141cc406Sopenharmony_ci    s->block_img.pages = img_pages;
2164141cc406Sopenharmony_ci    s->block_img.buffer = NULL;
2165141cc406Sopenharmony_ci
2166141cc406Sopenharmony_ci    /*=============================================================*/
2167141cc406Sopenharmony_ci    /* set up the output image structs */
2168141cc406Sopenharmony_ci    /* output image might be different from scan due to interpolation */
2169141cc406Sopenharmony_ci    s->front.mode = s->mode;
2170141cc406Sopenharmony_ci    s->front.x_res = s->resolution;
2171141cc406Sopenharmony_ci    s->front.y_res = s->resolution;
2172141cc406Sopenharmony_ci    if(s->source == SOURCE_FLATBED)
2173141cc406Sopenharmony_ci    {
2174141cc406Sopenharmony_ci      /* flatbed ignores the tly */
2175141cc406Sopenharmony_ci      s->front.height = SCANNER_UNIT_TO_PIX(s->max_y - s->tl_y, s->front.y_res);
2176141cc406Sopenharmony_ci    }
2177141cc406Sopenharmony_ci    else if(!s->page_height)
2178141cc406Sopenharmony_ci    {
2179141cc406Sopenharmony_ci      /* adf in autodetect always asks for all */
2180141cc406Sopenharmony_ci      s->front.height = SCANNER_UNIT_TO_PIX(s->max_y, s->front.y_res);
2181141cc406Sopenharmony_ci    }
2182141cc406Sopenharmony_ci    else
2183141cc406Sopenharmony_ci    {
2184141cc406Sopenharmony_ci      /* adf with specified paper size */
2185141cc406Sopenharmony_ci      s->front.height = SCANNER_UNIT_TO_PIX(s->page_height, s->front.y_res);
2186141cc406Sopenharmony_ci    }
2187141cc406Sopenharmony_ci    s->front.width_pix = SCANNER_UNIT_TO_PIX(s->page_width, s->resolution * img_heads);
2188141cc406Sopenharmony_ci    s->front.x_start_offset = (s->block_xfr.image->width_pix - s->front.width_pix)/2;
2189141cc406Sopenharmony_ci    switch (s->mode) {
2190141cc406Sopenharmony_ci      case MODE_COLOR:
2191141cc406Sopenharmony_ci        s->front.width_bytes = s->front.width_pix*3;
2192141cc406Sopenharmony_ci        s->front.x_offset_bytes = s->front.x_start_offset *3;
2193141cc406Sopenharmony_ci        break;
2194141cc406Sopenharmony_ci      case MODE_GRAYSCALE:
2195141cc406Sopenharmony_ci        s->front.width_bytes = s->front.width_pix;
2196141cc406Sopenharmony_ci        s->front.x_offset_bytes = s->front.x_start_offset;
2197141cc406Sopenharmony_ci        break;
2198141cc406Sopenharmony_ci      default: /*binary*/
2199141cc406Sopenharmony_ci        s->front.width_bytes = s->front.width_pix/8;
2200141cc406Sopenharmony_ci        s->front.width_pix = s->front.width_bytes * 8;
2201141cc406Sopenharmony_ci        /*s->page_width = PIX_TO_SCANNER_UNIT(s->front.width_pix, (img_heads * s->resolution_x));*/
2202141cc406Sopenharmony_ci        s->front.x_offset_bytes = s->front.x_start_offset/8;
2203141cc406Sopenharmony_ci        break;
2204141cc406Sopenharmony_ci    }
2205141cc406Sopenharmony_ci
2206141cc406Sopenharmony_ci    /* ADF front need to remove padding header */
2207141cc406Sopenharmony_ci    if (s->source != SOURCE_FLATBED)
2208141cc406Sopenharmony_ci    {
2209141cc406Sopenharmony_ci        s->front.y_skip_offset = SCANNER_UNIT_TO_PIX(s->tl_y+s->adf_height_padding, s->fullscan.y_res);
2210141cc406Sopenharmony_ci    }
2211141cc406Sopenharmony_ci    else
2212141cc406Sopenharmony_ci    {
2213141cc406Sopenharmony_ci        s->front.y_skip_offset = SCANNER_UNIT_TO_PIX(s->tl_y, s->fullscan.y_res);
2214141cc406Sopenharmony_ci    }
2215141cc406Sopenharmony_ci
2216141cc406Sopenharmony_ci    s->front.pages = 1;
2217141cc406Sopenharmony_ci    s->front.buffer = NULL;
2218141cc406Sopenharmony_ci
2219141cc406Sopenharmony_ci    /* back settings always same as front settings */
2220141cc406Sopenharmony_ci    s->back.width_pix = s->front.width_pix;
2221141cc406Sopenharmony_ci    s->back.width_bytes = s->front.width_bytes;
2222141cc406Sopenharmony_ci    s->back.mode = s->front.mode;
2223141cc406Sopenharmony_ci    s->back.x_res = s->front.x_res;
2224141cc406Sopenharmony_ci    s->back.y_res = s->front.y_res;
2225141cc406Sopenharmony_ci    s->back.height = s->front.height;
2226141cc406Sopenharmony_ci    s->back.x_start_offset = s->front.x_start_offset;
2227141cc406Sopenharmony_ci    s->back.x_offset_bytes = s->front.x_offset_bytes;
2228141cc406Sopenharmony_ci    s->back.y_skip_offset = SCANNER_UNIT_TO_PIX(s->tl_y, s->fullscan.y_res);
2229141cc406Sopenharmony_ci    s->back.pages = 1;
2230141cc406Sopenharmony_ci    s->back.buffer = NULL;
2231141cc406Sopenharmony_ci
2232141cc406Sopenharmony_ci    /* dynamic threshold temp buffer, in gray */
2233141cc406Sopenharmony_ci    s->dt.width_pix = s->front.width_pix;
2234141cc406Sopenharmony_ci    s->dt.width_bytes = s->front.width_pix;
2235141cc406Sopenharmony_ci    s->dt.mode = MODE_GRAYSCALE;
2236141cc406Sopenharmony_ci    s->dt.x_res = s->front.x_res;
2237141cc406Sopenharmony_ci    s->dt.y_res = s->front.y_res;
2238141cc406Sopenharmony_ci    s->dt.height = 1;
2239141cc406Sopenharmony_ci    s->dt.pages = 1;
2240141cc406Sopenharmony_ci    s->dt.buffer = NULL;
2241141cc406Sopenharmony_ci
2242141cc406Sopenharmony_ci    /* set up the pointers to the page images in the page structs */
2243141cc406Sopenharmony_ci    s->pages[SIDE_FRONT].image = &s->front;
2244141cc406Sopenharmony_ci    s->pages[SIDE_BACK].image = &s->back;
2245141cc406Sopenharmony_ci    s->pages[SIDE_FRONT].done = 0;
2246141cc406Sopenharmony_ci    s->pages[SIDE_BACK].done = 0;
2247141cc406Sopenharmony_ci
2248141cc406Sopenharmony_ci    DBG (10, "change_params: finish\n");
2249141cc406Sopenharmony_ci
2250141cc406Sopenharmony_ci    return ret;
2251141cc406Sopenharmony_ci}
2252141cc406Sopenharmony_ci
2253141cc406Sopenharmony_ci/* Function to build a lookup table (LUT), often
2254141cc406Sopenharmony_ci   used by scanners to implement brightness/contrast/gamma
2255141cc406Sopenharmony_ci   or by backends to speed binarization/thresholding
2256141cc406Sopenharmony_ci
2257141cc406Sopenharmony_ci   offset and slope inputs are -127 to +127
2258141cc406Sopenharmony_ci
2259141cc406Sopenharmony_ci   slope rotates line around central input/output val,
2260141cc406Sopenharmony_ci   0 makes horizontal line
2261141cc406Sopenharmony_ci
2262141cc406Sopenharmony_ci       pos           zero          neg
2263141cc406Sopenharmony_ci       .       x     .             .  x
2264141cc406Sopenharmony_ci       .      x      .             .   x
2265141cc406Sopenharmony_ci   out .     x       .xxxxxxxxxxx  .    x
2266141cc406Sopenharmony_ci       .    x        .             .     x
2267141cc406Sopenharmony_ci       ....x.......  ............  .......x....
2268141cc406Sopenharmony_ci            in            in            in
2269141cc406Sopenharmony_ci
2270141cc406Sopenharmony_ci   offset moves line vertically, and clamps to output range
2271141cc406Sopenharmony_ci   0 keeps the line crossing the center of the table
2272141cc406Sopenharmony_ci
2273141cc406Sopenharmony_ci       high           low
2274141cc406Sopenharmony_ci       .   xxxxxxxx   .
2275141cc406Sopenharmony_ci       . x            .
2276141cc406Sopenharmony_ci   out x              .          x
2277141cc406Sopenharmony_ci       .              .        x
2278141cc406Sopenharmony_ci       ............   xxxxxxxx....
2279141cc406Sopenharmony_ci            in             in
2280141cc406Sopenharmony_ci
2281141cc406Sopenharmony_ci   out_min/max provide bounds on output values,
2282141cc406Sopenharmony_ci   useful when building thresholding lut.
2283141cc406Sopenharmony_ci   0 and 255 are good defaults otherwise.
2284141cc406Sopenharmony_ci  */
2285141cc406Sopenharmony_cistatic SANE_Status
2286141cc406Sopenharmony_ciload_lut (unsigned char * lut,
2287141cc406Sopenharmony_ci  int in_bits, int out_bits,
2288141cc406Sopenharmony_ci  int out_min, int out_max,
2289141cc406Sopenharmony_ci  int slope, int offset)
2290141cc406Sopenharmony_ci{
2291141cc406Sopenharmony_ci  SANE_Status ret = SANE_STATUS_GOOD;
2292141cc406Sopenharmony_ci  int i, j;
2293141cc406Sopenharmony_ci  double shift, rise;
2294141cc406Sopenharmony_ci  int max_in_val = (1 << in_bits) - 1;
2295141cc406Sopenharmony_ci  int max_out_val = (1 << out_bits) - 1;
2296141cc406Sopenharmony_ci  unsigned char * lut_p = lut;
2297141cc406Sopenharmony_ci
2298141cc406Sopenharmony_ci  DBG (10, "load_lut: start\n");
2299141cc406Sopenharmony_ci
2300141cc406Sopenharmony_ci  /* slope is converted to rise per unit run:
2301141cc406Sopenharmony_ci   * first [-127,127] to [-1,1]
2302141cc406Sopenharmony_ci   * then multiply by PI/2 to convert to radians
2303141cc406Sopenharmony_ci   * then take the tangent (T.O.A)
2304141cc406Sopenharmony_ci   * then multiply by the normal linear slope
2305141cc406Sopenharmony_ci   * because the table may not be square, i.e. 1024x256*/
2306141cc406Sopenharmony_ci  rise = tan((double)slope/127 * M_PI/2) * max_out_val / max_in_val;
2307141cc406Sopenharmony_ci
2308141cc406Sopenharmony_ci  /* line must stay vertically centered, so figure
2309141cc406Sopenharmony_ci   * out vertical offset at central input value */
2310141cc406Sopenharmony_ci  shift = (double)max_out_val/2 - (rise*max_in_val/2);
2311141cc406Sopenharmony_ci
2312141cc406Sopenharmony_ci  /* convert the user offset setting to scale of output
2313141cc406Sopenharmony_ci   * first [-127,127] to [-1,1]
2314141cc406Sopenharmony_ci   * then to [-max_out_val/2,max_out_val/2]*/
2315141cc406Sopenharmony_ci  shift += (double)offset / 127 * max_out_val / 2;
2316141cc406Sopenharmony_ci
2317141cc406Sopenharmony_ci  for(i=0;i<=max_in_val;i++){
2318141cc406Sopenharmony_ci    j = rise*i + shift;
2319141cc406Sopenharmony_ci
2320141cc406Sopenharmony_ci    j = MAX(j, out_min);
2321141cc406Sopenharmony_ci    j = MIN(j, out_max);
2322141cc406Sopenharmony_ci
2323141cc406Sopenharmony_ci    *lut_p=j;
2324141cc406Sopenharmony_ci    lut_p++;
2325141cc406Sopenharmony_ci  }
2326141cc406Sopenharmony_ci
2327141cc406Sopenharmony_ci  hexdump(5, "load_lut: ", lut, max_in_val+1);
2328141cc406Sopenharmony_ci
2329141cc406Sopenharmony_ci  DBG (10, "load_lut: finish\n");
2330141cc406Sopenharmony_ci  return ret;
2331141cc406Sopenharmony_ci}
2332141cc406Sopenharmony_ci
2333141cc406Sopenharmony_ci/*
2334141cc406Sopenharmony_ci * @@ Section 4 - SANE scanning functions
2335141cc406Sopenharmony_ci */
2336141cc406Sopenharmony_ci/*
2337141cc406Sopenharmony_ci * Called by SANE to retrieve information about the type of data
2338141cc406Sopenharmony_ci * that the current scan will return.
2339141cc406Sopenharmony_ci *
2340141cc406Sopenharmony_ci * From the SANE spec:
2341141cc406Sopenharmony_ci * This function is used to obtain the current scan parameters. The
2342141cc406Sopenharmony_ci * returned parameters are guaranteed to be accurate between the time
2343141cc406Sopenharmony_ci * a scan has been started (sane_start() has been called) and the
2344141cc406Sopenharmony_ci * completion of that request. Outside of that window, the returned
2345141cc406Sopenharmony_ci * values are best-effort estimates of what the parameters will be
2346141cc406Sopenharmony_ci * when sane_start() gets invoked.
2347141cc406Sopenharmony_ci *
2348141cc406Sopenharmony_ci * Calling this function before a scan has actually started allows,
2349141cc406Sopenharmony_ci * for example, to get an estimate of how big the scanned image will
2350141cc406Sopenharmony_ci * be. The parameters passed to this function are the handle h of the
2351141cc406Sopenharmony_ci * device for which the parameters should be obtained and a pointer p
2352141cc406Sopenharmony_ci * to a parameter structure.
2353141cc406Sopenharmony_ci */
2354141cc406Sopenharmony_ciSANE_Status
2355141cc406Sopenharmony_cisane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
2356141cc406Sopenharmony_ci{
2357141cc406Sopenharmony_ci  struct scanner *s = (struct scanner *) handle;
2358141cc406Sopenharmony_ci
2359141cc406Sopenharmony_ci  DBG (10, "sane_get_parameters: start\n");
2360141cc406Sopenharmony_ci
2361141cc406Sopenharmony_ci  params->pixels_per_line = s->front.width_pix;
2362141cc406Sopenharmony_ci  params->bytes_per_line = s->front.width_bytes;
2363141cc406Sopenharmony_ci  if(!s->page_height){
2364141cc406Sopenharmony_ci    params->lines = -1;
2365141cc406Sopenharmony_ci  }
2366141cc406Sopenharmony_ci  else{
2367141cc406Sopenharmony_ci    params->lines = s->front.height;
2368141cc406Sopenharmony_ci  }
2369141cc406Sopenharmony_ci  params->last_frame = 1;
2370141cc406Sopenharmony_ci
2371141cc406Sopenharmony_ci  if (s->mode == MODE_COLOR) {
2372141cc406Sopenharmony_ci    params->format = SANE_FRAME_RGB;
2373141cc406Sopenharmony_ci    params->depth = 8;
2374141cc406Sopenharmony_ci  }
2375141cc406Sopenharmony_ci  else if (s->mode == MODE_GRAYSCALE) {
2376141cc406Sopenharmony_ci    params->format = SANE_FRAME_GRAY;
2377141cc406Sopenharmony_ci    params->depth = 8;
2378141cc406Sopenharmony_ci  }
2379141cc406Sopenharmony_ci  else if (s->mode == MODE_LINEART) {
2380141cc406Sopenharmony_ci    params->format = SANE_FRAME_GRAY;
2381141cc406Sopenharmony_ci    params->depth = 1;
2382141cc406Sopenharmony_ci  }
2383141cc406Sopenharmony_ci
2384141cc406Sopenharmony_ci  DBG (15, "\tdepth %d\n", params->depth);
2385141cc406Sopenharmony_ci  DBG (15, "\tlines %d\n", params->lines);
2386141cc406Sopenharmony_ci  DBG (15, "\tpixels_per_line %d\n", params->pixels_per_line);
2387141cc406Sopenharmony_ci  DBG (15, "\tbytes_per_line %d\n", params->bytes_per_line);
2388141cc406Sopenharmony_ci
2389141cc406Sopenharmony_ci  DBG (10, "sane_get_parameters: finish\n");
2390141cc406Sopenharmony_ci
2391141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
2392141cc406Sopenharmony_ci}
2393141cc406Sopenharmony_ci
2394141cc406Sopenharmony_ci/*
2395141cc406Sopenharmony_ci * Called by SANE when a page acquisition operation is to be started.
2396141cc406Sopenharmony_ci * FIXME: won't handle SOURCE_ADF_BACK
2397141cc406Sopenharmony_ci */
2398141cc406Sopenharmony_ciSANE_Status
2399141cc406Sopenharmony_cisane_start (SANE_Handle handle)
2400141cc406Sopenharmony_ci{
2401141cc406Sopenharmony_ci    struct scanner *s = handle;
2402141cc406Sopenharmony_ci    SANE_Status ret;
2403141cc406Sopenharmony_ci    int i;
2404141cc406Sopenharmony_ci
2405141cc406Sopenharmony_ci    DBG (10, "sane_start: start\n");
2406141cc406Sopenharmony_ci
2407141cc406Sopenharmony_ci    /* set side marker on first page */
2408141cc406Sopenharmony_ci    if(!s->started){
2409141cc406Sopenharmony_ci      if(s->source == SOURCE_ADF_BACK){
2410141cc406Sopenharmony_ci        s->side = SIDE_BACK;
2411141cc406Sopenharmony_ci      }
2412141cc406Sopenharmony_ci      else{
2413141cc406Sopenharmony_ci        s->side = SIDE_FRONT;
2414141cc406Sopenharmony_ci      }
2415141cc406Sopenharmony_ci    }
2416141cc406Sopenharmony_ci    /* if already running, duplex needs to switch sides */
2417141cc406Sopenharmony_ci    else if(s->source == SOURCE_ADF_DUPLEX){
2418141cc406Sopenharmony_ci        s->side = !s->side;
2419141cc406Sopenharmony_ci    }
2420141cc406Sopenharmony_ci
2421141cc406Sopenharmony_ci    /* recent scanners need ghs called before scanning */
2422141cc406Sopenharmony_ci    ret = get_hardware_status(s);
2423141cc406Sopenharmony_ci
2424141cc406Sopenharmony_ci    /* ingest paper with adf */
2425141cc406Sopenharmony_ci    if( s->source == SOURCE_ADF_BACK || s->source == SOURCE_ADF_FRONT
2426141cc406Sopenharmony_ci     || (s->source == SOURCE_ADF_DUPLEX && s->side == SIDE_FRONT) ){
2427141cc406Sopenharmony_ci        ret = object_position(s,EPJITSU_PAPER_INGEST);
2428141cc406Sopenharmony_ci        if (ret != SANE_STATUS_GOOD) {
2429141cc406Sopenharmony_ci            DBG (5, "sane_start: ERROR: failed to ingest\n");
2430141cc406Sopenharmony_ci            sane_cancel((SANE_Handle)s);
2431141cc406Sopenharmony_ci            return ret;
2432141cc406Sopenharmony_ci        }
2433141cc406Sopenharmony_ci    }
2434141cc406Sopenharmony_ci
2435141cc406Sopenharmony_ci    /* first page requires buffers, etc */
2436141cc406Sopenharmony_ci    if(!s->started){
2437141cc406Sopenharmony_ci
2438141cc406Sopenharmony_ci        DBG(15,"sane_start: first page\n");
2439141cc406Sopenharmony_ci
2440141cc406Sopenharmony_ci        s->started=1;
2441141cc406Sopenharmony_ci
2442141cc406Sopenharmony_ci        ret = teardown_buffers(s);
2443141cc406Sopenharmony_ci        if (ret != SANE_STATUS_GOOD) {
2444141cc406Sopenharmony_ci            DBG (5, "sane_start: ERROR: failed to teardown buffers\n");
2445141cc406Sopenharmony_ci            sane_cancel((SANE_Handle)s);
2446141cc406Sopenharmony_ci            return SANE_STATUS_NO_MEM;
2447141cc406Sopenharmony_ci        }
2448141cc406Sopenharmony_ci
2449141cc406Sopenharmony_ci        ret = change_params(s);
2450141cc406Sopenharmony_ci        if (ret != SANE_STATUS_GOOD) {
2451141cc406Sopenharmony_ci            DBG (5, "sane_start: ERROR: failed to change_params\n");
2452141cc406Sopenharmony_ci            sane_cancel((SANE_Handle)s);
2453141cc406Sopenharmony_ci            return SANE_STATUS_NO_MEM;
2454141cc406Sopenharmony_ci        }
2455141cc406Sopenharmony_ci
2456141cc406Sopenharmony_ci        ret = setup_buffers(s);
2457141cc406Sopenharmony_ci        if (ret != SANE_STATUS_GOOD) {
2458141cc406Sopenharmony_ci            DBG (5, "sane_start: ERROR: failed to setup buffers\n");
2459141cc406Sopenharmony_ci            sane_cancel((SANE_Handle)s);
2460141cc406Sopenharmony_ci            return SANE_STATUS_NO_MEM;
2461141cc406Sopenharmony_ci        }
2462141cc406Sopenharmony_ci
2463141cc406Sopenharmony_ci        ret = load_lut(s->dt_lut, 8, 8, 50, 205,
2464141cc406Sopenharmony_ci            s->threshold_curve, s->threshold-127);
2465141cc406Sopenharmony_ci        if (ret != SANE_STATUS_GOOD) {
2466141cc406Sopenharmony_ci            DBG (5, "sane_start: ERROR: failed to load_lut for dt\n");
2467141cc406Sopenharmony_ci            sane_cancel((SANE_Handle)s);
2468141cc406Sopenharmony_ci            return ret;
2469141cc406Sopenharmony_ci        }
2470141cc406Sopenharmony_ci
2471141cc406Sopenharmony_ci        ret = coarsecal(s);
2472141cc406Sopenharmony_ci        if (ret != SANE_STATUS_GOOD) {
2473141cc406Sopenharmony_ci            DBG (5, "sane_start: ERROR: failed to coarsecal\n");
2474141cc406Sopenharmony_ci            sane_cancel((SANE_Handle)s);
2475141cc406Sopenharmony_ci            return ret;
2476141cc406Sopenharmony_ci        }
2477141cc406Sopenharmony_ci
2478141cc406Sopenharmony_ci        ret = finecal(s);
2479141cc406Sopenharmony_ci        if (ret != SANE_STATUS_GOOD) {
2480141cc406Sopenharmony_ci            DBG (5, "sane_start: ERROR: failed to finecal\n");
2481141cc406Sopenharmony_ci            sane_cancel((SANE_Handle)s);
2482141cc406Sopenharmony_ci            return ret;
2483141cc406Sopenharmony_ci        }
2484141cc406Sopenharmony_ci
2485141cc406Sopenharmony_ci        ret = send_lut(s);
2486141cc406Sopenharmony_ci        if (ret != SANE_STATUS_GOOD) {
2487141cc406Sopenharmony_ci            DBG (5, "sane_start: ERROR: failed to send lut\n");
2488141cc406Sopenharmony_ci            sane_cancel((SANE_Handle)s);
2489141cc406Sopenharmony_ci            return ret;
2490141cc406Sopenharmony_ci        }
2491141cc406Sopenharmony_ci
2492141cc406Sopenharmony_ci        ret = lamp(s,1);
2493141cc406Sopenharmony_ci        if (ret != SANE_STATUS_GOOD) {
2494141cc406Sopenharmony_ci            DBG (5, "sane_start: ERROR: failed to heat lamp\n");
2495141cc406Sopenharmony_ci            sane_cancel((SANE_Handle)s);
2496141cc406Sopenharmony_ci            return ret;
2497141cc406Sopenharmony_ci        }
2498141cc406Sopenharmony_ci
2499141cc406Sopenharmony_ci        /*should this be between each page*/
2500141cc406Sopenharmony_ci        ret = set_window(s,WINDOW_SCAN);
2501141cc406Sopenharmony_ci        if (ret != SANE_STATUS_GOOD) {
2502141cc406Sopenharmony_ci            DBG (5, "sane_start: ERROR: failed to set window\n");
2503141cc406Sopenharmony_ci            sane_cancel((SANE_Handle)s);
2504141cc406Sopenharmony_ci            return ret;
2505141cc406Sopenharmony_ci        }
2506141cc406Sopenharmony_ci
2507141cc406Sopenharmony_ci    }
2508141cc406Sopenharmony_ci
2509141cc406Sopenharmony_ci    /* reset everything when starting any front, or just back */
2510141cc406Sopenharmony_ci    if(s->side == SIDE_FRONT || s->source == SOURCE_ADF_BACK){
2511141cc406Sopenharmony_ci
2512141cc406Sopenharmony_ci        DBG(15,"sane_start: reset counters\n");
2513141cc406Sopenharmony_ci
2514141cc406Sopenharmony_ci        /* reset scan */
2515141cc406Sopenharmony_ci        s->fullscan.done = 0;
2516141cc406Sopenharmony_ci        s->fullscan.rx_bytes = 0;
2517141cc406Sopenharmony_ci        s->fullscan.total_bytes = s->fullscan.width_bytes * s->fullscan.height;
2518141cc406Sopenharmony_ci
2519141cc406Sopenharmony_ci        /* reset block */
2520141cc406Sopenharmony_ci        update_transfer_totals(&s->block_xfr);
2521141cc406Sopenharmony_ci
2522141cc406Sopenharmony_ci        /* reset front and back page counters */
2523141cc406Sopenharmony_ci        for (i = 0; i < 2; i++)
2524141cc406Sopenharmony_ci        {
2525141cc406Sopenharmony_ci            struct image *page_img = s->pages[i].image;
2526141cc406Sopenharmony_ci            s->pages[i].bytes_total = page_img->width_bytes * page_img->height;
2527141cc406Sopenharmony_ci            s->pages[i].bytes_scanned = 0;
2528141cc406Sopenharmony_ci            s->pages[i].bytes_read = 0;
2529141cc406Sopenharmony_ci            s->pages[i].lines_rx = 0;
2530141cc406Sopenharmony_ci            s->pages[i].lines_pass = 0;
2531141cc406Sopenharmony_ci            s->pages[i].lines_tx = 0;
2532141cc406Sopenharmony_ci            s->pages[i].done = 0;
2533141cc406Sopenharmony_ci        }
2534141cc406Sopenharmony_ci
2535141cc406Sopenharmony_ci        ret = scan(s);
2536141cc406Sopenharmony_ci        if (ret != SANE_STATUS_GOOD) {
2537141cc406Sopenharmony_ci            DBG (5, "sane_start: ERROR: failed to start scan\n");
2538141cc406Sopenharmony_ci            sane_cancel((SANE_Handle)s);
2539141cc406Sopenharmony_ci            return ret;
2540141cc406Sopenharmony_ci        }
2541141cc406Sopenharmony_ci    }
2542141cc406Sopenharmony_ci    else{
2543141cc406Sopenharmony_ci        DBG(15,"sane_start: back side\n");
2544141cc406Sopenharmony_ci    }
2545141cc406Sopenharmony_ci
2546141cc406Sopenharmony_ci    DBG (10, "sane_start: finish\n");
2547141cc406Sopenharmony_ci
2548141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
2549141cc406Sopenharmony_ci}
2550141cc406Sopenharmony_ci
2551141cc406Sopenharmony_ci/* the +8 on all the lengths is to makeup for potential block trailers */
2552141cc406Sopenharmony_cistatic SANE_Status
2553141cc406Sopenharmony_cisetup_buffers(struct scanner *s)
2554141cc406Sopenharmony_ci{
2555141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
2556141cc406Sopenharmony_ci
2557141cc406Sopenharmony_ci    DBG (10, "setup_buffers: start\n");
2558141cc406Sopenharmony_ci
2559141cc406Sopenharmony_ci    /* temporary cal data */
2560141cc406Sopenharmony_ci    s->coarsecal.buffer = calloc (1,s->coarsecal.width_bytes * s->coarsecal.height * s->coarsecal.pages);
2561141cc406Sopenharmony_ci    if(!s->coarsecal.buffer){
2562141cc406Sopenharmony_ci        DBG (5, "setup_buffers: ERROR: failed to setup coarse cal buffer\n");
2563141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
2564141cc406Sopenharmony_ci    }
2565141cc406Sopenharmony_ci
2566141cc406Sopenharmony_ci    s->darkcal.buffer = calloc (1,s->darkcal.width_bytes * s->darkcal.height * s->darkcal.pages);
2567141cc406Sopenharmony_ci    if(!s->darkcal.buffer){
2568141cc406Sopenharmony_ci        DBG (5, "setup_buffers: ERROR: failed to setup fine cal buffer\n");
2569141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
2570141cc406Sopenharmony_ci    }
2571141cc406Sopenharmony_ci
2572141cc406Sopenharmony_ci    s->lightcal.buffer = calloc (1,s->lightcal.width_bytes * s->lightcal.height * s->lightcal.pages);
2573141cc406Sopenharmony_ci    if(!s->lightcal.buffer){
2574141cc406Sopenharmony_ci        DBG (5, "setup_buffers: ERROR: failed to setup fine cal buffer\n");
2575141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
2576141cc406Sopenharmony_ci    }
2577141cc406Sopenharmony_ci
2578141cc406Sopenharmony_ci    s->sendcal.buffer = calloc (1,s->sendcal.width_bytes * s->sendcal.height * s->sendcal.pages);
2579141cc406Sopenharmony_ci    if(!s->sendcal.buffer){
2580141cc406Sopenharmony_ci        DBG (5, "setup_buffers: ERROR: failed to setup send cal buffer\n");
2581141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
2582141cc406Sopenharmony_ci    }
2583141cc406Sopenharmony_ci
2584141cc406Sopenharmony_ci    s->cal_image.raw_data = calloc(1, s->cal_image.line_stride * 16 + 8); /* maximum 16 lines input for fine calibration */
2585141cc406Sopenharmony_ci    if(!s->cal_image.raw_data){
2586141cc406Sopenharmony_ci        DBG (5, "setup_buffers: ERROR: failed to setup calibration input raw data buffer\n");
2587141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
2588141cc406Sopenharmony_ci    }
2589141cc406Sopenharmony_ci
2590141cc406Sopenharmony_ci    s->cal_data.raw_data = calloc(1, s->cal_data.line_stride); /* only 1 line of data is sent */
2591141cc406Sopenharmony_ci    if(!s->cal_data.raw_data){
2592141cc406Sopenharmony_ci        DBG (5, "setup_buffers: ERROR: failed to setup calibration output raw data buffer\n");
2593141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
2594141cc406Sopenharmony_ci    }
2595141cc406Sopenharmony_ci
2596141cc406Sopenharmony_ci    /* grab up to 512K at a time */
2597141cc406Sopenharmony_ci    s->block_img.buffer = calloc (1,s->block_img.width_bytes * s->block_img.height * s->block_img.pages);
2598141cc406Sopenharmony_ci    if(!s->block_img.buffer){
2599141cc406Sopenharmony_ci        DBG (5, "setup_buffers: ERROR: failed to setup block image buffer\n");
2600141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
2601141cc406Sopenharmony_ci    }
2602141cc406Sopenharmony_ci    s->block_xfr.raw_data = calloc(1, s->block_xfr.line_stride * s->block_img.height + 8);
2603141cc406Sopenharmony_ci    if(!s->block_xfr.raw_data){
2604141cc406Sopenharmony_ci        DBG (5, "setup_buffers: ERROR: failed to setup block raw data buffer\n");
2605141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
2606141cc406Sopenharmony_ci    }
2607141cc406Sopenharmony_ci
2608141cc406Sopenharmony_ci    /* one grayscale line for dynamic threshold */
2609141cc406Sopenharmony_ci    s->dt.buffer = calloc (1,s->dt.width_bytes * s->dt.height * s->dt.pages);
2610141cc406Sopenharmony_ci    if(!s->dt.buffer){
2611141cc406Sopenharmony_ci        DBG (5, "setup_buffers: ERROR: failed to setup dt buffer\n");
2612141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
2613141cc406Sopenharmony_ci    }
2614141cc406Sopenharmony_ci
2615141cc406Sopenharmony_ci    /* make image buffer to hold frontside data */
2616141cc406Sopenharmony_ci    if(s->source != SOURCE_ADF_BACK){
2617141cc406Sopenharmony_ci
2618141cc406Sopenharmony_ci        s->front.buffer = calloc (1,s->front.width_bytes * s->front.height * s->front.pages);
2619141cc406Sopenharmony_ci        if(!s->front.buffer){
2620141cc406Sopenharmony_ci            DBG (5, "setup_buffers: ERROR: failed to setup front buffer\n");
2621141cc406Sopenharmony_ci            return SANE_STATUS_NO_MEM;
2622141cc406Sopenharmony_ci        }
2623141cc406Sopenharmony_ci    }
2624141cc406Sopenharmony_ci
2625141cc406Sopenharmony_ci    /* make image buffer to hold backside data */
2626141cc406Sopenharmony_ci    if(s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_ADF_BACK){
2627141cc406Sopenharmony_ci
2628141cc406Sopenharmony_ci        s->back.buffer = calloc (1,s->back.width_bytes * s->back.height * s->back.pages);
2629141cc406Sopenharmony_ci        if(!s->back.buffer){
2630141cc406Sopenharmony_ci            DBG (5, "setup_buffers: ERROR: failed to setup back buffer\n");
2631141cc406Sopenharmony_ci            return SANE_STATUS_NO_MEM;
2632141cc406Sopenharmony_ci        }
2633141cc406Sopenharmony_ci    }
2634141cc406Sopenharmony_ci
2635141cc406Sopenharmony_ci    DBG (10, "setup_buffers: finish\n");
2636141cc406Sopenharmony_ci    return ret;
2637141cc406Sopenharmony_ci}
2638141cc406Sopenharmony_ci
2639141cc406Sopenharmony_ci/*
2640141cc406Sopenharmony_ci coarse calibration consists of:
2641141cc406Sopenharmony_ci 1. turn lamp off (d0)
2642141cc406Sopenharmony_ci 2. set window for single line of data (d1)
2643141cc406Sopenharmony_ci 3. get line (d2)
2644141cc406Sopenharmony_ci 4. update dark coarse cal (c6)
2645141cc406Sopenharmony_ci 5. return to #3 if not dark enough
2646141cc406Sopenharmony_ci 6. turn lamp on (d0)
2647141cc406Sopenharmony_ci 7. get line (d2)
2648141cc406Sopenharmony_ci 8. update light coarse cal (c6)
2649141cc406Sopenharmony_ci 9. return to #7 if not light enough
2650141cc406Sopenharmony_ci*/
2651141cc406Sopenharmony_ci
2652141cc406Sopenharmony_cistatic SANE_Status
2653141cc406Sopenharmony_cicoarsecal_send_cal(struct scanner *s, unsigned char *pay)
2654141cc406Sopenharmony_ci{
2655141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
2656141cc406Sopenharmony_ci    unsigned char cmd[2];
2657141cc406Sopenharmony_ci    unsigned char stat[1];
2658141cc406Sopenharmony_ci    size_t cmdLen,statLen,payLen;
2659141cc406Sopenharmony_ci
2660141cc406Sopenharmony_ci    DBG (10, "coarsecal_send_cal: start\n");
2661141cc406Sopenharmony_ci    /* send coarse cal (c6) */
2662141cc406Sopenharmony_ci    cmd[0] = 0x1b;
2663141cc406Sopenharmony_ci    cmd[1] = 0xc6;
2664141cc406Sopenharmony_ci    cmdLen = 2;
2665141cc406Sopenharmony_ci    stat[0] = 0;
2666141cc406Sopenharmony_ci    statLen = 1;
2667141cc406Sopenharmony_ci
2668141cc406Sopenharmony_ci    ret = do_cmd(
2669141cc406Sopenharmony_ci      s, 0,
2670141cc406Sopenharmony_ci      cmd, cmdLen,
2671141cc406Sopenharmony_ci      NULL, 0,
2672141cc406Sopenharmony_ci      stat, &statLen
2673141cc406Sopenharmony_ci    );
2674141cc406Sopenharmony_ci    if(ret){
2675141cc406Sopenharmony_ci         DBG (5, "coarsecal_send_cal: error sending c6 cmd\n");
2676141cc406Sopenharmony_ci         return ret;
2677141cc406Sopenharmony_ci    }
2678141cc406Sopenharmony_ci    if(stat[0] != 6){
2679141cc406Sopenharmony_ci        DBG (5, "coarsecal_send_cal: cmd bad c6 status?\n");
2680141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
2681141cc406Sopenharmony_ci     }
2682141cc406Sopenharmony_ci
2683141cc406Sopenharmony_ci    /*send coarse cal payload*/
2684141cc406Sopenharmony_ci    stat[0] = 0;
2685141cc406Sopenharmony_ci    statLen = 1;
2686141cc406Sopenharmony_ci    payLen = 28;
2687141cc406Sopenharmony_ci
2688141cc406Sopenharmony_ci    ret = do_cmd(
2689141cc406Sopenharmony_ci      s, 0,
2690141cc406Sopenharmony_ci      pay, payLen,
2691141cc406Sopenharmony_ci      NULL, 0,
2692141cc406Sopenharmony_ci      stat, &statLen
2693141cc406Sopenharmony_ci    );
2694141cc406Sopenharmony_ci    if(ret){
2695141cc406Sopenharmony_ci        DBG (5, "coarsecal_send_cal: error sending c6 payload\n");
2696141cc406Sopenharmony_ci        return ret;
2697141cc406Sopenharmony_ci    }
2698141cc406Sopenharmony_ci    if(stat[0] != 6){
2699141cc406Sopenharmony_ci        DBG (5, "coarsecal_send_cal: c6 payload bad status?\n");
2700141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
2701141cc406Sopenharmony_ci    }
2702141cc406Sopenharmony_ci
2703141cc406Sopenharmony_ci    DBG (10, "coarsecal_send_cal: finish\n");
2704141cc406Sopenharmony_ci    return ret;
2705141cc406Sopenharmony_ci}
2706141cc406Sopenharmony_ci
2707141cc406Sopenharmony_cistatic SANE_Status
2708141cc406Sopenharmony_cicoarsecal_get_line(struct scanner *s, struct image *img)
2709141cc406Sopenharmony_ci{
2710141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
2711141cc406Sopenharmony_ci    unsigned char cmd[2];
2712141cc406Sopenharmony_ci    unsigned char stat[1];
2713141cc406Sopenharmony_ci    size_t cmdLen,statLen;
2714141cc406Sopenharmony_ci
2715141cc406Sopenharmony_ci    DBG (10, "coarsecal_get_line: start\n");
2716141cc406Sopenharmony_ci
2717141cc406Sopenharmony_ci    /* send scan d2 command */
2718141cc406Sopenharmony_ci    cmd[0] = 0x1b;
2719141cc406Sopenharmony_ci    cmd[1] = 0xd2;
2720141cc406Sopenharmony_ci    cmdLen = 2;
2721141cc406Sopenharmony_ci    stat[0] = 0;
2722141cc406Sopenharmony_ci    statLen = 1;
2723141cc406Sopenharmony_ci
2724141cc406Sopenharmony_ci    ret = do_cmd(
2725141cc406Sopenharmony_ci      s, 0,
2726141cc406Sopenharmony_ci      cmd, cmdLen,
2727141cc406Sopenharmony_ci      NULL, 0,
2728141cc406Sopenharmony_ci      stat, &statLen
2729141cc406Sopenharmony_ci    );
2730141cc406Sopenharmony_ci    if(ret){
2731141cc406Sopenharmony_ci        DBG (5, "coarsecal_get_line: error sending d2 cmd\n");
2732141cc406Sopenharmony_ci        return ret;
2733141cc406Sopenharmony_ci    }
2734141cc406Sopenharmony_ci    if(stat[0] != 6){
2735141cc406Sopenharmony_ci        DBG (5, "coarsecal_get_line: cmd bad d2 status?\n");
2736141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
2737141cc406Sopenharmony_ci    }
2738141cc406Sopenharmony_ci
2739141cc406Sopenharmony_ci    s->cal_image.image = img;
2740141cc406Sopenharmony_ci    update_transfer_totals(&s->cal_image);
2741141cc406Sopenharmony_ci
2742141cc406Sopenharmony_ci    while(!s->cal_image.done){
2743141cc406Sopenharmony_ci        ret = read_from_scanner(s,&s->cal_image);
2744141cc406Sopenharmony_ci        if(ret){
2745141cc406Sopenharmony_ci            DBG (5, "coarsecal_get_line: can't read from scanner\n");
2746141cc406Sopenharmony_ci            return ret;
2747141cc406Sopenharmony_ci        }
2748141cc406Sopenharmony_ci    }
2749141cc406Sopenharmony_ci    /* convert the raw data into normal packed pixel data */
2750141cc406Sopenharmony_ci    descramble_raw(s, &s->cal_image);
2751141cc406Sopenharmony_ci
2752141cc406Sopenharmony_ci    DBG (10, "coarsecal_get_line: finish\n");
2753141cc406Sopenharmony_ci    return ret;
2754141cc406Sopenharmony_ci}
2755141cc406Sopenharmony_ci
2756141cc406Sopenharmony_cistatic SANE_Status
2757141cc406Sopenharmony_cicoarsecal_dark(struct scanner *s, unsigned char *pay)
2758141cc406Sopenharmony_ci{
2759141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
2760141cc406Sopenharmony_ci
2761141cc406Sopenharmony_ci    int try_count, cal_good[2], x, j;
2762141cc406Sopenharmony_ci    int param[2], zcount[2], high_param[2], low_param[2], avg[2], maxval[2];
2763141cc406Sopenharmony_ci
2764141cc406Sopenharmony_ci    DBG (10, "coarsecal_dark: start\n");
2765141cc406Sopenharmony_ci
2766141cc406Sopenharmony_ci    /* dark cal, lamp off */
2767141cc406Sopenharmony_ci    ret = lamp(s,0);
2768141cc406Sopenharmony_ci    if(ret){
2769141cc406Sopenharmony_ci        DBG (5, "coarsecal_dark: error lamp off\n");
2770141cc406Sopenharmony_ci        return ret;
2771141cc406Sopenharmony_ci    }
2772141cc406Sopenharmony_ci
2773141cc406Sopenharmony_ci    try_count = 8;
2774141cc406Sopenharmony_ci    param[0] = 63;
2775141cc406Sopenharmony_ci    param[1] = 63;
2776141cc406Sopenharmony_ci    low_param[0] = low_param[1] = -64;  /* The S300 will accept coarse offsets from -128 to 127 */
2777141cc406Sopenharmony_ci    high_param[0] = high_param[1] = 63; /* By our range is limited to converge faster */
2778141cc406Sopenharmony_ci    cal_good[0] = cal_good[1] = 0;
2779141cc406Sopenharmony_ci
2780141cc406Sopenharmony_ci    while (try_count > 0){
2781141cc406Sopenharmony_ci        try_count--;
2782141cc406Sopenharmony_ci
2783141cc406Sopenharmony_ci        /* update the coarsecal payload to use our new dark offset parameters */
2784141cc406Sopenharmony_ci        if (s->model == MODEL_S300 || s->model == MODEL_S1300i)
2785141cc406Sopenharmony_ci        {
2786141cc406Sopenharmony_ci            pay[5] = param[0];
2787141cc406Sopenharmony_ci            pay[7] = param[1];
2788141cc406Sopenharmony_ci        }
2789141cc406Sopenharmony_ci        else /* MODEL_S1100 or MODEL_FI60F or MODEL_FI65F */
2790141cc406Sopenharmony_ci        {
2791141cc406Sopenharmony_ci            pay[5] = param[0];
2792141cc406Sopenharmony_ci            pay[7] = param[0];
2793141cc406Sopenharmony_ci            pay[9] = param[0];
2794141cc406Sopenharmony_ci        }
2795141cc406Sopenharmony_ci
2796141cc406Sopenharmony_ci        ret = coarsecal_send_cal(s, pay);
2797141cc406Sopenharmony_ci
2798141cc406Sopenharmony_ci        DBG(15, "coarsecal_dark offset: parameter front: %i back: %i\n", param[0], param[1]);
2799141cc406Sopenharmony_ci
2800141cc406Sopenharmony_ci        ret = coarsecal_get_line(s, &s->coarsecal);
2801141cc406Sopenharmony_ci
2802141cc406Sopenharmony_ci        /* gather statistics: count the proportion of 0-valued pixels */
2803141cc406Sopenharmony_ci        /* since the lamp is off, there's no point in looking at the green or blue data - they're all from the same sensor anyway */
2804141cc406Sopenharmony_ci        zcount[0] = zcount[1] = 0;
2805141cc406Sopenharmony_ci        avg[0] = avg[1] = 0;
2806141cc406Sopenharmony_ci        maxval[0] = maxval[1] = 0;
2807141cc406Sopenharmony_ci        for (j = 0; j < s->coarsecal.pages; j++)
2808141cc406Sopenharmony_ci        {
2809141cc406Sopenharmony_ci            int page_offset = j * s->coarsecal.width_bytes * s->coarsecal.height;
2810141cc406Sopenharmony_ci            for (x = 0; x < s->coarsecal.width_bytes; x++)
2811141cc406Sopenharmony_ci            {
2812141cc406Sopenharmony_ci                int val = s->coarsecal.buffer[page_offset + x];
2813141cc406Sopenharmony_ci                avg[j] += val;
2814141cc406Sopenharmony_ci                if (val == 0)        zcount[j]++;
2815141cc406Sopenharmony_ci                if (val > maxval[j]) maxval[j] = val;
2816141cc406Sopenharmony_ci            }
2817141cc406Sopenharmony_ci        }
2818141cc406Sopenharmony_ci        /* convert the zero counts from a pixel count to a proportion in tenths of a percent */
2819141cc406Sopenharmony_ci        for (j = 0; j < s->coarsecal.pages; j++)
2820141cc406Sopenharmony_ci        {
2821141cc406Sopenharmony_ci            avg[j] /= s->coarsecal.width_bytes;
2822141cc406Sopenharmony_ci            zcount[j] = zcount[j] * 1000 / s->coarsecal.width_bytes;
2823141cc406Sopenharmony_ci        }
2824141cc406Sopenharmony_ci        DBG(15, "coarsecal_dark offset: average pixel values front: %i  back: %i\n", avg[0], avg[1]);
2825141cc406Sopenharmony_ci        DBG(15, "coarsecal_dark offset: maximum pixel values front: %i  back: %i\n", maxval[0], maxval[1]);
2826141cc406Sopenharmony_ci        DBG(15, "coarsecal_dark offset: 0-valued pixel count front: %f%% back: %f%%\n", zcount[0] / 10.0f, zcount[1] / 10.0f);
2827141cc406Sopenharmony_ci
2828141cc406Sopenharmony_ci        /* check the values, adjust parameters if they are not within the target range */
2829141cc406Sopenharmony_ci        for (j = 0; j < s->coarsecal.pages; j++)
2830141cc406Sopenharmony_ci        {
2831141cc406Sopenharmony_ci            if (!cal_good[j])
2832141cc406Sopenharmony_ci            {
2833141cc406Sopenharmony_ci                if (avg[j] > COARSE_OFFSET_TARGET)
2834141cc406Sopenharmony_ci                {
2835141cc406Sopenharmony_ci                    high_param[j] = param[j];
2836141cc406Sopenharmony_ci                    param[j] = (low_param[j] + high_param[j]) / 2;
2837141cc406Sopenharmony_ci                }
2838141cc406Sopenharmony_ci                else if (avg[j] < COARSE_OFFSET_TARGET)
2839141cc406Sopenharmony_ci                {
2840141cc406Sopenharmony_ci                    low_param[j] = param[j];
2841141cc406Sopenharmony_ci                    param[j] = (low_param[j] + high_param[j]) / 2;
2842141cc406Sopenharmony_ci                }
2843141cc406Sopenharmony_ci                else cal_good[j] = 1;
2844141cc406Sopenharmony_ci            }
2845141cc406Sopenharmony_ci        }
2846141cc406Sopenharmony_ci        if (cal_good[0] + cal_good[1] == s->coarsecal.pages) break;
2847141cc406Sopenharmony_ci
2848141cc406Sopenharmony_ci    } /* continue looping for up to 8 tries */
2849141cc406Sopenharmony_ci
2850141cc406Sopenharmony_ci    DBG (10, "coarsecal_dark: finish\n");
2851141cc406Sopenharmony_ci    return ret;
2852141cc406Sopenharmony_ci}
2853141cc406Sopenharmony_ci
2854141cc406Sopenharmony_cistatic SANE_Status
2855141cc406Sopenharmony_cicoarsecal_light(struct scanner *s, unsigned char *pay)
2856141cc406Sopenharmony_ci{
2857141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
2858141cc406Sopenharmony_ci
2859141cc406Sopenharmony_ci    int try_count, cal_good[2], x, i, j;
2860141cc406Sopenharmony_ci    int param[2], zcount[2], high_param[2], low_param[2], avg[2];
2861141cc406Sopenharmony_ci    int rgb_avg[2][3], rgb_hicount[2][3];
2862141cc406Sopenharmony_ci
2863141cc406Sopenharmony_ci    DBG (10, "coarsecal_light: start\n");
2864141cc406Sopenharmony_ci
2865141cc406Sopenharmony_ci    /* light cal, lamp on */
2866141cc406Sopenharmony_ci    ret = lamp(s,1);
2867141cc406Sopenharmony_ci    if(ret){
2868141cc406Sopenharmony_ci        DBG (5, "coarsecal_light: error lamp on\n");
2869141cc406Sopenharmony_ci        return ret;
2870141cc406Sopenharmony_ci    }
2871141cc406Sopenharmony_ci
2872141cc406Sopenharmony_ci    try_count = 8;
2873141cc406Sopenharmony_ci    param[0] = pay[11];
2874141cc406Sopenharmony_ci    param[1] = pay[13];
2875141cc406Sopenharmony_ci    low_param[0] = low_param[1] = 0;
2876141cc406Sopenharmony_ci    high_param[0] = high_param[1] = 63;
2877141cc406Sopenharmony_ci    cal_good[0] = cal_good[1] = 0;
2878141cc406Sopenharmony_ci
2879141cc406Sopenharmony_ci    while (try_count > 0){
2880141cc406Sopenharmony_ci        try_count--;
2881141cc406Sopenharmony_ci
2882141cc406Sopenharmony_ci        ret = coarsecal_send_cal(s, pay);
2883141cc406Sopenharmony_ci
2884141cc406Sopenharmony_ci        DBG(15, "coarsecal_light gain: parameter front: %i back: %i\n", param[0], param[1]);
2885141cc406Sopenharmony_ci
2886141cc406Sopenharmony_ci        ret = coarsecal_get_line(s, &s->coarsecal);
2887141cc406Sopenharmony_ci
2888141cc406Sopenharmony_ci        /* gather statistics: count the proportion of 255-valued pixels in each color channel */
2889141cc406Sopenharmony_ci        /*                    count the average pixel value in each color channel */
2890141cc406Sopenharmony_ci        for (i = 0; i < s->coarsecal.pages; i++)
2891141cc406Sopenharmony_ci            for (j = 0; j < 3; j++)
2892141cc406Sopenharmony_ci                rgb_avg[i][j] = rgb_hicount[i][j] = 0;
2893141cc406Sopenharmony_ci        for (i = 0; i < s->coarsecal.pages; i++)
2894141cc406Sopenharmony_ci        {
2895141cc406Sopenharmony_ci            for (x = 0; x < s->coarsecal.width_pix; x++)
2896141cc406Sopenharmony_ci            {
2897141cc406Sopenharmony_ci                /* get color channel values and count of pixels pegged at 255 */
2898141cc406Sopenharmony_ci                unsigned char *rgbpix = s->coarsecal.buffer + (i * s->coarsecal.width_bytes * s->coarsecal.height) + x * 3;
2899141cc406Sopenharmony_ci                for (j = 0; j < 3; j++)
2900141cc406Sopenharmony_ci                {
2901141cc406Sopenharmony_ci                    rgb_avg[i][j] += rgbpix[j];
2902141cc406Sopenharmony_ci                    if (rgbpix[j] == 255)
2903141cc406Sopenharmony_ci                        rgb_hicount[i][j]++;
2904141cc406Sopenharmony_ci                }
2905141cc406Sopenharmony_ci            }
2906141cc406Sopenharmony_ci        }
2907141cc406Sopenharmony_ci        /* apply the color correction factors to the averages */
2908141cc406Sopenharmony_ci        for (i = 0; i < s->coarsecal.pages; i++)
2909141cc406Sopenharmony_ci            for (j = 0; j < 3; j++)
2910141cc406Sopenharmony_ci                rgb_avg[i][j] *= s->white_factor[j];
2911141cc406Sopenharmony_ci        /* set the gain so that none of the color channels are clipping, ie take the highest channel values */
2912141cc406Sopenharmony_ci        for (i = 0; i < s->coarsecal.pages; i++)
2913141cc406Sopenharmony_ci        {
2914141cc406Sopenharmony_ci            avg[i] = MAX3(rgb_avg[i][0], rgb_avg[i][1], rgb_avg[i][2]) / s->coarsecal.width_pix;
2915141cc406Sopenharmony_ci            for (j = 0; j < 3; j++)
2916141cc406Sopenharmony_ci                rgb_avg[i][j] /= s->coarsecal.width_pix;
2917141cc406Sopenharmony_ci        }
2918141cc406Sopenharmony_ci        /* convert the 255-counts from a pixel count to a proportion in tenths of a percent */
2919141cc406Sopenharmony_ci        for (i = 0; i < s->coarsecal.pages; i++)
2920141cc406Sopenharmony_ci        {
2921141cc406Sopenharmony_ci            for (j = 0; j < 3; j++)
2922141cc406Sopenharmony_ci            {
2923141cc406Sopenharmony_ci                rgb_hicount[i][j] = rgb_hicount[i][j] * 1000 / s->coarsecal.width_pix;
2924141cc406Sopenharmony_ci            }
2925141cc406Sopenharmony_ci            zcount[i] = MAX3(rgb_hicount[i][0], rgb_hicount[i][1], rgb_hicount[i][2]);
2926141cc406Sopenharmony_ci        }
2927141cc406Sopenharmony_ci        DBG(15, "coarsecal_light gain: average RGB values front: (%i,%i,%i)  back: (%i,%i,%i)\n",
2928141cc406Sopenharmony_ci            rgb_avg[0][0], rgb_avg[0][1], rgb_avg[0][2], rgb_avg[1][0], rgb_avg[1][1], rgb_avg[1][2]);
2929141cc406Sopenharmony_ci        DBG(15, "coarsecal_light gain: 255-valued pixel count front: (%g,%g,%g) back: (%g,%g,%g)\n",
2930141cc406Sopenharmony_ci            rgb_hicount[0][0]/10.0f, rgb_hicount[0][1]/10.0f, rgb_hicount[0][2]/10.0f,
2931141cc406Sopenharmony_ci            rgb_hicount[1][0]/10.0f, rgb_hicount[1][1]/10.0f, rgb_hicount[1][2]/10.0f);
2932141cc406Sopenharmony_ci
2933141cc406Sopenharmony_ci        /* check the values, adjust parameters if they are not within the target range */
2934141cc406Sopenharmony_ci        for (x = 0; x < s->coarsecal.pages; x++)
2935141cc406Sopenharmony_ci        {
2936141cc406Sopenharmony_ci            if (!cal_good[x])
2937141cc406Sopenharmony_ci            {
2938141cc406Sopenharmony_ci                if (zcount[x] > 9 || avg[x] > coarse_gain_max[x])
2939141cc406Sopenharmony_ci                {
2940141cc406Sopenharmony_ci                    high_param[x] = param[x];
2941141cc406Sopenharmony_ci                    param[x] = (low_param[x] + high_param[x]) / 2;
2942141cc406Sopenharmony_ci                }
2943141cc406Sopenharmony_ci                else if (avg[x] < coarse_gain_min[x])
2944141cc406Sopenharmony_ci                {
2945141cc406Sopenharmony_ci                    low_param[x] = param[x];
2946141cc406Sopenharmony_ci                    param[x] = (low_param[x] + high_param[x]) / 2;
2947141cc406Sopenharmony_ci                }
2948141cc406Sopenharmony_ci                else cal_good[x] = 1;
2949141cc406Sopenharmony_ci            }
2950141cc406Sopenharmony_ci        }
2951141cc406Sopenharmony_ci        if (cal_good[0] + cal_good[1] == s->coarsecal.pages) break;
2952141cc406Sopenharmony_ci
2953141cc406Sopenharmony_ci        /* update the coarsecal payload to use the new gain parameters */
2954141cc406Sopenharmony_ci        if (s->model == MODEL_S300 || s->model == MODEL_S1300i)
2955141cc406Sopenharmony_ci        {
2956141cc406Sopenharmony_ci            pay[11] = param[0];
2957141cc406Sopenharmony_ci            pay[13] = param[1];
2958141cc406Sopenharmony_ci        }
2959141cc406Sopenharmony_ci        else /* MODEL_S1100 or MODEL_FI60F or MODEL_FI65F */
2960141cc406Sopenharmony_ci        {
2961141cc406Sopenharmony_ci            pay[11] = param[0];
2962141cc406Sopenharmony_ci            pay[13] = param[0];
2963141cc406Sopenharmony_ci            pay[15] = param[0];
2964141cc406Sopenharmony_ci        }
2965141cc406Sopenharmony_ci    }
2966141cc406Sopenharmony_ci
2967141cc406Sopenharmony_ci    DBG (10, "coarsecal_light: finish\n");
2968141cc406Sopenharmony_ci    return ret;
2969141cc406Sopenharmony_ci}
2970141cc406Sopenharmony_ci
2971141cc406Sopenharmony_cistatic SANE_Status
2972141cc406Sopenharmony_cicoarsecal(struct scanner *s)
2973141cc406Sopenharmony_ci{
2974141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
2975141cc406Sopenharmony_ci    unsigned char pay[28];
2976141cc406Sopenharmony_ci    size_t payLen;
2977141cc406Sopenharmony_ci
2978141cc406Sopenharmony_ci    DBG (10, "coarsecal: start\n");
2979141cc406Sopenharmony_ci
2980141cc406Sopenharmony_ci    payLen = sizeof(pay);
2981141cc406Sopenharmony_ci
2982141cc406Sopenharmony_ci    if(s->model == MODEL_S300){
2983141cc406Sopenharmony_ci        memcpy(pay,coarseCalData_S300,payLen);
2984141cc406Sopenharmony_ci    }
2985141cc406Sopenharmony_ci    else if(s->model == MODEL_S1300i){
2986141cc406Sopenharmony_ci        memcpy(pay,coarseCalData_S1300i,payLen);
2987141cc406Sopenharmony_ci    }
2988141cc406Sopenharmony_ci    else if(s->model == MODEL_S1100){
2989141cc406Sopenharmony_ci        memcpy(pay,coarseCalData_S1100,payLen);
2990141cc406Sopenharmony_ci    }
2991141cc406Sopenharmony_ci    else{
2992141cc406Sopenharmony_ci        memcpy(pay,coarseCalData_FI60F,payLen);
2993141cc406Sopenharmony_ci    }
2994141cc406Sopenharmony_ci
2995141cc406Sopenharmony_ci    /* ask for 1 line */
2996141cc406Sopenharmony_ci    ret = set_window(s, WINDOW_COARSECAL);
2997141cc406Sopenharmony_ci    if(ret){
2998141cc406Sopenharmony_ci        DBG (5, "coarsecal: error sending setwindow\n");
2999141cc406Sopenharmony_ci        return ret;
3000141cc406Sopenharmony_ci    }
3001141cc406Sopenharmony_ci
3002141cc406Sopenharmony_ci    if(s->model == MODEL_S1100){
3003141cc406Sopenharmony_ci        ret = coarsecal_send_cal(s, pay);
3004141cc406Sopenharmony_ci    }
3005141cc406Sopenharmony_ci    else{
3006141cc406Sopenharmony_ci        ret = coarsecal_dark(s, pay);
3007141cc406Sopenharmony_ci        ret = coarsecal_light(s, pay);
3008141cc406Sopenharmony_ci    }
3009141cc406Sopenharmony_ci
3010141cc406Sopenharmony_ci    DBG (10, "coarsecal: finish\n");
3011141cc406Sopenharmony_ci    return ret;
3012141cc406Sopenharmony_ci}
3013141cc406Sopenharmony_ci
3014141cc406Sopenharmony_cistatic SANE_Status
3015141cc406Sopenharmony_cifinecal_send_cal(struct scanner *s)
3016141cc406Sopenharmony_ci{
3017141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
3018141cc406Sopenharmony_ci
3019141cc406Sopenharmony_ci    size_t cmdLen = 2;
3020141cc406Sopenharmony_ci    unsigned char cmd[2];
3021141cc406Sopenharmony_ci
3022141cc406Sopenharmony_ci    size_t statLen = 1;
3023141cc406Sopenharmony_ci    unsigned char stat[2];
3024141cc406Sopenharmony_ci
3025141cc406Sopenharmony_ci    int i, j, k;
3026141cc406Sopenharmony_ci    unsigned char *p_out, *p_in = s->sendcal.buffer;
3027141cc406Sopenharmony_ci    int planes;
3028141cc406Sopenharmony_ci
3029141cc406Sopenharmony_ci    DBG (10, "finecal_send_cal: start\n");
3030141cc406Sopenharmony_ci
3031141cc406Sopenharmony_ci    if(s->model == MODEL_FI60F || s->model == MODEL_FI65F)
3032141cc406Sopenharmony_ci      planes = 3;
3033141cc406Sopenharmony_ci    if(s->model == MODEL_S300 || s->model == MODEL_S1300i)
3034141cc406Sopenharmony_ci      planes = 2;
3035141cc406Sopenharmony_ci
3036141cc406Sopenharmony_ci    /* scramble the raster buffer data into scanner raw format */
3037141cc406Sopenharmony_ci    /* this is reverse of descramble_raw */
3038141cc406Sopenharmony_ci    memset(s->cal_data.raw_data, 0, s->cal_data.line_stride);
3039141cc406Sopenharmony_ci
3040141cc406Sopenharmony_ci    if(s->model == MODEL_S1100){
3041141cc406Sopenharmony_ci      planes = 1;
3042141cc406Sopenharmony_ci
3043141cc406Sopenharmony_ci      for (k = 0; k < s->sendcal.width_pix; k++){  /* column (x) */
3044141cc406Sopenharmony_ci
3045141cc406Sopenharmony_ci        /* input is RrGgBb (capital is offset, small is gain) */
3046141cc406Sopenharmony_ci        /* output is Bb...BbRr...RrGg...Gg*/
3047141cc406Sopenharmony_ci
3048141cc406Sopenharmony_ci        /*red*/
3049141cc406Sopenharmony_ci        p_out = s->cal_data.raw_data + s->cal_data.plane_stride + k*2;
3050141cc406Sopenharmony_ci        *p_out = *p_in;
3051141cc406Sopenharmony_ci        p_out++;
3052141cc406Sopenharmony_ci        p_in++;
3053141cc406Sopenharmony_ci        *p_out = *p_in;
3054141cc406Sopenharmony_ci        p_in++;
3055141cc406Sopenharmony_ci
3056141cc406Sopenharmony_ci        /*green*/
3057141cc406Sopenharmony_ci        p_out = s->cal_data.raw_data + 2*s->cal_data.plane_stride + k*2;
3058141cc406Sopenharmony_ci        *p_out = *p_in;
3059141cc406Sopenharmony_ci        p_out++;
3060141cc406Sopenharmony_ci        p_in++;
3061141cc406Sopenharmony_ci        *p_out = *p_in;
3062141cc406Sopenharmony_ci        p_in++;
3063141cc406Sopenharmony_ci
3064141cc406Sopenharmony_ci        /*blue*/
3065141cc406Sopenharmony_ci        p_out = s->cal_data.raw_data + k*2;
3066141cc406Sopenharmony_ci        *p_out = *p_in;
3067141cc406Sopenharmony_ci        p_out++;
3068141cc406Sopenharmony_ci        p_in++;
3069141cc406Sopenharmony_ci        *p_out = *p_in;
3070141cc406Sopenharmony_ci        p_in++;
3071141cc406Sopenharmony_ci      }
3072141cc406Sopenharmony_ci    }
3073141cc406Sopenharmony_ci
3074141cc406Sopenharmony_ci    else{
3075141cc406Sopenharmony_ci      for (i = 0; i < planes; i++)
3076141cc406Sopenharmony_ci        for (j = 0; j < s->cal_data.plane_width; j++)
3077141cc406Sopenharmony_ci            for (k = 0; k < 3; k++)
3078141cc406Sopenharmony_ci            {
3079141cc406Sopenharmony_ci                p_out = (s->cal_data.raw_data + k * s->cal_data.plane_stride + j * 6 + i * 2);
3080141cc406Sopenharmony_ci                *p_out = *p_in++; /* dark offset */
3081141cc406Sopenharmony_ci                p_out++;
3082141cc406Sopenharmony_ci                *p_out = *p_in++; /* gain */
3083141cc406Sopenharmony_ci            }
3084141cc406Sopenharmony_ci    }
3085141cc406Sopenharmony_ci
3086141cc406Sopenharmony_ci    ret = set_window(s, WINDOW_SENDCAL);
3087141cc406Sopenharmony_ci    if(ret){
3088141cc406Sopenharmony_ci        DBG (5, "finecal_send_cal: error sending setwindow\n");
3089141cc406Sopenharmony_ci        return ret;
3090141cc406Sopenharmony_ci    }
3091141cc406Sopenharmony_ci
3092141cc406Sopenharmony_ci    /*first unknown cal block*/
3093141cc406Sopenharmony_ci    cmd[0] = 0x1b;
3094141cc406Sopenharmony_ci    cmd[1] = 0xc3;
3095141cc406Sopenharmony_ci    stat[0] = 0;
3096141cc406Sopenharmony_ci    statLen = 1;
3097141cc406Sopenharmony_ci
3098141cc406Sopenharmony_ci    ret = do_cmd(
3099141cc406Sopenharmony_ci      s, 0,
3100141cc406Sopenharmony_ci      cmd, cmdLen,
3101141cc406Sopenharmony_ci      NULL, 0,
3102141cc406Sopenharmony_ci      stat, &statLen
3103141cc406Sopenharmony_ci    );
3104141cc406Sopenharmony_ci    if(ret){
3105141cc406Sopenharmony_ci        DBG (5, "finecal_send_cal: error sending c3 cmd\n");
3106141cc406Sopenharmony_ci        return ret;
3107141cc406Sopenharmony_ci    }
3108141cc406Sopenharmony_ci    if(stat[0] != 6){
3109141cc406Sopenharmony_ci        DBG (5, "finecal_send_cal: cmd bad c3 status?\n");
3110141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
3111141cc406Sopenharmony_ci    }
3112141cc406Sopenharmony_ci
3113141cc406Sopenharmony_ci    /*send header*/
3114141cc406Sopenharmony_ci    /*send payload*/
3115141cc406Sopenharmony_ci    statLen = 1;
3116141cc406Sopenharmony_ci
3117141cc406Sopenharmony_ci    ret = do_cmd(
3118141cc406Sopenharmony_ci      s, 0,
3119141cc406Sopenharmony_ci      s->sendCal1Header, s->sendCal1HeaderLen,
3120141cc406Sopenharmony_ci      s->cal_data.raw_data, s->cal_data.line_stride,
3121141cc406Sopenharmony_ci      stat, &statLen
3122141cc406Sopenharmony_ci    );
3123141cc406Sopenharmony_ci
3124141cc406Sopenharmony_ci    if(ret){
3125141cc406Sopenharmony_ci        DBG (5, "finecal_send_cal: error sending c3 payload\n");
3126141cc406Sopenharmony_ci        return ret;
3127141cc406Sopenharmony_ci    }
3128141cc406Sopenharmony_ci    if(stat[0] != 6){
3129141cc406Sopenharmony_ci        DBG (5, "finecal_send_cal: payload bad c3 status?\n");
3130141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
3131141cc406Sopenharmony_ci    }
3132141cc406Sopenharmony_ci
3133141cc406Sopenharmony_ci    /*second unknown cal block*/
3134141cc406Sopenharmony_ci    cmd[1] = 0xc4;
3135141cc406Sopenharmony_ci    statLen = 1;
3136141cc406Sopenharmony_ci
3137141cc406Sopenharmony_ci    ret = do_cmd(
3138141cc406Sopenharmony_ci      s, 0,
3139141cc406Sopenharmony_ci      cmd, cmdLen,
3140141cc406Sopenharmony_ci      NULL, 0,
3141141cc406Sopenharmony_ci      stat, &statLen
3142141cc406Sopenharmony_ci    );
3143141cc406Sopenharmony_ci
3144141cc406Sopenharmony_ci    if(ret){
3145141cc406Sopenharmony_ci        DBG (5, "finecal_send_cal: error sending c4 cmd\n");
3146141cc406Sopenharmony_ci        return ret;
3147141cc406Sopenharmony_ci    }
3148141cc406Sopenharmony_ci    if(stat[0] != 6){
3149141cc406Sopenharmony_ci        DBG (5, "finecal_send_cal: cmd bad c4 status?\n");
3150141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
3151141cc406Sopenharmony_ci    }
3152141cc406Sopenharmony_ci
3153141cc406Sopenharmony_ci    /*send header*/
3154141cc406Sopenharmony_ci    /*send payload*/
3155141cc406Sopenharmony_ci    statLen = 1;
3156141cc406Sopenharmony_ci
3157141cc406Sopenharmony_ci    ret = do_cmd(
3158141cc406Sopenharmony_ci      s, 0,
3159141cc406Sopenharmony_ci      s->sendCal2Header, s->sendCal2HeaderLen,
3160141cc406Sopenharmony_ci      s->cal_data.raw_data, s->cal_data.line_stride,
3161141cc406Sopenharmony_ci      stat, &statLen
3162141cc406Sopenharmony_ci    );
3163141cc406Sopenharmony_ci
3164141cc406Sopenharmony_ci    if(ret){
3165141cc406Sopenharmony_ci        DBG (5, "finecal_send_cal: error sending c4 payload\n");
3166141cc406Sopenharmony_ci        return ret;
3167141cc406Sopenharmony_ci    }
3168141cc406Sopenharmony_ci    if(stat[0] != 6){
3169141cc406Sopenharmony_ci        DBG (5, "finecal_send_cal: payload bad c4 status?\n");
3170141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
3171141cc406Sopenharmony_ci    }
3172141cc406Sopenharmony_ci
3173141cc406Sopenharmony_ci    DBG (10, "finecal_send_cal: finish\n");
3174141cc406Sopenharmony_ci    return ret;
3175141cc406Sopenharmony_ci}
3176141cc406Sopenharmony_ci
3177141cc406Sopenharmony_cistatic SANE_Status
3178141cc406Sopenharmony_cifinecal_get_line(struct scanner *s, struct image *img)
3179141cc406Sopenharmony_ci{
3180141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
3181141cc406Sopenharmony_ci
3182141cc406Sopenharmony_ci    size_t cmdLen = 2;
3183141cc406Sopenharmony_ci    unsigned char cmd[2];
3184141cc406Sopenharmony_ci
3185141cc406Sopenharmony_ci    size_t statLen = 1;
3186141cc406Sopenharmony_ci    unsigned char stat[2];
3187141cc406Sopenharmony_ci
3188141cc406Sopenharmony_ci    int round_offset = img->height / 2;
3189141cc406Sopenharmony_ci    int i, j, k;
3190141cc406Sopenharmony_ci
3191141cc406Sopenharmony_ci    DBG (10, "finecal_get_line: start\n");
3192141cc406Sopenharmony_ci
3193141cc406Sopenharmony_ci    /* ask for 16 lines */
3194141cc406Sopenharmony_ci    ret = set_window(s, WINDOW_FINECAL);
3195141cc406Sopenharmony_ci    if(ret){
3196141cc406Sopenharmony_ci        DBG (5, "finecal_get_line: error sending setwindowcal\n");
3197141cc406Sopenharmony_ci        return ret;
3198141cc406Sopenharmony_ci    }
3199141cc406Sopenharmony_ci
3200141cc406Sopenharmony_ci    /* send scan d2 command */
3201141cc406Sopenharmony_ci    cmd[0] = 0x1b;
3202141cc406Sopenharmony_ci    cmd[1] = 0xd2;
3203141cc406Sopenharmony_ci    stat[0] = 0;
3204141cc406Sopenharmony_ci    statLen = 1;
3205141cc406Sopenharmony_ci
3206141cc406Sopenharmony_ci    ret = do_cmd(
3207141cc406Sopenharmony_ci      s, 0,
3208141cc406Sopenharmony_ci      cmd, cmdLen,
3209141cc406Sopenharmony_ci      NULL, 0,
3210141cc406Sopenharmony_ci      stat, &statLen
3211141cc406Sopenharmony_ci    );
3212141cc406Sopenharmony_ci    if(ret){
3213141cc406Sopenharmony_ci        DBG (5, "finecal_get_line: error sending d2 cmd\n");
3214141cc406Sopenharmony_ci        return ret;
3215141cc406Sopenharmony_ci    }
3216141cc406Sopenharmony_ci    if(stat[0] != 6){
3217141cc406Sopenharmony_ci        DBG (5, "finecal_get_line: cmd bad d2 status?\n");
3218141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
3219141cc406Sopenharmony_ci    }
3220141cc406Sopenharmony_ci
3221141cc406Sopenharmony_ci    s->cal_image.image = img;
3222141cc406Sopenharmony_ci    update_transfer_totals(&s->cal_image);
3223141cc406Sopenharmony_ci
3224141cc406Sopenharmony_ci    while(!s->cal_image.done){
3225141cc406Sopenharmony_ci        ret = read_from_scanner(s,&s->cal_image);
3226141cc406Sopenharmony_ci        if(ret){
3227141cc406Sopenharmony_ci            DBG (5, "finecal_get_line: can't read from scanner\n");
3228141cc406Sopenharmony_ci            return ret;
3229141cc406Sopenharmony_ci        }
3230141cc406Sopenharmony_ci    }
3231141cc406Sopenharmony_ci    /* convert the raw data into normal packed pixel data */
3232141cc406Sopenharmony_ci    descramble_raw(s, &s->cal_image);
3233141cc406Sopenharmony_ci
3234141cc406Sopenharmony_ci    /* average the columns of pixels together and put the results in the top line(s) */
3235141cc406Sopenharmony_ci    for (i = 0; i < img->pages; i++)
3236141cc406Sopenharmony_ci    {
3237141cc406Sopenharmony_ci        unsigned char *linepix = img->buffer + i * img->width_bytes * img->height;
3238141cc406Sopenharmony_ci        unsigned char *avgpix = img->buffer + i * img->width_bytes;
3239141cc406Sopenharmony_ci        for (j = 0; j < img->width_bytes; j++)
3240141cc406Sopenharmony_ci        {
3241141cc406Sopenharmony_ci            int total = 0;
3242141cc406Sopenharmony_ci
3243141cc406Sopenharmony_ci            for (k = 0; k < img->height; k++)
3244141cc406Sopenharmony_ci                total += linepix[j + k * img->width_bytes];
3245141cc406Sopenharmony_ci
3246141cc406Sopenharmony_ci            avgpix[j] = (total + round_offset) / img->height;
3247141cc406Sopenharmony_ci        }
3248141cc406Sopenharmony_ci    }
3249141cc406Sopenharmony_ci
3250141cc406Sopenharmony_ci    DBG (10, "finecal_get_line: finish\n");
3251141cc406Sopenharmony_ci    return ret;
3252141cc406Sopenharmony_ci}
3253141cc406Sopenharmony_ci
3254141cc406Sopenharmony_ci/* roundf() is c99, so we provide our own, though this version won't return -0 */
3255141cc406Sopenharmony_cistatic float
3256141cc406Sopenharmony_ciround2(float x)
3257141cc406Sopenharmony_ci{
3258141cc406Sopenharmony_ci    return (float)(x >= 0.0) ? (int)(x+0.5) : (int)(x-0.5);
3259141cc406Sopenharmony_ci}
3260141cc406Sopenharmony_ci
3261141cc406Sopenharmony_cistatic SANE_Status
3262141cc406Sopenharmony_cifinecal(struct scanner *s)
3263141cc406Sopenharmony_ci{
3264141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
3265141cc406Sopenharmony_ci
3266141cc406Sopenharmony_ci    int max_pages;
3267141cc406Sopenharmony_ci    int gain_delta = 0xff - 0xbf;
3268141cc406Sopenharmony_ci    float *gain_slope, *last_error;
3269141cc406Sopenharmony_ci    int i, j, k, idx, try_count, cal_good;
3270141cc406Sopenharmony_ci
3271141cc406Sopenharmony_ci    DBG (10, "finecal: start\n");
3272141cc406Sopenharmony_ci
3273141cc406Sopenharmony_ci    if (s->model == MODEL_S300 || s->model == MODEL_S1300i) { /* S300, S1300 */
3274141cc406Sopenharmony_ci        max_pages = 2;
3275141cc406Sopenharmony_ci    }
3276141cc406Sopenharmony_ci    else /* fi-60f, S1100 */
3277141cc406Sopenharmony_ci    {
3278141cc406Sopenharmony_ci        max_pages = 1;
3279141cc406Sopenharmony_ci    }
3280141cc406Sopenharmony_ci
3281141cc406Sopenharmony_ci    /* set fine dark offset to 0 and fix all fine gains to lowest parameter (0xFF) */
3282141cc406Sopenharmony_ci    for (i = 0; i < s->sendcal.width_bytes * s->sendcal.pages / 2; i++)
3283141cc406Sopenharmony_ci    {
3284141cc406Sopenharmony_ci        s->sendcal.buffer[i*2] = 0;
3285141cc406Sopenharmony_ci        s->sendcal.buffer[i*2+1] = 0xff;
3286141cc406Sopenharmony_ci    }
3287141cc406Sopenharmony_ci    ret = finecal_send_cal(s);
3288141cc406Sopenharmony_ci    if(ret) return ret;
3289141cc406Sopenharmony_ci
3290141cc406Sopenharmony_ci    /* grab rows with lamp on */
3291141cc406Sopenharmony_ci    ret = lamp(s,1);
3292141cc406Sopenharmony_ci    if(ret){
3293141cc406Sopenharmony_ci        DBG (5, "finecal: error lamp on\n");
3294141cc406Sopenharmony_ci        return ret;
3295141cc406Sopenharmony_ci    }
3296141cc406Sopenharmony_ci
3297141cc406Sopenharmony_ci    /* read the low-gain average of 16 lines */
3298141cc406Sopenharmony_ci    ret = finecal_get_line(s, &s->darkcal);
3299141cc406Sopenharmony_ci    if(ret) return ret;
3300141cc406Sopenharmony_ci
3301141cc406Sopenharmony_ci    /* set fine dark offset to 0 and fine gain to a fixed higher-gain parameter (0xBF) */
3302141cc406Sopenharmony_ci    for (i = 0; i < s->sendcal.width_bytes * s->sendcal.pages / 2; i++)
3303141cc406Sopenharmony_ci    {
3304141cc406Sopenharmony_ci        s->sendcal.buffer[i*2] = 0;
3305141cc406Sopenharmony_ci        s->sendcal.buffer[i*2+1] = 0xbf;
3306141cc406Sopenharmony_ci    }
3307141cc406Sopenharmony_ci    ret = finecal_send_cal(s);
3308141cc406Sopenharmony_ci    if(ret) return ret;
3309141cc406Sopenharmony_ci
3310141cc406Sopenharmony_ci    /* read the high-gain average of 16 lines */
3311141cc406Sopenharmony_ci    ret = finecal_get_line(s, &s->lightcal);
3312141cc406Sopenharmony_ci    if(ret) return ret;
3313141cc406Sopenharmony_ci
3314141cc406Sopenharmony_ci    /* calculate the per pixel slope of pixel value delta over gain delta */
3315141cc406Sopenharmony_ci    gain_slope = malloc(s->lightcal.width_bytes * s->lightcal.pages * sizeof(float));
3316141cc406Sopenharmony_ci    if (!gain_slope)
3317141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
3318141cc406Sopenharmony_ci    idx = 0;
3319141cc406Sopenharmony_ci    for (i = 0; i < s->lightcal.pages; i++)
3320141cc406Sopenharmony_ci    {
3321141cc406Sopenharmony_ci        for (j = 0; j < s->lightcal.width_pix; j++)
3322141cc406Sopenharmony_ci        {
3323141cc406Sopenharmony_ci            for (k = 0; k < 3; k++)
3324141cc406Sopenharmony_ci            {
3325141cc406Sopenharmony_ci                int value_delta = s->lightcal.buffer[idx] - s->darkcal.buffer[idx];
3326141cc406Sopenharmony_ci                /* limit this slope to 1 or less, to avoid overshoot if the lightcal ref input is clipped at 255 */
3327141cc406Sopenharmony_ci                if (value_delta < gain_delta)
3328141cc406Sopenharmony_ci                    gain_slope[idx] = -1.0;
3329141cc406Sopenharmony_ci                else
3330141cc406Sopenharmony_ci                    gain_slope[idx] = (float) -gain_delta / value_delta;
3331141cc406Sopenharmony_ci                idx++;
3332141cc406Sopenharmony_ci            }
3333141cc406Sopenharmony_ci        }
3334141cc406Sopenharmony_ci    }
3335141cc406Sopenharmony_ci
3336141cc406Sopenharmony_ci    /* keep track of the last iteration's pixel error.  If we overshoot, we can reduce the value of the gain slope */
3337141cc406Sopenharmony_ci    last_error = malloc(s->lightcal.width_bytes * s->lightcal.pages * sizeof(float));
3338141cc406Sopenharmony_ci    if (!last_error)
3339141cc406Sopenharmony_ci    {
3340141cc406Sopenharmony_ci        free(gain_slope);
3341141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
3342141cc406Sopenharmony_ci    }
3343141cc406Sopenharmony_ci    for (i = 0; i < s->lightcal.width_bytes * s->lightcal.pages; i++)
3344141cc406Sopenharmony_ci        last_error[i] = 0.0;
3345141cc406Sopenharmony_ci
3346141cc406Sopenharmony_ci    /* fine calibration feedback loop */
3347141cc406Sopenharmony_ci    try_count = 8;
3348141cc406Sopenharmony_ci    while (try_count > 0)
3349141cc406Sopenharmony_ci    {
3350141cc406Sopenharmony_ci        int min_value[2][3], max_value[2][3];
3351141cc406Sopenharmony_ci        float avg_value[2][3], variance[2][3];
3352141cc406Sopenharmony_ci        int high_pegs = 0, low_pegs = 0;
3353141cc406Sopenharmony_ci        try_count--;
3354141cc406Sopenharmony_ci
3355141cc406Sopenharmony_ci        /* clear statistics arrays */
3356141cc406Sopenharmony_ci        for (i = 0; i < max_pages; i++)
3357141cc406Sopenharmony_ci        {
3358141cc406Sopenharmony_ci            for (k = 0; k < 3; k++)
3359141cc406Sopenharmony_ci            {
3360141cc406Sopenharmony_ci                min_value[i][k]  = 0xff;
3361141cc406Sopenharmony_ci                max_value[i][k]  = 0;
3362141cc406Sopenharmony_ci                avg_value[i][k]  = 0;
3363141cc406Sopenharmony_ci                variance[i][k]  = 0;
3364141cc406Sopenharmony_ci            }
3365141cc406Sopenharmony_ci        }
3366141cc406Sopenharmony_ci
3367141cc406Sopenharmony_ci        /* gather statistics and calculate new fine gain parameters based on observed error and the value/gain slope */
3368141cc406Sopenharmony_ci        idx = 0;
3369141cc406Sopenharmony_ci        for (i = 0; i < max_pages; i++)
3370141cc406Sopenharmony_ci        {
3371141cc406Sopenharmony_ci            for (j = 0; j < s->lightcal.width_pix; j++)
3372141cc406Sopenharmony_ci            {
3373141cc406Sopenharmony_ci                for (k = 0; k < 3; k++)
3374141cc406Sopenharmony_ci                {
3375141cc406Sopenharmony_ci                    int pixvalue = s->lightcal.buffer[idx];
3376141cc406Sopenharmony_ci                    float pixerror = (fine_gain_target[i] * s->white_factor[k] - pixvalue);
3377141cc406Sopenharmony_ci                    int oldgain = s->sendcal.buffer[idx * 2 + 1];
3378141cc406Sopenharmony_ci                    int newgain;
3379141cc406Sopenharmony_ci                    /* if we overshot the last correction, reduce the gain_slope */
3380141cc406Sopenharmony_ci                    if (pixerror * last_error[idx] < 0.0)
3381141cc406Sopenharmony_ci                        gain_slope[idx] *= 0.75;
3382141cc406Sopenharmony_ci                    last_error[idx] = pixerror;
3383141cc406Sopenharmony_ci                    /* set the new gain */
3384141cc406Sopenharmony_ci                    newgain = oldgain + (int) round2(pixerror * gain_slope[idx]);
3385141cc406Sopenharmony_ci                    if (newgain < 0)
3386141cc406Sopenharmony_ci                    {
3387141cc406Sopenharmony_ci                        low_pegs++;
3388141cc406Sopenharmony_ci                        s->sendcal.buffer[idx * 2 + 1] = 0;
3389141cc406Sopenharmony_ci                    }
3390141cc406Sopenharmony_ci                    else if (newgain > 0xff)
3391141cc406Sopenharmony_ci                    {
3392141cc406Sopenharmony_ci                        high_pegs++;
3393141cc406Sopenharmony_ci                        s->sendcal.buffer[idx * 2 + 1] = 0xff;
3394141cc406Sopenharmony_ci                    }
3395141cc406Sopenharmony_ci                    else
3396141cc406Sopenharmony_ci                        s->sendcal.buffer[idx * 2 + 1] = newgain;
3397141cc406Sopenharmony_ci                    /* update statistics */
3398141cc406Sopenharmony_ci                    min_value[i][k] = MIN(min_value[i][k], pixvalue);
3399141cc406Sopenharmony_ci                    max_value[i][k] = MAX(max_value[i][k], pixvalue);
3400141cc406Sopenharmony_ci                    avg_value[i][k] += pixerror;
3401141cc406Sopenharmony_ci                    variance[i][k] += (pixerror * pixerror);
3402141cc406Sopenharmony_ci                    idx++;
3403141cc406Sopenharmony_ci                }
3404141cc406Sopenharmony_ci            }
3405141cc406Sopenharmony_ci        }
3406141cc406Sopenharmony_ci        /* finish the statistics calculations */
3407141cc406Sopenharmony_ci        cal_good = 1;
3408141cc406Sopenharmony_ci        for (i = 0; i < max_pages; i++)
3409141cc406Sopenharmony_ci        {
3410141cc406Sopenharmony_ci            for (k = 0; k < 3; k++)
3411141cc406Sopenharmony_ci            {
3412141cc406Sopenharmony_ci                float sum = avg_value[i][k];
3413141cc406Sopenharmony_ci                float sum2 = variance[i][k];
3414141cc406Sopenharmony_ci                avg_value[i][k] = sum / s->lightcal.width_pix;
3415141cc406Sopenharmony_ci                variance[i][k] = ((sum2 - (sum * sum / s->lightcal.width_pix)) / s->lightcal.width_pix);
3416141cc406Sopenharmony_ci                /* if any color channel is too far out of whack, set cal_good to 0 so we'll iterate again */
3417141cc406Sopenharmony_ci                if (fabs(avg_value[i][k]) > 1.0 || variance[i][k] > 3.0)
3418141cc406Sopenharmony_ci                    cal_good = 0;
3419141cc406Sopenharmony_ci            }
3420141cc406Sopenharmony_ci        }
3421141cc406Sopenharmony_ci
3422141cc406Sopenharmony_ci        /* print debug info */
3423141cc406Sopenharmony_ci        DBG (15, "finecal: -------------------- Gain\n");
3424141cc406Sopenharmony_ci        DBG (15, "finecal: RGB Average Error - Front: (%.1f,%.1f,%.1f) - Back: (%.1f,%.1f,%.1f)\n",
3425141cc406Sopenharmony_ci             avg_value[0][0], avg_value[0][1], avg_value[0][2], avg_value[1][0], avg_value[1][1], avg_value[1][2]);
3426141cc406Sopenharmony_ci        DBG (15, "finecal: RGB Maximum - Front: (%i,%i,%i) - Back: (%i,%i,%i)\n",
3427141cc406Sopenharmony_ci             max_value[0][0], max_value[0][1], max_value[0][2], max_value[1][0], max_value[1][1], max_value[1][2]);
3428141cc406Sopenharmony_ci        DBG (15, "finecal: RGB Minimum - Front: (%i,%i,%i) - Back: (%i,%i,%i)\n",
3429141cc406Sopenharmony_ci             min_value[0][0], min_value[0][1], min_value[0][2], min_value[1][0], min_value[1][1], min_value[1][2]);
3430141cc406Sopenharmony_ci        DBG (15, "finecal: Variance - Front: (%.1f,%.1f,%.1f) - Back: (%.1f,%.1f,%.1f)\n",
3431141cc406Sopenharmony_ci             variance[0][0], variance[0][1], variance[0][2], variance[1][0], variance[1][1], variance[1][2]);
3432141cc406Sopenharmony_ci        DBG (15, "finecal: Pegged gain parameters - High (0xff): %i - Low (0): %i\n", high_pegs, low_pegs);
3433141cc406Sopenharmony_ci
3434141cc406Sopenharmony_ci        /* break out of the loop if our calibration is done */
3435141cc406Sopenharmony_ci        if (cal_good) break;
3436141cc406Sopenharmony_ci
3437141cc406Sopenharmony_ci        /* send the new calibration and read a new line */
3438141cc406Sopenharmony_ci        ret = finecal_send_cal(s);
3439141cc406Sopenharmony_ci        if(ret) { free(gain_slope); free(last_error); return ret; }
3440141cc406Sopenharmony_ci        ret = finecal_get_line(s, &s->lightcal);
3441141cc406Sopenharmony_ci        if(ret) { free(gain_slope); free(last_error); return ret; }
3442141cc406Sopenharmony_ci    }
3443141cc406Sopenharmony_ci
3444141cc406Sopenharmony_ci    /* release the memory for the reference slope data */
3445141cc406Sopenharmony_ci    free(gain_slope);
3446141cc406Sopenharmony_ci    free(last_error);
3447141cc406Sopenharmony_ci
3448141cc406Sopenharmony_ci    DBG (10, "finecal: finish\n");
3449141cc406Sopenharmony_ci    return ret;
3450141cc406Sopenharmony_ci}
3451141cc406Sopenharmony_ci
3452141cc406Sopenharmony_ci/*
3453141cc406Sopenharmony_ci * set scanner lamp brightness
3454141cc406Sopenharmony_ci */
3455141cc406Sopenharmony_cistatic SANE_Status
3456141cc406Sopenharmony_cilamp(struct scanner *s, unsigned char set)
3457141cc406Sopenharmony_ci{
3458141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
3459141cc406Sopenharmony_ci    unsigned char cmd[2];
3460141cc406Sopenharmony_ci    size_t cmdLen = 2;
3461141cc406Sopenharmony_ci    unsigned char stat[1];
3462141cc406Sopenharmony_ci    size_t statLen = 1;
3463141cc406Sopenharmony_ci
3464141cc406Sopenharmony_ci    DBG (10, "lamp: start (%d)\n", set);
3465141cc406Sopenharmony_ci
3466141cc406Sopenharmony_ci    /*send cmd*/
3467141cc406Sopenharmony_ci    cmd[0] = 0x1b;
3468141cc406Sopenharmony_ci    cmd[1] = 0xd0;
3469141cc406Sopenharmony_ci
3470141cc406Sopenharmony_ci    ret = do_cmd(
3471141cc406Sopenharmony_ci      s, 0,
3472141cc406Sopenharmony_ci      cmd, cmdLen,
3473141cc406Sopenharmony_ci      NULL, 0,
3474141cc406Sopenharmony_ci      stat, &statLen
3475141cc406Sopenharmony_ci    );
3476141cc406Sopenharmony_ci    if(ret){
3477141cc406Sopenharmony_ci        DBG (5, "lamp: error sending cmd\n");
3478141cc406Sopenharmony_ci        return ret;
3479141cc406Sopenharmony_ci    }
3480141cc406Sopenharmony_ci    if(stat[0] != 6){
3481141cc406Sopenharmony_ci        DBG (5, "lamp: cmd bad status?\n");
3482141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
3483141cc406Sopenharmony_ci    }
3484141cc406Sopenharmony_ci
3485141cc406Sopenharmony_ci    /*send payload*/
3486141cc406Sopenharmony_ci    cmd[0] = set;
3487141cc406Sopenharmony_ci    cmdLen = 1;
3488141cc406Sopenharmony_ci    statLen = 1;
3489141cc406Sopenharmony_ci
3490141cc406Sopenharmony_ci    ret = do_cmd(
3491141cc406Sopenharmony_ci      s, 0,
3492141cc406Sopenharmony_ci      cmd, cmdLen,
3493141cc406Sopenharmony_ci      NULL, 0,
3494141cc406Sopenharmony_ci      stat, &statLen
3495141cc406Sopenharmony_ci    );
3496141cc406Sopenharmony_ci    if(ret){
3497141cc406Sopenharmony_ci        DBG (5, "lamp: error sending payload\n");
3498141cc406Sopenharmony_ci        return ret;
3499141cc406Sopenharmony_ci    }
3500141cc406Sopenharmony_ci    if(stat[0] != 6){
3501141cc406Sopenharmony_ci        DBG (5, "lamp: payload bad status?\n");
3502141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
3503141cc406Sopenharmony_ci    }
3504141cc406Sopenharmony_ci
3505141cc406Sopenharmony_ci    DBG (10, "lamp: finish\n");
3506141cc406Sopenharmony_ci    return ret;
3507141cc406Sopenharmony_ci}
3508141cc406Sopenharmony_ci
3509141cc406Sopenharmony_cistatic SANE_Status
3510141cc406Sopenharmony_ciset_window(struct scanner *s, int window)
3511141cc406Sopenharmony_ci{
3512141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
3513141cc406Sopenharmony_ci
3514141cc406Sopenharmony_ci    unsigned char cmd[] = {0x1b, 0xd1};
3515141cc406Sopenharmony_ci    size_t cmdLen = sizeof(cmd);
3516141cc406Sopenharmony_ci    unsigned char stat[] = {0};
3517141cc406Sopenharmony_ci    size_t statLen = sizeof(stat);
3518141cc406Sopenharmony_ci    unsigned char * payload;
3519141cc406Sopenharmony_ci    size_t paylen = SET_WINDOW_LEN;
3520141cc406Sopenharmony_ci
3521141cc406Sopenharmony_ci    DBG (10, "set_window: start, window %d\n",window);
3522141cc406Sopenharmony_ci
3523141cc406Sopenharmony_ci    switch (window) {
3524141cc406Sopenharmony_ci      case WINDOW_COARSECAL:
3525141cc406Sopenharmony_ci        payload = s->setWindowCoarseCal;
3526141cc406Sopenharmony_ci	paylen  = s->setWindowCoarseCalLen;
3527141cc406Sopenharmony_ci	break;
3528141cc406Sopenharmony_ci      case WINDOW_FINECAL:
3529141cc406Sopenharmony_ci        payload = s->setWindowFineCal;
3530141cc406Sopenharmony_ci	paylen  = s->setWindowFineCalLen;
3531141cc406Sopenharmony_ci	break;
3532141cc406Sopenharmony_ci      case WINDOW_SENDCAL:
3533141cc406Sopenharmony_ci        payload = s->setWindowSendCal;
3534141cc406Sopenharmony_ci	paylen  = s->setWindowSendCalLen;
3535141cc406Sopenharmony_ci	break;
3536141cc406Sopenharmony_ci      case WINDOW_SCAN:
3537141cc406Sopenharmony_ci        payload = s->setWindowScan;
3538141cc406Sopenharmony_ci	paylen  = s->setWindowScanLen;
3539141cc406Sopenharmony_ci        set_SW_ypix(payload,s->fullscan.height);
3540141cc406Sopenharmony_ci	break;
3541141cc406Sopenharmony_ci      default:
3542141cc406Sopenharmony_ci        DBG (5, "set_window: unknown window\n");
3543141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
3544141cc406Sopenharmony_ci    }
3545141cc406Sopenharmony_ci
3546141cc406Sopenharmony_ci    /*send cmd*/
3547141cc406Sopenharmony_ci    ret = do_cmd(
3548141cc406Sopenharmony_ci      s, 0,
3549141cc406Sopenharmony_ci      cmd, cmdLen,
3550141cc406Sopenharmony_ci      NULL, 0,
3551141cc406Sopenharmony_ci      stat, &statLen
3552141cc406Sopenharmony_ci    );
3553141cc406Sopenharmony_ci    if(ret){
3554141cc406Sopenharmony_ci        DBG (5, "set_window: error sending cmd\n");
3555141cc406Sopenharmony_ci        return ret;
3556141cc406Sopenharmony_ci    }
3557141cc406Sopenharmony_ci    if(stat[0] != 6){
3558141cc406Sopenharmony_ci        DBG (5, "set_window: cmd bad status?\n");
3559141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
3560141cc406Sopenharmony_ci    }
3561141cc406Sopenharmony_ci
3562141cc406Sopenharmony_ci    /*send payload*/
3563141cc406Sopenharmony_ci    statLen = 1;
3564141cc406Sopenharmony_ci
3565141cc406Sopenharmony_ci    ret = do_cmd(
3566141cc406Sopenharmony_ci      s, 0,
3567141cc406Sopenharmony_ci      payload, paylen,
3568141cc406Sopenharmony_ci      NULL, 0,
3569141cc406Sopenharmony_ci      stat, &statLen
3570141cc406Sopenharmony_ci    );
3571141cc406Sopenharmony_ci    if(ret){
3572141cc406Sopenharmony_ci        DBG (5, "set_window: error sending payload\n");
3573141cc406Sopenharmony_ci        return ret;
3574141cc406Sopenharmony_ci    }
3575141cc406Sopenharmony_ci    if(stat[0] != 6){
3576141cc406Sopenharmony_ci        DBG (5, "set_window: payload bad status?\n");
3577141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
3578141cc406Sopenharmony_ci    }
3579141cc406Sopenharmony_ci
3580141cc406Sopenharmony_ci    DBG (10, "set_window: finish\n");
3581141cc406Sopenharmony_ci    return ret;
3582141cc406Sopenharmony_ci}
3583141cc406Sopenharmony_ci
3584141cc406Sopenharmony_ci/* instead of internal brightness/contrast/gamma
3585141cc406Sopenharmony_ci   scanners uses 12bit x 12bit LUT
3586141cc406Sopenharmony_ci   default is linear table of slope 1
3587141cc406Sopenharmony_ci   brightness and contrast inputs are -127 to +127
3588141cc406Sopenharmony_ci
3589141cc406Sopenharmony_ci   contrast rotates slope of line around central input val
3590141cc406Sopenharmony_ci
3591141cc406Sopenharmony_ci       high           low
3592141cc406Sopenharmony_ci       .       x      .
3593141cc406Sopenharmony_ci       .      x       .         xx
3594141cc406Sopenharmony_ci   out .     x        . xxxxxxxx
3595141cc406Sopenharmony_ci       .    x         xx
3596141cc406Sopenharmony_ci       ....x.......   ............
3597141cc406Sopenharmony_ci            in             in
3598141cc406Sopenharmony_ci
3599141cc406Sopenharmony_ci   then brightness moves line vertically, and clamps to 8bit
3600141cc406Sopenharmony_ci
3601141cc406Sopenharmony_ci       bright         dark
3602141cc406Sopenharmony_ci       .   xxxxxxxx   .
3603141cc406Sopenharmony_ci       . x            .
3604141cc406Sopenharmony_ci   out x              .          x
3605141cc406Sopenharmony_ci       .              .        x
3606141cc406Sopenharmony_ci       ............   xxxxxxxx....
3607141cc406Sopenharmony_ci            in             in
3608141cc406Sopenharmony_ci  */
3609141cc406Sopenharmony_cistatic SANE_Status
3610141cc406Sopenharmony_cisend_lut (struct scanner *s)
3611141cc406Sopenharmony_ci{
3612141cc406Sopenharmony_ci    SANE_Status ret=SANE_STATUS_GOOD;
3613141cc406Sopenharmony_ci
3614141cc406Sopenharmony_ci    unsigned char cmd[] = {0x1b, 0xc5};
3615141cc406Sopenharmony_ci    size_t cmdLen = 2;
3616141cc406Sopenharmony_ci    unsigned char stat[1];
3617141cc406Sopenharmony_ci    size_t statLen = 1;
3618141cc406Sopenharmony_ci    unsigned char *out;
3619141cc406Sopenharmony_ci    size_t outLen;
3620141cc406Sopenharmony_ci
3621141cc406Sopenharmony_ci    int i, j;
3622141cc406Sopenharmony_ci    double b, slope, offset;
3623141cc406Sopenharmony_ci    int width;
3624141cc406Sopenharmony_ci    int height;
3625141cc406Sopenharmony_ci
3626141cc406Sopenharmony_ci    DBG (10, "send_lut: start\n");
3627141cc406Sopenharmony_ci
3628141cc406Sopenharmony_ci    if (s->model == MODEL_S1100){
3629141cc406Sopenharmony_ci        outLen = 0x200;
3630141cc406Sopenharmony_ci        width = outLen / 2; /* 1 color, 2 bytes */
3631141cc406Sopenharmony_ci        height = width; /* square table */
3632141cc406Sopenharmony_ci    }
3633141cc406Sopenharmony_ci    else if (s->model == MODEL_FI65F){
3634141cc406Sopenharmony_ci        outLen = 0x600;
3635141cc406Sopenharmony_ci        width = outLen / 6; /* 3 color, 2 bytes */
3636141cc406Sopenharmony_ci        height = width; /* square table */
3637141cc406Sopenharmony_ci    }
3638141cc406Sopenharmony_ci    else {
3639141cc406Sopenharmony_ci        outLen = 0x6000;
3640141cc406Sopenharmony_ci        width = outLen / 6; /* 3 colors, 2 bytes */
3641141cc406Sopenharmony_ci        height = width; /* square table */
3642141cc406Sopenharmony_ci    }
3643141cc406Sopenharmony_ci    out = ( unsigned char *)malloc(outLen*sizeof(unsigned char));
3644141cc406Sopenharmony_ci    if (out == NULL){
3645141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
3646141cc406Sopenharmony_ci    }
3647141cc406Sopenharmony_ci
3648141cc406Sopenharmony_ci    /* contrast is converted to a slope [0,90] degrees:
3649141cc406Sopenharmony_ci     * first [-127,127] to [0,254] then to [0,1]
3650141cc406Sopenharmony_ci     * then multiply by PI/2 to convert to radians
3651141cc406Sopenharmony_ci     * then take the tangent to get slope (T.O.A)
3652141cc406Sopenharmony_ci     * then multiply by the normal linear slope
3653141cc406Sopenharmony_ci     * because the table may not be square, i.e. 1024x256*/
3654141cc406Sopenharmony_ci    slope = tan(((double)s->contrast+127)/254 * M_PI/2);
3655141cc406Sopenharmony_ci
3656141cc406Sopenharmony_ci    /* contrast slope must stay centered, so figure
3657141cc406Sopenharmony_ci     * out vertical offset at central input value */
3658141cc406Sopenharmony_ci    offset = height/2 - slope*width/2;
3659141cc406Sopenharmony_ci
3660141cc406Sopenharmony_ci    /* convert the user brightness setting (-127 to +127)
3661141cc406Sopenharmony_ci     * into a scale that covers the range required
3662141cc406Sopenharmony_ci     * to slide the contrast curve entirely off the table */
3663141cc406Sopenharmony_ci    b = ((double)s->brightness/127) * (slope*(width-1) + offset);
3664141cc406Sopenharmony_ci
3665141cc406Sopenharmony_ci    DBG (15, "send_lut: %d %f %d %f %f\n", s->brightness, b,
3666141cc406Sopenharmony_ci      s->contrast, slope, offset);
3667141cc406Sopenharmony_ci
3668141cc406Sopenharmony_ci    for(i=0;i<width;i++){
3669141cc406Sopenharmony_ci      j=slope*i + offset + b;
3670141cc406Sopenharmony_ci
3671141cc406Sopenharmony_ci      j = MAX(j, 0);
3672141cc406Sopenharmony_ci      j = MIN(j, height-1);
3673141cc406Sopenharmony_ci
3674141cc406Sopenharmony_ci        if (s->model == MODEL_S1100){
3675141cc406Sopenharmony_ci            /*only one table, be order*/
3676141cc406Sopenharmony_ci            out[i*2] = (j >> 8) & 0xff;
3677141cc406Sopenharmony_ci            out[i*2+1] = j & 0xff;
3678141cc406Sopenharmony_ci        }
3679141cc406Sopenharmony_ci        else if (s->model == MODEL_FI65F){
3680141cc406Sopenharmony_ci            /*first table, be order*/
3681141cc406Sopenharmony_ci            out[i*2] = (j >> 8) & 0xff;
3682141cc406Sopenharmony_ci            out[i*2+1] = j & 0xff;
3683141cc406Sopenharmony_ci
3684141cc406Sopenharmony_ci            /*second table, be order*/
3685141cc406Sopenharmony_ci            out[width*2 + i*2] = (j >> 8) & 0xff;
3686141cc406Sopenharmony_ci            out[width*2 + i*2+1] = j & 0xff;
3687141cc406Sopenharmony_ci
3688141cc406Sopenharmony_ci            /*third table, be order*/
3689141cc406Sopenharmony_ci            out[width*4 + i*2] = (j >> 8) & 0xff;
3690141cc406Sopenharmony_ci            out[width*4 + i*2+1] = j & 0xff;
3691141cc406Sopenharmony_ci        }
3692141cc406Sopenharmony_ci        else {
3693141cc406Sopenharmony_ci            /*first table, le order*/
3694141cc406Sopenharmony_ci            out[i*2] = j & 0xff;
3695141cc406Sopenharmony_ci            out[i*2+1] = (j >> 8) & 0x0f;
3696141cc406Sopenharmony_ci
3697141cc406Sopenharmony_ci            /*second table, le order*/
3698141cc406Sopenharmony_ci            out[width*2 + i*2] = j & 0xff;
3699141cc406Sopenharmony_ci            out[width*2 + i*2+1] = (j >> 8) & 0x0f;
3700141cc406Sopenharmony_ci
3701141cc406Sopenharmony_ci            /*third table, le order*/
3702141cc406Sopenharmony_ci            out[width*4 + i*2] = j & 0xff;
3703141cc406Sopenharmony_ci            out[width*4 + i*2+1] = (j >> 8) & 0x0f;
3704141cc406Sopenharmony_ci        }
3705141cc406Sopenharmony_ci    }
3706141cc406Sopenharmony_ci
3707141cc406Sopenharmony_ci    ret = do_cmd(
3708141cc406Sopenharmony_ci      s, 0,
3709141cc406Sopenharmony_ci      cmd, cmdLen,
3710141cc406Sopenharmony_ci      NULL, 0,
3711141cc406Sopenharmony_ci      stat, &statLen
3712141cc406Sopenharmony_ci    );
3713141cc406Sopenharmony_ci    if(ret){
3714141cc406Sopenharmony_ci        DBG (5, "send_lut: error sending cmd\n");
3715141cc406Sopenharmony_ci        return ret;
3716141cc406Sopenharmony_ci    }
3717141cc406Sopenharmony_ci    if(stat[0] != 6){
3718141cc406Sopenharmony_ci        DBG (5, "send_lut: cmd bad status?\n");
3719141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
3720141cc406Sopenharmony_ci    }
3721141cc406Sopenharmony_ci
3722141cc406Sopenharmony_ci    statLen = 1;
3723141cc406Sopenharmony_ci    ret = do_cmd(
3724141cc406Sopenharmony_ci      s, 0,
3725141cc406Sopenharmony_ci      out, outLen,
3726141cc406Sopenharmony_ci      NULL, 0,
3727141cc406Sopenharmony_ci      stat, &statLen
3728141cc406Sopenharmony_ci    );
3729141cc406Sopenharmony_ci    if(ret){
3730141cc406Sopenharmony_ci        DBG (5, "send_lut: error sending out\n");
3731141cc406Sopenharmony_ci        return ret;
3732141cc406Sopenharmony_ci    }
3733141cc406Sopenharmony_ci    if(stat[0] != 6){
3734141cc406Sopenharmony_ci        DBG (5, "send_lut: out bad status?\n");
3735141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
3736141cc406Sopenharmony_ci    }
3737141cc406Sopenharmony_ci
3738141cc406Sopenharmony_ci    DBG (10, "send_lut: finish\n");
3739141cc406Sopenharmony_ci
3740141cc406Sopenharmony_ci    return ret;
3741141cc406Sopenharmony_ci}
3742141cc406Sopenharmony_ci
3743141cc406Sopenharmony_cistatic SANE_Status
3744141cc406Sopenharmony_ciget_hardware_status (struct scanner *s)
3745141cc406Sopenharmony_ci{
3746141cc406Sopenharmony_ci  SANE_Status ret = SANE_STATUS_GOOD;
3747141cc406Sopenharmony_ci
3748141cc406Sopenharmony_ci  DBG (10, "get_hardware_status: start\n");
3749141cc406Sopenharmony_ci
3750141cc406Sopenharmony_ci  /* only run this once every second */
3751141cc406Sopenharmony_ci  if (s->last_ghs < time(NULL)) {
3752141cc406Sopenharmony_ci
3753141cc406Sopenharmony_ci    unsigned char cmd[2];
3754141cc406Sopenharmony_ci    size_t cmdLen = sizeof(cmd);
3755141cc406Sopenharmony_ci    unsigned char pay[4];
3756141cc406Sopenharmony_ci    size_t payLen = sizeof(pay);
3757141cc406Sopenharmony_ci
3758141cc406Sopenharmony_ci    DBG (15, "get_hardware_status: running\n");
3759141cc406Sopenharmony_ci
3760141cc406Sopenharmony_ci    cmd[0] = 0x1b;
3761141cc406Sopenharmony_ci    cmd[1] = 0x33;
3762141cc406Sopenharmony_ci
3763141cc406Sopenharmony_ci    ret = do_cmd(
3764141cc406Sopenharmony_ci      s, 0,
3765141cc406Sopenharmony_ci      cmd, cmdLen,
3766141cc406Sopenharmony_ci      NULL, 0,
3767141cc406Sopenharmony_ci      pay, &payLen
3768141cc406Sopenharmony_ci    );
3769141cc406Sopenharmony_ci    if(ret){
3770141cc406Sopenharmony_ci        DBG (5, "get_hardware_status: error sending cmd\n");
3771141cc406Sopenharmony_ci        return ret;
3772141cc406Sopenharmony_ci    }
3773141cc406Sopenharmony_ci
3774141cc406Sopenharmony_ci    hexdump(5,"ghspayload: ", pay, payLen);
3775141cc406Sopenharmony_ci
3776141cc406Sopenharmony_ci    s->last_ghs = time(NULL);
3777141cc406Sopenharmony_ci
3778141cc406Sopenharmony_ci    s->hw_top      =  ((pay[0] >> 7) & 0x01);
3779141cc406Sopenharmony_ci    s->hw_hopper   = !((pay[0] >> 6) & 0x01);
3780141cc406Sopenharmony_ci    s->hw_adf_open =  ((pay[0] >> 5) & 0x01);
3781141cc406Sopenharmony_ci
3782141cc406Sopenharmony_ci    s->hw_sleep    =  ((pay[1] >> 7) & 0x01);
3783141cc406Sopenharmony_ci    s->hw_scan_sw  =  ((pay[1] >> 0) & 0x01);
3784141cc406Sopenharmony_ci  }
3785141cc406Sopenharmony_ci
3786141cc406Sopenharmony_ci  DBG (10, "get_hardware_status: finish\n");
3787141cc406Sopenharmony_ci
3788141cc406Sopenharmony_ci  return ret;
3789141cc406Sopenharmony_ci}
3790141cc406Sopenharmony_ci
3791141cc406Sopenharmony_cistatic SANE_Status
3792141cc406Sopenharmony_ciobject_position(struct scanner *s, int ingest)
3793141cc406Sopenharmony_ci{
3794141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
3795141cc406Sopenharmony_ci    int i;
3796141cc406Sopenharmony_ci    unsigned char cmd[2];
3797141cc406Sopenharmony_ci    size_t cmdLen = sizeof(cmd);
3798141cc406Sopenharmony_ci    unsigned char stat[1];
3799141cc406Sopenharmony_ci    size_t statLen = sizeof(stat);
3800141cc406Sopenharmony_ci    unsigned char pay[2];
3801141cc406Sopenharmony_ci    size_t payLen = sizeof(pay);
3802141cc406Sopenharmony_ci
3803141cc406Sopenharmony_ci    DBG (10, "object_position: start\n");
3804141cc406Sopenharmony_ci
3805141cc406Sopenharmony_ci    i = (ingest)?5:1;
3806141cc406Sopenharmony_ci
3807141cc406Sopenharmony_ci    while(i--){
3808141cc406Sopenharmony_ci        /*send paper load cmd*/
3809141cc406Sopenharmony_ci        cmd[0] = 0x1b;
3810141cc406Sopenharmony_ci        cmd[1] = 0xd4;
3811141cc406Sopenharmony_ci        statLen = 1;
3812141cc406Sopenharmony_ci
3813141cc406Sopenharmony_ci        ret = do_cmd(
3814141cc406Sopenharmony_ci          s, 0,
3815141cc406Sopenharmony_ci          cmd, cmdLen,
3816141cc406Sopenharmony_ci          NULL, 0,
3817141cc406Sopenharmony_ci          stat, &statLen
3818141cc406Sopenharmony_ci        );
3819141cc406Sopenharmony_ci        if(ret){
3820141cc406Sopenharmony_ci            DBG (5, "object_position: error sending cmd\n");
3821141cc406Sopenharmony_ci            return ret;
3822141cc406Sopenharmony_ci        }
3823141cc406Sopenharmony_ci        if(stat[0] != 6){
3824141cc406Sopenharmony_ci            DBG (5, "object_position: cmd bad status? %d\n",stat[0]);
3825141cc406Sopenharmony_ci            continue;
3826141cc406Sopenharmony_ci        }
3827141cc406Sopenharmony_ci
3828141cc406Sopenharmony_ci        /*send payload*/
3829141cc406Sopenharmony_ci        statLen = 1;
3830141cc406Sopenharmony_ci        payLen = 1;
3831141cc406Sopenharmony_ci        pay[0] = ingest;
3832141cc406Sopenharmony_ci
3833141cc406Sopenharmony_ci        ret = do_cmd(
3834141cc406Sopenharmony_ci          s, 0,
3835141cc406Sopenharmony_ci          pay, payLen,
3836141cc406Sopenharmony_ci          NULL, 0,
3837141cc406Sopenharmony_ci          stat, &statLen
3838141cc406Sopenharmony_ci        );
3839141cc406Sopenharmony_ci        if(ret){
3840141cc406Sopenharmony_ci            DBG (5, "object_position: error sending payload\n");
3841141cc406Sopenharmony_ci            return ret;
3842141cc406Sopenharmony_ci        }
3843141cc406Sopenharmony_ci        if(stat[0] == 6){
3844141cc406Sopenharmony_ci            DBG (5, "object_position: found paper?\n");
3845141cc406Sopenharmony_ci            break;
3846141cc406Sopenharmony_ci        }
3847141cc406Sopenharmony_ci        else if(stat[0] == 0x15 || stat[0] == 0){
3848141cc406Sopenharmony_ci            DBG (5, "object_position: no paper?\n");
3849141cc406Sopenharmony_ci            ret=SANE_STATUS_NO_DOCS;
3850141cc406Sopenharmony_ci	    continue;
3851141cc406Sopenharmony_ci        }
3852141cc406Sopenharmony_ci        else{
3853141cc406Sopenharmony_ci            DBG (5, "object_position: payload bad status?\n");
3854141cc406Sopenharmony_ci            return SANE_STATUS_IO_ERROR;
3855141cc406Sopenharmony_ci        }
3856141cc406Sopenharmony_ci    }
3857141cc406Sopenharmony_ci
3858141cc406Sopenharmony_ci    DBG (10, "object_position: finish\n");
3859141cc406Sopenharmony_ci    return ret;
3860141cc406Sopenharmony_ci}
3861141cc406Sopenharmony_ci
3862141cc406Sopenharmony_cistatic SANE_Status
3863141cc406Sopenharmony_ciscan(struct scanner *s)
3864141cc406Sopenharmony_ci{
3865141cc406Sopenharmony_ci    SANE_Status ret=SANE_STATUS_GOOD;
3866141cc406Sopenharmony_ci    unsigned char cmd[] = {0x1b, 0xd2};
3867141cc406Sopenharmony_ci    size_t cmdLen = 2;
3868141cc406Sopenharmony_ci    unsigned char stat[1];
3869141cc406Sopenharmony_ci    size_t statLen = 1;
3870141cc406Sopenharmony_ci
3871141cc406Sopenharmony_ci    DBG (10, "scan: start\n");
3872141cc406Sopenharmony_ci
3873141cc406Sopenharmony_ci    if(s->model == MODEL_S300 || s->model == MODEL_S1100 || s->model == MODEL_S1300i){
3874141cc406Sopenharmony_ci        cmd[1] = 0xd6;
3875141cc406Sopenharmony_ci    }
3876141cc406Sopenharmony_ci
3877141cc406Sopenharmony_ci    ret = do_cmd(
3878141cc406Sopenharmony_ci      s, 0,
3879141cc406Sopenharmony_ci      cmd, cmdLen,
3880141cc406Sopenharmony_ci      NULL, 0,
3881141cc406Sopenharmony_ci      stat, &statLen
3882141cc406Sopenharmony_ci    );
3883141cc406Sopenharmony_ci    if(ret){
3884141cc406Sopenharmony_ci        DBG (5, "scan: error sending cmd\n");
3885141cc406Sopenharmony_ci        return ret;
3886141cc406Sopenharmony_ci    }
3887141cc406Sopenharmony_ci    if(stat[0] != 6){
3888141cc406Sopenharmony_ci        DBG (5, "scan: cmd bad status?\n");
3889141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
3890141cc406Sopenharmony_ci    }
3891141cc406Sopenharmony_ci
3892141cc406Sopenharmony_ci    DBG (10, "scan: finish\n");
3893141cc406Sopenharmony_ci
3894141cc406Sopenharmony_ci    return ret;
3895141cc406Sopenharmony_ci}
3896141cc406Sopenharmony_ci
3897141cc406Sopenharmony_ci/*
3898141cc406Sopenharmony_ci * Called by SANE to read data.
3899141cc406Sopenharmony_ci *
3900141cc406Sopenharmony_ci * From the SANE spec:
3901141cc406Sopenharmony_ci * This function is used to read image data from the device
3902141cc406Sopenharmony_ci * represented by handle h.  Argument buf is a pointer to a memory
3903141cc406Sopenharmony_ci * area that is at least maxlen bytes long.  The number of bytes
3904141cc406Sopenharmony_ci * returned is stored in *len. A backend must set this to zero when
3905141cc406Sopenharmony_ci * the call fails (i.e., when a status other than SANE_STATUS_GOOD is
3906141cc406Sopenharmony_ci * returned).
3907141cc406Sopenharmony_ci *
3908141cc406Sopenharmony_ci * When the call succeeds, the number of bytes returned can be
3909141cc406Sopenharmony_ci * anywhere in the range from 0 to maxlen bytes.
3910141cc406Sopenharmony_ci */
3911141cc406Sopenharmony_ciSANE_Status
3912141cc406Sopenharmony_cisane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int * len)
3913141cc406Sopenharmony_ci{
3914141cc406Sopenharmony_ci    struct scanner *s = (struct scanner *) handle;
3915141cc406Sopenharmony_ci    SANE_Status ret=SANE_STATUS_GOOD;
3916141cc406Sopenharmony_ci    struct page * page;
3917141cc406Sopenharmony_ci
3918141cc406Sopenharmony_ci    DBG (10, "sane_read: start si:%d len:%d max:%d\n",s->side,*len,max_len);
3919141cc406Sopenharmony_ci
3920141cc406Sopenharmony_ci    *len = 0;
3921141cc406Sopenharmony_ci
3922141cc406Sopenharmony_ci    /* cancelled? */
3923141cc406Sopenharmony_ci    if(!s->started){
3924141cc406Sopenharmony_ci        DBG (5, "sane_read: call sane_start first\n");
3925141cc406Sopenharmony_ci        return SANE_STATUS_CANCELLED;
3926141cc406Sopenharmony_ci    }
3927141cc406Sopenharmony_ci
3928141cc406Sopenharmony_ci    page = &s->pages[s->side];
3929141cc406Sopenharmony_ci
3930141cc406Sopenharmony_ci    /* have sent all of current buffer */
3931141cc406Sopenharmony_ci    if(s->fullscan.done && page->done){
3932141cc406Sopenharmony_ci        DBG (10, "sane_read: returning eof\n");
3933141cc406Sopenharmony_ci
3934141cc406Sopenharmony_ci      /*S1100 needs help to turn off button*/
3935141cc406Sopenharmony_ci      if(s->model == MODEL_S1100){
3936141cc406Sopenharmony_ci        usleep(15000);
3937141cc406Sopenharmony_ci
3938141cc406Sopenharmony_ci        /* eject paper */
3939141cc406Sopenharmony_ci        ret = object_position(s,EPJITSU_PAPER_EJECT);
3940141cc406Sopenharmony_ci        if (ret != SANE_STATUS_GOOD && ret != SANE_STATUS_NO_DOCS) {
3941141cc406Sopenharmony_ci          DBG (5, "sane_read: ERROR: failed to eject\n");
3942141cc406Sopenharmony_ci          return ret;
3943141cc406Sopenharmony_ci        }
3944141cc406Sopenharmony_ci
3945141cc406Sopenharmony_ci        /* reset flashing button? */
3946141cc406Sopenharmony_ci        ret = six5(s);
3947141cc406Sopenharmony_ci        if (ret != SANE_STATUS_GOOD) {
3948141cc406Sopenharmony_ci          DBG (5, "sane_read: ERROR: failed to six5\n");
3949141cc406Sopenharmony_ci          return ret;
3950141cc406Sopenharmony_ci        }
3951141cc406Sopenharmony_ci      }
3952141cc406Sopenharmony_ci
3953141cc406Sopenharmony_ci      return SANE_STATUS_EOF;
3954141cc406Sopenharmony_ci    }
3955141cc406Sopenharmony_ci
3956141cc406Sopenharmony_ci    /* scan not finished, get more into block buffer */
3957141cc406Sopenharmony_ci    if(!s->fullscan.done)
3958141cc406Sopenharmony_ci    {
3959141cc406Sopenharmony_ci        /* block buffer currently empty, clean up */
3960141cc406Sopenharmony_ci        if(!s->block_xfr.rx_bytes)
3961141cc406Sopenharmony_ci        {
3962141cc406Sopenharmony_ci            /* block buffer bigger than remainder of scan, shrink block */
3963141cc406Sopenharmony_ci            int remainTotal = s->fullscan.total_bytes - s->fullscan.rx_bytes;
3964141cc406Sopenharmony_ci            if(remainTotal < s->block_xfr.total_bytes)
3965141cc406Sopenharmony_ci            {
3966141cc406Sopenharmony_ci                DBG (15, "sane_read: shrinking block to %lu\n", (unsigned long)remainTotal);
3967141cc406Sopenharmony_ci                s->block_xfr.total_bytes = remainTotal;
3968141cc406Sopenharmony_ci            }
3969141cc406Sopenharmony_ci            /* send d3 cmd for S300, S1100, S1300 */
3970141cc406Sopenharmony_ci            if(s->model == MODEL_S300 || s->model == MODEL_S1100 || s->model == MODEL_S1300i)
3971141cc406Sopenharmony_ci            {
3972141cc406Sopenharmony_ci                unsigned char cmd[] = {0x1b, 0xd3};
3973141cc406Sopenharmony_ci                size_t cmdLen = 2;
3974141cc406Sopenharmony_ci                unsigned char stat[1];
3975141cc406Sopenharmony_ci                size_t statLen = 1;
3976141cc406Sopenharmony_ci
3977141cc406Sopenharmony_ci                DBG (15, "sane_read: d3\n");
3978141cc406Sopenharmony_ci
3979141cc406Sopenharmony_ci                ret = do_cmd(
3980141cc406Sopenharmony_ci                  s, 0,
3981141cc406Sopenharmony_ci                  cmd, cmdLen,
3982141cc406Sopenharmony_ci                  NULL, 0,
3983141cc406Sopenharmony_ci                  stat, &statLen
3984141cc406Sopenharmony_ci                );
3985141cc406Sopenharmony_ci                if(ret){
3986141cc406Sopenharmony_ci                    DBG (5, "sane_read: error sending d3 cmd\n");
3987141cc406Sopenharmony_ci                    return ret;
3988141cc406Sopenharmony_ci                }
3989141cc406Sopenharmony_ci                if(stat[0] != 6){
3990141cc406Sopenharmony_ci                    DBG (5, "sane_read: cmd bad status?\n");
3991141cc406Sopenharmony_ci                    return SANE_STATUS_IO_ERROR;
3992141cc406Sopenharmony_ci                }
3993141cc406Sopenharmony_ci            }
3994141cc406Sopenharmony_ci        }
3995141cc406Sopenharmony_ci
3996141cc406Sopenharmony_ci        ret = read_from_scanner(s, &s->block_xfr);
3997141cc406Sopenharmony_ci        if(ret){
3998141cc406Sopenharmony_ci            DBG (5, "sane_read: can't read from scanner\n");
3999141cc406Sopenharmony_ci            return ret;
4000141cc406Sopenharmony_ci        }
4001141cc406Sopenharmony_ci
4002141cc406Sopenharmony_ci        /* block filled, copy to front/back */
4003141cc406Sopenharmony_ci        if(s->block_xfr.done)
4004141cc406Sopenharmony_ci        {
4005141cc406Sopenharmony_ci            DBG (15, "sane_read: block buffer full\n");
4006141cc406Sopenharmony_ci
4007141cc406Sopenharmony_ci            /* convert the raw color data into normal packed pixel data */
4008141cc406Sopenharmony_ci            descramble_raw(s, &s->block_xfr);
4009141cc406Sopenharmony_ci
4010141cc406Sopenharmony_ci            s->block_xfr.done = 0;
4011141cc406Sopenharmony_ci
4012141cc406Sopenharmony_ci            /* get the 0x43 cmd for the S300, S1100, S1300  */
4013141cc406Sopenharmony_ci            if(s->model == MODEL_S300 || s->model == MODEL_S1100 || s->model == MODEL_S1300i){
4014141cc406Sopenharmony_ci
4015141cc406Sopenharmony_ci                unsigned char cmd[] = {0x1b, 0x43};
4016141cc406Sopenharmony_ci                size_t cmdLen = 2;
4017141cc406Sopenharmony_ci                unsigned char in[10];
4018141cc406Sopenharmony_ci                size_t inLen = 10;
4019141cc406Sopenharmony_ci
4020141cc406Sopenharmony_ci                ret = do_cmd(
4021141cc406Sopenharmony_ci                  s, 0,
4022141cc406Sopenharmony_ci                  cmd, cmdLen,
4023141cc406Sopenharmony_ci                  NULL, 0,
4024141cc406Sopenharmony_ci                  in, &inLen
4025141cc406Sopenharmony_ci                );
4026141cc406Sopenharmony_ci                hexdump(15, "cmd 43: ", in, inLen);
4027141cc406Sopenharmony_ci
4028141cc406Sopenharmony_ci                if(ret){
4029141cc406Sopenharmony_ci                    DBG (5, "sane_read: error sending 43 cmd\n");
4030141cc406Sopenharmony_ci                    return ret;
4031141cc406Sopenharmony_ci                }
4032141cc406Sopenharmony_ci
4033141cc406Sopenharmony_ci                /*copy backside data into buffer*/
4034141cc406Sopenharmony_ci                if( s->source == SOURCE_ADF_DUPLEX || s->source == SOURCE_ADF_BACK )
4035141cc406Sopenharmony_ci                    ret = copy_block_to_page(s, SIDE_BACK);
4036141cc406Sopenharmony_ci
4037141cc406Sopenharmony_ci                /*copy frontside data into buffer*/
4038141cc406Sopenharmony_ci                if( s->source != SOURCE_ADF_BACK )
4039141cc406Sopenharmony_ci                    ret = copy_block_to_page(s, SIDE_FRONT);
4040141cc406Sopenharmony_ci
4041141cc406Sopenharmony_ci                if(ret){
4042141cc406Sopenharmony_ci                    DBG (5, "sane_read: can't copy to front/back\n");
4043141cc406Sopenharmony_ci                    return ret;
4044141cc406Sopenharmony_ci                }
4045141cc406Sopenharmony_ci
4046141cc406Sopenharmony_ci                s->fullscan.rx_bytes += s->block_xfr.rx_bytes;
4047141cc406Sopenharmony_ci
4048141cc406Sopenharmony_ci                /* autodetect mode, check for change length */
4049141cc406Sopenharmony_ci                if( s->source != SOURCE_FLATBED && !s->page_height ){
4050141cc406Sopenharmony_ci                    int get = (in[6] << 8) | in[7];
4051141cc406Sopenharmony_ci
4052141cc406Sopenharmony_ci                    /*always have to get full blocks*/
4053141cc406Sopenharmony_ci                    if(get % s->block_img.height){
4054141cc406Sopenharmony_ci                      get += s->block_img.height - (get % s->block_img.height);
4055141cc406Sopenharmony_ci                    }
4056141cc406Sopenharmony_ci
4057141cc406Sopenharmony_ci                    if(get < s->fullscan.height){
4058141cc406Sopenharmony_ci                      DBG (15, "sane_read: paper out? %d\n",get);
4059141cc406Sopenharmony_ci                      s->fullscan.total_bytes = s->fullscan.width_bytes * get;
4060141cc406Sopenharmony_ci                    }
4061141cc406Sopenharmony_ci                }
4062141cc406Sopenharmony_ci            }
4063141cc406Sopenharmony_ci
4064141cc406Sopenharmony_ci            else { /*fi-60f*/
4065141cc406Sopenharmony_ci                ret = copy_block_to_page(s, SIDE_FRONT);
4066141cc406Sopenharmony_ci                if(ret){
4067141cc406Sopenharmony_ci                    DBG (5, "sane_read: can't copy to front/back\n");
4068141cc406Sopenharmony_ci                    return ret;
4069141cc406Sopenharmony_ci                }
4070141cc406Sopenharmony_ci
4071141cc406Sopenharmony_ci                s->fullscan.rx_bytes += s->block_xfr.rx_bytes;
4072141cc406Sopenharmony_ci	    }
4073141cc406Sopenharmony_ci
4074141cc406Sopenharmony_ci            /* reset for next pass */
4075141cc406Sopenharmony_ci            update_transfer_totals(&s->block_xfr);
4076141cc406Sopenharmony_ci
4077141cc406Sopenharmony_ci            /* scan now finished */
4078141cc406Sopenharmony_ci            if(s->fullscan.rx_bytes == s->fullscan.total_bytes){
4079141cc406Sopenharmony_ci                DBG (15, "sane_read: last block\n");
4080141cc406Sopenharmony_ci                s->fullscan.done = 1;
4081141cc406Sopenharmony_ci            }
4082141cc406Sopenharmony_ci	}
4083141cc406Sopenharmony_ci    }
4084141cc406Sopenharmony_ci
4085141cc406Sopenharmony_ci    *len = page->bytes_scanned - page->bytes_read;
4086141cc406Sopenharmony_ci    *len = MIN(*len, max_len);
4087141cc406Sopenharmony_ci
4088141cc406Sopenharmony_ci    if(*len){
4089141cc406Sopenharmony_ci        DBG (10, "sane_read: copy rx:%d tx:%d tot:%d len:%d\n",
4090141cc406Sopenharmony_ci          page->bytes_scanned, page->bytes_read, page->bytes_total,*len);
4091141cc406Sopenharmony_ci
4092141cc406Sopenharmony_ci        memcpy(buf, page->image->buffer + page->bytes_read, *len);
4093141cc406Sopenharmony_ci        page->bytes_read += *len;
4094141cc406Sopenharmony_ci    }
4095141cc406Sopenharmony_ci
4096141cc406Sopenharmony_ci    /* sent it all, return eof on next read */
4097141cc406Sopenharmony_ci    if(page->bytes_read == page->bytes_scanned && s->fullscan.done){
4098141cc406Sopenharmony_ci        DBG (10, "sane_read: side done\n");
4099141cc406Sopenharmony_ci        page->done = 1;
4100141cc406Sopenharmony_ci    }
4101141cc406Sopenharmony_ci
4102141cc406Sopenharmony_ci    DBG (10, "sane_read: finish si:%d len:%d max:%d\n",s->side,*len,max_len);
4103141cc406Sopenharmony_ci
4104141cc406Sopenharmony_ci    return ret;
4105141cc406Sopenharmony_ci}
4106141cc406Sopenharmony_ci
4107141cc406Sopenharmony_cistatic SANE_Status
4108141cc406Sopenharmony_cisix5 (struct scanner *s)
4109141cc406Sopenharmony_ci{
4110141cc406Sopenharmony_ci  SANE_Status ret = SANE_STATUS_GOOD;
4111141cc406Sopenharmony_ci
4112141cc406Sopenharmony_ci  unsigned char cmd[2];
4113141cc406Sopenharmony_ci  size_t cmdLen = sizeof(cmd);
4114141cc406Sopenharmony_ci  unsigned char stat[1];
4115141cc406Sopenharmony_ci  size_t statLen = sizeof(stat);
4116141cc406Sopenharmony_ci
4117141cc406Sopenharmony_ci  DBG (10, "six5: start\n");
4118141cc406Sopenharmony_ci
4119141cc406Sopenharmony_ci  cmd[0] = 0x1b;
4120141cc406Sopenharmony_ci  cmd[1] = 0x65;
4121141cc406Sopenharmony_ci  statLen = 1;
4122141cc406Sopenharmony_ci
4123141cc406Sopenharmony_ci  ret = do_cmd(
4124141cc406Sopenharmony_ci    s, 0,
4125141cc406Sopenharmony_ci    cmd, cmdLen,
4126141cc406Sopenharmony_ci    NULL, 0,
4127141cc406Sopenharmony_ci    stat, &statLen
4128141cc406Sopenharmony_ci  );
4129141cc406Sopenharmony_ci  if(ret){
4130141cc406Sopenharmony_ci      DBG (5, "six5: error sending cmd\n");
4131141cc406Sopenharmony_ci      return ret;
4132141cc406Sopenharmony_ci  }
4133141cc406Sopenharmony_ci  if(stat[0] != 6){
4134141cc406Sopenharmony_ci      DBG (5, "six5: cmd bad status? %d\n",stat[0]);
4135141cc406Sopenharmony_ci      return SANE_STATUS_IO_ERROR;
4136141cc406Sopenharmony_ci  }
4137141cc406Sopenharmony_ci
4138141cc406Sopenharmony_ci  DBG (10, "six5: finish\n");
4139141cc406Sopenharmony_ci
4140141cc406Sopenharmony_ci  return ret;
4141141cc406Sopenharmony_ci}
4142141cc406Sopenharmony_ci
4143141cc406Sopenharmony_ci/* de-scrambles the raw data from the scanner into the image buffer */
4144141cc406Sopenharmony_ci/* the output image might be lower dpi than input image, so we scale horizontally */
4145141cc406Sopenharmony_ci/* if the input image is mirrored left to right, we do not correct it here */
4146141cc406Sopenharmony_ci/* if the input image has padding (at the end or between heads), it is removed here */
4147141cc406Sopenharmony_cistatic SANE_Status
4148141cc406Sopenharmony_cidescramble_raw(struct scanner *s, struct transfer * tp)
4149141cc406Sopenharmony_ci{
4150141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
4151141cc406Sopenharmony_ci    unsigned char *p_out = tp->image->buffer;
4152141cc406Sopenharmony_ci    int height = tp->total_bytes / tp->line_stride;
4153141cc406Sopenharmony_ci    int i, j, k;
4154141cc406Sopenharmony_ci
4155141cc406Sopenharmony_ci    /* raw gray data handled in another function */
4156141cc406Sopenharmony_ci    if(tp->mode == MODE_GRAYSCALE){
4157141cc406Sopenharmony_ci      return descramble_raw_gray(s, tp);
4158141cc406Sopenharmony_ci    }
4159141cc406Sopenharmony_ci
4160141cc406Sopenharmony_ci    DBG(15, "descramble_raw: start\n");
4161141cc406Sopenharmony_ci
4162141cc406Sopenharmony_ci    if (s->model == MODEL_S300 || s->model == MODEL_S1300i) {
4163141cc406Sopenharmony_ci      for (i = 0; i < 2; i++){                   /* page, front/back */
4164141cc406Sopenharmony_ci        for (j = 0; j < height; j++){             /* row (y)*/
4165141cc406Sopenharmony_ci          int curr_col = 0;
4166141cc406Sopenharmony_ci          int r=0, g=0, b=0, ppc=0;
4167141cc406Sopenharmony_ci          int g_offset=0, b_offset=0;
4168141cc406Sopenharmony_ci
4169141cc406Sopenharmony_ci          for (k = 0; k <= tp->plane_width; k++){  /* column (x) */
4170141cc406Sopenharmony_ci            int this_col = k*tp->image->x_res/tp->x_res;
4171141cc406Sopenharmony_ci
4172141cc406Sopenharmony_ci            /* going to change output pixel, dump rgb and reset */
4173141cc406Sopenharmony_ci            if(ppc && curr_col != this_col){
4174141cc406Sopenharmony_ci              *p_out = r/ppc;
4175141cc406Sopenharmony_ci              p_out++;
4176141cc406Sopenharmony_ci
4177141cc406Sopenharmony_ci              *p_out = g/ppc;
4178141cc406Sopenharmony_ci              p_out++;
4179141cc406Sopenharmony_ci
4180141cc406Sopenharmony_ci              *p_out = b/ppc;
4181141cc406Sopenharmony_ci              p_out++;
4182141cc406Sopenharmony_ci
4183141cc406Sopenharmony_ci              r = g = b = ppc = 0;
4184141cc406Sopenharmony_ci
4185141cc406Sopenharmony_ci              curr_col = this_col;
4186141cc406Sopenharmony_ci            }
4187141cc406Sopenharmony_ci
4188141cc406Sopenharmony_ci            if(k == tp->plane_width || this_col >= tp->image->width_pix){
4189141cc406Sopenharmony_ci              break;
4190141cc406Sopenharmony_ci            }
4191141cc406Sopenharmony_ci
4192141cc406Sopenharmony_ci            /* if we're using an S1300i with scan resolution 225 or 300, on AC power, the color planes are shifted */
4193141cc406Sopenharmony_ci            if(s->model == MODEL_S1300i && !s->usb_power && (tp->x_res == 225 || tp->x_res == 300) && tp != &s->cal_image && k + 2 <= tp->plane_width){
4194141cc406Sopenharmony_ci              g_offset = 3;
4195141cc406Sopenharmony_ci              b_offset = 6;
4196141cc406Sopenharmony_ci            }
4197141cc406Sopenharmony_ci
4198141cc406Sopenharmony_ci            /*red is first*/
4199141cc406Sopenharmony_ci            r += tp->raw_data[j*tp->line_stride + k*3 + i];
4200141cc406Sopenharmony_ci
4201141cc406Sopenharmony_ci            /*green is second*/
4202141cc406Sopenharmony_ci            g += tp->raw_data[j*tp->line_stride + tp->plane_stride + k*3 + i + g_offset];
4203141cc406Sopenharmony_ci
4204141cc406Sopenharmony_ci            /*blue is third*/
4205141cc406Sopenharmony_ci            b += tp->raw_data[j*tp->line_stride + 2*tp->plane_stride + k*3 + i + b_offset];
4206141cc406Sopenharmony_ci
4207141cc406Sopenharmony_ci            ppc++;
4208141cc406Sopenharmony_ci          }
4209141cc406Sopenharmony_ci        }
4210141cc406Sopenharmony_ci      }
4211141cc406Sopenharmony_ci    }
4212141cc406Sopenharmony_ci    else if (s->model == MODEL_S1100){
4213141cc406Sopenharmony_ci      for (j = 0; j < height; j++){             /* row (y)*/
4214141cc406Sopenharmony_ci        int curr_col = 0;
4215141cc406Sopenharmony_ci        int r=0, g=0, b=0, ppc=0;
4216141cc406Sopenharmony_ci
4217141cc406Sopenharmony_ci        for (k = 0; k <= tp->plane_width; k++){  /* column (x) */
4218141cc406Sopenharmony_ci          int this_col = k*tp->image->x_res/tp->x_res;
4219141cc406Sopenharmony_ci
4220141cc406Sopenharmony_ci          /* going to change output pixel, dump rgb and reset */
4221141cc406Sopenharmony_ci          if(ppc && curr_col != this_col){
4222141cc406Sopenharmony_ci            *p_out = r/ppc;
4223141cc406Sopenharmony_ci            p_out++;
4224141cc406Sopenharmony_ci
4225141cc406Sopenharmony_ci            *p_out = g/ppc;
4226141cc406Sopenharmony_ci            p_out++;
4227141cc406Sopenharmony_ci
4228141cc406Sopenharmony_ci            *p_out = b/ppc;
4229141cc406Sopenharmony_ci            p_out++;
4230141cc406Sopenharmony_ci
4231141cc406Sopenharmony_ci            r = g = b = ppc = 0;
4232141cc406Sopenharmony_ci
4233141cc406Sopenharmony_ci            curr_col = this_col;
4234141cc406Sopenharmony_ci          }
4235141cc406Sopenharmony_ci
4236141cc406Sopenharmony_ci          if(k == tp->plane_width || this_col >= tp->image->width_pix){
4237141cc406Sopenharmony_ci            break;
4238141cc406Sopenharmony_ci          }
4239141cc406Sopenharmony_ci
4240141cc406Sopenharmony_ci          /*red is second*/
4241141cc406Sopenharmony_ci          r += tp->raw_data[j*tp->line_stride + tp->plane_stride + k];
4242141cc406Sopenharmony_ci
4243141cc406Sopenharmony_ci          /*green is third*/
4244141cc406Sopenharmony_ci          g += tp->raw_data[j*tp->line_stride + 2*tp->plane_stride + k];
4245141cc406Sopenharmony_ci
4246141cc406Sopenharmony_ci          /*blue is first*/
4247141cc406Sopenharmony_ci          b += tp->raw_data[j*tp->line_stride + k];
4248141cc406Sopenharmony_ci
4249141cc406Sopenharmony_ci          ppc++;
4250141cc406Sopenharmony_ci        }
4251141cc406Sopenharmony_ci      }
4252141cc406Sopenharmony_ci    }
4253141cc406Sopenharmony_ci    else { /* MODEL_FI60F or MODEL_FI65F */
4254141cc406Sopenharmony_ci
4255141cc406Sopenharmony_ci      for (j = 0; j < height; j++){             /* row (y)*/
4256141cc406Sopenharmony_ci        int curr_col = 0;
4257141cc406Sopenharmony_ci
4258141cc406Sopenharmony_ci        for (i = 0; i < 3; i++){                /* read head */
4259141cc406Sopenharmony_ci          int r=0, g=0, b=0, ppc=0;
4260141cc406Sopenharmony_ci
4261141cc406Sopenharmony_ci          for (k = 0; k <= tp->plane_width; k++){  /* column (x) within the read head */
4262141cc406Sopenharmony_ci            int this_col = (k+i*tp->plane_width)*tp->image->x_res/tp->x_res;
4263141cc406Sopenharmony_ci
4264141cc406Sopenharmony_ci            /* going to change output pixel, dump rgb and reset */
4265141cc406Sopenharmony_ci            if(ppc && curr_col != this_col){
4266141cc406Sopenharmony_ci              *p_out = r/ppc;
4267141cc406Sopenharmony_ci              p_out++;
4268141cc406Sopenharmony_ci
4269141cc406Sopenharmony_ci              *p_out = g/ppc;
4270141cc406Sopenharmony_ci              p_out++;
4271141cc406Sopenharmony_ci
4272141cc406Sopenharmony_ci              *p_out = b/ppc;
4273141cc406Sopenharmony_ci              p_out++;
4274141cc406Sopenharmony_ci
4275141cc406Sopenharmony_ci              r = g = b = ppc = 0;
4276141cc406Sopenharmony_ci
4277141cc406Sopenharmony_ci              curr_col = this_col;
4278141cc406Sopenharmony_ci            }
4279141cc406Sopenharmony_ci
4280141cc406Sopenharmony_ci            if(k == tp->plane_width || this_col >= tp->image->width_pix){
4281141cc406Sopenharmony_ci              break;
4282141cc406Sopenharmony_ci            }
4283141cc406Sopenharmony_ci
4284141cc406Sopenharmony_ci            /*red is first*/
4285141cc406Sopenharmony_ci            r += tp->raw_data[j*tp->line_stride + k*3 + i];
4286141cc406Sopenharmony_ci
4287141cc406Sopenharmony_ci            /*green is second*/
4288141cc406Sopenharmony_ci            g += tp->raw_data[j*tp->line_stride + tp->plane_stride + k*3 + i];
4289141cc406Sopenharmony_ci
4290141cc406Sopenharmony_ci            /*blue is third*/
4291141cc406Sopenharmony_ci            b += tp->raw_data[j*tp->line_stride + 2*tp->plane_stride + k*3 + i];
4292141cc406Sopenharmony_ci
4293141cc406Sopenharmony_ci            ppc++;
4294141cc406Sopenharmony_ci          }
4295141cc406Sopenharmony_ci        }
4296141cc406Sopenharmony_ci      }
4297141cc406Sopenharmony_ci    }
4298141cc406Sopenharmony_ci
4299141cc406Sopenharmony_ci    DBG(15, "descramble_raw: finish %d\n", ret);
4300141cc406Sopenharmony_ci
4301141cc406Sopenharmony_ci    return ret;
4302141cc406Sopenharmony_ci}
4303141cc406Sopenharmony_ci
4304141cc406Sopenharmony_ci/* de-scrambles the raw gray data from the scanner into the image buffer */
4305141cc406Sopenharmony_ci/* the output image might be lower dpi than input image, so we scale horizontally */
4306141cc406Sopenharmony_ci/* if the input image is mirrored left to right, we do not correct it here */
4307141cc406Sopenharmony_ci/* if the input image has padding (at the end or between heads), it is removed here */
4308141cc406Sopenharmony_cistatic SANE_Status
4309141cc406Sopenharmony_cidescramble_raw_gray(struct scanner *s, struct transfer * tp)
4310141cc406Sopenharmony_ci{
4311141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
4312141cc406Sopenharmony_ci    int height = tp->total_bytes / tp->line_stride;
4313141cc406Sopenharmony_ci    int row, col_out;
4314141cc406Sopenharmony_ci
4315141cc406Sopenharmony_ci    DBG(15, "descramble_raw_gray: start\n");
4316141cc406Sopenharmony_ci
4317141cc406Sopenharmony_ci    if (s->model == MODEL_FI60F || s->model == MODEL_FI65F) {
4318141cc406Sopenharmony_ci      for (row = 0; row < height; row++){
4319141cc406Sopenharmony_ci
4320141cc406Sopenharmony_ci        unsigned char *p_in = tp->raw_data + row * tp->line_stride;
4321141cc406Sopenharmony_ci        unsigned char *p_out = tp->image->buffer + row * tp->image->width_pix;
4322141cc406Sopenharmony_ci
4323141cc406Sopenharmony_ci        for (col_out = 0; col_out < tp->image->width_pix; col_out++){
4324141cc406Sopenharmony_ci          int col_in = col_out * tp->x_res/tp->image->x_res;
4325141cc406Sopenharmony_ci          int offset = col_in%tp->plane_width;
4326141cc406Sopenharmony_ci          int step   = col_in/tp->plane_width;
4327141cc406Sopenharmony_ci
4328141cc406Sopenharmony_ci          *p_out = *(p_in + offset*3 + step);
4329141cc406Sopenharmony_ci          p_out++;
4330141cc406Sopenharmony_ci        }
4331141cc406Sopenharmony_ci      }
4332141cc406Sopenharmony_ci    }
4333141cc406Sopenharmony_ci
4334141cc406Sopenharmony_ci    else{
4335141cc406Sopenharmony_ci        DBG(5, "internal error: descramble_raw_gray not supported\n");
4336141cc406Sopenharmony_ci        ret = SANE_STATUS_INVAL;
4337141cc406Sopenharmony_ci    }
4338141cc406Sopenharmony_ci
4339141cc406Sopenharmony_ci    DBG(15, "descramble_raw_gray: finish %d\n", ret);
4340141cc406Sopenharmony_ci    return ret;
4341141cc406Sopenharmony_ci}
4342141cc406Sopenharmony_ci
4343141cc406Sopenharmony_ci/* fills block buffer a little per pass */
4344141cc406Sopenharmony_cistatic SANE_Status
4345141cc406Sopenharmony_ciread_from_scanner(struct scanner *s, struct transfer * tp)
4346141cc406Sopenharmony_ci{
4347141cc406Sopenharmony_ci    SANE_Status ret=SANE_STATUS_GOOD;
4348141cc406Sopenharmony_ci    size_t bytes = MAX_IMG_PASS;
4349141cc406Sopenharmony_ci    size_t remainBlock = tp->total_bytes - tp->rx_bytes + 8;
4350141cc406Sopenharmony_ci    unsigned char * buf;
4351141cc406Sopenharmony_ci    size_t bufLen;
4352141cc406Sopenharmony_ci
4353141cc406Sopenharmony_ci    /* determine amount to ask for, S1300i wants big requests */
4354141cc406Sopenharmony_ci    if(s->model != MODEL_S1300i){
4355141cc406Sopenharmony_ci        bytes = MIN(bytes, remainBlock);
4356141cc406Sopenharmony_ci    }
4357141cc406Sopenharmony_ci
4358141cc406Sopenharmony_ci    if (tp->image == NULL)
4359141cc406Sopenharmony_ci    {
4360141cc406Sopenharmony_ci        DBG(5, "internal error: read_from_scanner called with no destination image.\n");
4361141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
4362141cc406Sopenharmony_ci    }
4363141cc406Sopenharmony_ci
4364141cc406Sopenharmony_ci    DBG (10, "read_from_scanner: start rB:%lu len:%lu\n",
4365141cc406Sopenharmony_ci      (unsigned long)remainBlock, (unsigned long)bytes);
4366141cc406Sopenharmony_ci
4367141cc406Sopenharmony_ci    if(!bytes){
4368141cc406Sopenharmony_ci        DBG(10, "read_from_scanner: no bytes!\n");
4369141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
4370141cc406Sopenharmony_ci    }
4371141cc406Sopenharmony_ci
4372141cc406Sopenharmony_ci    bufLen = bytes;
4373141cc406Sopenharmony_ci    buf = malloc(bufLen);
4374141cc406Sopenharmony_ci    if(!buf){
4375141cc406Sopenharmony_ci        DBG (5, "read_from_scanner: failed to alloc mem\n");
4376141cc406Sopenharmony_ci        return SANE_STATUS_NO_MEM;
4377141cc406Sopenharmony_ci    }
4378141cc406Sopenharmony_ci
4379141cc406Sopenharmony_ci    ret = do_cmd(
4380141cc406Sopenharmony_ci      s, 0,
4381141cc406Sopenharmony_ci      NULL, 0,
4382141cc406Sopenharmony_ci      NULL, 0,
4383141cc406Sopenharmony_ci      buf, &bytes
4384141cc406Sopenharmony_ci    );
4385141cc406Sopenharmony_ci
4386141cc406Sopenharmony_ci    /* full read or short read */
4387141cc406Sopenharmony_ci    if (ret == SANE_STATUS_GOOD || (ret == SANE_STATUS_EOF && bytes) ) {
4388141cc406Sopenharmony_ci
4389141cc406Sopenharmony_ci        DBG(15,"read_from_scanner: got GOOD/EOF (%lu)\n",(unsigned long)bytes);
4390141cc406Sopenharmony_ci
4391141cc406Sopenharmony_ci        if(bytes > remainBlock){
4392141cc406Sopenharmony_ci          DBG(15,"read_from_scanner: block too big?\n");
4393141cc406Sopenharmony_ci          bytes = remainBlock;
4394141cc406Sopenharmony_ci        }
4395141cc406Sopenharmony_ci
4396141cc406Sopenharmony_ci        if(bytes == remainBlock){
4397141cc406Sopenharmony_ci          DBG(15,"read_from_scanner: block done, ignoring trailer\n");
4398141cc406Sopenharmony_ci          bytes -= 8;
4399141cc406Sopenharmony_ci          tp->done = 1;
4400141cc406Sopenharmony_ci        }
4401141cc406Sopenharmony_ci
4402141cc406Sopenharmony_ci        memcpy(tp->raw_data + tp->rx_bytes, buf, bytes);
4403141cc406Sopenharmony_ci        tp->rx_bytes += bytes;
4404141cc406Sopenharmony_ci
4405141cc406Sopenharmony_ci        ret = SANE_STATUS_GOOD;
4406141cc406Sopenharmony_ci    }
4407141cc406Sopenharmony_ci    else {
4408141cc406Sopenharmony_ci        DBG(5, "read_from_scanner: error reading status = %d\n", ret);
4409141cc406Sopenharmony_ci    }
4410141cc406Sopenharmony_ci
4411141cc406Sopenharmony_ci    free(buf);
4412141cc406Sopenharmony_ci
4413141cc406Sopenharmony_ci    DBG (10, "read_from_scanner: finish rB:%lu len:%lu\n",
4414141cc406Sopenharmony_ci      (unsigned long)(tp->total_bytes - tp->rx_bytes + 8), (unsigned long)bytes);
4415141cc406Sopenharmony_ci
4416141cc406Sopenharmony_ci    return ret;
4417141cc406Sopenharmony_ci}
4418141cc406Sopenharmony_ci
4419141cc406Sopenharmony_ci/* copies block buffer into front or back image buffer */
4420141cc406Sopenharmony_ci/* converts pixel data from input mode (color/gray) to output mode (color/gray/binary) */
4421141cc406Sopenharmony_ci/* the output image might be lower dpi than input image, so we scale vertically */
4422141cc406Sopenharmony_ci/* the input is already scaled horizontally and padding skipped if required */
4423141cc406Sopenharmony_ci/* if the input is mirrored left to right, we fix it here */
4424141cc406Sopenharmony_cistatic SANE_Status
4425141cc406Sopenharmony_cicopy_block_to_page(struct scanner *s,int side)
4426141cc406Sopenharmony_ci{
4427141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
4428141cc406Sopenharmony_ci    struct transfer * block = &s->block_xfr;
4429141cc406Sopenharmony_ci    struct page * page = &s->pages[side];
4430141cc406Sopenharmony_ci    int image_height = block->total_bytes / block->line_stride;
4431141cc406Sopenharmony_ci    int page_width = page->image->width_pix;
4432141cc406Sopenharmony_ci    int block_page_stride = block->image->width_bytes * block->image->height;
4433141cc406Sopenharmony_ci    int line_reverse = (side == SIDE_BACK) || (s->model == MODEL_FI60F) || (s->model == MODEL_FI65F);
4434141cc406Sopenharmony_ci    int i,j,k=0;
4435141cc406Sopenharmony_ci
4436141cc406Sopenharmony_ci    int curr_in_row = s->fullscan.rx_bytes/s->fullscan.width_bytes;
4437141cc406Sopenharmony_ci    int last_out_row = (page->bytes_scanned / page->image->width_bytes) - 1;
4438141cc406Sopenharmony_ci
4439141cc406Sopenharmony_ci    DBG (10, "copy_block_to_page: start\n");
4440141cc406Sopenharmony_ci
4441141cc406Sopenharmony_ci    /* skip padding and tl_y */
4442141cc406Sopenharmony_ci    if (s->fullscan.rx_bytes + s->block_xfr.rx_bytes <= block->line_stride * page->image->y_skip_offset)
4443141cc406Sopenharmony_ci    {
4444141cc406Sopenharmony_ci        DBG (10, "copy_block_to_page: before the start? %d\n", side);
4445141cc406Sopenharmony_ci        return ret;
4446141cc406Sopenharmony_ci    }
4447141cc406Sopenharmony_ci    else if (s->fullscan.rx_bytes < block->line_stride * page->image->y_skip_offset)
4448141cc406Sopenharmony_ci    {
4449141cc406Sopenharmony_ci        k = page->image->y_skip_offset - s->fullscan.rx_bytes / block->line_stride;
4450141cc406Sopenharmony_ci        DBG (10, "copy_block_to_page: k start? %d\n", k);
4451141cc406Sopenharmony_ci    }
4452141cc406Sopenharmony_ci
4453141cc406Sopenharmony_ci    /* loop over all the lines in the block */
4454141cc406Sopenharmony_ci    for (i = k; i < image_height; i++)
4455141cc406Sopenharmony_ci    {
4456141cc406Sopenharmony_ci      /* determine source and dest rows (dpi scaling) */
4457141cc406Sopenharmony_ci      int this_in_row = curr_in_row + i;
4458141cc406Sopenharmony_ci      int this_out_row = (this_in_row - page->image->y_skip_offset) * page->image->y_res / s->fullscan.y_res;
4459141cc406Sopenharmony_ci      DBG (15, "copy_block_to_page: in %d out %d lastout %d\n", this_in_row, this_out_row, last_out_row);
4460141cc406Sopenharmony_ci      DBG (15, "copy_block_to_page: bs %d wb %d\n", page->bytes_scanned, page->image->width_bytes);
4461141cc406Sopenharmony_ci
4462141cc406Sopenharmony_ci      /* don't walk off the end of the output buffer */
4463141cc406Sopenharmony_ci      if(this_out_row >= page->image->height || this_out_row < 0){
4464141cc406Sopenharmony_ci          DBG (10, "copy_block_to_page: out of space? %d\n", side);
4465141cc406Sopenharmony_ci          DBG (10, "copy_block_to_page: rx:%d tx:%d tot:%d line:%d\n",
4466141cc406Sopenharmony_ci            page->bytes_scanned, page->bytes_read, page->bytes_total,page->image->width_bytes);
4467141cc406Sopenharmony_ci          return ret;
4468141cc406Sopenharmony_ci      }
4469141cc406Sopenharmony_ci
4470141cc406Sopenharmony_ci      /* ok, different output row, so we do the math */
4471141cc406Sopenharmony_ci      if(this_out_row > last_out_row){
4472141cc406Sopenharmony_ci
4473141cc406Sopenharmony_ci        unsigned char * p_in = block->image->buffer + (side * block_page_stride)
4474141cc406Sopenharmony_ci            + (i * block->image->width_bytes) + page->image->x_start_offset * 3;
4475141cc406Sopenharmony_ci        unsigned char * p_out = page->image->buffer + this_out_row * page->image->width_bytes;
4476141cc406Sopenharmony_ci        unsigned char * lineStart = p_out;
4477141cc406Sopenharmony_ci
4478141cc406Sopenharmony_ci        last_out_row = this_out_row;
4479141cc406Sopenharmony_ci
4480141cc406Sopenharmony_ci        if (block->mode == MODE_COLOR){
4481141cc406Sopenharmony_ci
4482141cc406Sopenharmony_ci          /* reverse order for back side or FI-60F scanner */
4483141cc406Sopenharmony_ci          if (line_reverse)
4484141cc406Sopenharmony_ci            p_in += (page_width - 1) * 3;
4485141cc406Sopenharmony_ci
4486141cc406Sopenharmony_ci          /* convert all of the pixels in this row */
4487141cc406Sopenharmony_ci          for (j = 0; j < page_width; j++)
4488141cc406Sopenharmony_ci          {
4489141cc406Sopenharmony_ci            unsigned char r, g, b;
4490141cc406Sopenharmony_ci            if (s->model == MODEL_S300 || s->model == MODEL_S1300i)
4491141cc406Sopenharmony_ci                { r = p_in[1]; g = p_in[2]; b = p_in[0]; }
4492141cc406Sopenharmony_ci            else /* MODEL_FI60F or MODEL_FI65F or MODEL_S1100 */
4493141cc406Sopenharmony_ci                { r = p_in[0]; g = p_in[1]; b = p_in[2]; }
4494141cc406Sopenharmony_ci            if (s->mode == MODE_COLOR)
4495141cc406Sopenharmony_ci            {
4496141cc406Sopenharmony_ci                *p_out++ = r;
4497141cc406Sopenharmony_ci                *p_out++ = g;
4498141cc406Sopenharmony_ci                *p_out++ = b;
4499141cc406Sopenharmony_ci            }
4500141cc406Sopenharmony_ci            else if (s->mode == MODE_GRAYSCALE)
4501141cc406Sopenharmony_ci            {
4502141cc406Sopenharmony_ci                *p_out++ = (r + g + b) / 3;
4503141cc406Sopenharmony_ci            }
4504141cc406Sopenharmony_ci            else if (s->mode == MODE_LINEART)
4505141cc406Sopenharmony_ci            {
4506141cc406Sopenharmony_ci                s->dt.buffer[j] = (r + g + b) / 3; /* stores dt temp image buffer and binarize afterward */
4507141cc406Sopenharmony_ci            }
4508141cc406Sopenharmony_ci            if (line_reverse)
4509141cc406Sopenharmony_ci                p_in -= 3;
4510141cc406Sopenharmony_ci            else
4511141cc406Sopenharmony_ci                p_in += 3;
4512141cc406Sopenharmony_ci           }
4513141cc406Sopenharmony_ci         }
4514141cc406Sopenharmony_ci
4515141cc406Sopenharmony_ci         /* grayscale input */
4516141cc406Sopenharmony_ci         else{
4517141cc406Sopenharmony_ci           unsigned char * p_in = block->image->buffer + (side * block_page_stride)
4518141cc406Sopenharmony_ci               + (i * block->image->width_bytes) + page->image->x_start_offset;
4519141cc406Sopenharmony_ci
4520141cc406Sopenharmony_ci           /* reverse order for back side or FI-60F scanner */
4521141cc406Sopenharmony_ci           if (line_reverse)
4522141cc406Sopenharmony_ci             p_in += (page_width - 1);
4523141cc406Sopenharmony_ci
4524141cc406Sopenharmony_ci           //memcpy(p_out,p_in,page->image->width_bytes);
4525141cc406Sopenharmony_ci
4526141cc406Sopenharmony_ci           for (j = 0; j < page_width; j++)
4527141cc406Sopenharmony_ci           {
4528141cc406Sopenharmony_ci             if (s->mode == MODE_GRAYSCALE)
4529141cc406Sopenharmony_ci             {
4530141cc406Sopenharmony_ci                 *p_out++ = *p_in;
4531141cc406Sopenharmony_ci             }
4532141cc406Sopenharmony_ci             else if (s->mode == MODE_LINEART)
4533141cc406Sopenharmony_ci             {
4534141cc406Sopenharmony_ci                 s->dt.buffer[j] = *p_in; /* stores dt temp image buffer and binarize afterward */
4535141cc406Sopenharmony_ci             }
4536141cc406Sopenharmony_ci             if (line_reverse)
4537141cc406Sopenharmony_ci                 p_in--;
4538141cc406Sopenharmony_ci             else
4539141cc406Sopenharmony_ci                 p_in++;
4540141cc406Sopenharmony_ci           }
4541141cc406Sopenharmony_ci        }
4542141cc406Sopenharmony_ci
4543141cc406Sopenharmony_ci	/* skip non-transfer pixels in block image buffer */
4544141cc406Sopenharmony_ci        if (line_reverse)
4545141cc406Sopenharmony_ci            p_in -= page->image->x_offset_bytes;
4546141cc406Sopenharmony_ci        else
4547141cc406Sopenharmony_ci            p_in += page->image->x_offset_bytes;
4548141cc406Sopenharmony_ci
4549141cc406Sopenharmony_ci        /* for MODE_LINEART, binarize the gray line stored in the temp image buffer(dt) */
4550141cc406Sopenharmony_ci        /* because dt.width = page_width, we pass page_width */
4551141cc406Sopenharmony_ci        if (s->mode == MODE_LINEART)
4552141cc406Sopenharmony_ci            binarize_line(s, lineStart, page_width);
4553141cc406Sopenharmony_ci
4554141cc406Sopenharmony_ci        page->bytes_scanned += page->image->width_bytes;
4555141cc406Sopenharmony_ci      }
4556141cc406Sopenharmony_ci    }
4557141cc406Sopenharmony_ci
4558141cc406Sopenharmony_ci    DBG (10, "copy_block_to_page: finish\n");
4559141cc406Sopenharmony_ci
4560141cc406Sopenharmony_ci    return ret;
4561141cc406Sopenharmony_ci}
4562141cc406Sopenharmony_ci
4563141cc406Sopenharmony_ci/*uses the threshold/threshold_curve to control binarization*/
4564141cc406Sopenharmony_cistatic SANE_Status
4565141cc406Sopenharmony_cibinarize_line(struct scanner *s, unsigned char *lineOut, int width)
4566141cc406Sopenharmony_ci{
4567141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
4568141cc406Sopenharmony_ci    int j, windowX, sum = 0;
4569141cc406Sopenharmony_ci
4570141cc406Sopenharmony_ci    /* ~1mm works best, but the window needs to have odd # of pixels */
4571141cc406Sopenharmony_ci    windowX = 6 * s->resolution / 150;
4572141cc406Sopenharmony_ci    if (!(windowX % 2)) windowX++;
4573141cc406Sopenharmony_ci
4574141cc406Sopenharmony_ci    /*second, prefill the sliding sum*/
4575141cc406Sopenharmony_ci    for (j = 0; j < windowX; j++)
4576141cc406Sopenharmony_ci        sum += s->dt.buffer[j];
4577141cc406Sopenharmony_ci
4578141cc406Sopenharmony_ci    /* third, walk the dt buffer, update the sliding sum, */
4579141cc406Sopenharmony_ci    /* determine threshold, output bits */
4580141cc406Sopenharmony_ci    for (j = 0; j < width; j++)
4581141cc406Sopenharmony_ci    {
4582141cc406Sopenharmony_ci        /*output image location*/
4583141cc406Sopenharmony_ci        int offset = j % 8;
4584141cc406Sopenharmony_ci        unsigned char mask = 0x80 >> offset;
4585141cc406Sopenharmony_ci        int thresh = s->threshold;
4586141cc406Sopenharmony_ci
4587141cc406Sopenharmony_ci        /* move sum/update threshold only if there is a curve*/
4588141cc406Sopenharmony_ci        if (s->threshold_curve)
4589141cc406Sopenharmony_ci        {
4590141cc406Sopenharmony_ci            int addCol  = j + windowX/2;
4591141cc406Sopenharmony_ci            int dropCol = addCol - windowX;
4592141cc406Sopenharmony_ci
4593141cc406Sopenharmony_ci            if (dropCol >= 0 && addCol < width)
4594141cc406Sopenharmony_ci            {
4595141cc406Sopenharmony_ci                sum -= s->dt.buffer[dropCol];
4596141cc406Sopenharmony_ci                sum += s->dt.buffer[addCol];
4597141cc406Sopenharmony_ci            }
4598141cc406Sopenharmony_ci            thresh = s->dt_lut[sum/windowX];
4599141cc406Sopenharmony_ci        }
4600141cc406Sopenharmony_ci
4601141cc406Sopenharmony_ci        /*use average to lookup threshold*/
4602141cc406Sopenharmony_ci        if (s->dt.buffer[j] > thresh)
4603141cc406Sopenharmony_ci          *lineOut &= ~mask;     /* white */
4604141cc406Sopenharmony_ci        else
4605141cc406Sopenharmony_ci          *lineOut |= mask;      /* black */
4606141cc406Sopenharmony_ci
4607141cc406Sopenharmony_ci        if (offset == 7)
4608141cc406Sopenharmony_ci            lineOut++;
4609141cc406Sopenharmony_ci      }
4610141cc406Sopenharmony_ci
4611141cc406Sopenharmony_ci    return ret;
4612141cc406Sopenharmony_ci}
4613141cc406Sopenharmony_ci
4614141cc406Sopenharmony_ci/*
4615141cc406Sopenharmony_ci * @@ Section 4 - SANE cleanup functions
4616141cc406Sopenharmony_ci */
4617141cc406Sopenharmony_ci/*
4618141cc406Sopenharmony_ci * Cancels a scan.
4619141cc406Sopenharmony_ci *
4620141cc406Sopenharmony_ci * From the SANE spec:
4621141cc406Sopenharmony_ci * This function is used to immediately or as quickly as possible
4622141cc406Sopenharmony_ci * cancel the currently pending operation of the device represented by
4623141cc406Sopenharmony_ci * handle h.  This function can be called at any time (as long as
4624141cc406Sopenharmony_ci * handle h is a valid handle) but usually affects long-running
4625141cc406Sopenharmony_ci * operations only (such as image is acquisition). It is safe to call
4626141cc406Sopenharmony_ci * this function asynchronously (e.g., from within a signal handler).
4627141cc406Sopenharmony_ci * It is important to note that completion of this operation does not
4628141cc406Sopenharmony_ci * imply that the currently pending operation has been cancelled. It
4629141cc406Sopenharmony_ci * only guarantees that cancellation has been initiated. Cancellation
4630141cc406Sopenharmony_ci * completes only when the cancelled call returns (typically with a
4631141cc406Sopenharmony_ci * status value of SANE_STATUS_CANCELLED).  Since the SANE API does
4632141cc406Sopenharmony_ci * not require any other operations to be re-entrant, this implies
4633141cc406Sopenharmony_ci * that a frontend must not call any other operation until the
4634141cc406Sopenharmony_ci * cancelled operation has returned.
4635141cc406Sopenharmony_ci */
4636141cc406Sopenharmony_civoid
4637141cc406Sopenharmony_cisane_cancel (SANE_Handle handle)
4638141cc406Sopenharmony_ci{
4639141cc406Sopenharmony_ci  /*FIXME: actually ask the scanner to stop?*/
4640141cc406Sopenharmony_ci  struct scanner * s = (struct scanner *) handle;
4641141cc406Sopenharmony_ci  DBG (10, "sane_cancel: start\n");
4642141cc406Sopenharmony_ci  s->started = 0;
4643141cc406Sopenharmony_ci  DBG (10, "sane_cancel: finish\n");
4644141cc406Sopenharmony_ci}
4645141cc406Sopenharmony_ci
4646141cc406Sopenharmony_ci/*
4647141cc406Sopenharmony_ci * Ends use of the scanner.
4648141cc406Sopenharmony_ci *
4649141cc406Sopenharmony_ci * From the SANE spec:
4650141cc406Sopenharmony_ci * This function terminates the association between the device handle
4651141cc406Sopenharmony_ci * passed in argument h and the device it represents. If the device is
4652141cc406Sopenharmony_ci * presently active, a call to sane_cancel() is performed first. After
4653141cc406Sopenharmony_ci * this function returns, handle h must not be used anymore.
4654141cc406Sopenharmony_ci */
4655141cc406Sopenharmony_civoid
4656141cc406Sopenharmony_cisane_close (SANE_Handle handle)
4657141cc406Sopenharmony_ci{
4658141cc406Sopenharmony_ci  struct scanner * s = (struct scanner *) handle;
4659141cc406Sopenharmony_ci
4660141cc406Sopenharmony_ci  DBG (10, "sane_close: start\n");
4661141cc406Sopenharmony_ci
4662141cc406Sopenharmony_ci  /* still connected- drop it */
4663141cc406Sopenharmony_ci  if(s->fd >= 0){
4664141cc406Sopenharmony_ci      sane_cancel(handle);
4665141cc406Sopenharmony_ci      lamp(s, 0);
4666141cc406Sopenharmony_ci      disconnect_fd(s);
4667141cc406Sopenharmony_ci  }
4668141cc406Sopenharmony_ci
4669141cc406Sopenharmony_ci  DBG (10, "sane_close: finish\n");
4670141cc406Sopenharmony_ci}
4671141cc406Sopenharmony_ci
4672141cc406Sopenharmony_cistatic SANE_Status
4673141cc406Sopenharmony_cidisconnect_fd (struct scanner *s)
4674141cc406Sopenharmony_ci{
4675141cc406Sopenharmony_ci  DBG (10, "disconnect_fd: start\n");
4676141cc406Sopenharmony_ci
4677141cc406Sopenharmony_ci  if(s->fd > -1){
4678141cc406Sopenharmony_ci    DBG (15, "disconnecting usb device\n");
4679141cc406Sopenharmony_ci    sanei_usb_close (s->fd);
4680141cc406Sopenharmony_ci    s->fd = -1;
4681141cc406Sopenharmony_ci  }
4682141cc406Sopenharmony_ci
4683141cc406Sopenharmony_ci  DBG (10, "disconnect_fd: finish\n");
4684141cc406Sopenharmony_ci
4685141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
4686141cc406Sopenharmony_ci}
4687141cc406Sopenharmony_ci
4688141cc406Sopenharmony_cistatic SANE_Status
4689141cc406Sopenharmony_cidestroy(struct scanner *s)
4690141cc406Sopenharmony_ci{
4691141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
4692141cc406Sopenharmony_ci
4693141cc406Sopenharmony_ci    DBG (10, "destroy: start\n");
4694141cc406Sopenharmony_ci
4695141cc406Sopenharmony_ci    teardown_buffers(s);
4696141cc406Sopenharmony_ci
4697141cc406Sopenharmony_ci    if(s->sane.name){
4698141cc406Sopenharmony_ci      free((void *) s->sane.name);
4699141cc406Sopenharmony_ci    }
4700141cc406Sopenharmony_ci    if(s->sane.vendor){
4701141cc406Sopenharmony_ci      free((void *) s->sane.vendor);
4702141cc406Sopenharmony_ci    }
4703141cc406Sopenharmony_ci    if(s->sane.model){
4704141cc406Sopenharmony_ci      free((void *) s->sane.model);
4705141cc406Sopenharmony_ci    }
4706141cc406Sopenharmony_ci
4707141cc406Sopenharmony_ci    free(s);
4708141cc406Sopenharmony_ci
4709141cc406Sopenharmony_ci    DBG (10, "destroy: finish\n");
4710141cc406Sopenharmony_ci    return ret;
4711141cc406Sopenharmony_ci}
4712141cc406Sopenharmony_ci
4713141cc406Sopenharmony_cistatic SANE_Status
4714141cc406Sopenharmony_citeardown_buffers(struct scanner *s)
4715141cc406Sopenharmony_ci{
4716141cc406Sopenharmony_ci    SANE_Status ret = SANE_STATUS_GOOD;
4717141cc406Sopenharmony_ci
4718141cc406Sopenharmony_ci    DBG (10, "teardown_buffers: start\n");
4719141cc406Sopenharmony_ci
4720141cc406Sopenharmony_ci    /* temporary cal data */
4721141cc406Sopenharmony_ci    if(s->coarsecal.buffer){
4722141cc406Sopenharmony_ci        free(s->coarsecal.buffer);
4723141cc406Sopenharmony_ci	s->coarsecal.buffer = NULL;
4724141cc406Sopenharmony_ci    }
4725141cc406Sopenharmony_ci
4726141cc406Sopenharmony_ci    if(s->darkcal.buffer){
4727141cc406Sopenharmony_ci        free(s->darkcal.buffer);
4728141cc406Sopenharmony_ci	s->darkcal.buffer = NULL;
4729141cc406Sopenharmony_ci    }
4730141cc406Sopenharmony_ci
4731141cc406Sopenharmony_ci    if(s->sendcal.buffer){
4732141cc406Sopenharmony_ci        free(s->sendcal.buffer);
4733141cc406Sopenharmony_ci	s->sendcal.buffer = NULL;
4734141cc406Sopenharmony_ci    }
4735141cc406Sopenharmony_ci
4736141cc406Sopenharmony_ci    if(s->cal_image.raw_data){
4737141cc406Sopenharmony_ci        free(s->cal_image.raw_data);
4738141cc406Sopenharmony_ci	s->cal_image.raw_data = NULL;
4739141cc406Sopenharmony_ci    }
4740141cc406Sopenharmony_ci
4741141cc406Sopenharmony_ci    if(s->cal_data.raw_data){
4742141cc406Sopenharmony_ci        free(s->cal_data.raw_data);
4743141cc406Sopenharmony_ci	s->cal_data.raw_data = NULL;
4744141cc406Sopenharmony_ci    }
4745141cc406Sopenharmony_ci
4746141cc406Sopenharmony_ci    /* image slice */
4747141cc406Sopenharmony_ci    if(s->block_img.buffer){
4748141cc406Sopenharmony_ci        free(s->block_img.buffer);
4749141cc406Sopenharmony_ci	s->block_img.buffer = NULL;
4750141cc406Sopenharmony_ci    }
4751141cc406Sopenharmony_ci    if(s->block_xfr.raw_data){
4752141cc406Sopenharmony_ci        free(s->block_xfr.raw_data);
4753141cc406Sopenharmony_ci	s->block_xfr.raw_data = NULL;
4754141cc406Sopenharmony_ci    }
4755141cc406Sopenharmony_ci
4756141cc406Sopenharmony_ci    /* dynamic thresh slice */
4757141cc406Sopenharmony_ci    if(s->dt.buffer){
4758141cc406Sopenharmony_ci        free(s->dt.buffer);
4759141cc406Sopenharmony_ci	s->dt.buffer = NULL;
4760141cc406Sopenharmony_ci    }
4761141cc406Sopenharmony_ci
4762141cc406Sopenharmony_ci    /* image buffer to hold frontside data */
4763141cc406Sopenharmony_ci    if(s->front.buffer){
4764141cc406Sopenharmony_ci        free(s->front.buffer);
4765141cc406Sopenharmony_ci	s->front.buffer = NULL;
4766141cc406Sopenharmony_ci    }
4767141cc406Sopenharmony_ci
4768141cc406Sopenharmony_ci    /* image buffer to hold backside data */
4769141cc406Sopenharmony_ci    if(s->back.buffer){
4770141cc406Sopenharmony_ci        free(s->back.buffer);
4771141cc406Sopenharmony_ci	s->back.buffer = NULL;
4772141cc406Sopenharmony_ci    }
4773141cc406Sopenharmony_ci
4774141cc406Sopenharmony_ci    DBG (10, "teardown_buffers: finish\n");
4775141cc406Sopenharmony_ci    return ret;
4776141cc406Sopenharmony_ci}
4777141cc406Sopenharmony_ci
4778141cc406Sopenharmony_ci/*
4779141cc406Sopenharmony_ci * Terminates the backend.
4780141cc406Sopenharmony_ci *
4781141cc406Sopenharmony_ci * From the SANE spec:
4782141cc406Sopenharmony_ci * This function must be called to terminate use of a backend. The
4783141cc406Sopenharmony_ci * function will first close all device handles that still might be
4784141cc406Sopenharmony_ci * open (it is recommended to close device handles explicitly through
4785141cc406Sopenharmony_ci * a call to sane_close(), but backends are required to release all
4786141cc406Sopenharmony_ci * resources upon a call to this function). After this function
4787141cc406Sopenharmony_ci * returns, no function other than sane_init() may be called
4788141cc406Sopenharmony_ci * (regardless of the status value returned by sane_exit(). Neglecting
4789141cc406Sopenharmony_ci * to call this function may result in some resources not being
4790141cc406Sopenharmony_ci * released properly.
4791141cc406Sopenharmony_ci */
4792141cc406Sopenharmony_civoid
4793141cc406Sopenharmony_cisane_exit (void)
4794141cc406Sopenharmony_ci{
4795141cc406Sopenharmony_ci  struct scanner *dev, *next;
4796141cc406Sopenharmony_ci
4797141cc406Sopenharmony_ci  DBG (10, "sane_exit: start\n");
4798141cc406Sopenharmony_ci
4799141cc406Sopenharmony_ci  for (dev = scanner_devList; dev; dev = next) {
4800141cc406Sopenharmony_ci      next = dev->next;
4801141cc406Sopenharmony_ci      destroy(dev);
4802141cc406Sopenharmony_ci  }
4803141cc406Sopenharmony_ci
4804141cc406Sopenharmony_ci  if (sane_devArray)
4805141cc406Sopenharmony_ci    free (sane_devArray);
4806141cc406Sopenharmony_ci
4807141cc406Sopenharmony_ci  scanner_devList = NULL;
4808141cc406Sopenharmony_ci  sane_devArray = NULL;
4809141cc406Sopenharmony_ci
4810141cc406Sopenharmony_ci  DBG (10, "sane_exit: finish\n");
4811141cc406Sopenharmony_ci}
4812141cc406Sopenharmony_ci
4813141cc406Sopenharmony_ci/*
4814141cc406Sopenharmony_ci * @@ Section 5 - misc helper functions
4815141cc406Sopenharmony_ci */
4816141cc406Sopenharmony_ci/*
4817141cc406Sopenharmony_ci * take a bunch of pointers, send commands to scanner
4818141cc406Sopenharmony_ci */
4819141cc406Sopenharmony_cistatic SANE_Status
4820141cc406Sopenharmony_cido_cmd(struct scanner *s, int shortTime,
4821141cc406Sopenharmony_ci unsigned char * cmdBuff, size_t cmdLen,
4822141cc406Sopenharmony_ci unsigned char * outBuff, size_t outLen,
4823141cc406Sopenharmony_ci unsigned char * inBuff, size_t * inLen
4824141cc406Sopenharmony_ci)
4825141cc406Sopenharmony_ci{
4826141cc406Sopenharmony_ci    /* sanei_usb overwrites the transfer size, so make some local copies */
4827141cc406Sopenharmony_ci    size_t loc_cmdLen = cmdLen;
4828141cc406Sopenharmony_ci    size_t loc_outLen = outLen;
4829141cc406Sopenharmony_ci    size_t loc_inLen = 0;
4830141cc406Sopenharmony_ci
4831141cc406Sopenharmony_ci    int cmdTime = USB_COMMAND_TIME;
4832141cc406Sopenharmony_ci    int outTime = USB_DATA_TIME;
4833141cc406Sopenharmony_ci    int inTime = USB_DATA_TIME;
4834141cc406Sopenharmony_ci
4835141cc406Sopenharmony_ci    int ret = 0;
4836141cc406Sopenharmony_ci
4837141cc406Sopenharmony_ci    DBG (10, "do_cmd: start\n");
4838141cc406Sopenharmony_ci
4839141cc406Sopenharmony_ci    if(shortTime){
4840141cc406Sopenharmony_ci        cmdTime /= 20;
4841141cc406Sopenharmony_ci        outTime /= 20;
4842141cc406Sopenharmony_ci        inTime /= 20;
4843141cc406Sopenharmony_ci    }
4844141cc406Sopenharmony_ci
4845141cc406Sopenharmony_ci    /* this command has a cmd component, and a place to get it */
4846141cc406Sopenharmony_ci    if(cmdBuff && cmdLen && cmdTime){
4847141cc406Sopenharmony_ci
4848141cc406Sopenharmony_ci        /* change timeout */
4849141cc406Sopenharmony_ci        sanei_usb_set_timeout(cmdTime);
4850141cc406Sopenharmony_ci
4851141cc406Sopenharmony_ci        /* write the command out */
4852141cc406Sopenharmony_ci        DBG(25, "cmd: writing %ld bytes, timeout %d\n", (long)cmdLen, cmdTime);
4853141cc406Sopenharmony_ci        hexdump(30, "cmd: >>", cmdBuff, cmdLen);
4854141cc406Sopenharmony_ci        ret = sanei_usb_write_bulk(s->fd, cmdBuff, &cmdLen);
4855141cc406Sopenharmony_ci        DBG(25, "cmd: wrote %ld bytes, retVal %d\n", (long)cmdLen, ret);
4856141cc406Sopenharmony_ci
4857141cc406Sopenharmony_ci        if(ret == SANE_STATUS_EOF){
4858141cc406Sopenharmony_ci            DBG(5,"cmd: got EOF, returning IO_ERROR\n");
4859141cc406Sopenharmony_ci            return SANE_STATUS_IO_ERROR;
4860141cc406Sopenharmony_ci        }
4861141cc406Sopenharmony_ci        if(ret != SANE_STATUS_GOOD){
4862141cc406Sopenharmony_ci            DBG(5,"cmd: return error '%s'\n",sane_strstatus(ret));
4863141cc406Sopenharmony_ci            return ret;
4864141cc406Sopenharmony_ci        }
4865141cc406Sopenharmony_ci        if(loc_cmdLen != cmdLen){
4866141cc406Sopenharmony_ci            DBG(5,"cmd: wrong size %ld/%ld\n", (long)loc_cmdLen, (long)cmdLen);
4867141cc406Sopenharmony_ci            return SANE_STATUS_IO_ERROR;
4868141cc406Sopenharmony_ci        }
4869141cc406Sopenharmony_ci    }
4870141cc406Sopenharmony_ci
4871141cc406Sopenharmony_ci    /* this command has a write component, and a place to get it */
4872141cc406Sopenharmony_ci    if(outBuff && outLen && outTime){
4873141cc406Sopenharmony_ci
4874141cc406Sopenharmony_ci        /* change timeout */
4875141cc406Sopenharmony_ci        sanei_usb_set_timeout(outTime);
4876141cc406Sopenharmony_ci
4877141cc406Sopenharmony_ci        DBG(25, "out: writing %ld bytes, timeout %d\n", (long)outLen, outTime);
4878141cc406Sopenharmony_ci        hexdump(30, "out: >>", outBuff, outLen);
4879141cc406Sopenharmony_ci        ret = sanei_usb_write_bulk(s->fd, outBuff, &outLen);
4880141cc406Sopenharmony_ci        DBG(25, "out: wrote %ld bytes, retVal %d\n", (long)outLen, ret);
4881141cc406Sopenharmony_ci
4882141cc406Sopenharmony_ci        if(ret == SANE_STATUS_EOF){
4883141cc406Sopenharmony_ci            DBG(5,"out: got EOF, returning IO_ERROR\n");
4884141cc406Sopenharmony_ci            return SANE_STATUS_IO_ERROR;
4885141cc406Sopenharmony_ci        }
4886141cc406Sopenharmony_ci        if(ret != SANE_STATUS_GOOD){
4887141cc406Sopenharmony_ci            DBG(5,"out: return error '%s'\n",sane_strstatus(ret));
4888141cc406Sopenharmony_ci            return ret;
4889141cc406Sopenharmony_ci        }
4890141cc406Sopenharmony_ci        if(loc_outLen != outLen){
4891141cc406Sopenharmony_ci            DBG(5,"out: wrong size %ld/%ld\n", (long)loc_outLen, (long)outLen);
4892141cc406Sopenharmony_ci            return SANE_STATUS_IO_ERROR;
4893141cc406Sopenharmony_ci        }
4894141cc406Sopenharmony_ci    }
4895141cc406Sopenharmony_ci
4896141cc406Sopenharmony_ci    /* this command has a read component, and a place to put it */
4897141cc406Sopenharmony_ci    if(inBuff && inLen && inTime){
4898141cc406Sopenharmony_ci
4899141cc406Sopenharmony_ci        loc_inLen = *inLen;
4900141cc406Sopenharmony_ci        DBG(25, "in: memset %ld bytes\n", (long)*inLen);
4901141cc406Sopenharmony_ci        memset(inBuff,0,*inLen);
4902141cc406Sopenharmony_ci
4903141cc406Sopenharmony_ci        /* change timeout */
4904141cc406Sopenharmony_ci        sanei_usb_set_timeout(inTime);
4905141cc406Sopenharmony_ci
4906141cc406Sopenharmony_ci        DBG(25, "in: reading %ld bytes, timeout %d\n", (long)*inLen, inTime);
4907141cc406Sopenharmony_ci        ret = sanei_usb_read_bulk(s->fd, inBuff, inLen);
4908141cc406Sopenharmony_ci        DBG(25, "in: retVal %d\n", ret);
4909141cc406Sopenharmony_ci
4910141cc406Sopenharmony_ci        if(ret == SANE_STATUS_EOF){
4911141cc406Sopenharmony_ci            DBG(5,"in: got EOF, continuing\n");
4912141cc406Sopenharmony_ci        }
4913141cc406Sopenharmony_ci        else if(ret != SANE_STATUS_GOOD){
4914141cc406Sopenharmony_ci            DBG(5,"in: return error '%s'\n",sane_strstatus(ret));
4915141cc406Sopenharmony_ci            return ret;
4916141cc406Sopenharmony_ci        }
4917141cc406Sopenharmony_ci
4918141cc406Sopenharmony_ci        DBG(25, "in: read %ld bytes\n", (long)*inLen);
4919141cc406Sopenharmony_ci        if(*inLen){
4920141cc406Sopenharmony_ci            hexdump(30, "in: <<", inBuff, *inLen);
4921141cc406Sopenharmony_ci        }
4922141cc406Sopenharmony_ci
4923141cc406Sopenharmony_ci        if(loc_inLen != *inLen){
4924141cc406Sopenharmony_ci            ret = SANE_STATUS_EOF;
4925141cc406Sopenharmony_ci            DBG(5,"in: short read %ld/%ld\n", (long)loc_inLen, (long)*inLen);
4926141cc406Sopenharmony_ci        }
4927141cc406Sopenharmony_ci    }
4928141cc406Sopenharmony_ci
4929141cc406Sopenharmony_ci    DBG (10, "do_cmd: finish\n");
4930141cc406Sopenharmony_ci
4931141cc406Sopenharmony_ci    return ret;
4932141cc406Sopenharmony_ci}
4933141cc406Sopenharmony_ci
4934141cc406Sopenharmony_ci/**
4935141cc406Sopenharmony_ci * Convenience method to determine longest string size in a list.
4936141cc406Sopenharmony_ci */
4937141cc406Sopenharmony_cistatic size_t
4938141cc406Sopenharmony_cimaxStringSize (const SANE_String_Const strings[])
4939141cc406Sopenharmony_ci{
4940141cc406Sopenharmony_ci  size_t size, max_size = 0;
4941141cc406Sopenharmony_ci  int i;
4942141cc406Sopenharmony_ci
4943141cc406Sopenharmony_ci  for (i = 0; strings[i]; ++i) {
4944141cc406Sopenharmony_ci    size = strlen (strings[i]) + 1;
4945141cc406Sopenharmony_ci    max_size = MAX(max_size, size);
4946141cc406Sopenharmony_ci  }
4947141cc406Sopenharmony_ci
4948141cc406Sopenharmony_ci  return max_size;
4949141cc406Sopenharmony_ci}
4950141cc406Sopenharmony_ci
4951141cc406Sopenharmony_ci/**
4952141cc406Sopenharmony_ci * Prints a hex dump of the given buffer onto the debug output stream.
4953141cc406Sopenharmony_ci */
4954141cc406Sopenharmony_cistatic void
4955141cc406Sopenharmony_cihexdump (int level, char *comment, unsigned char *p, int l)
4956141cc406Sopenharmony_ci{
4957141cc406Sopenharmony_ci  int i;
4958141cc406Sopenharmony_ci  char line[128];
4959141cc406Sopenharmony_ci  char *ptr;
4960141cc406Sopenharmony_ci
4961141cc406Sopenharmony_ci  if(DBG_LEVEL < level)
4962141cc406Sopenharmony_ci    return;
4963141cc406Sopenharmony_ci
4964141cc406Sopenharmony_ci  DBG (level, "%s\n", comment);
4965141cc406Sopenharmony_ci  ptr = line;
4966141cc406Sopenharmony_ci  for (i = 0; i < l; i++, p++)
4967141cc406Sopenharmony_ci    {
4968141cc406Sopenharmony_ci      if ((i % 16) == 0)
4969141cc406Sopenharmony_ci        {
4970141cc406Sopenharmony_ci          if (ptr != line)
4971141cc406Sopenharmony_ci            {
4972141cc406Sopenharmony_ci              *ptr = '\0';
4973141cc406Sopenharmony_ci              DBG (level, "%s\n", line);
4974141cc406Sopenharmony_ci              ptr = line;
4975141cc406Sopenharmony_ci            }
4976141cc406Sopenharmony_ci          sprintf (ptr, "%3.3x:", i);
4977141cc406Sopenharmony_ci          ptr += 4;
4978141cc406Sopenharmony_ci        }
4979141cc406Sopenharmony_ci      sprintf (ptr, " %2.2x", *p);
4980141cc406Sopenharmony_ci      ptr += 3;
4981141cc406Sopenharmony_ci    }
4982141cc406Sopenharmony_ci  *ptr = '\0';
4983141cc406Sopenharmony_ci  DBG (level, "%s\n", line);
4984141cc406Sopenharmony_ci}
4985141cc406Sopenharmony_ci
4986141cc406Sopenharmony_ci/**
4987141cc406Sopenharmony_ci * An advanced method we don't support but have to define.
4988141cc406Sopenharmony_ci */
4989141cc406Sopenharmony_ciSANE_Status
4990141cc406Sopenharmony_cisane_set_io_mode (SANE_Handle h, SANE_Bool non_blocking)
4991141cc406Sopenharmony_ci{
4992141cc406Sopenharmony_ci  DBG (10, "sane_set_io_mode\n");
4993141cc406Sopenharmony_ci  DBG (15, "%d %p\n", non_blocking, h);
4994141cc406Sopenharmony_ci  return SANE_STATUS_UNSUPPORTED;
4995141cc406Sopenharmony_ci}
4996141cc406Sopenharmony_ci
4997141cc406Sopenharmony_ci/**
4998141cc406Sopenharmony_ci * An advanced method we don't support but have to define.
4999141cc406Sopenharmony_ci */
5000141cc406Sopenharmony_ciSANE_Status
5001141cc406Sopenharmony_cisane_get_select_fd (SANE_Handle h, SANE_Int *fdp)
5002141cc406Sopenharmony_ci{
5003141cc406Sopenharmony_ci  DBG (10, "sane_get_select_fd\n");
5004141cc406Sopenharmony_ci  DBG (15, "%p %d\n", h, *fdp);
5005141cc406Sopenharmony_ci  return SANE_STATUS_UNSUPPORTED;
5006141cc406Sopenharmony_ci}
5007141cc406Sopenharmony_ci
5008141cc406Sopenharmony_ci/* s->page_width stores the user setting
5009141cc406Sopenharmony_ci * for the paper width in adf. sometimes,
5010141cc406Sopenharmony_ci * we need a value that differs from this
5011141cc406Sopenharmony_ci * due to using FB
5012141cc406Sopenharmony_ci */
5013141cc406Sopenharmony_cistatic int
5014141cc406Sopenharmony_ciget_page_width(struct scanner *s)
5015141cc406Sopenharmony_ci{
5016141cc406Sopenharmony_ci  /* scanner max for fb */
5017141cc406Sopenharmony_ci  if(s->source == SOURCE_FLATBED){
5018141cc406Sopenharmony_ci      return s->max_x;
5019141cc406Sopenharmony_ci  }
5020141cc406Sopenharmony_ci
5021141cc406Sopenharmony_ci  return s->page_width;
5022141cc406Sopenharmony_ci}
5023141cc406Sopenharmony_ci
5024141cc406Sopenharmony_ci/* s->page_height stores the user setting
5025141cc406Sopenharmony_ci * for the paper height in adf. sometimes,
5026141cc406Sopenharmony_ci * we need a value that differs from this
5027141cc406Sopenharmony_ci * due to using FB.
5028141cc406Sopenharmony_ci */
5029141cc406Sopenharmony_cistatic int
5030141cc406Sopenharmony_ciget_page_height(struct scanner *s)
5031141cc406Sopenharmony_ci{
5032141cc406Sopenharmony_ci  /* scanner max for fb */
5033141cc406Sopenharmony_ci  if(s->source == SOURCE_FLATBED){
5034141cc406Sopenharmony_ci      return s->max_y;
5035141cc406Sopenharmony_ci  }
5036141cc406Sopenharmony_ci
5037141cc406Sopenharmony_ci  return s->page_height;
5038141cc406Sopenharmony_ci}
5039