1 /* sane - Scanner Access Now Easy.
2 
3    Copyright (C) 2003, 2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
4    Copyright (C) 2004, 2005 Gerhard Jaeger <gerhard@gjaeger.de>
5    Copyright (C) 2004-2016 Stéphane Voltz <stef.dev@free.fr>
6    Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
7    Copyright (C) 2006 Laurent Charpentier <laurent_pubs@yahoo.com>
8    Copyright (C) 2007 Luke <iceyfor@gmail.com>
9    Copyright (C) 2010 Chris Berry <s0457957@sms.ed.ac.uk> and Michael Rickmann <mrickma@gwdg.de>
10                  for Plustek Opticbook 3600 support
11 
12    Dynamic rasterization code was taken from the epjistsu backend by
13    m. allan noah <kitno455 at gmail dot com>
14 
15    Software processing for deskew, crop and dspeckle are inspired by allan's
16    noah work in the fujitsu backend
17 
18    This file is part of the SANE package.
19 
20    This program is free software; you can redistribute it and/or
21    modify it under the terms of the GNU General Public License as
22    published by the Free Software Foundation; either version 2 of the
23    License, or (at your option) any later version.
24 
25    This program is distributed in the hope that it will be useful, but
26    WITHOUT ANY WARRANTY; without even the implied warranty of
27    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
28    General Public License for more details.
29 
30    You should have received a copy of the GNU General Public License
31    along with this program.  If not, see <https://www.gnu.org/licenses/>.
32 
33    As a special exception, the authors of SANE give permission for
34    additional uses of the libraries contained in this release of SANE.
35 
36    The exception is that, if you link a SANE library with other files
37    to produce an executable, this does not by itself cause the
38    resulting executable to be covered by the GNU General Public
39    License.  Your use of that executable is in no way restricted on
40    account of linking the SANE library code into it.
41 
42    This exception does not, however, invalidate any other reasons why
43    the executable file might be covered by the GNU General Public
44    License.
45 
46    If you submit changes to SANE to the maintainers to be included in
47    a subsequent release, you agree by submitting the changes that
48    those changes may be distributed with this exception intact.
49 
50    If you write modifications of your own for SANE, it is your choice
51    whether to permit this exception to apply to your modifications.
52    If you do not wish that, delete this exception notice.
53 */
54 
55 /*
56  * SANE backend for Genesys Logic GL646/GL841/GL842/GL843/GL846/GL847/GL124 based scanners
57  */
58 
59 #define DEBUG_NOT_STATIC
60 
61 #include "genesys.h"
62 #include "gl124_registers.h"
63 #include "gl841_registers.h"
64 #include "gl842_registers.h"
65 #include "gl843_registers.h"
66 #include "gl846_registers.h"
67 #include "gl847_registers.h"
68 #include "usb_device.h"
69 #include "utilities.h"
70 #include "scanner_interface_usb.h"
71 #include "test_scanner_interface.h"
72 #include "test_settings.h"
73 #include "../include/sane/sanei_config.h"
74 
75 #include <array>
76 #include <cmath>
77 #include <cstring>
78 #include <fstream>
79 #include <iterator>
80 #include <list>
81 #include <numeric>
82 #include <exception>
83 #include <vector>
84 
85 #ifndef SANE_GENESYS_API_LINKAGE
86 #define SANE_GENESYS_API_LINKAGE extern "C"
87 #endif
88 
89 namespace genesys {
90 
91 // Data that we allocate to back SANE_Device objects in s_sane_devices
92 struct SANE_Device_Data
93 {
94     std::string name;
95 };
96 
97 namespace {
98     StaticInit<std::list<Genesys_Scanner>> s_scanners;
99     StaticInit<std::vector<SANE_Device>> s_sane_devices;
100     StaticInit<std::vector<SANE_Device_Data>> s_sane_devices_data;
101     StaticInit<std::vector<SANE_Device*>> s_sane_devices_ptrs;
102     StaticInit<std::list<Genesys_Device>> s_devices;
103 
104     // Maximum time for lamp warm-up
105     constexpr unsigned WARMUP_TIME = 65;
106 } // namespace
107 
108 static SANE_String_Const mode_list[] = {
109   SANE_VALUE_SCAN_MODE_COLOR,
110   SANE_VALUE_SCAN_MODE_GRAY,
111     // SANE_TITLE_HALFTONE, not used
112     // SANE_VALUE_SCAN_MODE_LINEART, not used
113     nullptr
114 };
115 
116 static SANE_String_Const color_filter_list[] = {
117   SANE_I18N ("Red"),
118   SANE_I18N ("Green"),
119   SANE_I18N ("Blue"),
120     nullptr
121 };
122 
123 static SANE_String_Const cis_color_filter_list[] = {
124   SANE_I18N ("Red"),
125   SANE_I18N ("Green"),
126   SANE_I18N ("Blue"),
127   SANE_I18N ("None"),
128     nullptr
129 };
130 
131 static SANE_Range time_range = {
132   0,				/* minimum */
133   60,				/* maximum */
134   0				/* quantization */
135 };
136 
137 static const SANE_Range u12_range = {
138   0,				/* minimum */
139   4095,				/* maximum */
140   0				/* quantization */
141 };
142 
143 static const SANE_Range u14_range = {
144   0,				/* minimum */
145   16383,			/* maximum */
146   0				/* quantization */
147 };
148 
149 static const SANE_Range u16_range = {
150   0,				/* minimum */
151   65535,			/* maximum */
152   0				/* quantization */
153 };
154 
155 static const SANE_Range percentage_range = {
156     float_to_fixed(0),     // minimum
157     float_to_fixed(100),   // maximum
158     float_to_fixed(1)      // quantization
159 };
160 
161 /**
162  * range for brightness and contrast
163  */
164 static const SANE_Range enhance_range = {
165   -100,	/* minimum */
166   100,		/* maximum */
167   1		/* quantization */
168 };
169 
170 /**
171  * range for expiration time
172  */
173 static const SANE_Range expiration_range = {
174   -1,	        /* minimum */
175   30000,	/* maximum */
176   1		/* quantization */
177 };
178 
sanei_genesys_find_sensor_any(const Genesys_Device * dev)179 const Genesys_Sensor& sanei_genesys_find_sensor_any(const Genesys_Device* dev)
180 {
181     DBG_HELPER(dbg);
182     for (const auto& sensor : *s_sensors) {
183         if (dev->model->sensor_id == sensor.sensor_id) {
184             return sensor;
185         }
186     }
187     throw std::runtime_error("Given device does not have sensor defined");
188 }
189 
find_sensor_impl(const Genesys_Device * dev,unsigned dpi,unsigned channels,ScanMethod scan_method)190 Genesys_Sensor* find_sensor_impl(const Genesys_Device* dev, unsigned dpi, unsigned channels,
191                                  ScanMethod scan_method)
192 {
193     DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels,
194                     static_cast<unsigned>(scan_method));
195     for (auto& sensor : *s_sensors) {
196         if (dev->model->sensor_id == sensor.sensor_id && sensor.resolutions.matches(dpi) &&
197             sensor.matches_channel_count(channels) && sensor.method == scan_method)
198         {
199             return &sensor;
200         }
201     }
202     return nullptr;
203 }
204 
sanei_genesys_has_sensor(const Genesys_Device * dev,unsigned dpi,unsigned channels,ScanMethod scan_method)205 bool sanei_genesys_has_sensor(const Genesys_Device* dev, unsigned dpi, unsigned channels,
206                               ScanMethod scan_method)
207 {
208     DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels,
209                     static_cast<unsigned>(scan_method));
210     return find_sensor_impl(dev, dpi, channels, scan_method) != nullptr;
211 }
212 
sanei_genesys_find_sensor(const Genesys_Device * dev,unsigned dpi,unsigned channels,ScanMethod scan_method)213 const Genesys_Sensor& sanei_genesys_find_sensor(const Genesys_Device* dev, unsigned dpi,
214                                                 unsigned channels, ScanMethod scan_method)
215 {
216     DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels,
217                     static_cast<unsigned>(scan_method));
218     const auto* sensor = find_sensor_impl(dev, dpi, channels, scan_method);
219     if (sensor)
220         return *sensor;
221     throw std::runtime_error("Given device does not have sensor defined");
222 }
223 
sanei_genesys_find_sensor_for_write(Genesys_Device * dev,unsigned dpi,unsigned channels,ScanMethod scan_method)224 Genesys_Sensor& sanei_genesys_find_sensor_for_write(Genesys_Device* dev, unsigned dpi,
225                                                     unsigned channels,
226                                                     ScanMethod scan_method)
227 {
228     DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels,
229                     static_cast<unsigned>(scan_method));
230     auto* sensor = find_sensor_impl(dev, dpi, channels, scan_method);
231     if (sensor)
232         return *sensor;
233     throw std::runtime_error("Given device does not have sensor defined");
234 }
235 
236 
237 std::vector<std::reference_wrapper<const Genesys_Sensor>>
sanei_genesys_find_sensors_all(const Genesys_Device * dev,ScanMethod scan_method)238     sanei_genesys_find_sensors_all(const Genesys_Device* dev, ScanMethod scan_method)
239 {
240     DBG_HELPER_ARGS(dbg, "scan_method: %d", static_cast<unsigned>(scan_method));
241     std::vector<std::reference_wrapper<const Genesys_Sensor>> ret;
242     for (auto& sensor : *s_sensors) {
243         if (dev->model->sensor_id == sensor.sensor_id && sensor.method == scan_method) {
244             ret.push_back(sensor);
245         }
246     }
247     return ret;
248 }
249 
250 std::vector<std::reference_wrapper<Genesys_Sensor>>
sanei_genesys_find_sensors_all_for_write(Genesys_Device * dev,ScanMethod scan_method)251     sanei_genesys_find_sensors_all_for_write(Genesys_Device* dev, ScanMethod scan_method)
252 {
253     DBG_HELPER_ARGS(dbg, "scan_method: %d", static_cast<unsigned>(scan_method));
254     std::vector<std::reference_wrapper<Genesys_Sensor>> ret;
255     for (auto& sensor : *s_sensors) {
256         if (dev->model->sensor_id == sensor.sensor_id && sensor.method == scan_method) {
257             ret.push_back(sensor);
258         }
259     }
260     return ret;
261 }
262 
sanei_genesys_init_structs(Genesys_Device * dev)263 void sanei_genesys_init_structs (Genesys_Device * dev)
264 {
265     DBG_HELPER(dbg);
266 
267     bool gpo_ok = false;
268     bool motor_ok = false;
269     bool fe_ok = false;
270 
271   /* initialize the GPO data stuff */
272     for (const auto& gpo : *s_gpo) {
273         if (dev->model->gpio_id == gpo.id) {
274             dev->gpo = gpo;
275             gpo_ok = true;
276             break;
277         }
278     }
279 
280     // initialize the motor data stuff
281     for (const auto& motor : *s_motors) {
282         if (dev->model->motor_id == motor.id) {
283             dev->motor = motor;
284             motor_ok = true;
285             break;
286         }
287     }
288 
289     for (const auto& frontend : *s_frontends) {
290         if (dev->model->adc_id == frontend.id) {
291             dev->frontend_initial = frontend;
292             dev->frontend = frontend;
293             fe_ok = true;
294             break;
295         }
296     }
297 
298     if (dev->model->asic_type == AsicType::GL845 ||
299         dev->model->asic_type == AsicType::GL846 ||
300         dev->model->asic_type == AsicType::GL847 ||
301         dev->model->asic_type == AsicType::GL124)
302     {
303         bool memory_layout_found = false;
304         for (const auto& memory_layout : *s_memory_layout) {
305             if (memory_layout.models.matches(dev->model->model_id)) {
306                 dev->memory_layout = memory_layout;
307                 memory_layout_found = true;
308                 break;
309             }
310         }
311         if (!memory_layout_found) {
312             throw SaneException("Could not find memory layout");
313         }
314     }
315 
316     if (!motor_ok || !gpo_ok || !fe_ok) {
317         throw SaneException("bad description(s) for fe/gpo/motor=%d/%d/%d\n",
318                             static_cast<unsigned>(dev->model->sensor_id),
319                             static_cast<unsigned>(dev->model->gpio_id),
320                             static_cast<unsigned>(dev->model->motor_id));
321     }
322 }
323 
324 /** @brief computes gamma table
325  * Generates a gamma table of the given length within 0 and the given
326  * maximum value
327  * @param gamma_table gamma table to fill
328  * @param size size of the table
329  * @param maximum value allowed for gamma
330  * @param gamma_max maximum gamma value
331  * @param gamma gamma to compute values
332  * @return a gamma table filled with the computed values
333  * */
334 void
sanei_genesys_create_gamma_table(std::vector<uint16_t> & gamma_table,int size,float maximum,float gamma_max,float gamma)335 sanei_genesys_create_gamma_table (std::vector<uint16_t>& gamma_table, int size,
336                                   float maximum, float gamma_max, float gamma)
337 {
338     gamma_table.clear();
339     gamma_table.resize(size, 0);
340 
341   int i;
342   float value;
343 
344   DBG(DBG_proc, "%s: size = %d, ""maximum = %g, gamma_max = %g, gamma = %g\n", __func__, size,
345       maximum, gamma_max, gamma);
346   for (i = 0; i < size; i++)
347     {
348         value = static_cast<float>(gamma_max * std::pow(static_cast<double>(i) / size, 1.0 / gamma));
349         if (value > maximum) {
350             value = maximum;
351         }
352         gamma_table[i] = static_cast<std::uint16_t>(value);
353     }
354   DBG(DBG_proc, "%s: completed\n", __func__);
355 }
356 
sanei_genesys_create_default_gamma_table(Genesys_Device * dev,std::vector<uint16_t> & gamma_table,float gamma)357 void sanei_genesys_create_default_gamma_table(Genesys_Device* dev,
358                                               std::vector<uint16_t>& gamma_table, float gamma)
359 {
360     int size = 0;
361     int max = 0;
362     if (dev->model->asic_type == AsicType::GL646) {
363         if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) {
364             size = 16384;
365         } else {
366             size = 4096;
367         }
368         max = size - 1;
369     } else if (dev->model->asic_type == AsicType::GL124 ||
370                dev->model->asic_type == AsicType::GL846 ||
371                dev->model->asic_type == AsicType::GL847) {
372         size = 257;
373         max = 65535;
374     } else {
375         size = 256;
376         max = 65535;
377     }
378     sanei_genesys_create_gamma_table(gamma_table, size, max, max, gamma);
379 }
380 
381 /* computes the exposure_time on the basis of the given vertical dpi,
382    the number of pixels the ccd needs to send,
383    the step_type and the corresponding maximum speed from the motor struct */
384 /*
385   Currently considers maximum motor speed at given step_type, minimum
386   line exposure needed for conversion and led exposure time.
387 
388   TODO: Should also consider maximum transfer rate: ~6.5MB/s.
389     Note: The enhance option of the scanners does _not_ help. It only halves
390           the amount of pixels transferred.
391  */
sanei_genesys_exposure_time2(Genesys_Device * dev,const MotorProfile & profile,float ydpi,int endpixel,int exposure_by_led)392 SANE_Int sanei_genesys_exposure_time2(Genesys_Device * dev, const MotorProfile& profile, float ydpi,
393                                       int endpixel, int exposure_by_led)
394 {
395   int exposure_by_ccd = endpixel + 32;
396     unsigned max_speed_motor_w = profile.slope.max_speed_w;
397     int exposure_by_motor = static_cast<int>((max_speed_motor_w * dev->motor.base_ydpi) / ydpi);
398 
399   int exposure = exposure_by_ccd;
400 
401     if (exposure < exposure_by_motor) {
402         exposure = exposure_by_motor;
403     }
404 
405     if (exposure < exposure_by_led && dev->model->is_cis) {
406         exposure = exposure_by_led;
407     }
408 
409     return exposure;
410 }
411 
412 
413 /* Sends a block of shading information to the scanner.
414    The data is placed at address 0x0000 for color mode, gray mode and
415    unconditionally for the following CCD chips: HP2300, HP2400 and HP5345
416 
417    The data needs to be of size "size", and in little endian byte order.
418  */
genesys_send_offset_and_shading(Genesys_Device * dev,const Genesys_Sensor & sensor,uint8_t * data,int size)419 static void genesys_send_offset_and_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
420                                             uint8_t* data, int size)
421 {
422     DBG_HELPER_ARGS(dbg, "(size = %d)", size);
423   int start_address;
424 
425   /* ASIC higher than gl843 doesn't have register 2A/2B, so we route to
426    * a per ASIC shading data loading function if available.
427    * It is also used for scanners using SHDAREA */
428     if (dev->cmd_set->has_send_shading_data()) {
429         dev->cmd_set->send_shading_data(dev, sensor, data, size);
430         return;
431     }
432 
433     start_address = 0x00;
434 
435     dev->interface->write_buffer(0x3c, start_address, data, size);
436 }
437 
sanei_genesys_init_shading_data(Genesys_Device * dev,const Genesys_Sensor & sensor,int pixels_per_line)438 void sanei_genesys_init_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor,
439                                      int pixels_per_line)
440 {
441     DBG_HELPER_ARGS(dbg, "pixels_per_line: %d", pixels_per_line);
442 
443     if (dev->cmd_set->has_send_shading_data()) {
444         return;
445     }
446 
447   DBG(DBG_proc, "%s (pixels_per_line = %d)\n", __func__, pixels_per_line);
448 
449     unsigned channels = dev->settings.get_channels();
450 
451   // 16 bit black, 16 bit white
452   std::vector<uint8_t> shading_data(pixels_per_line * 4 * channels, 0);
453 
454   uint8_t* shading_data_ptr = shading_data.data();
455 
456     for (unsigned i = 0; i < pixels_per_line * channels; i++) {
457       *shading_data_ptr++ = 0x00;	/* dark lo */
458       *shading_data_ptr++ = 0x00;	/* dark hi */
459       *shading_data_ptr++ = 0x00;	/* white lo */
460       *shading_data_ptr++ = 0x40;	/* white hi -> 0x4000 */
461     }
462 
463     genesys_send_offset_and_shading(dev, sensor, shading_data.data(),
464                                     pixels_per_line * 4 * channels);
465 }
466 
467 namespace gl124 {
468     void gl124_setup_scan_gpio(Genesys_Device* dev, int resolution);
469 } // namespace gl124
470 
scanner_clear_scan_and_feed_counts(Genesys_Device & dev)471 void scanner_clear_scan_and_feed_counts(Genesys_Device& dev)
472 {
473     switch (dev.model->asic_type) {
474         case AsicType::GL841: {
475             dev.interface->write_register(gl841::REG_0x0D,
476                                           gl841::REG_0x0D_CLRLNCNT);
477             break;
478         }
479         case AsicType::GL842: {
480             dev.interface->write_register(gl842::REG_0x0D,
481                                           gl842::REG_0x0D_CLRLNCNT);
482             break;
483         }
484         case AsicType::GL843: {
485             dev.interface->write_register(gl843::REG_0x0D,
486                                           gl843::REG_0x0D_CLRLNCNT | gl843::REG_0x0D_CLRMCNT);
487             break;
488         }
489         case AsicType::GL845:
490         case AsicType::GL846: {
491             dev.interface->write_register(gl846::REG_0x0D,
492                                           gl846::REG_0x0D_CLRLNCNT | gl846::REG_0x0D_CLRMCNT);
493             break;
494         }
495         case AsicType::GL847:{
496             dev.interface->write_register(gl847::REG_0x0D,
497                                           gl847::REG_0x0D_CLRLNCNT | gl847::REG_0x0D_CLRMCNT);
498             break;
499         }
500         case AsicType::GL124:{
501             dev.interface->write_register(gl124::REG_0x0D,
502                                           gl124::REG_0x0D_CLRLNCNT | gl124::REG_0x0D_CLRMCNT);
503             break;
504         }
505         default:
506             throw SaneException("Unsupported asic type");
507     }
508 }
509 
scanner_send_slope_table(Genesys_Device * dev,const Genesys_Sensor & sensor,unsigned table_nr,const std::vector<uint16_t> & slope_table)510 void scanner_send_slope_table(Genesys_Device* dev, const Genesys_Sensor& sensor, unsigned table_nr,
511                               const std::vector<uint16_t>& slope_table)
512 {
513     DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %zu", table_nr, slope_table.size());
514 
515     unsigned max_table_nr = 0;
516     switch (dev->model->asic_type) {
517         case AsicType::GL646: {
518             max_table_nr = 2;
519             break;
520         }
521         case AsicType::GL841:
522         case AsicType::GL842:
523         case AsicType::GL843:
524         case AsicType::GL845:
525         case AsicType::GL846:
526         case AsicType::GL847:
527         case AsicType::GL124: {
528             max_table_nr = 4;
529             break;
530         }
531         default:
532             throw SaneException("Unsupported ASIC type");
533     }
534 
535     if (table_nr > max_table_nr) {
536         throw SaneException("invalid table number %d", table_nr);
537     }
538 
539     std::vector<uint8_t> table;
540     table.reserve(slope_table.size() * 2);
541     for (std::size_t i = 0; i < slope_table.size(); i++) {
542         table.push_back(slope_table[i] & 0xff);
543         table.push_back(slope_table[i] >> 8);
544     }
545     if (dev->model->asic_type == AsicType::GL841 ||
546         dev->model->model_id == ModelId::CANON_LIDE_90)
547     {
548         // BUG: do this on all gl842 scanners
549         auto max_table_size = get_slope_table_max_size(dev->model->asic_type);
550         table.reserve(max_table_size * 2);
551         while (table.size() < max_table_size * 2) {
552             table.push_back(slope_table.back() & 0xff);
553             table.push_back(slope_table.back() >> 8);
554         }
555     }
556 
557     if (dev->interface->is_mock()) {
558         dev->interface->record_slope_table(table_nr, slope_table);
559     }
560 
561     switch (dev->model->asic_type) {
562         case AsicType::GL646: {
563             unsigned dpihw = dev->reg.find_reg(0x05).value >> 6;
564             unsigned start_address = 0;
565             if (dpihw == 0) { // 600 dpi
566                 start_address = 0x08000;
567             } else if (dpihw == 1) { // 1200 dpi
568                 start_address = 0x10000;
569             } else if (dpihw == 2) { // 2400 dpi
570                 start_address = 0x1f800;
571             } else {
572                 throw SaneException("Unexpected dpihw");
573             }
574             dev->interface->write_buffer(0x3c, start_address + table_nr * 0x100, table.data(),
575                                          table.size());
576             break;
577         }
578         case AsicType::GL841:
579         case AsicType::GL842: {
580             unsigned start_address = 0;
581             switch (sensor.register_dpihw) {
582                 case 600: start_address = 0x08000; break;
583                 case 1200: start_address = 0x10000; break;
584                 case 2400: start_address = 0x20000; break;
585                 default: throw SaneException("Unexpected dpihw");
586             }
587             dev->interface->write_buffer(0x3c, start_address + table_nr * 0x200, table.data(),
588                                          table.size());
589             break;
590         }
591         case AsicType::GL843: {
592             // slope table addresses are fixed : 0x40000,  0x48000,  0x50000,  0x58000,  0x60000
593             // XXX STEF XXX USB 1.1 ? sanei_genesys_write_0x8c (dev, 0x0f, 0x14);
594             dev->interface->write_gamma(0x28,  0x40000 + 0x8000 * table_nr, table.data(),
595                                         table.size());
596             break;
597         }
598         case AsicType::GL845:
599         case AsicType::GL846:
600         case AsicType::GL847:
601         case AsicType::GL124: {
602             // slope table addresses are fixed
603             dev->interface->write_ahb(0x10000000 + 0x4000 * table_nr, table.size(),
604                                       table.data());
605             break;
606         }
607         default:
608             throw SaneException("Unsupported ASIC type");
609     }
610 
611 }
612 
scanner_is_motor_stopped(Genesys_Device & dev)613 bool scanner_is_motor_stopped(Genesys_Device& dev)
614 {
615     switch (dev.model->asic_type) {
616         case AsicType::GL646: {
617             auto status = scanner_read_status(dev);
618             return !status.is_motor_enabled && status.is_feeding_finished;
619         }
620         case AsicType::GL841: {
621             auto status = scanner_read_status(dev);
622             auto reg = dev.interface->read_register(gl841::REG_0x40);
623 
624             return (!(reg & gl841::REG_0x40_DATAENB) && !(reg & gl841::REG_0x40_MOTMFLG) &&
625                     !status.is_motor_enabled);
626         }
627         case AsicType::GL842: {
628             auto status = scanner_read_status(dev);
629             auto reg = dev.interface->read_register(gl842::REG_0x40);
630 
631             return (!(reg & gl842::REG_0x40_DATAENB) && !(reg & gl842::REG_0x40_MOTMFLG) &&
632                     !status.is_motor_enabled);
633         }
634         case AsicType::GL843: {
635             auto status = scanner_read_status(dev);
636             auto reg = dev.interface->read_register(gl843::REG_0x40);
637 
638             return (!(reg & gl843::REG_0x40_DATAENB) && !(reg & gl843::REG_0x40_MOTMFLG) &&
639                     !status.is_motor_enabled);
640         }
641         case AsicType::GL845:
642         case AsicType::GL846: {
643             auto status = scanner_read_status(dev);
644             auto reg = dev.interface->read_register(gl846::REG_0x40);
645 
646             return (!(reg & gl846::REG_0x40_DATAENB) && !(reg & gl846::REG_0x40_MOTMFLG) &&
647                     !status.is_motor_enabled);
648         }
649         case AsicType::GL847: {
650             auto status = scanner_read_status(dev);
651             auto reg = dev.interface->read_register(gl847::REG_0x40);
652 
653             return (!(reg & gl847::REG_0x40_DATAENB) && !(reg & gl847::REG_0x40_MOTMFLG) &&
654                     !status.is_motor_enabled);
655         }
656         case AsicType::GL124: {
657             auto status = scanner_read_status(dev);
658             auto reg = dev.interface->read_register(gl124::REG_0x100);
659 
660             return (!(reg & gl124::REG_0x100_DATAENB) && !(reg & gl124::REG_0x100_MOTMFLG) &&
661                     !status.is_motor_enabled);
662         }
663         default:
664             throw SaneException("Unsupported asic type");
665     }
666 }
667 
scanner_setup_sensor(Genesys_Device & dev,const Genesys_Sensor & sensor,Genesys_Register_Set & regs)668 void scanner_setup_sensor(Genesys_Device& dev, const Genesys_Sensor& sensor,
669                           Genesys_Register_Set& regs)
670 {
671     DBG_HELPER(dbg);
672 
673     for (const auto& custom_reg : sensor.custom_regs) {
674         regs.set8(custom_reg.address, custom_reg.value);
675     }
676 
677     if (dev.model->asic_type != AsicType::GL841 &&
678         dev.model->asic_type != AsicType::GL843)
679     {
680         regs_set_exposure(dev.model->asic_type, regs, sensor.exposure);
681     }
682 
683     dev.segment_order = sensor.segment_order;
684 }
685 
scanner_stop_action(Genesys_Device & dev)686 void scanner_stop_action(Genesys_Device& dev)
687 {
688     DBG_HELPER(dbg);
689 
690     switch (dev.model->asic_type) {
691         case AsicType::GL841:
692         case AsicType::GL842:
693         case AsicType::GL843:
694         case AsicType::GL845:
695         case AsicType::GL846:
696         case AsicType::GL847:
697         case AsicType::GL124:
698             break;
699         default:
700             throw SaneException("Unsupported asic type");
701     }
702 
703     dev.cmd_set->update_home_sensor_gpio(dev);
704 
705     if (scanner_is_motor_stopped(dev)) {
706         DBG(DBG_info, "%s: already stopped\n", __func__);
707         return;
708     }
709 
710     scanner_stop_action_no_move(dev, dev.reg);
711 
712     if (is_testing_mode()) {
713         return;
714     }
715 
716     for (unsigned i = 0; i < 10; ++i) {
717         if (scanner_is_motor_stopped(dev)) {
718             return;
719         }
720 
721         dev.interface->sleep_ms(100);
722     }
723 
724     throw SaneException(SANE_STATUS_IO_ERROR, "could not stop motor");
725 }
726 
scanner_stop_action_no_move(Genesys_Device & dev,genesys::Genesys_Register_Set & regs)727 void scanner_stop_action_no_move(Genesys_Device& dev, genesys::Genesys_Register_Set& regs)
728 {
729     switch (dev.model->asic_type) {
730         case AsicType::GL646:
731         case AsicType::GL841:
732         case AsicType::GL842:
733         case AsicType::GL843:
734         case AsicType::GL845:
735         case AsicType::GL846:
736         case AsicType::GL847:
737         case AsicType::GL124:
738             break;
739         default:
740             throw SaneException("Unsupported asic type");
741     }
742 
743     regs_set_optical_off(dev.model->asic_type, regs);
744     // same across all supported ASICs
745     dev.interface->write_register(0x01, regs.get8(0x01));
746 
747     // looks like certain scanners lock up if we try to scan immediately after stopping previous
748     // action.
749     dev.interface->sleep_ms(100);
750 }
751 
scanner_move(Genesys_Device & dev,ScanMethod scan_method,unsigned steps,Direction direction)752 void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, Direction direction)
753 {
754     DBG_HELPER_ARGS(dbg, "steps=%d direction=%d", steps, static_cast<unsigned>(direction));
755 
756     auto local_reg = dev.reg;
757 
758     unsigned resolution = dev.model->get_resolution_settings(scan_method).get_min_resolution_y();
759 
760     const auto& sensor = sanei_genesys_find_sensor(&dev, resolution, 3, scan_method);
761 
762     bool uses_secondary_head = (scan_method == ScanMethod::TRANSPARENCY ||
763                                 scan_method == ScanMethod::TRANSPARENCY_INFRARED) &&
764                                (!has_flag(dev.model->flags, ModelFlag::UTA_NO_SECONDARY_MOTOR));
765 
766     bool uses_secondary_pos = uses_secondary_head &&
767                               dev.model->default_method == ScanMethod::FLATBED;
768 
769     if (!dev.is_head_pos_known(ScanHeadId::PRIMARY)) {
770         throw SaneException("Unknown head position");
771     }
772     if (uses_secondary_pos && !dev.is_head_pos_known(ScanHeadId::SECONDARY)) {
773         throw SaneException("Unknown head position");
774     }
775     if (direction == Direction::BACKWARD && steps > dev.head_pos(ScanHeadId::PRIMARY)) {
776         throw SaneException("Trying to feed behind the home position %d %d",
777                             steps, dev.head_pos(ScanHeadId::PRIMARY));
778     }
779     if (uses_secondary_pos && direction == Direction::BACKWARD &&
780         steps > dev.head_pos(ScanHeadId::SECONDARY))
781     {
782         throw SaneException("Trying to feed behind the home position %d %d",
783                             steps, dev.head_pos(ScanHeadId::SECONDARY));
784     }
785 
786     ScanSession session;
787     session.params.xres = resolution;
788     session.params.yres = resolution;
789     session.params.startx = 0;
790     session.params.starty = steps;
791     session.params.pixels = 50;
792     session.params.lines = 3;
793     session.params.depth = 8;
794     session.params.channels = 1;
795     session.params.scan_method = scan_method;
796     session.params.scan_mode = ScanColorMode::GRAY;
797     session.params.color_filter = ColorFilter::GREEN;
798 
799     session.params.flags = ScanFlag::DISABLE_SHADING |
800                            ScanFlag::DISABLE_GAMMA |
801                            ScanFlag::FEEDING |
802                            ScanFlag::IGNORE_STAGGER_OFFSET |
803                            ScanFlag::IGNORE_COLOR_OFFSET;
804 
805     if (dev.model->asic_type == AsicType::GL124) {
806         session.params.flags |= ScanFlag::DISABLE_BUFFER_FULL_MOVE;
807     }
808 
809     if (direction == Direction::BACKWARD) {
810         session.params.flags |= ScanFlag::REVERSE;
811     }
812 
813     compute_session(&dev, session, sensor);
814 
815     dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session);
816 
817     if (dev.model->asic_type != AsicType::GL843) {
818         regs_set_exposure(dev.model->asic_type, local_reg,
819                           sanei_genesys_fixup_exposure({0, 0, 0}));
820     }
821     scanner_clear_scan_and_feed_counts(dev);
822 
823     dev.interface->write_registers(local_reg);
824     if (uses_secondary_head) {
825         dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY_AND_SECONDARY);
826     }
827 
828     try {
829         scanner_start_action(dev, true);
830     } catch (...) {
831         catch_all_exceptions(__func__, [&]() {
832             dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);
833         });
834         catch_all_exceptions(__func__, [&]() { scanner_stop_action(dev); });
835         // restore original registers
836         catch_all_exceptions(__func__, [&]() { dev.interface->write_registers(dev.reg); });
837         throw;
838     }
839 
840     if (is_testing_mode()) {
841         dev.interface->test_checkpoint("feed");
842 
843         dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, direction, steps);
844         if (uses_secondary_pos) {
845             dev.advance_head_pos_by_steps(ScanHeadId::SECONDARY, direction, steps);
846         }
847 
848         scanner_stop_action(dev);
849         if (uses_secondary_head) {
850             dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);
851         }
852         return;
853     }
854 
855     // wait until feed count reaches the required value
856     if (dev.model->model_id == ModelId::CANON_LIDE_700F) {
857         dev.cmd_set->update_home_sensor_gpio(dev);
858     }
859 
860     // FIXME: should porbably wait for some timeout
861     Status status;
862     for (unsigned i = 0;; ++i) {
863         status = scanner_read_status(dev);
864         if (status.is_feeding_finished || (
865             direction == Direction::BACKWARD && status.is_at_home))
866         {
867             break;
868         }
869         dev.interface->sleep_ms(10);
870     }
871 
872     scanner_stop_action(dev);
873     if (uses_secondary_head) {
874         dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);
875     }
876 
877     dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, direction, steps);
878     if (uses_secondary_pos) {
879         dev.advance_head_pos_by_steps(ScanHeadId::SECONDARY, direction, steps);
880     }
881 
882     // looks like certain scanners lock up if we scan immediately after feeding
883     dev.interface->sleep_ms(100);
884 }
885 
scanner_move_to_ta(Genesys_Device & dev)886 void scanner_move_to_ta(Genesys_Device& dev)
887 {
888     DBG_HELPER(dbg);
889 
890     unsigned feed = static_cast<unsigned>((dev.model->y_offset_sensor_to_ta * dev.motor.base_ydpi) /
891                                            MM_PER_INCH);
892     scanner_move(dev, dev.model->default_method, feed, Direction::FORWARD);
893 }
894 
scanner_move_back_home(Genesys_Device & dev,bool wait_until_home)895 void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home)
896 {
897     DBG_HELPER_ARGS(dbg, "wait_until_home = %d", wait_until_home);
898 
899     switch (dev.model->asic_type) {
900         case AsicType::GL841:
901         case AsicType::GL842:
902         case AsicType::GL843:
903         case AsicType::GL845:
904         case AsicType::GL846:
905         case AsicType::GL847:
906         case AsicType::GL124:
907             break;
908         default:
909             throw SaneException("Unsupported asic type");
910     }
911 
912     if (dev.model->is_sheetfed) {
913         dbg.vlog(DBG_proc, "sheetfed scanner, skipping going back home");
914         return;
915     }
916 
917     // FIXME: also check whether the scanner actually has a secondary head
918     if ((!dev.is_head_pos_known(ScanHeadId::SECONDARY) ||
919         dev.head_pos(ScanHeadId::SECONDARY) > 0 ||
920         dev.settings.scan_method == ScanMethod::TRANSPARENCY ||
921         dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) &&
922             (!has_flag(dev.model->flags, ModelFlag::UTA_NO_SECONDARY_MOTOR)))
923     {
924         scanner_move_back_home_ta(dev);
925     }
926 
927     if (dev.is_head_pos_known(ScanHeadId::PRIMARY) &&
928         dev.head_pos(ScanHeadId::PRIMARY) > 1000)
929     {
930         // leave 500 steps for regular slow back home
931         scanner_move(dev, dev.model->default_method, dev.head_pos(ScanHeadId::PRIMARY) - 500,
932                      Direction::BACKWARD);
933     }
934 
935     dev.cmd_set->update_home_sensor_gpio(dev);
936 
937     auto status = scanner_read_reliable_status(dev);
938 
939     if (status.is_at_home) {
940         dbg.log(DBG_info, "already at home");
941         dev.set_head_pos_zero(ScanHeadId::PRIMARY);
942         return;
943     }
944 
945     Genesys_Register_Set local_reg = dev.reg;
946     unsigned resolution = sanei_genesys_get_lowest_ydpi(&dev);
947 
948     const auto& sensor = sanei_genesys_find_sensor(&dev, resolution, 1, dev.model->default_method);
949 
950     ScanSession session;
951     session.params.xres = resolution;
952     session.params.yres = resolution;
953     session.params.startx = 0;
954     session.params.starty = 40000;
955     session.params.pixels = 50;
956     session.params.lines = 3;
957     session.params.depth = 8;
958     session.params.channels = 1;
959     session.params.scan_method = dev.settings.scan_method;
960     session.params.scan_mode = ScanColorMode::GRAY;
961     session.params.color_filter = ColorFilter::GREEN;
962 
963     session.params.flags =  ScanFlag::DISABLE_SHADING |
964                             ScanFlag::DISABLE_GAMMA |
965                             ScanFlag::IGNORE_STAGGER_OFFSET |
966                             ScanFlag::IGNORE_COLOR_OFFSET |
967                             ScanFlag::REVERSE;
968 
969     if (dev.model->asic_type == AsicType::GL843) {
970         session.params.flags |= ScanFlag::DISABLE_BUFFER_FULL_MOVE;
971     }
972 
973     compute_session(&dev, session, sensor);
974 
975     dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session);
976 
977     scanner_clear_scan_and_feed_counts(dev);
978 
979     dev.interface->write_registers(local_reg);
980 
981     if (dev.model->asic_type == AsicType::GL124) {
982         gl124::gl124_setup_scan_gpio(&dev, resolution);
983     }
984 
985     try {
986         scanner_start_action(dev, true);
987     } catch (...) {
988         catch_all_exceptions(__func__, [&]() { scanner_stop_action(dev); });
989         // restore original registers
990         catch_all_exceptions(__func__, [&]()
991         {
992             dev.interface->write_registers(dev.reg);
993         });
994         throw;
995     }
996 
997     dev.cmd_set->update_home_sensor_gpio(dev);
998 
999     if (is_testing_mode()) {
1000         dev.interface->test_checkpoint("move_back_home");
1001         dev.set_head_pos_zero(ScanHeadId::PRIMARY);
1002         return;
1003     }
1004 
1005     if (wait_until_home) {
1006         for (unsigned i = 0; i < 300; ++i) {
1007             auto status = scanner_read_status(dev);
1008 
1009             if (status.is_at_home) {
1010                 dbg.log(DBG_info, "reached home position");
1011                 if (dev.model->asic_type == AsicType::GL846 ||
1012                     dev.model->asic_type == AsicType::GL847)
1013                 {
1014                     scanner_stop_action(dev);
1015                 }
1016                 dev.set_head_pos_zero(ScanHeadId::PRIMARY);
1017                 return;
1018             }
1019 
1020             dev.interface->sleep_ms(100);
1021         }
1022 
1023         // when we come here then the scanner needed too much time for this, so we better stop
1024         // the motor
1025         catch_all_exceptions(__func__, [&](){ scanner_stop_action(dev); });
1026         dev.set_head_pos_unknown(ScanHeadId::PRIMARY | ScanHeadId::SECONDARY);
1027         throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for scanhead to go home");
1028     }
1029     dbg.log(DBG_info, "scanhead is still moving");
1030 }
1031 
1032 namespace {
should_use_secondary_motor_mode(Genesys_Device & dev)1033     bool should_use_secondary_motor_mode(Genesys_Device& dev)
1034     {
1035         bool should_use = !dev.is_head_pos_known(ScanHeadId::SECONDARY) ||
1036                           !dev.is_head_pos_known(ScanHeadId::PRIMARY) ||
1037                           dev.head_pos(ScanHeadId::SECONDARY) > dev.head_pos(ScanHeadId::PRIMARY);
1038         bool supports = dev.model->model_id == ModelId::CANON_8600F;
1039         return should_use && supports;
1040     }
1041 
handle_motor_position_after_move_back_home_ta(Genesys_Device & dev,MotorMode motor_mode)1042     void handle_motor_position_after_move_back_home_ta(Genesys_Device& dev, MotorMode motor_mode)
1043     {
1044         if (motor_mode == MotorMode::SECONDARY) {
1045             dev.set_head_pos_zero(ScanHeadId::SECONDARY);
1046             return;
1047         }
1048 
1049         if (dev.is_head_pos_known(ScanHeadId::PRIMARY)) {
1050             if (dev.head_pos(ScanHeadId::PRIMARY) > dev.head_pos(ScanHeadId::SECONDARY)) {
1051                 dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, Direction::BACKWARD,
1052                                               dev.head_pos(ScanHeadId::SECONDARY));
1053             } else {
1054                 dev.set_head_pos_zero(ScanHeadId::PRIMARY);
1055             }
1056             dev.set_head_pos_zero(ScanHeadId::SECONDARY);
1057         }
1058     }
1059 } // namespace
1060 
scanner_move_back_home_ta(Genesys_Device & dev)1061 void scanner_move_back_home_ta(Genesys_Device& dev)
1062 {
1063     DBG_HELPER(dbg);
1064 
1065     switch (dev.model->asic_type) {
1066         case AsicType::GL842:
1067         case AsicType::GL843:
1068         case AsicType::GL845:
1069             break;
1070         default:
1071             throw SaneException("Unsupported asic type");
1072     }
1073 
1074     Genesys_Register_Set local_reg = dev.reg;
1075 
1076     auto scan_method = ScanMethod::TRANSPARENCY;
1077     unsigned resolution = dev.model->get_resolution_settings(scan_method).get_min_resolution_y();
1078 
1079     const auto& sensor = sanei_genesys_find_sensor(&dev, resolution, 1, scan_method);
1080 
1081     if (dev.is_head_pos_known(ScanHeadId::SECONDARY) &&
1082         dev.is_head_pos_known(ScanHeadId::PRIMARY) &&
1083         dev.head_pos(ScanHeadId::SECONDARY) > 1000 &&
1084         dev.head_pos(ScanHeadId::SECONDARY) <= dev.head_pos(ScanHeadId::PRIMARY))
1085     {
1086         // leave 500 steps for regular slow back home
1087         scanner_move(dev, scan_method, dev.head_pos(ScanHeadId::SECONDARY) - 500,
1088                      Direction::BACKWARD);
1089     }
1090 
1091     ScanSession session;
1092     session.params.xres = resolution;
1093     session.params.yres = resolution;
1094     session.params.startx = 0;
1095     session.params.starty = 40000;
1096     session.params.pixels = 50;
1097     session.params.lines = 3;
1098     session.params.depth = 8;
1099     session.params.channels = 1;
1100     session.params.scan_method = scan_method;
1101     session.params.scan_mode = ScanColorMode::GRAY;
1102     session.params.color_filter = ColorFilter::GREEN;
1103 
1104     session.params.flags =  ScanFlag::DISABLE_SHADING |
1105                             ScanFlag::DISABLE_GAMMA |
1106                             ScanFlag::IGNORE_STAGGER_OFFSET |
1107                             ScanFlag::IGNORE_COLOR_OFFSET |
1108                             ScanFlag::REVERSE;
1109 
1110     compute_session(&dev, session, sensor);
1111 
1112     dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session);
1113 
1114     scanner_clear_scan_and_feed_counts(dev);
1115 
1116     dev.interface->write_registers(local_reg);
1117 
1118     auto motor_mode = should_use_secondary_motor_mode(dev) ? MotorMode::SECONDARY
1119                                                            : MotorMode::PRIMARY_AND_SECONDARY;
1120 
1121     dev.cmd_set->set_motor_mode(dev, local_reg, motor_mode);
1122 
1123     try {
1124         scanner_start_action(dev, true);
1125     } catch (...) {
1126         catch_all_exceptions(__func__, [&]() { scanner_stop_action(dev); });
1127         // restore original registers
1128         catch_all_exceptions(__func__, [&]() { dev.interface->write_registers(dev.reg); });
1129         throw;
1130     }
1131 
1132     if (is_testing_mode()) {
1133         dev.interface->test_checkpoint("move_back_home_ta");
1134 
1135         handle_motor_position_after_move_back_home_ta(dev, motor_mode);
1136 
1137         scanner_stop_action(dev);
1138         dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);
1139         return;
1140     }
1141 
1142     for (unsigned i = 0; i < 1200; ++i) {
1143 
1144         auto status = scanner_read_status(dev);
1145 
1146         if (status.is_at_home) {
1147             dbg.log(DBG_info, "TA reached home position");
1148 
1149             handle_motor_position_after_move_back_home_ta(dev, motor_mode);
1150 
1151             scanner_stop_action(dev);
1152             dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);
1153             return;
1154         }
1155 
1156         dev.interface->sleep_ms(100);
1157     }
1158 
1159     throw SaneException("Timeout waiting for XPA lamp to park");
1160 }
1161 
scanner_search_strip(Genesys_Device & dev,bool forward,bool black)1162 void scanner_search_strip(Genesys_Device& dev, bool forward, bool black)
1163 {
1164     DBG_HELPER_ARGS(dbg, "%s %s", black ? "black" : "white", forward ? "forward" : "reverse");
1165 
1166     if (dev.model->asic_type == AsicType::GL841 && !black && forward) {
1167         dev.frontend.set_gain(0, 0xff);
1168         dev.frontend.set_gain(1, 0xff);
1169         dev.frontend.set_gain(2, 0xff);
1170     }
1171 
1172     // set up for a gray scan at lowest dpi
1173     const auto& resolution_settings = dev.model->get_resolution_settings(dev.settings.scan_method);
1174     unsigned dpi = resolution_settings.get_min_resolution_x();
1175     unsigned channels = 1;
1176 
1177     auto& sensor = sanei_genesys_find_sensor(&dev, dpi, channels, dev.settings.scan_method);
1178     dev.cmd_set->set_fe(&dev, sensor, AFE_SET);
1179     scanner_stop_action(dev);
1180 
1181 
1182     // shading calibration is done with dev.motor.base_ydpi
1183     unsigned lines = static_cast<unsigned>(dev.model->y_size_calib_mm * dpi / MM_PER_INCH);
1184     if (dev.model->asic_type == AsicType::GL841) {
1185         lines = 10; // TODO: use dev.model->search_lines
1186         lines = static_cast<unsigned>((lines * dpi) / MM_PER_INCH);
1187     }
1188 
1189     unsigned pixels = dev.model->x_size_calib_mm * dpi / MM_PER_INCH;
1190 
1191     dev.set_head_pos_zero(ScanHeadId::PRIMARY);
1192 
1193     unsigned length = 20;
1194     if (dev.model->asic_type == AsicType::GL841) {
1195         // 20 cm max length for calibration sheet
1196         length = static_cast<unsigned>(((200 * dpi) / MM_PER_INCH) / lines);
1197     }
1198 
1199     auto local_reg = dev.reg;
1200 
1201     ScanSession session;
1202     session.params.xres = dpi;
1203     session.params.yres = dpi;
1204     session.params.startx = 0;
1205     session.params.starty = 0;
1206     session.params.pixels = pixels;
1207     session.params.lines = lines;
1208     session.params.depth = 8;
1209     session.params.channels = channels;
1210     session.params.scan_method = dev.settings.scan_method;
1211     session.params.scan_mode = ScanColorMode::GRAY;
1212     session.params.color_filter = ColorFilter::RED;
1213     session.params.flags = ScanFlag::DISABLE_SHADING |
1214                            ScanFlag::DISABLE_GAMMA;
1215     if (dev.model->asic_type != AsicType::GL841 && !forward) {
1216         session.params.flags |= ScanFlag::REVERSE;
1217     }
1218     compute_session(&dev, session, sensor);
1219 
1220     dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session);
1221 
1222     dev.interface->write_registers(local_reg);
1223 
1224     dev.cmd_set->begin_scan(&dev, sensor, &local_reg, true);
1225 
1226     if (is_testing_mode()) {
1227         dev.interface->test_checkpoint("search_strip");
1228         scanner_stop_action(dev);
1229         return;
1230     }
1231 
1232     wait_until_buffer_non_empty(&dev);
1233 
1234     // now we're on target, we can read data
1235     auto image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
1236 
1237     scanner_stop_action(dev);
1238 
1239     unsigned pass = 0;
1240     if (dbg_log_image_data()) {
1241         char title[80];
1242         std::sprintf(title, "gl_search_strip_%s_%s%02d.tiff",
1243                      black ? "black" : "white", forward ? "fwd" : "bwd", pass);
1244         write_tiff_file(title, image);
1245     }
1246 
1247     // loop until strip is found or maximum pass number done
1248     bool found = false;
1249     while (pass < length && !found) {
1250         dev.interface->write_registers(local_reg);
1251 
1252         // now start scan
1253         dev.cmd_set->begin_scan(&dev, sensor, &local_reg, true);
1254 
1255         wait_until_buffer_non_empty(&dev);
1256 
1257         // now we're on target, we can read data
1258         image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
1259 
1260         scanner_stop_action(dev);
1261 
1262         if (dbg_log_image_data()) {
1263             char title[80];
1264             std::sprintf(title, "gl_search_strip_%s_%s%02d.tiff",
1265                          black ? "black" : "white",
1266                          forward ? "fwd" : "bwd", static_cast<int>(pass));
1267             write_tiff_file(title, image);
1268         }
1269 
1270         unsigned white_level = 90;
1271         unsigned black_level = 60;
1272 
1273         std::size_t count = 0;
1274         // Search data to find black strip
1275         // When searching forward, we only need one line of the searched color since we
1276         // will scan forward. But when doing backward search, we need all the area of the ame color
1277         if (forward) {
1278 
1279             for (std::size_t y = 0; y < image.get_height() && !found; y++) {
1280                 count = 0;
1281 
1282                 // count of white/black pixels depending on the color searched
1283                 for (std::size_t x = 0; x < image.get_width(); x++) {
1284 
1285                     // when searching for black, detect white pixels
1286                     if (black && image.get_raw_channel(x, y, 0) > white_level) {
1287                         count++;
1288                     }
1289 
1290                     // when searching for white, detect black pixels
1291                     if (!black && image.get_raw_channel(x, y, 0) < black_level) {
1292                         count++;
1293                     }
1294                 }
1295 
1296                 // at end of line, if count >= 3%, line is not fully of the desired color
1297                 // so we must go to next line of the buffer */
1298                 // count*100/pixels < 3
1299 
1300                 auto found_percentage = (count * 100 / image.get_width());
1301                 if (found_percentage < 3) {
1302                     found = 1;
1303                     DBG(DBG_data, "%s: strip found forward during pass %d at line %zu\n", __func__,
1304                         pass, y);
1305                 } else {
1306                     DBG(DBG_data, "%s: pixels=%zu, count=%zu (%zu%%)\n", __func__,
1307                         image.get_width(), count, found_percentage);
1308                 }
1309             }
1310         } else {
1311             /*  since calibration scans are done forward, we need the whole area
1312                 to be of the required color when searching backward
1313             */
1314             count = 0;
1315             for (std::size_t y = 0; y < image.get_height(); y++) {
1316                 // count of white/black pixels depending on the color searched
1317                 for (std::size_t x = 0; x < image.get_width(); x++) {
1318                     // when searching for black, detect white pixels
1319                     if (black && image.get_raw_channel(x, y, 0) > white_level) {
1320                         count++;
1321                     }
1322                     // when searching for white, detect black pixels
1323                     if (!black && image.get_raw_channel(x, y, 0) < black_level) {
1324                         count++;
1325                     }
1326                 }
1327             }
1328 
1329             // at end of area, if count >= 3%, area is not fully of the desired color
1330             // so we must go to next buffer
1331             auto found_percentage = count * 100 / (image.get_width() * image.get_height());
1332             if (found_percentage < 3) {
1333                 found = 1;
1334                 DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass);
1335             } else {
1336                 DBG(DBG_data, "%s: pixels=%zu, count=%zu (%zu%%)\n", __func__, image.get_width(),
1337                     count, found_percentage);
1338             }
1339         }
1340         pass++;
1341     }
1342 
1343     if (found) {
1344         DBG(DBG_info, "%s: %s strip found\n", __func__, black ? "black" : "white");
1345     } else {
1346         throw SaneException(SANE_STATUS_UNSUPPORTED, "%s strip not found",
1347                             black ? "black" : "white");
1348     }
1349 }
1350 
dark_average_channel(const Image & image,unsigned black,unsigned channel)1351 static int dark_average_channel(const Image& image, unsigned black, unsigned channel)
1352 {
1353     auto channels = get_pixel_channels(image.get_format());
1354 
1355     unsigned avg[3];
1356 
1357     // computes average values on black margin
1358     for (unsigned ch = 0; ch < channels; ch++) {
1359         avg[ch] = 0;
1360         unsigned count = 0;
1361         // FIXME: start with the second line because the black pixels often have noise on the first
1362         // line; the cause is probably incorrectly cleaned up previous scan
1363         for (std::size_t y = 1; y < image.get_height(); y++) {
1364             for (unsigned j = 0; j < black; j++) {
1365                 avg[ch] += image.get_raw_channel(j, y, ch);
1366                 count++;
1367             }
1368         }
1369         if (count > 0) {
1370             avg[ch] /= count;
1371         }
1372         DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, ch, avg[ch]);
1373     }
1374     DBG(DBG_info, "%s: average = %d\n", __func__, avg[channel]);
1375     return avg[channel];
1376 }
1377 
should_calibrate_only_active_area(const Genesys_Device & dev,const Genesys_Settings & settings)1378 bool should_calibrate_only_active_area(const Genesys_Device& dev,
1379                                        const Genesys_Settings& settings)
1380 {
1381     if (settings.scan_method == ScanMethod::TRANSPARENCY ||
1382         settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
1383     {
1384         if (dev.model->model_id == ModelId::CANON_4400F && settings.xres >= 4800) {
1385             return true;
1386         }
1387         if (dev.model->model_id == ModelId::CANON_8600F && settings.xres == 4800) {
1388             return true;
1389         }
1390     }
1391     return false;
1392 }
1393 
scanner_offset_calibration(Genesys_Device & dev,const Genesys_Sensor & sensor,Genesys_Register_Set & regs)1394 void scanner_offset_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor,
1395                                 Genesys_Register_Set& regs)
1396 {
1397     DBG_HELPER(dbg);
1398 
1399     if (dev.model->asic_type == AsicType::GL842 &&
1400         dev.frontend.layout.type != FrontendType::WOLFSON)
1401     {
1402         return;
1403     }
1404 
1405     if (dev.model->asic_type == AsicType::GL843 &&
1406         dev.frontend.layout.type != FrontendType::WOLFSON)
1407     {
1408         return;
1409     }
1410 
1411     if (dev.model->asic_type == AsicType::GL845 ||
1412         dev.model->asic_type == AsicType::GL846)
1413     {
1414         // no gain nor offset for AKM AFE
1415         std::uint8_t reg04 = dev.interface->read_register(gl846::REG_0x04);
1416         if ((reg04 & gl846::REG_0x04_FESET) == 0x02) {
1417             return;
1418         }
1419     }
1420     if (dev.model->asic_type == AsicType::GL847) {
1421         // no gain nor offset for AKM AFE
1422         std::uint8_t reg04 = dev.interface->read_register(gl847::REG_0x04);
1423         if ((reg04 & gl847::REG_0x04_FESET) == 0x02) {
1424             return;
1425         }
1426     }
1427 
1428     if (dev.model->asic_type == AsicType::GL124) {
1429         std::uint8_t reg0a = dev.interface->read_register(gl124::REG_0x0A);
1430         if (((reg0a & gl124::REG_0x0A_SIFSEL) >> gl124::REG_0x0AS_SIFSEL) == 3) {
1431             return;
1432         }
1433     }
1434 
1435     unsigned target_pixels = dev.model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH;
1436     unsigned start_pixel = 0;
1437     unsigned black_pixels = (sensor.black_pixels * sensor.full_resolution) / sensor.full_resolution;
1438 
1439     unsigned channels = 3;
1440     unsigned lines = 1;
1441     unsigned resolution = sensor.full_resolution;
1442 
1443     const Genesys_Sensor* calib_sensor = &sensor;
1444     if (dev.model->asic_type == AsicType::GL843) {
1445         lines = 8;
1446 
1447         // compute divider factor to compute final pixels number
1448         const auto& dpihw_sensor = sanei_genesys_find_sensor(&dev, dev.settings.xres, channels,
1449                                                              dev.settings.scan_method);
1450         resolution = dpihw_sensor.shading_resolution;
1451         unsigned factor = sensor.full_resolution / resolution;
1452 
1453         calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels,
1454                                                   dev.settings.scan_method);
1455 
1456         target_pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH;
1457         black_pixels = calib_sensor->black_pixels / factor;
1458 
1459         if (should_calibrate_only_active_area(dev, dev.settings)) {
1460             float offset = dev.model->x_offset_ta;
1461             start_pixel = static_cast<int>((offset * calib_sensor->get_optical_resolution()) / MM_PER_INCH);
1462 
1463             float size = dev.model->x_size_ta;
1464             target_pixels = static_cast<int>((size * calib_sensor->get_optical_resolution()) / MM_PER_INCH);
1465         }
1466 
1467         if (dev.model->model_id == ModelId::CANON_4400F &&
1468             dev.settings.scan_method == ScanMethod::FLATBED)
1469         {
1470             return;
1471         }
1472     }
1473 
1474     if (dev.model->model_id == ModelId::CANON_5600F) {
1475         // FIXME: use same approach as for GL843 scanners
1476         lines = 8;
1477     }
1478 
1479     if (dev.model->asic_type == AsicType::GL847) {
1480         calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels,
1481                                                   dev.settings.scan_method);
1482     }
1483 
1484     ScanFlag flags = ScanFlag::DISABLE_SHADING |
1485                      ScanFlag::DISABLE_GAMMA |
1486                      ScanFlag::SINGLE_LINE |
1487                      ScanFlag::IGNORE_STAGGER_OFFSET |
1488                      ScanFlag::IGNORE_COLOR_OFFSET;
1489 
1490     if (dev.settings.scan_method == ScanMethod::TRANSPARENCY ||
1491         dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
1492     {
1493         flags |= ScanFlag::USE_XPA;
1494     }
1495 
1496     ScanSession session;
1497     session.params.xres = resolution;
1498     session.params.yres = resolution;
1499     session.params.startx = start_pixel;
1500     session.params.starty = 0;
1501     session.params.pixels = target_pixels;
1502     session.params.lines = lines;
1503     session.params.depth = 8;
1504     session.params.channels = channels;
1505     session.params.scan_method = dev.settings.scan_method;
1506     session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
1507     session.params.color_filter = dev.model->asic_type == AsicType::GL843 ? ColorFilter::RED
1508                                                                           : dev.settings.color_filter;
1509     session.params.flags = flags;
1510     compute_session(&dev, session, *calib_sensor);
1511 
1512     dev.cmd_set->init_regs_for_scan_session(&dev, *calib_sensor, &regs, session);
1513 
1514     unsigned output_pixels = session.output_pixels;
1515 
1516     sanei_genesys_set_motor_power(regs, false);
1517 
1518     int top[3], bottom[3];
1519     int topavg[3], bottomavg[3], avg[3];
1520 
1521     // init gain and offset
1522     for (unsigned ch = 0; ch < 3; ch++)
1523     {
1524         bottom[ch] = 10;
1525         dev.frontend.set_offset(ch, bottom[ch]);
1526         dev.frontend.set_gain(ch, 0);
1527     }
1528     dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
1529 
1530     // scan with bottom AFE settings
1531     dev.interface->write_registers(regs);
1532     DBG(DBG_info, "%s: starting first line reading\n", __func__);
1533 
1534     dev.cmd_set->begin_scan(&dev, *calib_sensor, &regs, true);
1535 
1536     if (is_testing_mode()) {
1537         dev.interface->test_checkpoint("offset_calibration");
1538         if (dev.model->asic_type == AsicType::GL842 ||
1539             dev.model->asic_type == AsicType::GL843)
1540         {
1541             scanner_stop_action_no_move(dev, regs);
1542         }
1543         return;
1544     }
1545 
1546     Image first_line;
1547     if (dev.model->asic_type == AsicType::GL842 ||
1548         dev.model->asic_type == AsicType::GL843)
1549     {
1550         first_line = read_unshuffled_image_from_scanner(&dev, session,
1551                                                         session.output_total_bytes_raw);
1552         scanner_stop_action_no_move(dev, regs);
1553     } else {
1554         first_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
1555 
1556         if (dev.model->model_id == ModelId::CANON_5600F) {
1557             scanner_stop_action_no_move(dev, regs);
1558         }
1559     }
1560 
1561     if (dbg_log_image_data()) {
1562         char fn[40];
1563         std::snprintf(fn, 40, "gl843_bottom_offset_%03d_%03d_%03d.tiff",
1564                       bottom[0], bottom[1], bottom[2]);
1565         write_tiff_file(fn, first_line);
1566     }
1567 
1568     for (unsigned ch = 0; ch < 3; ch++) {
1569         bottomavg[ch] = dark_average_channel(first_line, black_pixels, ch);
1570         DBG(DBG_info, "%s: bottom avg %d=%d\n", __func__, ch, bottomavg[ch]);
1571     }
1572 
1573     // now top value
1574     for (unsigned ch = 0; ch < 3; ch++) {
1575         top[ch] = 255;
1576         dev.frontend.set_offset(ch, top[ch]);
1577     }
1578     dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
1579 
1580     // scan with top AFE values
1581     dev.interface->write_registers(regs);
1582     DBG(DBG_info, "%s: starting second line reading\n", __func__);
1583 
1584     dev.cmd_set->begin_scan(&dev, *calib_sensor, &regs, true);
1585 
1586     Image second_line;
1587     if (dev.model->asic_type == AsicType::GL842 ||
1588         dev.model->asic_type == AsicType::GL843)
1589     {
1590         second_line = read_unshuffled_image_from_scanner(&dev, session,
1591                                                          session.output_total_bytes_raw);
1592         scanner_stop_action_no_move(dev, regs);
1593     } else {
1594         second_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
1595 
1596         if (dev.model->model_id == ModelId::CANON_5600F) {
1597             scanner_stop_action_no_move(dev, regs);
1598         }
1599     }
1600 
1601     for (unsigned ch = 0; ch < 3; ch++){
1602         topavg[ch] = dark_average_channel(second_line, black_pixels, ch);
1603         DBG(DBG_info, "%s: top avg %d=%d\n", __func__, ch, topavg[ch]);
1604     }
1605 
1606     unsigned pass = 0;
1607 
1608     std::vector<std::uint8_t> debug_image;
1609     std::size_t debug_image_lines = 0;
1610     std::string debug_image_info;
1611 
1612     // loop until acceptable level
1613     while ((pass < 32) && ((top[0] - bottom[0] > 1) ||
1614                            (top[1] - bottom[1] > 1) ||
1615                            (top[2] - bottom[2] > 1)))
1616     {
1617         pass++;
1618 
1619         for (unsigned ch = 0; ch < 3; ch++) {
1620             if (top[ch] - bottom[ch] > 1) {
1621                 dev.frontend.set_offset(ch, (top[ch] + bottom[ch]) / 2);
1622             }
1623         }
1624         dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
1625 
1626         // scan with no move
1627         dev.interface->write_registers(regs);
1628         DBG(DBG_info, "%s: starting second line reading\n", __func__);
1629         dev.cmd_set->begin_scan(&dev, *calib_sensor, &regs, true);
1630 
1631         if (dev.model->asic_type == AsicType::GL842 ||
1632             dev.model->asic_type == AsicType::GL843)
1633         {
1634             second_line = read_unshuffled_image_from_scanner(&dev, session,
1635                                                              session.output_total_bytes_raw);
1636             scanner_stop_action_no_move(dev, regs);
1637         } else {
1638             second_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
1639 
1640             if (dev.model->model_id == ModelId::CANON_5600F) {
1641                 scanner_stop_action_no_move(dev, regs);
1642             }
1643         }
1644 
1645         if (dbg_log_image_data()) {
1646             char title[100];
1647             std::snprintf(title, 100, "lines: %d pixels_per_line: %d offsets[0..2]: %d %d %d\n",
1648                           lines, output_pixels,
1649                           dev.frontend.get_offset(0),
1650                           dev.frontend.get_offset(1),
1651                           dev.frontend.get_offset(2));
1652             debug_image_info += title;
1653             std::copy(second_line.get_row_ptr(0),
1654                       second_line.get_row_ptr(0) + second_line.get_row_bytes() * second_line.get_height(),
1655                       std::back_inserter(debug_image));
1656             debug_image_lines += lines;
1657         }
1658 
1659         for (unsigned ch = 0; ch < 3; ch++) {
1660             avg[ch] = dark_average_channel(second_line, black_pixels, ch);
1661             DBG(DBG_info, "%s: avg[%d]=%d offset=%d\n", __func__, ch, avg[ch],
1662                 dev.frontend.get_offset(ch));
1663         }
1664 
1665         // compute new boundaries
1666         for (unsigned ch = 0; ch < 3; ch++) {
1667             if (topavg[ch] >= avg[ch]) {
1668                 topavg[ch] = avg[ch];
1669                 top[ch] = dev.frontend.get_offset(ch);
1670             } else {
1671                 bottomavg[ch] = avg[ch];
1672                 bottom[ch] = dev.frontend.get_offset(ch);
1673             }
1674         }
1675     }
1676 
1677     if (dbg_log_image_data()) {
1678         sanei_genesys_write_file("gl_offset_all_desc.txt",
1679                                  reinterpret_cast<const std::uint8_t*>(debug_image_info.data()),
1680                                  debug_image_info.size());
1681         write_tiff_file("gl_offset_all.tiff", debug_image.data(), session.params.depth, channels,
1682                         output_pixels, debug_image_lines);
1683     }
1684 
1685     DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
1686         dev.frontend.get_offset(0),
1687         dev.frontend.get_offset(1),
1688         dev.frontend.get_offset(2));
1689 }
1690 
1691 /*  With offset and coarse calibration we only want to get our input range into
1692     a reasonable shape. the fine calibration of the upper and lower bounds will
1693     be done with shading.
1694 */
scanner_coarse_gain_calibration(Genesys_Device & dev,const Genesys_Sensor & sensor,Genesys_Register_Set & regs,unsigned dpi)1695 void scanner_coarse_gain_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor,
1696                                      Genesys_Register_Set& regs, unsigned dpi)
1697 {
1698     DBG_HELPER_ARGS(dbg, "dpi = %d", dpi);
1699 
1700     if (dev.model->asic_type == AsicType::GL842 &&
1701         dev.frontend.layout.type != FrontendType::WOLFSON)
1702     {
1703         return;
1704     }
1705 
1706     if (dev.model->asic_type == AsicType::GL843 &&
1707         dev.frontend.layout.type != FrontendType::WOLFSON)
1708     {
1709         return;
1710     }
1711 
1712     if (dev.model->asic_type == AsicType::GL845 ||
1713         dev.model->asic_type == AsicType::GL846)
1714     {
1715         // no gain nor offset for AKM AFE
1716         std::uint8_t reg04 = dev.interface->read_register(gl846::REG_0x04);
1717         if ((reg04 & gl846::REG_0x04_FESET) == 0x02) {
1718             return;
1719         }
1720     }
1721 
1722     if (dev.model->asic_type == AsicType::GL847) {
1723         // no gain nor offset for AKM AFE
1724         std::uint8_t reg04 = dev.interface->read_register(gl847::REG_0x04);
1725         if ((reg04 & gl847::REG_0x04_FESET) == 0x02) {
1726             return;
1727         }
1728     }
1729 
1730     if (dev.model->asic_type == AsicType::GL124) {
1731         // no gain nor offset for TI AFE
1732         std::uint8_t reg0a = dev.interface->read_register(gl124::REG_0x0A);
1733         if (((reg0a & gl124::REG_0x0A_SIFSEL) >> gl124::REG_0x0AS_SIFSEL) == 3) {
1734             return;
1735         }
1736     }
1737 
1738     if (dev.model->asic_type == AsicType::GL841) {
1739         // feed to white strip if needed
1740         if (dev.model->y_offset_calib_white > 0) {
1741             unsigned move = static_cast<unsigned>(
1742                     (dev.model->y_offset_calib_white * (dev.motor.base_ydpi)) / MM_PER_INCH);
1743             scanner_move(dev, dev.model->default_method, move, Direction::FORWARD);
1744         }
1745     }
1746 
1747     // coarse gain calibration is always done in color mode
1748     unsigned channels = 3;
1749 
1750     unsigned resolution = sensor.full_resolution;
1751     if (dev.model->asic_type == AsicType::GL841) {
1752         const auto& dpihw_sensor = sanei_genesys_find_sensor(&dev, dev.settings.xres, channels,
1753                                                              dev.settings.scan_method);
1754         resolution = dpihw_sensor.shading_resolution;
1755     }
1756 
1757     if (dev.model->asic_type == AsicType::GL842 ||
1758         dev.model->asic_type == AsicType::GL843)
1759     {
1760         const auto& dpihw_sensor = sanei_genesys_find_sensor(&dev, dpi, channels,
1761                                                              dev.settings.scan_method);
1762         resolution = dpihw_sensor.shading_resolution;
1763     }
1764 
1765     float coeff = 1;
1766 
1767     // Follow CKSEL
1768     if (dev.model->sensor_id == SensorId::CCD_KVSS080 ||
1769         dev.model->asic_type == AsicType::GL845 ||
1770         dev.model->asic_type == AsicType::GL846 ||
1771         dev.model->asic_type == AsicType::GL847 ||
1772         dev.model->asic_type == AsicType::GL124)
1773     {
1774         if (dev.settings.xres < sensor.full_resolution) {
1775             coeff = 0.9f;
1776         }
1777     }
1778 
1779     unsigned lines = 10;
1780     if (dev.model->asic_type == AsicType::GL841) {
1781         lines = 1;
1782     }
1783 
1784     const Genesys_Sensor* calib_sensor = &sensor;
1785     if (dev.model->asic_type == AsicType::GL841 ||
1786         dev.model->asic_type == AsicType::GL842 ||
1787         dev.model->asic_type == AsicType::GL843 ||
1788         dev.model->asic_type == AsicType::GL847)
1789     {
1790         calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels,
1791                                                   dev.settings.scan_method);
1792     }
1793 
1794     ScanFlag flags = ScanFlag::DISABLE_SHADING |
1795                      ScanFlag::DISABLE_GAMMA |
1796                      ScanFlag::SINGLE_LINE |
1797                      ScanFlag::IGNORE_STAGGER_OFFSET |
1798                      ScanFlag::IGNORE_COLOR_OFFSET;
1799 
1800     if (dev.settings.scan_method == ScanMethod::TRANSPARENCY ||
1801         dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
1802     {
1803         flags |= ScanFlag::USE_XPA;
1804     }
1805 
1806     ScanSession session;
1807     session.params.xres = resolution;
1808     session.params.yres = dev.model->asic_type == AsicType::GL841 ? dev.settings.yres : resolution;
1809     session.params.startx = 0;
1810     session.params.starty = 0;
1811     session.params.pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH;
1812     session.params.lines = lines;
1813     session.params.depth = dev.model->asic_type == AsicType::GL841 ? 16 : 8;
1814     session.params.channels = channels;
1815     session.params.scan_method = dev.settings.scan_method;
1816     session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
1817     session.params.color_filter = dev.settings.color_filter;
1818     session.params.flags = flags;
1819     compute_session(&dev, session, *calib_sensor);
1820 
1821     std::size_t pixels = session.output_pixels;
1822 
1823     try {
1824         dev.cmd_set->init_regs_for_scan_session(&dev, *calib_sensor, &regs, session);
1825     } catch (...) {
1826         if (dev.model->asic_type != AsicType::GL841) {
1827             catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); });
1828         }
1829         throw;
1830     }
1831 
1832     if (dev.model->asic_type != AsicType::GL841) {
1833         sanei_genesys_set_motor_power(regs, false);
1834     }
1835 
1836     dev.interface->write_registers(regs);
1837 
1838     if (dev.model->asic_type != AsicType::GL841) {
1839         dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
1840     }
1841     dev.cmd_set->begin_scan(&dev, *calib_sensor, &regs, true);
1842 
1843     if (is_testing_mode()) {
1844         dev.interface->test_checkpoint("coarse_gain_calibration");
1845         scanner_stop_action(dev);
1846         dev.cmd_set->move_back_home(&dev, true);
1847         return;
1848     }
1849 
1850     Image image;
1851     if (dev.model->asic_type == AsicType::GL842 ||
1852         dev.model->asic_type == AsicType::GL843)
1853     {
1854         image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes_raw);
1855     } else if (dev.model->asic_type == AsicType::GL124) {
1856         // BUG: we probably want to read whole image, not just first line
1857         image = read_unshuffled_image_from_scanner(&dev, session, session.output_line_bytes);
1858     } else {
1859         image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
1860     }
1861 
1862     if (dev.model->asic_type == AsicType::GL842 ||
1863         dev.model->asic_type == AsicType::GL843)
1864     {
1865         scanner_stop_action_no_move(dev, regs);
1866     }
1867 
1868     if (dbg_log_image_data()) {
1869         write_tiff_file("gl_coarse_gain.tiff", image);
1870     }
1871 
1872     for (unsigned ch = 0; ch < channels; ch++) {
1873         float curr_output = 0;
1874         float target_value = 0;
1875 
1876         if (dev.model->asic_type == AsicType::GL842 ||
1877             dev.model->asic_type == AsicType::GL843)
1878         {
1879             std::vector<uint16_t> values;
1880             // FIXME: start from the second line because the first line often has artifacts. Probably
1881             // caused by unclean cleanup of previous scan
1882             for (std::size_t x = pixels / 4; x < (pixels * 3 / 4); x++) {
1883                 values.push_back(image.get_raw_channel(x, 1, ch));
1884             }
1885 
1886             // pick target value at 95th percentile of all values. There may be a lot of black values
1887             // in transparency scans for example
1888             std::sort(values.begin(), values.end());
1889             curr_output = static_cast<float>(values[unsigned((values.size() - 1) * 0.95)]);
1890             target_value = calib_sensor->gain_white_ref * coeff;
1891 
1892         } else if (dev.model->asic_type == AsicType::GL841) {
1893             // FIXME: use the GL843 approach
1894             unsigned max = 0;
1895             for (std::size_t x = 0; x < image.get_width(); x++) {
1896                 auto value = image.get_raw_channel(x, 0, ch);
1897                 if (value > max) {
1898                     max = value;
1899                 }
1900             }
1901 
1902             curr_output = max;
1903             target_value = 65535.0f;
1904         } else {
1905             // FIXME: use the GL843 approach
1906             auto width = image.get_width();
1907 
1908             std::uint64_t total = 0;
1909             for (std::size_t x = width / 4; x < (width * 3 / 4); x++) {
1910                 total += image.get_raw_channel(x, 0, ch);
1911             }
1912 
1913             curr_output = total / (width / 2);
1914             target_value = calib_sensor->gain_white_ref * coeff;
1915         }
1916 
1917         std::uint8_t out_gain = compute_frontend_gain(curr_output, target_value,
1918                                                       dev.frontend.layout.type);
1919         dev.frontend.set_gain(ch, out_gain);
1920 
1921         DBG(DBG_proc, "%s: channel %d, curr=%f, target=%f, out_gain:%d\n", __func__, ch,
1922             curr_output, target_value, out_gain);
1923 
1924         if (dev.model->asic_type == AsicType::GL841 &&
1925             target_value / curr_output > 30)
1926         {
1927             DBG(DBG_error0, "****************************************\n");
1928             DBG(DBG_error0, "*                                      *\n");
1929             DBG(DBG_error0, "*  Extremely low Brightness detected.  *\n");
1930             DBG(DBG_error0, "*  Check the scanning head is          *\n");
1931             DBG(DBG_error0, "*  unlocked and moving.                *\n");
1932             DBG(DBG_error0, "*                                      *\n");
1933             DBG(DBG_error0, "****************************************\n");
1934             throw SaneException(SANE_STATUS_JAMMED, "scanning head is locked");
1935         }
1936 
1937         dbg.vlog(DBG_info, "gain=(%d, %d, %d)", dev.frontend.get_gain(0), dev.frontend.get_gain(1),
1938                  dev.frontend.get_gain(2));
1939     }
1940 
1941     if (dev.model->is_cis) {
1942         std::uint8_t min_gain = std::min({dev.frontend.get_gain(0),
1943                                           dev.frontend.get_gain(1),
1944                                           dev.frontend.get_gain(2)});
1945 
1946         dev.frontend.set_gain(0, min_gain);
1947         dev.frontend.set_gain(1, min_gain);
1948         dev.frontend.set_gain(2, min_gain);
1949     }
1950 
1951     dbg.vlog(DBG_info, "final gain=(%d, %d, %d)", dev.frontend.get_gain(0),
1952              dev.frontend.get_gain(1), dev.frontend.get_gain(2));
1953 
1954     scanner_stop_action(dev);
1955 
1956     dev.cmd_set->move_back_home(&dev, true);
1957 }
1958 
1959 namespace gl124 {
1960     void move_to_calibration_area(Genesys_Device* dev, const Genesys_Sensor& sensor,
1961                                   Genesys_Register_Set& regs);
1962 } // namespace gl124
1963 
scanner_led_calibration(Genesys_Device & dev,const Genesys_Sensor & sensor,Genesys_Register_Set & regs)1964 SensorExposure scanner_led_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor,
1965                                        Genesys_Register_Set& regs)
1966 {
1967     DBG_HELPER(dbg);
1968 
1969     float move = 0;
1970 
1971     if (dev.model->asic_type == AsicType::GL841) {
1972         if (dev.model->y_offset_calib_white > 0) {
1973             move = (dev.model->y_offset_calib_white * (dev.motor.base_ydpi)) / MM_PER_INCH;
1974             scanner_move(dev, dev.model->default_method, static_cast<unsigned>(move),
1975                          Direction::FORWARD);
1976         }
1977     } else if (dev.model->asic_type == AsicType::GL842 ||
1978                dev.model->asic_type == AsicType::GL843)
1979     {
1980         // do nothing
1981     } else if (dev.model->asic_type == AsicType::GL845 ||
1982                dev.model->asic_type == AsicType::GL846 ||
1983                dev.model->asic_type == AsicType::GL847)
1984     {
1985         move = dev.model->y_offset_calib_white;
1986         move = static_cast<float>((move * (dev.motor.base_ydpi / 4)) / MM_PER_INCH);
1987         if (move > 20) {
1988             scanner_move(dev, dev.model->default_method, static_cast<unsigned>(move),
1989                          Direction::FORWARD);
1990         }
1991     } else if (dev.model->asic_type == AsicType::GL124) {
1992         gl124::move_to_calibration_area(&dev, sensor, regs);
1993     }
1994 
1995 
1996     unsigned channels = 3;
1997     unsigned resolution = sensor.shading_resolution;
1998     const auto& calib_sensor = sanei_genesys_find_sensor(&dev, resolution, channels,
1999                                                          dev.settings.scan_method);
2000 
2001     if (dev.model->asic_type == AsicType::GL845 ||
2002         dev.model->asic_type == AsicType::GL846 ||
2003         dev.model->asic_type == AsicType::GL847 ||
2004         dev.model->asic_type == AsicType::GL124)
2005     {
2006         regs = dev.reg; // FIXME: apply this to all ASICs
2007     }
2008 
2009     unsigned yres = resolution;
2010     if (dev.model->asic_type == AsicType::GL841) {
2011         yres = dev.settings.yres; // FIXME: remove this
2012     }
2013 
2014     ScanSession session;
2015     session.params.xres = resolution;
2016     session.params.yres = yres;
2017     session.params.startx = 0;
2018     session.params.starty = 0;
2019     session.params.pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH;
2020     session.params.lines = 1;
2021     session.params.depth = 16;
2022     session.params.channels = channels;
2023     session.params.scan_method = dev.settings.scan_method;
2024     session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
2025     session.params.color_filter = dev.settings.color_filter;
2026     session.params.flags = ScanFlag::DISABLE_SHADING |
2027                            ScanFlag::DISABLE_GAMMA |
2028                            ScanFlag::SINGLE_LINE |
2029                            ScanFlag::IGNORE_STAGGER_OFFSET |
2030                            ScanFlag::IGNORE_COLOR_OFFSET;
2031     compute_session(&dev, session, calib_sensor);
2032 
2033     dev.cmd_set->init_regs_for_scan_session(&dev, calib_sensor, &regs, session);
2034 
2035     if (dev.model->asic_type == AsicType::GL841) {
2036         dev.interface->write_registers(regs); // FIXME: remove this
2037     }
2038 
2039     std::uint16_t exp[3];
2040 
2041     if (dev.model->asic_type == AsicType::GL841) {
2042         exp[0] = sensor.exposure.red;
2043         exp[1] = sensor.exposure.green;
2044         exp[2] = sensor.exposure.blue;
2045     } else {
2046         exp[0] = calib_sensor.exposure.red;
2047         exp[1] = calib_sensor.exposure.green;
2048         exp[2] = calib_sensor.exposure.blue;
2049     }
2050 
2051     std::uint16_t target = sensor.gain_white_ref * 256;
2052 
2053     std::uint16_t min_exposure = 500; // only gl841
2054     std::uint16_t max_exposure = ((exp[0] + exp[1] + exp[2]) / 3) * 2; // only gl841
2055 
2056     std::uint16_t top[3] = {};
2057     std::uint16_t bottom[3] = {};
2058 
2059     if (dev.model->asic_type == AsicType::GL845 ||
2060         dev.model->asic_type == AsicType::GL846)
2061     {
2062         bottom[0] = 29000;
2063         bottom[1] = 29000;
2064         bottom[2] = 29000;
2065 
2066         top[0] = 41000;
2067         top[1] = 51000;
2068         top[2] = 51000;
2069     } else if (dev.model->asic_type == AsicType::GL847) {
2070         bottom[0] = 28000;
2071         bottom[1] = 28000;
2072         bottom[2] = 28000;
2073 
2074         top[0] = 32000;
2075         top[1] = 32000;
2076         top[2] = 32000;
2077     }
2078 
2079     if (dev.model->asic_type == AsicType::GL845 ||
2080         dev.model->asic_type == AsicType::GL846 ||
2081         dev.model->asic_type == AsicType::GL847 ||
2082         dev.model->asic_type == AsicType::GL124)
2083     {
2084         sanei_genesys_set_motor_power(regs, false);
2085     }
2086 
2087     bool acceptable = false;
2088     for (unsigned i_test = 0; i_test < 100 && !acceptable; ++i_test) {
2089         regs_set_exposure(dev.model->asic_type, regs, { exp[0], exp[1], exp[2] });
2090 
2091         if (dev.model->asic_type == AsicType::GL841) {
2092             // FIXME: remove
2093             dev.interface->write_register(0x10, (exp[0] >> 8) & 0xff);
2094             dev.interface->write_register(0x11, exp[0] & 0xff);
2095             dev.interface->write_register(0x12, (exp[1] >> 8) & 0xff);
2096             dev.interface->write_register(0x13, exp[1] & 0xff);
2097             dev.interface->write_register(0x14, (exp[2] >> 8) & 0xff);
2098             dev.interface->write_register(0x15, exp[2] & 0xff);
2099         }
2100 
2101         dev.interface->write_registers(regs);
2102 
2103         dbg.log(DBG_info, "starting line reading");
2104         dev.cmd_set->begin_scan(&dev, calib_sensor, &regs, true);
2105 
2106         if (is_testing_mode()) {
2107             dev.interface->test_checkpoint("led_calibration");
2108             if (dev.model->asic_type == AsicType::GL841) {
2109                 scanner_stop_action(dev);
2110                 dev.cmd_set->move_back_home(&dev, true);
2111                 return { exp[0], exp[1], exp[2] };
2112             } else if (dev.model->asic_type == AsicType::GL124) {
2113                 scanner_stop_action(dev);
2114                 return calib_sensor.exposure;
2115             } else {
2116                 scanner_stop_action(dev);
2117                 dev.cmd_set->move_back_home(&dev, true);
2118                 return calib_sensor.exposure;
2119             }
2120         }
2121 
2122         auto image = read_unshuffled_image_from_scanner(&dev, session, session.output_line_bytes);
2123 
2124         scanner_stop_action(dev);
2125 
2126         if (dbg_log_image_data()) {
2127             char fn[30];
2128             std::snprintf(fn, 30, "gl_led_%02d.tiff", i_test);
2129             write_tiff_file(fn, image);
2130         }
2131 
2132         int avg[3];
2133         for (unsigned ch = 0; ch < channels; ch++) {
2134             avg[ch] = 0;
2135             for (std::size_t x = 0; x < image.get_width(); x++) {
2136                 avg[ch] += image.get_raw_channel(x, 0, ch);
2137             }
2138             avg[ch] /= image.get_width();
2139         }
2140 
2141         dbg.vlog(DBG_info, "average: %d, %d, %d", avg[0], avg[1], avg[2]);
2142 
2143         acceptable = true;
2144 
2145         if (dev.model->asic_type == AsicType::GL841) {
2146             if (avg[0] < avg[1] * 0.95 || avg[1] < avg[0] * 0.95 ||
2147                 avg[0] < avg[2] * 0.95 || avg[2] < avg[0] * 0.95 ||
2148                 avg[1] < avg[2] * 0.95 || avg[2] < avg[1] * 0.95)
2149             {
2150                 acceptable = false;
2151             }
2152 
2153             // led exposure is not acceptable if white level is too low.
2154             // ~80 hardcoded value for white level
2155             if (avg[0] < 20000 || avg[1] < 20000 || avg[2] < 20000) {
2156                 acceptable = false;
2157             }
2158 
2159             // for scanners using target value
2160             if (target > 0) {
2161                 acceptable = true;
2162                 for (unsigned i = 0; i < 3; i++) {
2163                     // we accept +- 2% delta from target
2164                     if (std::abs(avg[i] - target) > target / 50) {
2165                         exp[i] = (exp[i] * target) / avg[i];
2166                         acceptable = false;
2167                     }
2168                 }
2169             } else {
2170                 if (!acceptable) {
2171                     unsigned avga = (avg[0] + avg[1] + avg[2]) / 3;
2172                     exp[0] = (exp[0] * avga) / avg[0];
2173                     exp[1] = (exp[1] * avga) / avg[1];
2174                     exp[2] = (exp[2] * avga) / avg[2];
2175                     /*  Keep the resulting exposures below this value. Too long exposure drives
2176                         the ccd into saturation. We may fix this by relying on the fact that
2177                         we get a striped scan without shading, by means of statistical calculation
2178                     */
2179                     unsigned avge = (exp[0] + exp[1] + exp[2]) / 3;
2180 
2181                     if (avge > max_exposure) {
2182                         exp[0] = (exp[0] * max_exposure) / avge;
2183                         exp[1] = (exp[1] * max_exposure) / avge;
2184                         exp[2] = (exp[2] * max_exposure) / avge;
2185                     }
2186                     if (avge < min_exposure) {
2187                         exp[0] = (exp[0] * min_exposure) / avge;
2188                         exp[1] = (exp[1] * min_exposure) / avge;
2189                         exp[2] = (exp[2] * min_exposure) / avge;
2190                     }
2191 
2192                 }
2193             }
2194         } else if (dev.model->asic_type == AsicType::GL845 ||
2195                    dev.model->asic_type == AsicType::GL846)
2196         {
2197             for (unsigned i = 0; i < 3; i++) {
2198                 if (avg[i] < bottom[i]) {
2199                     if (avg[i] != 0) {
2200                         exp[i] = (exp[i] * bottom[i]) / avg[i];
2201                     } else {
2202                         exp[i] *= 10;
2203                     }
2204                     acceptable = false;
2205                 }
2206                 if (avg[i] > top[i]) {
2207                     if (avg[i] != 0) {
2208                         exp[i] = (exp[i] * top[i]) / avg[i];
2209                     } else {
2210                         exp[i] *= 10;
2211                     }
2212                     acceptable = false;
2213                 }
2214             }
2215         } else if (dev.model->asic_type == AsicType::GL847) {
2216             for (unsigned i = 0; i < 3; i++) {
2217                 if (avg[i] < bottom[i] || avg[i] > top[i]) {
2218                     auto target = (bottom[i] + top[i]) / 2;
2219                     if (avg[i] != 0) {
2220                         exp[i] = (exp[i] * target) / avg[i];
2221                     } else {
2222                         exp[i] *= 10;
2223                     }
2224 
2225                     acceptable = false;
2226                 }
2227             }
2228         } else if (dev.model->asic_type == AsicType::GL124) {
2229             for (unsigned i = 0; i < 3; i++) {
2230                 // we accept +- 2% delta from target
2231                 if (std::abs(avg[i] - target) > target / 50) {
2232                     float prev_weight = 0.5;
2233                     if (avg[i] != 0) {
2234                         exp[i] = exp[i] * prev_weight + ((exp[i] * target) / avg[i]) * (1 - prev_weight);
2235                     } else {
2236                         exp[i] = exp[i] * prev_weight + (exp[i] * 10) * (1 - prev_weight);
2237                     }
2238                     acceptable = false;
2239                 }
2240             }
2241         }
2242     }
2243 
2244     if (dev.model->asic_type == AsicType::GL845 ||
2245         dev.model->asic_type == AsicType::GL846 ||
2246         dev.model->asic_type == AsicType::GL847 ||
2247         dev.model->asic_type == AsicType::GL124)
2248     {
2249         // set these values as final ones for scan
2250         regs_set_exposure(dev.model->asic_type, dev.reg, { exp[0], exp[1], exp[2] });
2251     }
2252 
2253     if (dev.model->asic_type == AsicType::GL841 ||
2254         dev.model->asic_type == AsicType::GL842 ||
2255         dev.model->asic_type == AsicType::GL843)
2256     {
2257         dev.cmd_set->move_back_home(&dev, true);
2258     }
2259 
2260     if (dev.model->asic_type == AsicType::GL845 ||
2261         dev.model->asic_type == AsicType::GL846 ||
2262         dev.model->asic_type == AsicType::GL847)
2263     {
2264         if (move > 20) {
2265             dev.cmd_set->move_back_home(&dev, true);
2266         }
2267     }
2268 
2269     dbg.vlog(DBG_info,"acceptable exposure: %d, %d, %d\n", exp[0], exp[1], exp[2]);
2270 
2271     return { exp[0], exp[1], exp[2] };
2272 }
2273 
sanei_genesys_calculate_zmod(bool two_table,uint32_t exposure_time,const std::vector<uint16_t> & slope_table,unsigned acceleration_steps,unsigned move_steps,unsigned buffer_acceleration_steps,uint32_t * out_z1,uint32_t * out_z2)2274 void sanei_genesys_calculate_zmod(bool two_table,
2275                                   uint32_t exposure_time,
2276                                   const std::vector<uint16_t>& slope_table,
2277                                   unsigned acceleration_steps,
2278                                   unsigned move_steps,
2279                                   unsigned buffer_acceleration_steps,
2280                                   uint32_t* out_z1, uint32_t* out_z2)
2281 {
2282     // acceleration total time
2283     unsigned sum = std::accumulate(slope_table.begin(), slope_table.begin() + acceleration_steps,
2284                                    0, std::plus<unsigned>());
2285 
2286     /* Z1MOD:
2287         c = sum(slope_table; reg_stepno)
2288         d = reg_fwdstep * <cruising speed>
2289         Z1MOD = (c+d) % exposure_time
2290     */
2291     *out_z1 = (sum + buffer_acceleration_steps * slope_table[acceleration_steps - 1]) % exposure_time;
2292 
2293     /* Z2MOD:
2294         a = sum(slope_table; reg_stepno)
2295         b = move_steps or 1 if 2 tables
2296         Z1MOD = (a+b) % exposure_time
2297     */
2298     if (!two_table) {
2299         sum = sum + (move_steps * slope_table[acceleration_steps - 1]);
2300     } else {
2301         sum = sum + slope_table[acceleration_steps - 1];
2302     }
2303     *out_z2 = sum % exposure_time;
2304 }
2305 
2306 /**
2307  * scans a white area with motor and lamp off to get the per CCD pixel offset
2308  * that will be used to compute shading coefficient
2309  * @param dev scanner's device
2310  */
genesys_shading_calibration_impl(Genesys_Device * dev,const Genesys_Sensor & sensor,Genesys_Register_Set & local_reg,std::vector<std::uint16_t> & out_average_data,bool is_dark,const std::string & log_filename_prefix)2311 static void genesys_shading_calibration_impl(Genesys_Device* dev, const Genesys_Sensor& sensor,
2312                                              Genesys_Register_Set& local_reg,
2313                                              std::vector<std::uint16_t>& out_average_data,
2314                                              bool is_dark, const std::string& log_filename_prefix)
2315 {
2316     DBG_HELPER(dbg);
2317 
2318     if (dev->model->asic_type == AsicType::GL646) {
2319         dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg);
2320         local_reg = dev->reg;
2321     } else {
2322         local_reg = dev->reg;
2323         dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg);
2324         dev->interface->write_registers(local_reg);
2325     }
2326 
2327     debug_dump(DBG_info, dev->calib_session);
2328 
2329   size_t size;
2330   uint32_t pixels_per_line;
2331 
2332     if (dev->model->asic_type == AsicType::GL842 ||
2333         dev->model->asic_type == AsicType::GL843 ||
2334         dev->model->model_id == ModelId::CANON_5600F)
2335     {
2336         pixels_per_line = dev->calib_session.output_pixels;
2337     } else {
2338         // BUG: this selects incorrect pixel number
2339         pixels_per_line = dev->calib_session.params.pixels;
2340     }
2341     unsigned channels = dev->calib_session.params.channels;
2342 
2343     // BUG: we are using wrong pixel number here
2344     unsigned start_offset =
2345             dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres;
2346     unsigned out_pixels_per_line = pixels_per_line + start_offset;
2347 
2348     // FIXME: we set this during both dark and white calibration. A cleaner approach should
2349     // probably be used
2350     dev->average_size = channels * out_pixels_per_line;
2351 
2352     out_average_data.clear();
2353     out_average_data.resize(dev->average_size);
2354 
2355     if (is_dark && dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) {
2356         // FIXME: dark shading currently not supported on infrared transparency scans
2357         return;
2358     }
2359 
2360     // FIXME: the current calculation is likely incorrect on non-GL843 implementations,
2361     // but this needs checking. Note the extra line when computing size.
2362     if (dev->model->asic_type == AsicType::GL842 ||
2363         dev->model->asic_type == AsicType::GL843 ||
2364         dev->model->model_id == ModelId::CANON_5600F)
2365     {
2366         size = dev->calib_session.output_total_bytes_raw;
2367     } else {
2368         size = channels * 2 * pixels_per_line * (dev->calib_session.params.lines + 1);
2369     }
2370 
2371   std::vector<uint16_t> calibration_data(size / 2);
2372 
2373     // turn off motor and lamp power for flatbed scanners, but not for sheetfed scanners
2374     // because they have a calibration sheet with a sufficient black strip
2375     if (is_dark && !dev->model->is_sheetfed) {
2376         sanei_genesys_set_lamp_power(dev, sensor, local_reg, false);
2377     } else {
2378         sanei_genesys_set_lamp_power(dev, sensor, local_reg, true);
2379     }
2380     sanei_genesys_set_motor_power(local_reg, true);
2381 
2382     dev->interface->write_registers(local_reg);
2383 
2384     if (is_dark) {
2385         // wait some time to let lamp to get dark
2386         dev->interface->sleep_ms(200);
2387     } else if (has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) {
2388         // make sure lamp is bright again
2389         // FIXME: what about scanners that take a long time to warm the lamp?
2390         dev->interface->sleep_ms(500);
2391     }
2392 
2393     bool start_motor = !is_dark;
2394     dev->cmd_set->begin_scan(dev, sensor, &local_reg, start_motor);
2395 
2396 
2397     if (is_testing_mode()) {
2398         dev->interface->test_checkpoint(is_dark ? "dark_shading_calibration"
2399                                                 : "white_shading_calibration");
2400         dev->cmd_set->end_scan(dev, &local_reg, true);
2401         return;
2402     }
2403 
2404     sanei_genesys_read_data_from_scanner(dev, reinterpret_cast<std::uint8_t*>(calibration_data.data()),
2405                                          size);
2406 
2407     dev->cmd_set->end_scan(dev, &local_reg, true);
2408 
2409     if (has_flag(dev->model->flags, ModelFlag::SWAP_16BIT_DATA)) {
2410         for (std::size_t i = 0; i < size / 2; ++i) {
2411             auto value = calibration_data[i];
2412             value = ((value >> 8) & 0xff) | ((value << 8) & 0xff00);
2413             calibration_data[i] = value;
2414         }
2415     }
2416 
2417     if (has_flag(dev->model->flags, ModelFlag::INVERT_PIXEL_DATA)) {
2418         for (std::size_t i = 0; i < size / 2; ++i) {
2419             calibration_data[i] = 0xffff - calibration_data[i];
2420         }
2421     }
2422 
2423     std::fill(out_average_data.begin(),
2424               out_average_data.begin() + start_offset * channels, 0);
2425 
2426     compute_array_percentile_approx(out_average_data.data() +
2427                                         start_offset * channels,
2428                                     calibration_data.data(),
2429                                     dev->calib_session.params.lines, pixels_per_line * channels,
2430                                     0.5f);
2431 
2432     if (dbg_log_image_data()) {
2433         write_tiff_file(log_filename_prefix + "_shading.tiff", calibration_data.data(), 16,
2434                         channels, pixels_per_line, dev->calib_session.params.lines);
2435         write_tiff_file(log_filename_prefix + "_average.tiff", out_average_data.data(), 16,
2436                         channels, out_pixels_per_line, 1);
2437     }
2438 }
2439 
2440 /*
2441  * this function builds dummy dark calibration data so that we can
2442  * compute shading coefficient in a clean way
2443  *  todo: current values are hardcoded, we have to find if they
2444  * can be computed from previous calibration data (when doing offset
2445  * calibration ?)
2446  */
genesys_dark_shading_by_dummy_pixel(Genesys_Device * dev,const Genesys_Sensor & sensor)2447 static void genesys_dark_shading_by_dummy_pixel(Genesys_Device* dev, const Genesys_Sensor& sensor)
2448 {
2449     DBG_HELPER(dbg);
2450   uint32_t pixels_per_line;
2451   uint32_t skip, xend;
2452   int dummy1, dummy2, dummy3;	/* dummy black average per channel */
2453 
2454     if (dev->model->asic_type == AsicType::GL842 ||
2455         dev->model->asic_type == AsicType::GL843)
2456     {
2457         pixels_per_line = dev->calib_session.output_pixels;
2458     } else {
2459         pixels_per_line = dev->calib_session.params.pixels;
2460     }
2461 
2462     unsigned channels = dev->calib_session.params.channels;
2463 
2464     // BUG: we are using wrong pixel number here
2465     unsigned start_offset =
2466             dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres;
2467 
2468     unsigned out_pixels_per_line = pixels_per_line + start_offset;
2469 
2470     dev->average_size = channels * out_pixels_per_line;
2471   dev->dark_average_data.clear();
2472   dev->dark_average_data.resize(dev->average_size, 0);
2473 
2474   /* we average values on 'the left' where CCD pixels are under casing and
2475      give darkest values. We then use these as dummy dark calibration */
2476     if (dev->settings.xres <= sensor.full_resolution / 2) {
2477       skip = 4;
2478       xend = 36;
2479     }
2480   else
2481     {
2482       skip = 4;
2483       xend = 68;
2484     }
2485     if (dev->model->sensor_id==SensorId::CCD_G4050 ||
2486         dev->model->sensor_id==SensorId::CCD_HP_4850C
2487      || dev->model->sensor_id==SensorId::CCD_CANON_4400F
2488      || dev->model->sensor_id==SensorId::CCD_CANON_8400F
2489      || dev->model->sensor_id==SensorId::CCD_KVSS080)
2490     {
2491       skip = 2;
2492       xend = sensor.black_pixels;
2493     }
2494 
2495   /* average each channels on half left margin */
2496   dummy1 = 0;
2497   dummy2 = 0;
2498   dummy3 = 0;
2499 
2500     for (unsigned x = skip + 1; x <= xend; x++) {
2501         dummy1 += dev->white_average_data[channels * x];
2502         if (channels > 1) {
2503             dummy2 += dev->white_average_data[channels * x + 1];
2504             dummy3 += dev->white_average_data[channels * x + 2];
2505         }
2506     }
2507 
2508   dummy1 /= (xend - skip);
2509   if (channels > 1)
2510     {
2511       dummy2 /= (xend - skip);
2512       dummy3 /= (xend - skip);
2513     }
2514   DBG(DBG_proc, "%s: dummy1=%d, dummy2=%d, dummy3=%d \n", __func__, dummy1, dummy2, dummy3);
2515 
2516   /* fill dark_average */
2517     for (unsigned x = 0; x < out_pixels_per_line; x++) {
2518         dev->dark_average_data[channels * x] = dummy1;
2519         if (channels > 1) {
2520             dev->dark_average_data[channels * x + 1] = dummy2;
2521             dev->dark_average_data[channels * x + 2] = dummy3;
2522         }
2523     }
2524 }
2525 
genesys_dark_shading_by_constant(Genesys_Device & dev)2526 static void genesys_dark_shading_by_constant(Genesys_Device& dev)
2527 {
2528     dev.dark_average_data.clear();
2529     dev.dark_average_data.resize(dev.average_size, 0x0101);
2530 }
2531 
genesys_repark_sensor_before_shading(Genesys_Device * dev)2532 static void genesys_repark_sensor_before_shading(Genesys_Device* dev)
2533 {
2534     DBG_HELPER(dbg);
2535     if (has_flag(dev->model->flags, ModelFlag::SHADING_REPARK)) {
2536         dev->cmd_set->move_back_home(dev, true);
2537 
2538         if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
2539             dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
2540         {
2541             scanner_move_to_ta(*dev);
2542         }
2543     }
2544 }
2545 
genesys_repark_sensor_after_white_shading(Genesys_Device * dev)2546 static void genesys_repark_sensor_after_white_shading(Genesys_Device* dev)
2547 {
2548     DBG_HELPER(dbg);
2549     if (has_flag(dev->model->flags, ModelFlag::SHADING_REPARK)) {
2550         dev->cmd_set->move_back_home(dev, true);
2551     }
2552 }
2553 
genesys_host_shading_calibration_impl(Genesys_Device & dev,const Genesys_Sensor & sensor,std::vector<std::uint16_t> & out_average_data,bool is_dark,const std::string & log_filename_prefix)2554 static void genesys_host_shading_calibration_impl(Genesys_Device& dev, const Genesys_Sensor& sensor,
2555                                                   std::vector<std::uint16_t>& out_average_data,
2556                                                   bool is_dark,
2557                                                   const std::string& log_filename_prefix)
2558 {
2559     DBG_HELPER(dbg);
2560 
2561     if (is_dark && dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) {
2562         // FIXME: dark shading currently not supported on infrared transparency scans
2563         return;
2564     }
2565 
2566     auto local_reg = dev.reg;
2567     dev.cmd_set->init_regs_for_shading(&dev, sensor, local_reg);
2568 
2569     auto& session = dev.calib_session;
2570     debug_dump(DBG_info, session);
2571 
2572     // turn off motor and lamp power for flatbed scanners, but not for sheetfed scanners
2573     // because they have a calibration sheet with a sufficient black strip
2574     if (is_dark && !dev.model->is_sheetfed) {
2575         sanei_genesys_set_lamp_power(&dev, sensor, local_reg, false);
2576     } else {
2577         sanei_genesys_set_lamp_power(&dev, sensor, local_reg, true);
2578     }
2579     sanei_genesys_set_motor_power(local_reg, true);
2580 
2581     dev.interface->write_registers(local_reg);
2582 
2583     if (is_dark) {
2584         // wait some time to let lamp to get dark
2585         dev.interface->sleep_ms(200);
2586     } else if (has_flag(dev.model->flags, ModelFlag::DARK_CALIBRATION)) {
2587         // make sure lamp is bright again
2588         // FIXME: what about scanners that take a long time to warm the lamp?
2589         dev.interface->sleep_ms(500);
2590     }
2591 
2592     bool start_motor = !is_dark;
2593     dev.cmd_set->begin_scan(&dev, sensor, &local_reg, start_motor);
2594 
2595     if (is_testing_mode()) {
2596         dev.interface->test_checkpoint(is_dark ? "host_dark_shading_calibration"
2597                                                : "host_white_shading_calibration");
2598         dev.cmd_set->end_scan(&dev, &local_reg, true);
2599         return;
2600     }
2601 
2602     Image image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes_raw);
2603     scanner_stop_action(dev);
2604 
2605     auto start_offset = session.params.startx;
2606     auto out_pixels_per_line = start_offset + session.output_pixels;
2607 
2608     // FIXME: we set this during both dark and white calibration. A cleaner approach should
2609     // probably be used
2610     dev.average_size = session.params.channels * out_pixels_per_line;
2611 
2612     out_average_data.clear();
2613     out_average_data.resize(dev.average_size);
2614 
2615     std::fill(out_average_data.begin(),
2616               out_average_data.begin() + start_offset * session.params.channels, 0);
2617 
2618     compute_array_percentile_approx(out_average_data.data() +
2619                                         start_offset * session.params.channels,
2620                                     reinterpret_cast<std::uint16_t*>(image.get_row_ptr(0)),
2621                                     session.params.lines,
2622                                     session.output_pixels * session.params.channels,
2623                                     0.5f);
2624 
2625     if (dbg_log_image_data()) {
2626         write_tiff_file(log_filename_prefix + "_host_shading.tiff", image);
2627         write_tiff_file(log_filename_prefix + "_host_average.tiff", out_average_data.data(), 16,
2628                         session.params.channels, out_pixels_per_line, 1);
2629     }
2630 }
2631 
genesys_dark_shading_calibration(Genesys_Device * dev,const Genesys_Sensor & sensor,Genesys_Register_Set & local_reg)2632 static void genesys_dark_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
2633                                              Genesys_Register_Set& local_reg)
2634 {
2635     DBG_HELPER(dbg);
2636     if (has_flag(dev->model->flags, ModelFlag::HOST_SIDE_CALIBRATION_COMPLETE_SCAN)) {
2637         genesys_host_shading_calibration_impl(*dev, sensor, dev->dark_average_data, true,
2638                                               "gl_black");
2639     } else {
2640         genesys_shading_calibration_impl(dev, sensor, local_reg, dev->dark_average_data, true,
2641                                          "gl_black");
2642     }
2643 }
2644 
genesys_white_shading_calibration(Genesys_Device * dev,const Genesys_Sensor & sensor,Genesys_Register_Set & local_reg)2645 static void genesys_white_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
2646                                               Genesys_Register_Set& local_reg)
2647 {
2648     DBG_HELPER(dbg);
2649     if (has_flag(dev->model->flags, ModelFlag::HOST_SIDE_CALIBRATION_COMPLETE_SCAN)) {
2650         genesys_host_shading_calibration_impl(*dev, sensor, dev->white_average_data, false,
2651                                               "gl_white");
2652     } else {
2653         genesys_shading_calibration_impl(dev, sensor, local_reg, dev->white_average_data, false,
2654                                          "gl_white");
2655     }
2656 }
2657 
2658 // This calibration uses a scan over the calibration target, comprising a black and a white strip.
2659 // (So the motor must be on.)
genesys_dark_white_shading_calibration(Genesys_Device * dev,const Genesys_Sensor & sensor,Genesys_Register_Set & local_reg)2660 static void genesys_dark_white_shading_calibration(Genesys_Device* dev,
2661                                                    const Genesys_Sensor& sensor,
2662                                                    Genesys_Register_Set& local_reg)
2663 {
2664     DBG_HELPER(dbg);
2665 
2666     if (dev->model->asic_type == AsicType::GL646) {
2667         dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg);
2668         local_reg = dev->reg;
2669     } else {
2670         local_reg = dev->reg;
2671         dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg);
2672         dev->interface->write_registers(local_reg);
2673     }
2674 
2675   size_t size;
2676   uint32_t pixels_per_line;
2677   unsigned int x;
2678   uint32_t dark, white, dark_sum, white_sum, dark_count, white_count, col,
2679     dif;
2680 
2681     if (dev->model->asic_type == AsicType::GL842 ||
2682         dev->model->asic_type == AsicType::GL843)
2683     {
2684         pixels_per_line = dev->calib_session.output_pixels;
2685     } else {
2686         pixels_per_line = dev->calib_session.params.pixels;
2687     }
2688 
2689     unsigned channels = dev->calib_session.params.channels;
2690 
2691     // BUG: we are using wrong pixel number here
2692     unsigned start_offset =
2693             dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres;
2694 
2695     unsigned out_pixels_per_line = pixels_per_line + start_offset;
2696 
2697     dev->average_size = channels * out_pixels_per_line;
2698 
2699   dev->white_average_data.clear();
2700   dev->white_average_data.resize(dev->average_size);
2701 
2702   dev->dark_average_data.clear();
2703   dev->dark_average_data.resize(dev->average_size);
2704 
2705     if (dev->model->asic_type == AsicType::GL842 ||
2706         dev->model->asic_type == AsicType::GL843)
2707     {
2708         size = dev->calib_session.output_total_bytes_raw;
2709     } else {
2710         // FIXME: on GL841 this is different than dev->calib_session.output_total_bytes_raw,
2711         // needs checking
2712         size = channels * 2 * pixels_per_line * dev->calib_session.params.lines;
2713     }
2714 
2715   std::vector<uint8_t> calibration_data(size);
2716 
2717     // turn on motor and lamp power
2718     sanei_genesys_set_lamp_power(dev, sensor, local_reg, true);
2719     sanei_genesys_set_motor_power(local_reg, true);
2720 
2721     dev->interface->write_registers(local_reg);
2722 
2723     dev->cmd_set->begin_scan(dev, sensor, &local_reg, false);
2724 
2725     if (is_testing_mode()) {
2726         dev->interface->test_checkpoint("dark_white_shading_calibration");
2727         dev->cmd_set->end_scan(dev, &local_reg, true);
2728         return;
2729     }
2730 
2731     sanei_genesys_read_data_from_scanner(dev, calibration_data.data(), size);
2732 
2733     dev->cmd_set->end_scan(dev, &local_reg, true);
2734 
2735     if (dbg_log_image_data()) {
2736         if (dev->model->is_cis) {
2737             write_tiff_file("gl_black_white_shading.tiff", calibration_data.data(),
2738                             16, 1, pixels_per_line*channels,
2739                             dev->calib_session.params.lines);
2740         } else {
2741             write_tiff_file("gl_black_white_shading.tiff", calibration_data.data(),
2742                             16, channels, pixels_per_line,
2743                             dev->calib_session.params.lines);
2744         }
2745     }
2746 
2747 
2748     std::fill(dev->dark_average_data.begin(),
2749               dev->dark_average_data.begin() + start_offset * channels, 0);
2750     std::fill(dev->white_average_data.begin(),
2751               dev->white_average_data.begin() + start_offset * channels, 0);
2752 
2753     uint16_t* average_white = dev->white_average_data.data() +
2754                               start_offset * channels;
2755     uint16_t* average_dark = dev->dark_average_data.data() +
2756                              start_offset * channels;
2757 
2758   for (x = 0; x < pixels_per_line * channels; x++)
2759     {
2760       dark = 0xffff;
2761       white = 0;
2762 
2763             for (std::size_t y = 0; y < dev->calib_session.params.lines; y++)
2764 	{
2765 	  col = calibration_data[(x + y * pixels_per_line * channels) * 2];
2766 	  col |=
2767 	    calibration_data[(x + y * pixels_per_line * channels) * 2 +
2768 			     1] << 8;
2769 
2770 	  if (col > white)
2771 	    white = col;
2772 	  if (col < dark)
2773 	    dark = col;
2774 	}
2775 
2776       dif = white - dark;
2777 
2778       dark = dark + dif / 8;
2779       white = white - dif / 8;
2780 
2781       dark_count = 0;
2782       dark_sum = 0;
2783 
2784       white_count = 0;
2785       white_sum = 0;
2786 
2787             for (std::size_t y = 0; y < dev->calib_session.params.lines; y++)
2788 	{
2789 	  col = calibration_data[(x + y * pixels_per_line * channels) * 2];
2790 	  col |=
2791 	    calibration_data[(x + y * pixels_per_line * channels) * 2 +
2792 			     1] << 8;
2793 
2794 	  if (col >= white)
2795 	    {
2796 	      white_sum += col;
2797 	      white_count++;
2798 	    }
2799 	  if (col <= dark)
2800 	    {
2801 	      dark_sum += col;
2802 	      dark_count++;
2803 	    }
2804 
2805 	}
2806 
2807       dark_sum /= dark_count;
2808       white_sum /= white_count;
2809 
2810         *average_dark++ = dark_sum;
2811         *average_white++ = white_sum;
2812     }
2813 
2814     if (dbg_log_image_data()) {
2815         write_tiff_file("gl_white_average.tiff", dev->white_average_data.data(), 16, channels,
2816                         out_pixels_per_line, 1);
2817         write_tiff_file("gl_dark_average.tiff", dev->dark_average_data.data(), 16, channels,
2818                         out_pixels_per_line, 1);
2819     }
2820 }
2821 
2822 /* computes one coefficient given bright-dark value
2823  * @param coeff factor giving 1.00 gain
2824  * @param target desired target code
2825  * @param value brght-dark value
2826  * */
2827 static unsigned int
compute_coefficient(unsigned int coeff,unsigned int target,unsigned int value)2828 compute_coefficient (unsigned int coeff, unsigned int target, unsigned int value)
2829 {
2830   int result;
2831 
2832   if (value > 0)
2833     {
2834       result = (coeff * target) / value;
2835       if (result >= 65535)
2836 	{
2837 	  result = 65535;
2838 	}
2839     }
2840   else
2841     {
2842       result = coeff;
2843     }
2844   return result;
2845 }
2846 
2847 /** @brief compute shading coefficients for LiDE scanners
2848  * The dark/white shading is actually performed _after_ reducing
2849  * resolution via averaging. only dark/white shading data for what would be
2850  * first pixel at full resolution is used.
2851  *
2852  * scanner raw input to output value calculation:
2853  *   o=(i-off)*(gain/coeff)
2854  *
2855  * from datasheet:
2856  *   off=dark_average
2857  *   gain=coeff*bright_target/(bright_average-dark_average)
2858  * works for dark_target==0
2859  *
2860  * what we want is these:
2861  *   bright_target=(bright_average-off)*(gain/coeff)
2862  *   dark_target=(dark_average-off)*(gain/coeff)
2863  * leading to
2864  *  off = (dark_average*bright_target - bright_average*dark_target)/(bright_target - dark_target)
2865  *  gain = (bright_target - dark_target)/(bright_average - dark_average)*coeff
2866  *
2867  * @param dev scanner's device
2868  * @param shading_data memory area where to store the computed shading coefficients
2869  * @param pixels_per_line number of pixels per line
2870  * @param words_per_color memory words per color channel
2871  * @param channels number of color channels (actually 1 or 3)
2872  * @param o shading coefficients left offset
2873  * @param coeff 4000h or 2000h depending on fast scan mode or not (GAIN4 bit)
2874  * @param target_bright value of the white target code
2875  * @param target_dark value of the black target code
2876 */
2877 static void
compute_averaged_planar(Genesys_Device * dev,const Genesys_Sensor & sensor,uint8_t * shading_data,unsigned int pixels_per_line,unsigned int words_per_color,unsigned int channels,unsigned int o,unsigned int coeff,unsigned int target_bright,unsigned int target_dark)2878 compute_averaged_planar (Genesys_Device * dev, const Genesys_Sensor& sensor,
2879 			 uint8_t * shading_data,
2880 			 unsigned int pixels_per_line,
2881 			 unsigned int words_per_color,
2882 			 unsigned int channels,
2883 			 unsigned int o,
2884 			 unsigned int coeff,
2885 			 unsigned int target_bright,
2886 			 unsigned int target_dark)
2887 {
2888   unsigned int x, i, j, br, dk, res, avgpixels, basepixels, val;
2889   unsigned int fill,factor;
2890 
2891   DBG(DBG_info, "%s: pixels=%d, offset=%d\n", __func__, pixels_per_line, o);
2892 
2893   /* initialize result */
2894   memset (shading_data, 0xff, words_per_color * 3 * 2);
2895 
2896   /*
2897      strangely i can write 0x20000 bytes beginning at 0x00000 without overwriting
2898      slope tables - which begin at address 0x10000(for 1200dpi hw mode):
2899      memory is organized in words(2 bytes) instead of single bytes. explains
2900      quite some things
2901    */
2902 /*
2903   another one: the dark/white shading is actually performed _after_ reducing
2904   resolution via averaging. only dark/white shading data for what would be
2905   first pixel at full resolution is used.
2906  */
2907 /*
2908   scanner raw input to output value calculation:
2909     o=(i-off)*(gain/coeff)
2910 
2911   from datasheet:
2912     off=dark_average
2913     gain=coeff*bright_target/(bright_average-dark_average)
2914   works for dark_target==0
2915 
2916   what we want is these:
2917     bright_target=(bright_average-off)*(gain/coeff)
2918     dark_target=(dark_average-off)*(gain/coeff)
2919   leading to
2920     off = (dark_average*bright_target - bright_average*dark_target)/(bright_target - dark_target)
2921     gain = (bright_target - dark_target)/(bright_average - dark_average)*coeff
2922  */
2923   res = dev->settings.xres;
2924 
2925     if (sensor.full_resolution > sensor.get_optical_resolution()) {
2926         res *= 2;
2927     }
2928 
2929     // this should be evenly dividable
2930     basepixels = sensor.full_resolution / res;
2931 
2932   /* gl841 supports 1/1 1/2 1/3 1/4 1/5 1/6 1/8 1/10 1/12 1/15 averaging */
2933   if (basepixels < 1)
2934     avgpixels = 1;
2935   else if (basepixels < 6)
2936     avgpixels = basepixels;
2937   else if (basepixels < 8)
2938     avgpixels = 6;
2939   else if (basepixels < 10)
2940     avgpixels = 8;
2941   else if (basepixels < 12)
2942     avgpixels = 10;
2943   else if (basepixels < 15)
2944     avgpixels = 12;
2945   else
2946     avgpixels = 15;
2947 
2948   /* LiDE80 packs shading data */
2949     if (dev->model->sensor_id != SensorId::CIS_CANON_LIDE_80) {
2950       factor=1;
2951       fill=avgpixels;
2952     }
2953   else
2954     {
2955       factor=avgpixels;
2956       fill=1;
2957     }
2958 
2959   DBG(DBG_info, "%s: averaging over %d pixels\n", __func__, avgpixels);
2960   DBG(DBG_info, "%s: packing factor is %d\n", __func__, factor);
2961   DBG(DBG_info, "%s: fill length is %d\n", __func__, fill);
2962 
2963   for (x = 0; x <= pixels_per_line - avgpixels; x += avgpixels)
2964     {
2965       if ((x + o) * 2 * 2 + 3 > words_per_color * 2)
2966 	break;
2967 
2968       for (j = 0; j < channels; j++)
2969 	{
2970 
2971 	  dk = 0;
2972 	  br = 0;
2973 	  for (i = 0; i < avgpixels; i++)
2974 	    {
2975                 // dark data
2976                 dk += dev->dark_average_data[(x + i + pixels_per_line * j)];
2977                 // white data
2978                 br += dev->white_average_data[(x + i + pixels_per_line * j)];
2979 	    }
2980 
2981 	  br /= avgpixels;
2982 	  dk /= avgpixels;
2983 
2984 	  if (br * target_dark > dk * target_bright)
2985 	    val = 0;
2986 	  else if (dk * target_bright - br * target_dark >
2987 		   65535 * (target_bright - target_dark))
2988 	    val = 65535;
2989 	  else
2990             {
2991 	      val = (dk * target_bright - br * target_dark) / (target_bright - target_dark);
2992             }
2993 
2994           /*fill all pixels, even if only the last one is relevant*/
2995 	  for (i = 0; i < fill; i++)
2996 	    {
2997 	      shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j] = val & 0xff;
2998 	      shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 1] = val >> 8;
2999 	    }
3000 
3001 	  val = br - dk;
3002 
3003 	  if (65535 * val > (target_bright - target_dark) * coeff)
3004             {
3005 	      val = (coeff * (target_bright - target_dark)) / val;
3006             }
3007 	  else
3008             {
3009 	      val = 65535;
3010             }
3011 
3012           /*fill all pixels, even if only the last one is relevant*/
3013 	  for (i = 0; i < fill; i++)
3014 	    {
3015 	      shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 2] = val & 0xff;
3016 	      shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 3] = val >> 8;
3017 	    }
3018 	}
3019 
3020       /* fill remaining channels */
3021       for (j = channels; j < 3; j++)
3022 	{
3023 	  for (i = 0; i < fill; i++)
3024 	    {
3025 	      shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j    ] = shading_data[(x/factor + o + i) * 2 * 2    ];
3026 	      shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 1] = shading_data[(x/factor + o + i) * 2 * 2 + 1];
3027 	      shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 2] = shading_data[(x/factor + o + i) * 2 * 2 + 2];
3028 	      shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 3] = shading_data[(x/factor + o + i) * 2 * 2 + 3];
3029 	    }
3030 	}
3031     }
3032 }
3033 
color_order_to_cmat(ColorOrder color_order)3034 static std::array<unsigned, 3> color_order_to_cmat(ColorOrder color_order)
3035 {
3036     switch (color_order) {
3037         case ColorOrder::RGB: return {0, 1, 2};
3038         case ColorOrder::GBR: return {2, 0, 1};
3039         default:
3040             throw std::logic_error("Unknown color order");
3041     }
3042 }
3043 
3044 /**
3045  * Computes shading coefficient using formula in data sheet. 16bit data values
3046  * manipulated here are little endian. For now we assume deletion scanning type
3047  * and that there is always 3 channels.
3048  * @param dev scanner's device
3049  * @param shading_data memory area where to store the computed shading coefficients
3050  * @param pixels_per_line number of pixels per line
3051  * @param channels number of color channels (actually 1 or 3)
3052  * @param cmat color transposition matrix
3053  * @param offset shading coefficients left offset
3054  * @param coeff 4000h or 2000h depending on fast scan mode or not
3055  * @param target value of the target code
3056  */
compute_coefficients(Genesys_Device * dev,uint8_t * shading_data,unsigned int pixels_per_line,unsigned int channels,ColorOrder color_order,int offset,unsigned int coeff,unsigned int target)3057 static void compute_coefficients(Genesys_Device * dev,
3058 		      uint8_t * shading_data,
3059 		      unsigned int pixels_per_line,
3060 		      unsigned int channels,
3061                                  ColorOrder color_order,
3062 		      int offset,
3063 		      unsigned int coeff,
3064 		      unsigned int target)
3065 {
3066   uint8_t *ptr;			/* contain 16bit words in little endian */
3067   unsigned int x, c;
3068   unsigned int val, br, dk;
3069   unsigned int start, end;
3070 
3071   DBG(DBG_io, "%s: pixels_per_line=%d,  coeff=0x%04x\n", __func__, pixels_per_line, coeff);
3072 
3073     auto cmat = color_order_to_cmat(color_order);
3074 
3075   /* compute start & end values depending of the offset */
3076   if (offset < 0)
3077    {
3078       start = -1 * offset;
3079       end = pixels_per_line;
3080    }
3081   else
3082    {
3083      start = 0;
3084      end = pixels_per_line - offset;
3085    }
3086 
3087   for (c = 0; c < channels; c++)
3088     {
3089       for (x = start; x < end; x++)
3090 	{
3091 	  /* TODO if channels=1 , use filter to know the base addr */
3092 	  ptr = shading_data + 4 * ((x + offset) * channels + cmat[c]);
3093 
3094         // dark data
3095         dk = dev->dark_average_data[x * channels + c];
3096 
3097         // white data
3098         br = dev->white_average_data[x * channels + c];
3099 
3100 	  /* compute coeff */
3101 	  val=compute_coefficient(coeff,target,br-dk);
3102 
3103 	  /* assign it */
3104 	  ptr[0] = dk & 255;
3105 	  ptr[1] = dk / 256;
3106 	  ptr[2] = val & 0xff;
3107 	  ptr[3] = val / 256;
3108 
3109 	}
3110     }
3111 }
3112 
3113 /**
3114  * Computes shading coefficient using formula in data sheet. 16bit data values
3115  * manipulated here are little endian. Data is in planar form, ie grouped by
3116  * lines of the same color component.
3117  * @param dev scanner's device
3118  * @param shading_data memory area where to store the computed shading coefficients
3119  * @param factor averaging factor when the calibration scan is done at a higher resolution
3120  * than the final scan
3121  * @param pixels_per_line number of pixels per line
3122  * @param words_per_color total number of shading data words for one color element
3123  * @param channels number of color channels (actually 1 or 3)
3124  * @param cmat transcoding matrix for color channel order
3125  * @param offset shading coefficients left offset
3126  * @param coeff 4000h or 2000h depending on fast scan mode or not
3127  * @param target white target value
3128  */
compute_planar_coefficients(Genesys_Device * dev,uint8_t * shading_data,unsigned int factor,unsigned int pixels_per_line,unsigned int words_per_color,unsigned int channels,ColorOrder color_order,unsigned int offset,unsigned int coeff,unsigned int target)3129 static void compute_planar_coefficients(Genesys_Device * dev,
3130 			     uint8_t * shading_data,
3131 			     unsigned int factor,
3132 			     unsigned int pixels_per_line,
3133 			     unsigned int words_per_color,
3134 			     unsigned int channels,
3135                                         ColorOrder color_order,
3136 			     unsigned int offset,
3137 			     unsigned int coeff,
3138 			     unsigned int target)
3139 {
3140   uint8_t *ptr;			/* contains 16bit words in little endian */
3141   uint32_t x, c, i;
3142   uint32_t val, dk, br;
3143 
3144     auto cmat = color_order_to_cmat(color_order);
3145 
3146   DBG(DBG_io, "%s: factor=%d, pixels_per_line=%d, words=0x%X, coeff=0x%04x\n", __func__, factor,
3147       pixels_per_line, words_per_color, coeff);
3148   for (c = 0; c < channels; c++)
3149     {
3150       /* shading data is larger than pixels_per_line so offset can be neglected */
3151       for (x = 0; x < pixels_per_line; x+=factor)
3152 	{
3153 	  /* x2 because of 16 bit values, and x2 since one coeff for dark
3154 	   * and another for white */
3155 	  ptr = shading_data + words_per_color * cmat[c] * 2 + (x + offset) * 4;
3156 
3157 	  dk = 0;
3158 	  br = 0;
3159 
3160 	  /* average case */
3161 	  for(i=0;i<factor;i++)
3162 	  {
3163                 dk += dev->dark_average_data[((x+i) + pixels_per_line * c)];
3164                 br += dev->white_average_data[((x+i) + pixels_per_line * c)];
3165 	  }
3166 	  dk /= factor;
3167 	  br /= factor;
3168 
3169 	  val = compute_coefficient (coeff, target, br - dk);
3170 
3171 	  /* we duplicate the information to have calibration data at optical resolution */
3172 	  for (i = 0; i < factor; i++)
3173 	    {
3174 	      ptr[0 + 4 * i] = dk & 255;
3175 	      ptr[1 + 4 * i] = dk / 256;
3176 	      ptr[2 + 4 * i] = val & 0xff;
3177 	      ptr[3 + 4 * i] = val / 256;
3178 	    }
3179 	}
3180     }
3181   /* in case of gray level scan, we duplicate shading information on all
3182    * three color channels */
3183   if(channels==1)
3184   {
3185 	  memcpy(shading_data+cmat[1]*2*words_per_color,
3186 	         shading_data+cmat[0]*2*words_per_color,
3187 		 words_per_color*2);
3188 	  memcpy(shading_data+cmat[2]*2*words_per_color,
3189 	         shading_data+cmat[0]*2*words_per_color,
3190 		 words_per_color*2);
3191   }
3192 }
3193 
3194 static void
compute_shifted_coefficients(Genesys_Device * dev,const Genesys_Sensor & sensor,uint8_t * shading_data,unsigned int pixels_per_line,unsigned int channels,ColorOrder color_order,int offset,unsigned int coeff,unsigned int target_dark,unsigned int target_bright,unsigned int patch_size)3195 compute_shifted_coefficients (Genesys_Device * dev,
3196                               const Genesys_Sensor& sensor,
3197 			      uint8_t * shading_data,
3198 			      unsigned int pixels_per_line,
3199 			      unsigned int channels,
3200                               ColorOrder color_order,
3201 			      int offset,
3202 			      unsigned int coeff,
3203 			      unsigned int target_dark,
3204 			      unsigned int target_bright,
3205 			      unsigned int patch_size)		/* contiguous extent */
3206 {
3207   unsigned int x, avgpixels, basepixels, i, j, val1, val2;
3208   unsigned int br_tmp [3], dk_tmp [3];
3209   uint8_t *ptr = shading_data + offset * 3 * 4;                 /* contain 16bit words in little endian */
3210   unsigned int patch_cnt = offset * 3;                          /* at start, offset of first patch */
3211 
3212     auto cmat = color_order_to_cmat(color_order);
3213 
3214   x = dev->settings.xres;
3215     if (sensor.full_resolution > sensor.get_optical_resolution()) {
3216         x *= 2;	// scanner is using half-ccd mode
3217     }
3218     basepixels = sensor.full_resolution / x; // this should be evenly dividable
3219 
3220       /* gl841 supports 1/1 1/2 1/3 1/4 1/5 1/6 1/8 1/10 1/12 1/15 averaging */
3221       if (basepixels < 1)
3222         avgpixels = 1;
3223       else if (basepixels < 6)
3224         avgpixels = basepixels;
3225       else if (basepixels < 8)
3226         avgpixels = 6;
3227       else if (basepixels < 10)
3228         avgpixels = 8;
3229       else if (basepixels < 12)
3230         avgpixels = 10;
3231       else if (basepixels < 15)
3232         avgpixels = 12;
3233       else
3234         avgpixels = 15;
3235   DBG(DBG_info, "%s: pixels_per_line=%d,  coeff=0x%04x,  averaging over %d pixels\n", __func__,
3236       pixels_per_line, coeff, avgpixels);
3237 
3238   for (x = 0; x <= pixels_per_line - avgpixels; x += avgpixels) {
3239     memset (&br_tmp, 0, sizeof(br_tmp));
3240     memset (&dk_tmp, 0, sizeof(dk_tmp));
3241 
3242     for (i = 0; i < avgpixels; i++) {
3243       for (j = 0; j < channels; j++) {
3244                 br_tmp[j] += dev->white_average_data[((x + i) * channels + j)];
3245                 dk_tmp[i] += dev->dark_average_data[((x + i) * channels + j)];
3246       }
3247     }
3248     for (j = 0; j < channels; j++) {
3249       br_tmp[j] /= avgpixels;
3250       dk_tmp[j] /= avgpixels;
3251 
3252       if (br_tmp[j] * target_dark > dk_tmp[j] * target_bright)
3253         val1 = 0;
3254       else if (dk_tmp[j] * target_bright - br_tmp[j] * target_dark > 65535 * (target_bright - target_dark))
3255         val1 = 65535;
3256       else
3257         val1 = (dk_tmp[j] * target_bright - br_tmp[j] * target_dark) / (target_bright - target_dark);
3258 
3259       val2 = br_tmp[j] - dk_tmp[j];
3260       if (65535 * val2 > (target_bright - target_dark) * coeff)
3261         val2 = (coeff * (target_bright - target_dark)) / val2;
3262       else
3263         val2 = 65535;
3264 
3265       br_tmp[j] = val1;
3266       dk_tmp[j] = val2;
3267     }
3268     for (i = 0; i < avgpixels; i++) {
3269       for (j = 0; j < channels; j++) {
3270         * ptr++ = br_tmp[ cmat[j] ] & 0xff;
3271         * ptr++ = br_tmp[ cmat[j] ] >> 8;
3272         * ptr++ = dk_tmp[ cmat[j] ] & 0xff;
3273         * ptr++ = dk_tmp[ cmat[j] ] >> 8;
3274         patch_cnt++;
3275         if (patch_cnt == patch_size) {
3276           patch_cnt = 0;
3277           val1 = cmat[2];
3278           cmat[2] = cmat[1];
3279           cmat[1] = cmat[0];
3280           cmat[0] = val1;
3281         }
3282       }
3283     }
3284   }
3285 }
3286 
genesys_send_shading_coefficient(Genesys_Device * dev,const Genesys_Sensor & sensor)3287 static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_Sensor& sensor)
3288 {
3289     DBG_HELPER(dbg);
3290 
3291     if (sensor.use_host_side_calib) {
3292         return;
3293     }
3294 
3295   uint32_t pixels_per_line;
3296   int o;
3297   unsigned int length;		/**> number of shading calibration data words */
3298   unsigned int factor;
3299   unsigned int coeff, target_code, words_per_color = 0;
3300 
3301 
3302     // BUG: we are using wrong pixel number here
3303     unsigned start_offset =
3304             dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres;
3305 
3306     if (dev->model->asic_type == AsicType::GL842 ||
3307         dev->model->asic_type == AsicType::GL843)
3308     {
3309         pixels_per_line = dev->calib_session.output_pixels + start_offset;
3310     } else {
3311         pixels_per_line = dev->calib_session.params.pixels + start_offset;
3312     }
3313 
3314     unsigned channels = dev->calib_session.params.channels;
3315 
3316   /* we always build data for three channels, even for gray
3317    * we make the shading data such that each color channel data line is contiguous
3318    * to the next one, which allow to write the 3 channels in 1 write
3319    * during genesys_send_shading_coefficient, some values are words, other bytes
3320    * hence the x2 factor */
3321   switch (dev->reg.get8(0x05) >> 6)
3322     {
3323       /* 600 dpi */
3324     case 0:
3325       words_per_color = 0x2a00;
3326       break;
3327       /* 1200 dpi */
3328     case 1:
3329       words_per_color = 0x5500;
3330       break;
3331       /* 2400 dpi */
3332     case 2:
3333       words_per_color = 0xa800;
3334       break;
3335       /* 4800 dpi */
3336     case 3:
3337       words_per_color = 0x15000;
3338       break;
3339     }
3340 
3341   /* special case, memory is aligned on 0x5400, this has yet to be explained */
3342   /* could be 0xa800 because sensor is truly 2400 dpi, then halved because
3343    * we only set 1200 dpi */
3344   if(dev->model->sensor_id==SensorId::CIS_CANON_LIDE_80)
3345     {
3346       words_per_color = 0x5400;
3347     }
3348 
3349   length = words_per_color * 3 * 2;
3350 
3351   /* allocate computed size */
3352   // contains 16bit words in little endian
3353   std::vector<uint8_t> shading_data(length, 0);
3354 
3355     if (!dev->calib_session.computed) {
3356         genesys_send_offset_and_shading(dev, sensor, shading_data.data(), length);
3357         return;
3358     }
3359 
3360   /* TARGET/(Wn-Dn) = white gain -> ~1.xxx then it is multiplied by 0x2000
3361      or 0x4000 to give an integer
3362      Wn = white average for column n
3363      Dn = dark average for column n
3364    */
3365     if (get_registers_gain4_bit(dev->model->asic_type, dev->reg)) {
3366         coeff = 0x4000;
3367     } else {
3368         coeff = 0x2000;
3369     }
3370 
3371   /* compute avg factor */
3372     if (dev->settings.xres > sensor.full_resolution) {
3373         factor = 1;
3374     } else {
3375         factor = sensor.full_resolution / dev->settings.xres;
3376     }
3377 
3378   /* for GL646, shading data is planar if REG_0x01_FASTMOD is set and
3379    * chunky if not. For now we rely on the fact that we know that
3380    * each sensor is used only in one mode. Currently only the CIS_XP200
3381    * sets REG_0x01_FASTMOD.
3382    */
3383 
3384   /* TODO setup a struct in genesys_devices that
3385    * will handle these settings instead of having this switch growing up */
3386   switch (dev->model->sensor_id)
3387     {
3388     case SensorId::CCD_XP300:
3389         case SensorId::CCD_DOCKETPORT_487:
3390     case SensorId::CCD_ROADWARRIOR:
3391     case SensorId::CCD_DP665:
3392     case SensorId::CCD_DP685:
3393     case SensorId::CCD_DSMOBILE600:
3394       target_code = 0xdc00;
3395       o = 4;
3396       compute_planar_coefficients (dev,
3397                    shading_data.data(),
3398 				   factor,
3399 				   pixels_per_line,
3400 				   words_per_color,
3401 				   channels,
3402                    ColorOrder::RGB,
3403 				   o,
3404 				   coeff,
3405 				   target_code);
3406       break;
3407     case SensorId::CIS_XP200:
3408       target_code = 0xdc00;
3409       o = 2;
3410       compute_planar_coefficients (dev,
3411                    shading_data.data(),
3412 				   1,
3413 				   pixels_per_line,
3414 				   words_per_color,
3415 				   channels,
3416                    ColorOrder::GBR,
3417 				   o,
3418 				   coeff,
3419 				   target_code);
3420       break;
3421     case SensorId::CCD_HP2300:
3422       target_code = 0xdc00;
3423       o = 2;
3424             if (dev->settings.xres <= sensor.full_resolution / 2) {
3425                 o = o - sensor.dummy_pixel / 2;
3426             }
3427       compute_coefficients (dev,
3428                 shading_data.data(),
3429 			    pixels_per_line,
3430 			    3,
3431                             ColorOrder::RGB,
3432                             o,
3433                             coeff,
3434                             target_code);
3435       break;
3436     case SensorId::CCD_5345:
3437       target_code = 0xe000;
3438       o = 4;
3439       if(dev->settings.xres<=sensor.full_resolution/2)
3440        {
3441           o = o - sensor.dummy_pixel;
3442        }
3443       compute_coefficients (dev,
3444                 shading_data.data(),
3445 			    pixels_per_line,
3446 			    3,
3447                             ColorOrder::RGB,
3448                             o,
3449                             coeff,
3450                             target_code);
3451       break;
3452     case SensorId::CCD_HP3670:
3453     case SensorId::CCD_HP2400:
3454       target_code = 0xe000;
3455             // offset is dependent on ccd_pixels_per_system_pixel(), but we couldn't use this in
3456             // common code previously.
3457             // FIXME: use sensor.ccd_pixels_per_system_pixel()
3458       if(dev->settings.xres<=300)
3459         {
3460                 o = -10;
3461         }
3462       else if(dev->settings.xres<=600)
3463         {
3464                 o = -6;
3465         }
3466       else
3467         {
3468           o = +2;
3469         }
3470       compute_coefficients (dev,
3471                 shading_data.data(),
3472 			    pixels_per_line,
3473 			    3,
3474                             ColorOrder::RGB,
3475                             o,
3476                             coeff,
3477                             target_code);
3478       break;
3479     case SensorId::CCD_KVSS080:
3480     case SensorId::CCD_PLUSTEK_OPTICBOOK_3800:
3481     case SensorId::CCD_G4050:
3482         case SensorId::CCD_HP_4850C:
3483     case SensorId::CCD_CANON_4400F:
3484     case SensorId::CCD_CANON_8400F:
3485     case SensorId::CCD_CANON_8600F:
3486         case SensorId::CCD_PLUSTEK_OPTICFILM_7200:
3487     case SensorId::CCD_PLUSTEK_OPTICFILM_7200I:
3488     case SensorId::CCD_PLUSTEK_OPTICFILM_7300:
3489         case SensorId::CCD_PLUSTEK_OPTICFILM_7400:
3490     case SensorId::CCD_PLUSTEK_OPTICFILM_7500I:
3491         case SensorId::CCD_PLUSTEK_OPTICFILM_8200I:
3492       target_code = 0xe000;
3493       o = 0;
3494       compute_coefficients (dev,
3495                 shading_data.data(),
3496 			    pixels_per_line,
3497 			    3,
3498                             ColorOrder::RGB,
3499                             o,
3500                             coeff,
3501                             target_code);
3502       break;
3503     case SensorId::CIS_CANON_LIDE_700F:
3504     case SensorId::CIS_CANON_LIDE_100:
3505     case SensorId::CIS_CANON_LIDE_200:
3506     case SensorId::CIS_CANON_LIDE_110:
3507     case SensorId::CIS_CANON_LIDE_120:
3508     case SensorId::CIS_CANON_LIDE_210:
3509     case SensorId::CIS_CANON_LIDE_220:
3510         case SensorId::CCD_CANON_5600F:
3511         /* TODO store this in a data struct so we avoid
3512          * growing this switch */
3513         switch(dev->model->sensor_id)
3514           {
3515           case SensorId::CIS_CANON_LIDE_110:
3516           case SensorId::CIS_CANON_LIDE_120:
3517           case SensorId::CIS_CANON_LIDE_210:
3518           case SensorId::CIS_CANON_LIDE_220:
3519           case SensorId::CIS_CANON_LIDE_700F:
3520                 target_code = 0xc000;
3521             break;
3522           default:
3523             target_code = 0xdc00;
3524           }
3525         words_per_color=pixels_per_line*2;
3526         length = words_per_color * 3 * 2;
3527         shading_data.clear();
3528         shading_data.resize(length, 0);
3529         compute_planar_coefficients (dev,
3530                                      shading_data.data(),
3531                                      1,
3532                                      pixels_per_line,
3533                                      words_per_color,
3534                                      channels,
3535                                      ColorOrder::RGB,
3536                                      0,
3537                                      coeff,
3538                                      target_code);
3539       break;
3540     case SensorId::CIS_CANON_LIDE_35:
3541         case SensorId::CIS_CANON_LIDE_60:
3542             case SensorId::CIS_CANON_LIDE_90:
3543       compute_averaged_planar (dev, sensor,
3544                                shading_data.data(),
3545                                pixels_per_line,
3546                                words_per_color,
3547                                channels,
3548                                4,
3549                                coeff,
3550                                0xe000,
3551                                0x0a00);
3552       break;
3553     case SensorId::CIS_CANON_LIDE_80:
3554       compute_averaged_planar (dev, sensor,
3555                                shading_data.data(),
3556                                pixels_per_line,
3557                                words_per_color,
3558                                channels,
3559                                0,
3560                                coeff,
3561 			       0xe000,
3562                                0x0800);
3563       break;
3564     case SensorId::CCD_PLUSTEK_OPTICPRO_3600:
3565       compute_shifted_coefficients (dev, sensor,
3566                         shading_data.data(),
3567 			            pixels_per_line,
3568 			            channels,
3569                         ColorOrder::RGB,
3570 			            12,         /* offset */
3571 			            coeff,
3572  			            0x0001,      /* target_dark */
3573 			            0xf900,      /* target_bright */
3574 			            256);        /* patch_size: contiguous extent */
3575       break;
3576     default:
3577         throw SaneException(SANE_STATUS_UNSUPPORTED, "sensor %d not supported",
3578                             static_cast<unsigned>(dev->model->sensor_id));
3579       break;
3580     }
3581 
3582     // do the actual write of shading calibration data to the scanner
3583     genesys_send_offset_and_shading(dev, sensor, shading_data.data(), length);
3584 }
3585 
3586 
3587 /**
3588  * search calibration cache list for an entry matching required scan.
3589  * If one is found, set device calibration with it
3590  * @param dev scanner's device
3591  * @return false if no matching cache entry has been
3592  * found, true if one has been found and used.
3593  */
3594 static bool
genesys_restore_calibration(Genesys_Device * dev,Genesys_Sensor & sensor)3595 genesys_restore_calibration(Genesys_Device * dev, Genesys_Sensor& sensor)
3596 {
3597     DBG_HELPER(dbg);
3598 
3599     // if no cache or no function to evaluate cache entry there can be no match/
3600     if (dev->calibration_cache.empty()) {
3601         return false;
3602     }
3603 
3604     auto session = dev->cmd_set->calculate_scan_session(dev, sensor, dev->settings);
3605 
3606   /* we walk the link list of calibration cache in search for a
3607    * matching one */
3608   for (auto& cache : dev->calibration_cache)
3609     {
3610         if (sanei_genesys_is_compatible_calibration(dev, session, &cache, false)) {
3611             dev->frontend = cache.frontend;
3612           /* we don't restore the gamma fields */
3613           sensor.exposure = cache.sensor.exposure;
3614 
3615             dev->calib_session = cache.session;
3616           dev->average_size = cache.average_size;
3617 
3618           dev->dark_average_data = cache.dark_average_data;
3619           dev->white_average_data = cache.white_average_data;
3620 
3621             if (!dev->cmd_set->has_send_shading_data()) {
3622             genesys_send_shading_coefficient(dev, sensor);
3623           }
3624 
3625           DBG(DBG_proc, "%s: restored\n", __func__);
3626           return true;
3627 	}
3628     }
3629   DBG(DBG_proc, "%s: completed(nothing found)\n", __func__);
3630   return false;
3631 }
3632 
3633 
genesys_save_calibration(Genesys_Device * dev,const Genesys_Sensor & sensor)3634 static void genesys_save_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor)
3635 {
3636     DBG_HELPER(dbg);
3637 #ifdef HAVE_SYS_TIME_H
3638   struct timeval time;
3639 #endif
3640 
3641     auto session = dev->cmd_set->calculate_scan_session(dev, sensor, dev->settings);
3642 
3643   auto found_cache_it = dev->calibration_cache.end();
3644   for (auto cache_it = dev->calibration_cache.begin(); cache_it != dev->calibration_cache.end();
3645        cache_it++)
3646     {
3647         if (sanei_genesys_is_compatible_calibration(dev, session, &*cache_it, true)) {
3648             found_cache_it = cache_it;
3649             break;
3650         }
3651     }
3652 
3653   /* if we found on overridable cache, we reuse it */
3654   if (found_cache_it == dev->calibration_cache.end())
3655     {
3656       /* create a new cache entry and insert it in the linked list */
3657       dev->calibration_cache.push_back(Genesys_Calibration_Cache());
3658       found_cache_it = std::prev(dev->calibration_cache.end());
3659     }
3660 
3661   found_cache_it->average_size = dev->average_size;
3662 
3663   found_cache_it->dark_average_data = dev->dark_average_data;
3664   found_cache_it->white_average_data = dev->white_average_data;
3665 
3666     found_cache_it->params = session.params;
3667   found_cache_it->frontend = dev->frontend;
3668   found_cache_it->sensor = sensor;
3669 
3670     found_cache_it->session = dev->calib_session;
3671 
3672 #ifdef HAVE_SYS_TIME_H
3673     gettimeofday(&time, nullptr);
3674   found_cache_it->last_calibration = time.tv_sec;
3675 #endif
3676 }
3677 
genesys_flatbed_calibration(Genesys_Device * dev,Genesys_Sensor & sensor)3678 static void genesys_flatbed_calibration(Genesys_Device* dev, Genesys_Sensor& sensor)
3679 {
3680     DBG_HELPER(dbg);
3681     uint32_t pixels_per_line;
3682 
3683     unsigned coarse_res = sensor.full_resolution;
3684     if (dev->settings.yres <= sensor.full_resolution / 2) {
3685         coarse_res /= 2;
3686     }
3687 
3688     if (dev->model->model_id == ModelId::CANON_8400F) {
3689         coarse_res = 1600;
3690     }
3691 
3692     if (dev->model->model_id == ModelId::CANON_4400F ||
3693         dev->model->model_id == ModelId::CANON_8600F)
3694     {
3695         coarse_res = 1200;
3696     }
3697 
3698     auto local_reg = dev->initial_regs;
3699 
3700     if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) {
3701         // do ADC calibration first.
3702         dev->interface->record_progress_message("offset_calibration");
3703         dev->cmd_set->offset_calibration(dev, sensor, local_reg);
3704 
3705         dev->interface->record_progress_message("coarse_gain_calibration");
3706         dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res);
3707     }
3708 
3709     if (dev->model->is_cis &&
3710         !has_flag(dev->model->flags, ModelFlag::DISABLE_EXPOSURE_CALIBRATION))
3711     {
3712         // ADC now sends correct data, we can configure the exposure for the LEDs
3713         dev->interface->record_progress_message("led_calibration");
3714         switch (dev->model->asic_type) {
3715             case AsicType::GL124:
3716             case AsicType::GL841:
3717             case AsicType::GL845:
3718             case AsicType::GL846:
3719             case AsicType::GL847: {
3720                 auto calib_exposure = dev->cmd_set->led_calibration(dev, sensor, local_reg);
3721                 for (auto& sensor_update :
3722                         sanei_genesys_find_sensors_all_for_write(dev, sensor.method)) {
3723                     sensor_update.get().exposure = calib_exposure;
3724                 }
3725                 sensor.exposure = calib_exposure;
3726                 break;
3727             }
3728             default: {
3729                 sensor.exposure = dev->cmd_set->led_calibration(dev, sensor, local_reg);
3730             }
3731         }
3732 
3733         if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) {
3734             // recalibrate ADC again for the new LED exposure
3735             dev->interface->record_progress_message("offset_calibration");
3736             dev->cmd_set->offset_calibration(dev, sensor, local_reg);
3737 
3738             dev->interface->record_progress_message("coarse_gain_calibration");
3739             dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res);
3740         }
3741     }
3742 
3743   /* we always use sensor pixel number when the ASIC can't handle multi-segments sensor */
3744     if (!has_flag(dev->model->flags, ModelFlag::SIS_SENSOR)) {
3745         pixels_per_line = static_cast<std::uint32_t>((dev->model->x_size * dev->settings.xres) /
3746                                                      MM_PER_INCH);
3747     } else {
3748         pixels_per_line = static_cast<std::uint32_t>((dev->model->x_size_calib_mm * dev->settings.xres)
3749                                                       / MM_PER_INCH);
3750     }
3751 
3752     // send default shading data
3753     dev->interface->record_progress_message("sanei_genesys_init_shading_data");
3754     sanei_genesys_init_shading_data(dev, sensor, pixels_per_line);
3755 
3756     if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
3757         dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
3758     {
3759         scanner_move_to_ta(*dev);
3760     }
3761 
3762     // shading calibration
3763     if (!has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION)) {
3764         if (has_flag(dev->model->flags, ModelFlag::DARK_WHITE_CALIBRATION)) {
3765             dev->interface->record_progress_message("genesys_dark_white_shading_calibration");
3766             genesys_dark_white_shading_calibration(dev, sensor, local_reg);
3767         } else {
3768             DBG(DBG_proc, "%s : genesys_dark_shading_calibration local_reg ", __func__);
3769             debug_dump(DBG_proc, local_reg);
3770 
3771             if (has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) {
3772                 dev->interface->record_progress_message("genesys_dark_shading_calibration");
3773                 genesys_dark_shading_calibration(dev, sensor, local_reg);
3774                 genesys_repark_sensor_before_shading(dev);
3775             }
3776 
3777             dev->interface->record_progress_message("genesys_white_shading_calibration");
3778             genesys_white_shading_calibration(dev, sensor, local_reg);
3779 
3780             genesys_repark_sensor_after_white_shading(dev);
3781 
3782             if (!has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) {
3783                 if (has_flag(dev->model->flags, ModelFlag::USE_CONSTANT_FOR_DARK_CALIBRATION)) {
3784                     genesys_dark_shading_by_constant(*dev);
3785                 } else {
3786                     genesys_dark_shading_by_dummy_pixel(dev, sensor);
3787                 }
3788             }
3789         }
3790     }
3791 
3792     if (!dev->cmd_set->has_send_shading_data()) {
3793         dev->interface->record_progress_message("genesys_send_shading_coefficient");
3794         genesys_send_shading_coefficient(dev, sensor);
3795     }
3796 }
3797 
3798 /**
3799  * Does the calibration process for a sheetfed scanner
3800  * - offset calibration
3801  * - gain calibration
3802  * - shading calibration
3803  * During calibration a predefined calibration sheet with specific black and white
3804  * areas is used.
3805  * @param dev device to calibrate
3806  */
genesys_sheetfed_calibration(Genesys_Device * dev,Genesys_Sensor & sensor)3807 static void genesys_sheetfed_calibration(Genesys_Device* dev, Genesys_Sensor& sensor)
3808 {
3809     DBG_HELPER(dbg);
3810     bool forward = true;
3811 
3812     auto local_reg = dev->initial_regs;
3813 
3814     // first step, load document
3815     dev->cmd_set->load_document(dev);
3816 
3817     unsigned coarse_res = sensor.full_resolution;
3818 
3819   /* the afe needs to sends valid data even before calibration */
3820 
3821   /* go to a white area */
3822     try {
3823         scanner_search_strip(*dev, forward, false);
3824     } catch (...) {
3825         catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
3826         throw;
3827     }
3828 
3829     if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) {
3830         // do ADC calibration first.
3831         dev->interface->record_progress_message("offset_calibration");
3832         dev->cmd_set->offset_calibration(dev, sensor, local_reg);
3833 
3834         dev->interface->record_progress_message("coarse_gain_calibration");
3835         dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res);
3836     }
3837 
3838     if (dev->model->is_cis &&
3839         !has_flag(dev->model->flags, ModelFlag::DISABLE_EXPOSURE_CALIBRATION))
3840     {
3841         // ADC now sends correct data, we can configure the exposure for the LEDs
3842         dev->interface->record_progress_message("led_calibration");
3843         dev->cmd_set->led_calibration(dev, sensor, local_reg);
3844 
3845         if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) {
3846             // recalibrate ADC again for the new LED exposure
3847             dev->interface->record_progress_message("offset_calibration");
3848             dev->cmd_set->offset_calibration(dev, sensor, local_reg);
3849 
3850             dev->interface->record_progress_message("coarse_gain_calibration");
3851             dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res);
3852         }
3853     }
3854 
3855   /* search for a full width black strip and then do a 16 bit scan to
3856    * gather black shading data */
3857     if (has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) {
3858         // seek black/white reverse/forward
3859         try {
3860             scanner_search_strip(*dev, forward, true);
3861         } catch (...) {
3862             catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
3863             throw;
3864         }
3865 
3866         try {
3867             genesys_dark_shading_calibration(dev, sensor, local_reg);
3868         } catch (...) {
3869             catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
3870             throw;
3871         }
3872         forward = false;
3873     }
3874 
3875 
3876   /* go to a white area */
3877     try {
3878         scanner_search_strip(*dev, forward, false);
3879     } catch (...) {
3880         catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
3881         throw;
3882     }
3883 
3884   genesys_repark_sensor_before_shading(dev);
3885 
3886     try {
3887         genesys_white_shading_calibration(dev, sensor, local_reg);
3888         genesys_repark_sensor_after_white_shading(dev);
3889     } catch (...) {
3890         catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
3891         throw;
3892     }
3893 
3894     // in case we haven't black shading data, build it from black pixels of white calibration
3895     // FIXME: shouldn't we use genesys_dark_shading_by_dummy_pixel() ?
3896     if (!has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) {
3897         genesys_dark_shading_by_constant(*dev);
3898     }
3899 
3900   /* send the shading coefficient when doing whole line shading
3901    * but not when using SHDAREA like GL124 */
3902     if (!dev->cmd_set->has_send_shading_data()) {
3903         genesys_send_shading_coefficient(dev, sensor);
3904     }
3905 
3906     // save the calibration data
3907     genesys_save_calibration(dev, sensor);
3908 
3909     // and finally eject calibration sheet
3910     dev->cmd_set->eject_document(dev);
3911 
3912     // restore settings
3913     dev->settings.xres = sensor.full_resolution;
3914 }
3915 
3916 /**
3917  * does the calibration process for a device
3918  * @param dev device to calibrate
3919  */
genesys_scanner_calibration(Genesys_Device * dev,Genesys_Sensor & sensor)3920 static void genesys_scanner_calibration(Genesys_Device* dev, Genesys_Sensor& sensor)
3921 {
3922     DBG_HELPER(dbg);
3923     if (!dev->model->is_sheetfed) {
3924         genesys_flatbed_calibration(dev, sensor);
3925         return;
3926     }
3927     genesys_sheetfed_calibration(dev, sensor);
3928 }
3929 
3930 
3931 /* ------------------------------------------------------------------------ */
3932 /*                  High level (exported) functions                         */
3933 /* ------------------------------------------------------------------------ */
3934 
3935 /*
3936  * wait lamp to be warm enough by scanning the same line until
3937  * differences between two scans are below a threshold
3938  */
genesys_warmup_lamp(Genesys_Device * dev)3939 static void genesys_warmup_lamp(Genesys_Device* dev)
3940 {
3941     DBG_HELPER(dbg);
3942     unsigned seconds = 0;
3943 
3944   const auto& sensor = sanei_genesys_find_sensor_any(dev);
3945 
3946     dev->cmd_set->init_regs_for_warmup(dev, sensor, &dev->reg);
3947     dev->interface->write_registers(dev->reg);
3948 
3949     auto total_pixels =  dev->session.output_pixels;
3950     auto total_size = dev->session.output_line_bytes;
3951     auto channels = dev->session.params.channels;
3952     auto lines = dev->session.output_line_count;
3953 
3954   std::vector<uint8_t> first_line(total_size);
3955   std::vector<uint8_t> second_line(total_size);
3956 
3957     do {
3958         first_line = second_line;
3959 
3960         dev->cmd_set->begin_scan(dev, sensor, &dev->reg, false);
3961 
3962         if (is_testing_mode()) {
3963             dev->interface->test_checkpoint("warmup_lamp");
3964             dev->cmd_set->end_scan(dev, &dev->reg, true);
3965             return;
3966         }
3967 
3968         wait_until_buffer_non_empty(dev);
3969 
3970         sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size);
3971         dev->cmd_set->end_scan(dev, &dev->reg, true);
3972 
3973         // compute difference between the two scans
3974         double first_average = 0;
3975         double second_average = 0;
3976         for (unsigned pixel = 0; pixel < total_size; pixel++) {
3977             // 16 bit data
3978             if (dev->session.params.depth == 16) {
3979                 first_average += (first_line[pixel] + first_line[pixel + 1] * 256);
3980                 second_average += (second_line[pixel] + second_line[pixel + 1] * 256);
3981                 pixel++;
3982             } else {
3983                 first_average += first_line[pixel];
3984                 second_average += second_line[pixel];
3985             }
3986         }
3987 
3988         first_average /= total_pixels;
3989         second_average /= total_pixels;
3990 
3991         if (dbg_log_image_data()) {
3992             write_tiff_file("gl_warmup1.tiff", first_line.data(), dev->session.params.depth,
3993                             channels, total_size / (lines * channels), lines);
3994             write_tiff_file("gl_warmup2.tiff", second_line.data(), dev->session.params.depth,
3995                             channels, total_size / (lines * channels), lines);
3996         }
3997 
3998         DBG(DBG_info, "%s: average 1 = %.2f, average 2 = %.2f\n", __func__, first_average,
3999             second_average);
4000 
4001         float average_difference = std::fabs(first_average - second_average) / second_average;
4002         if (second_average > 0 && average_difference < 0.005)
4003         {
4004             dbg.vlog(DBG_info, "difference: %f, exiting", average_difference);
4005             break;
4006         }
4007 
4008         dev->interface->sleep_ms(1000);
4009         seconds++;
4010     } while (seconds < WARMUP_TIME);
4011 
4012   if (seconds >= WARMUP_TIME)
4013     {
4014         throw SaneException(SANE_STATUS_IO_ERROR,
4015                             "warmup timed out after %d seconds. Lamp defective?", seconds);
4016     }
4017   else
4018     {
4019       DBG(DBG_info, "%s: warmup succeeded after %d seconds\n", __func__, seconds);
4020     }
4021 }
4022 
init_regs_for_scan(Genesys_Device & dev,const Genesys_Sensor & sensor,Genesys_Register_Set & regs)4023 static void init_regs_for_scan(Genesys_Device& dev, const Genesys_Sensor& sensor,
4024                                Genesys_Register_Set& regs)
4025 {
4026     DBG_HELPER(dbg);
4027     debug_dump(DBG_info, dev.settings);
4028 
4029     auto session = dev.cmd_set->calculate_scan_session(&dev, sensor, dev.settings);
4030 
4031     if (dev.model->asic_type == AsicType::GL124 ||
4032         dev.model->asic_type == AsicType::GL845 ||
4033         dev.model->asic_type == AsicType::GL846 ||
4034         dev.model->asic_type == AsicType::GL847)
4035     {
4036         /*  Fast move to scan area:
4037 
4038             We don't move fast the whole distance since it would involve computing
4039             acceleration/deceleration distance for scan resolution. So leave a remainder for it so
4040             scan makes the final move tuning
4041         */
4042 
4043         if (dev.settings.get_channels() * dev.settings.yres >= 600 && session.params.starty > 700) {
4044             scanner_move(dev, dev.model->default_method,
4045                          static_cast<unsigned>(session.params.starty - 500),
4046                          Direction::FORWARD);
4047             session.params.starty = 500;
4048         }
4049         compute_session(&dev, session, sensor);
4050     }
4051 
4052     dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &regs, session);
4053 }
4054 
4055 // High-level start of scanning
genesys_start_scan(Genesys_Device * dev,bool lamp_off)4056 static void genesys_start_scan(Genesys_Device* dev, bool lamp_off)
4057 {
4058     DBG_HELPER(dbg);
4059   unsigned int steps, expected;
4060 
4061 
4062   /* since not all scanners are set to wait for head to park
4063    * we check we are not still parking before starting a new scan */
4064     if (dev->parking) {
4065         sanei_genesys_wait_for_home(dev);
4066     }
4067 
4068     // disable power saving
4069     dev->cmd_set->save_power(dev, false);
4070 
4071   /* wait for lamp warmup : until a warmup for TRANSPARENCY is designed, skip
4072    * it when scanning from XPA. */
4073     if (has_flag(dev->model->flags, ModelFlag::WARMUP) &&
4074         (dev->settings.scan_method != ScanMethod::TRANSPARENCY_INFRARED))
4075     {
4076         if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
4077             dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
4078         {
4079             scanner_move_to_ta(*dev);
4080         }
4081 
4082         genesys_warmup_lamp(dev);
4083     }
4084 
4085   /* set top left x and y values by scanning the internals if flatbed scanners */
4086     if (!dev->model->is_sheetfed) {
4087         // TODO: check we can drop this since we cannot have the scanner's head wandering here
4088         dev->parking = false;
4089         dev->cmd_set->move_back_home(dev, true);
4090     }
4091 
4092   /* move to calibration area for transparency adapter */
4093     if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
4094         dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
4095     {
4096         scanner_move_to_ta(*dev);
4097     }
4098 
4099   /* load document if needed (for sheetfed scanner for instance) */
4100     if (dev->model->is_sheetfed) {
4101         dev->cmd_set->load_document(dev);
4102     }
4103 
4104     auto& sensor = sanei_genesys_find_sensor_for_write(dev, dev->settings.xres,
4105                                                        dev->settings.get_channels(),
4106                                                        dev->settings.scan_method);
4107 
4108     // send gamma tables. They have been set to device or user value
4109     // when setting option value */
4110     dev->cmd_set->send_gamma_table(dev, sensor);
4111 
4112   /* try to use cached calibration first */
4113   if (!genesys_restore_calibration (dev, sensor))
4114     {
4115         // calibration : sheetfed scanners can't calibrate before each scan.
4116         // also don't run calibration for those scanners where all passes are disabled
4117         bool shading_disabled =
4118                 has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION) &&
4119                 has_flag(dev->model->flags, ModelFlag::DISABLE_EXPOSURE_CALIBRATION) &&
4120                 has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION);
4121         if (!shading_disabled && !dev->model->is_sheetfed) {
4122             genesys_scanner_calibration(dev, sensor);
4123             genesys_save_calibration(dev, sensor);
4124         } else {
4125           DBG(DBG_warn, "%s: no calibration done\n", __func__);
4126         }
4127     }
4128 
4129     dev->cmd_set->wait_for_motor_stop(dev);
4130 
4131     if (dev->cmd_set->needs_home_before_init_regs_for_scan(dev)) {
4132         dev->cmd_set->move_back_home(dev, true);
4133     }
4134 
4135     if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
4136         dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
4137     {
4138         scanner_move_to_ta(*dev);
4139     }
4140 
4141     init_regs_for_scan(*dev, sensor, dev->reg);
4142 
4143   /* no lamp during scan */
4144     if (lamp_off) {
4145         sanei_genesys_set_lamp_power(dev, sensor, dev->reg, false);
4146     }
4147 
4148   /* GL124 is using SHDAREA, so we have to wait for scan to be set up before
4149    * sending shading data */
4150     if (dev->cmd_set->has_send_shading_data() &&
4151         !has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION))
4152     {
4153         genesys_send_shading_coefficient(dev, sensor);
4154     }
4155 
4156     // now send registers for scan
4157     dev->interface->write_registers(dev->reg);
4158 
4159     // start effective scan
4160     dev->cmd_set->begin_scan(dev, sensor, &dev->reg, true);
4161 
4162     if (is_testing_mode()) {
4163         dev->interface->test_checkpoint("start_scan");
4164         return;
4165     }
4166 
4167   /*do we really need this? the valid data check should be sufficient -- pierre*/
4168   /* waits for head to reach scanning position */
4169   expected = dev->reg.get8(0x3d) * 65536
4170            + dev->reg.get8(0x3e) * 256
4171            + dev->reg.get8(0x3f);
4172   do
4173     {
4174         // wait some time between each test to avoid overloading USB and CPU
4175         dev->interface->sleep_ms(100);
4176         sanei_genesys_read_feed_steps (dev, &steps);
4177     }
4178   while (steps < expected);
4179 
4180     wait_until_buffer_non_empty(dev);
4181 
4182     // we wait for at least one word of valid scan data
4183     // this is also done in sanei_genesys_read_data_from_scanner -- pierre
4184     if (!dev->model->is_sheetfed) {
4185         do {
4186             dev->interface->sleep_ms(100);
4187             sanei_genesys_read_valid_words(dev, &steps);
4188         }
4189       while (steps < 1);
4190     }
4191 }
4192 
4193 /* this function does the effective data read in a manner that suits
4194    the scanner. It does data reordering and resizing if need.
4195    It also manages EOF and I/O errors, and line distance correction.
4196     Returns true on success, false on end-of-file.
4197 */
genesys_read_ordered_data(Genesys_Device * dev,SANE_Byte * destination,size_t * len)4198 static void genesys_read_ordered_data(Genesys_Device* dev, SANE_Byte* destination, size_t* len)
4199 {
4200     DBG_HELPER(dbg);
4201     size_t bytes = 0;
4202 
4203     if (!dev->read_active) {
4204       *len = 0;
4205         throw SaneException("read is not active");
4206     }
4207 
4208     DBG(DBG_info, "%s: frontend requested %zu bytes\n", __func__, *len);
4209     DBG(DBG_info, "%s: bytes_to_read=%zu, total_bytes_read=%zu\n", __func__,
4210         dev->total_bytes_to_read, dev->total_bytes_read);
4211 
4212   /* is there data left to scan */
4213   if (dev->total_bytes_read >= dev->total_bytes_to_read)
4214     {
4215       /* issue park command immediately in case scanner can handle it
4216        * so we save time */
4217         if (!dev->model->is_sheetfed && !has_flag(dev->model->flags, ModelFlag::MUST_WAIT) &&
4218             !dev->parking)
4219         {
4220             dev->cmd_set->move_back_home(dev, false);
4221             dev->parking = true;
4222         }
4223         throw SaneException(SANE_STATUS_EOF, "nothing more to scan: EOF");
4224     }
4225 
4226     if (is_testing_mode()) {
4227         if (dev->total_bytes_read + *len > dev->total_bytes_to_read) {
4228             *len = dev->total_bytes_to_read - dev->total_bytes_read;
4229         }
4230         dev->total_bytes_read += *len;
4231     } else {
4232         if (dev->model->is_sheetfed) {
4233             dev->cmd_set->detect_document_end(dev);
4234         }
4235 
4236         if (dev->total_bytes_read + *len > dev->total_bytes_to_read) {
4237             *len = dev->total_bytes_to_read - dev->total_bytes_read;
4238         }
4239 
4240         dev->pipeline_buffer.get_data(*len, destination);
4241         dev->total_bytes_read += *len;
4242     }
4243 
4244   /* end scan if all needed data have been read */
4245    if(dev->total_bytes_read >= dev->total_bytes_to_read)
4246     {
4247         dev->cmd_set->end_scan(dev, &dev->reg, true);
4248         if (dev->model->is_sheetfed) {
4249             dev->cmd_set->eject_document (dev);
4250         }
4251     }
4252 
4253     DBG(DBG_proc, "%s: completed, %zu bytes read\n", __func__, bytes);
4254 }
4255 
4256 
4257 
4258 /* ------------------------------------------------------------------------ */
4259 /*                  Start of higher level functions                         */
4260 /* ------------------------------------------------------------------------ */
4261 
4262 static size_t
max_string_size(const SANE_String_Const strings[])4263 max_string_size (const SANE_String_Const strings[])
4264 {
4265   size_t size, max_size = 0;
4266   SANE_Int i;
4267 
4268   for (i = 0; strings[i]; ++i)
4269     {
4270       size = strlen (strings[i]) + 1;
4271       if (size > max_size)
4272 	max_size = size;
4273     }
4274   return max_size;
4275 }
4276 
max_string_size(const std::vector<const char * > & strings)4277 static std::size_t max_string_size(const std::vector<const char*>& strings)
4278 {
4279     std::size_t max_size = 0;
4280     for (const auto& s : strings) {
4281         if (!s) {
4282             continue;
4283         }
4284         max_size = std::max(max_size, std::strlen(s));
4285     }
4286     return max_size;
4287 }
4288 
pick_resolution(const std::vector<unsigned> & resolutions,unsigned resolution,const char * direction)4289 static unsigned pick_resolution(const std::vector<unsigned>& resolutions, unsigned resolution,
4290                                 const char* direction)
4291 {
4292     DBG_HELPER(dbg);
4293 
4294     if (resolutions.empty())
4295         throw SaneException("Empty resolution list");
4296 
4297     unsigned best_res = resolutions.front();
4298     unsigned min_diff = abs_diff(best_res, resolution);
4299 
4300     for (auto it = std::next(resolutions.begin()); it != resolutions.end(); ++it) {
4301         unsigned curr_diff = abs_diff(*it, resolution);
4302         if (curr_diff < min_diff) {
4303             min_diff = curr_diff;
4304             best_res = *it;
4305         }
4306     }
4307 
4308     if (best_res != resolution) {
4309         DBG(DBG_warn, "%s: using resolution %d that is nearest to %d for direction %s\n",
4310             __func__, best_res, resolution, direction);
4311     }
4312     return best_res;
4313 }
4314 
calculate_scan_settings(Genesys_Scanner * s)4315 static Genesys_Settings calculate_scan_settings(Genesys_Scanner* s)
4316 {
4317     DBG_HELPER(dbg);
4318 
4319     const auto* dev = s->dev;
4320     Genesys_Settings settings;
4321     settings.scan_method = s->scan_method;
4322     settings.scan_mode = option_string_to_scan_color_mode(s->mode);
4323 
4324     settings.depth = s->bit_depth;
4325 
4326     if (settings.depth > 8) {
4327         settings.depth = 16;
4328     } else if (settings.depth < 8) {
4329         settings.depth = 1;
4330     }
4331 
4332     const auto& resolutions = dev->model->get_resolution_settings(settings.scan_method);
4333 
4334     settings.xres = pick_resolution(resolutions.resolutions_x, s->resolution, "X");
4335     settings.yres = pick_resolution(resolutions.resolutions_y, s->resolution, "Y");
4336 
4337     settings.tl_x = fixed_to_float(s->pos_top_left_x);
4338     settings.tl_y = fixed_to_float(s->pos_top_left_y);
4339     float br_x = fixed_to_float(s->pos_bottom_right_x);
4340     float br_y = fixed_to_float(s->pos_bottom_right_y);
4341 
4342     settings.lines = static_cast<unsigned>(((br_y - settings.tl_y) * settings.yres) /
4343                                             MM_PER_INCH);
4344 
4345 
4346     unsigned pixels_per_line = static_cast<unsigned>(((br_x - settings.tl_x) * settings.xres) /
4347                                                      MM_PER_INCH);
4348 
4349     const auto& sensor = sanei_genesys_find_sensor(dev, settings.xres, settings.get_channels(),
4350                                                    settings.scan_method);
4351 
4352     pixels_per_line = session_adjust_output_pixels(pixels_per_line, *dev, sensor,
4353                                                    settings.xres, settings.yres, true);
4354 
4355     unsigned xres_factor = s->resolution / settings.xres;
4356     settings.pixels = pixels_per_line;
4357     settings.requested_pixels = pixels_per_line * xres_factor;
4358 
4359     if (s->color_filter == "Red") {
4360         settings.color_filter = ColorFilter::RED;
4361     } else if (s->color_filter == "Green") {
4362         settings.color_filter = ColorFilter::GREEN;
4363     } else if (s->color_filter == "Blue") {
4364         settings.color_filter = ColorFilter::BLUE;
4365     } else {
4366         settings.color_filter = ColorFilter::NONE;
4367     }
4368 
4369     if (s->color_filter == "None") {
4370         settings.true_gray = 1;
4371     } else {
4372         settings.true_gray = 0;
4373     }
4374 
4375     // brightness and contrast only for for 8 bit scans
4376     if (s->bit_depth == 8) {
4377         settings.contrast = (s->contrast * 127) / 100;
4378         settings.brightness = (s->brightness * 127) / 100;
4379     } else {
4380         settings.contrast = 0;
4381         settings.brightness = 0;
4382     }
4383 
4384     settings.expiration_time = s->expiration_time;
4385 
4386     return settings;
4387 }
4388 
calculate_scan_parameters(const Genesys_Device & dev,const Genesys_Settings & settings)4389 static SANE_Parameters calculate_scan_parameters(const Genesys_Device& dev,
4390                                                  const Genesys_Settings& settings)
4391 {
4392     DBG_HELPER(dbg);
4393 
4394     auto sensor = sanei_genesys_find_sensor(&dev, settings.xres, settings.get_channels(),
4395                                             settings.scan_method);
4396     auto session = dev.cmd_set->calculate_scan_session(&dev, sensor, settings);
4397     auto pipeline = build_image_pipeline(dev, session, 0, false);
4398 
4399     SANE_Parameters params;
4400     if (settings.scan_mode == ScanColorMode::GRAY) {
4401         params.format = SANE_FRAME_GRAY;
4402     } else {
4403         params.format = SANE_FRAME_RGB;
4404     }
4405     // only single-pass scanning supported
4406     params.last_frame = true;
4407     params.depth = settings.depth;
4408     params.lines = pipeline.get_output_height();
4409     params.pixels_per_line = pipeline.get_output_width();
4410     params.bytes_per_line = pipeline.get_output_row_bytes();
4411 
4412     return params;
4413 }
4414 
calc_parameters(Genesys_Scanner * s)4415 static void calc_parameters(Genesys_Scanner* s)
4416 {
4417     DBG_HELPER(dbg);
4418 
4419     s->dev->settings = calculate_scan_settings(s);
4420     s->params = calculate_scan_parameters(*s->dev, s->dev->settings);
4421 }
4422 
create_bpp_list(Genesys_Scanner * s,const std::vector<unsigned> & bpp)4423 static void create_bpp_list (Genesys_Scanner * s, const std::vector<unsigned>& bpp)
4424 {
4425     s->bpp_list[0] = bpp.size();
4426     std::reverse_copy(bpp.begin(), bpp.end(), s->bpp_list + 1);
4427 }
4428 
4429 /** @brief this function initialize a gamma vector based on the ASIC:
4430  * Set up a default gamma table vector based on device description
4431  * gl646: 12 or 14 bits gamma table depending on ModelFlag::GAMMA_14BIT
4432  * gl84x: 16 bits
4433  * gl12x: 16 bits
4434  * @param scanner pointer to scanner session to get options
4435  * @param option option number of the gamma table to set
4436  */
4437 static void
init_gamma_vector_option(Genesys_Scanner * scanner,int option)4438 init_gamma_vector_option (Genesys_Scanner * scanner, int option)
4439 {
4440   /* the option is inactive until the custom gamma control
4441    * is enabled */
4442   scanner->opt[option].type = SANE_TYPE_INT;
4443   scanner->opt[option].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED;
4444   scanner->opt[option].unit = SANE_UNIT_NONE;
4445   scanner->opt[option].constraint_type = SANE_CONSTRAINT_RANGE;
4446     if (scanner->dev->model->asic_type == AsicType::GL646) {
4447         if (has_flag(scanner->dev->model->flags, ModelFlag::GAMMA_14BIT)) {
4448 	  scanner->opt[option].size = 16384 * sizeof (SANE_Word);
4449 	  scanner->opt[option].constraint.range = &u14_range;
4450 	}
4451       else
4452 	{			/* 12 bits gamma tables */
4453 	  scanner->opt[option].size = 4096 * sizeof (SANE_Word);
4454 	  scanner->opt[option].constraint.range = &u12_range;
4455 	}
4456     }
4457   else
4458     {				/* other asics have 16 bits words gamma table */
4459       scanner->opt[option].size = 256 * sizeof (SANE_Word);
4460       scanner->opt[option].constraint.range = &u16_range;
4461     }
4462 }
4463 
4464 /**
4465  * allocate a geometry range
4466  * @param size maximum size of the range
4467  * @return a pointer to a valid range or nullptr
4468  */
create_range(float size)4469 static SANE_Range create_range(float size)
4470 {
4471     SANE_Range range;
4472     range.min = float_to_fixed(0.0);
4473     range.max = float_to_fixed(size);
4474     range.quant = float_to_fixed(0.0);
4475     return range;
4476 }
4477 
4478 /** @brief generate calibration cache file nam
4479  * Generates the calibration cache file name to use.
4480  * Tries to store the cache in $HOME/.sane or
4481  * then fallbacks to $TMPDIR or TMP. The filename
4482  * uses the model name if only one scanner is plugged
4483  * else is uses the device name when several identical
4484  * scanners are in use.
4485  * @param currdev current scanner device
4486  * @return an allocated string containing a file name
4487  */
calibration_filename(Genesys_Device * currdev)4488 static std::string calibration_filename(Genesys_Device *currdev)
4489 {
4490     std::string ret;
4491     ret.resize(PATH_MAX);
4492 
4493   char filename[80];
4494   unsigned int count;
4495   unsigned int i;
4496 
4497   /* first compute the DIR where we can store cache:
4498    * 1 - home dir
4499    * 2 - $TMPDIR
4500    * 3 - $TMP
4501    * 4 - tmp dir
4502    * 5 - temp dir
4503    * 6 - then resort to current dir
4504    */
4505     char* ptr = std::getenv("HOME");
4506     if (ptr == nullptr) {
4507         ptr = std::getenv("USERPROFILE");
4508     }
4509     if (ptr == nullptr) {
4510         ptr = std::getenv("TMPDIR");
4511     }
4512     if (ptr == nullptr) {
4513         ptr = std::getenv("TMP");
4514     }
4515 
4516   /* now choose filename:
4517    * 1 - if only one scanner, name of the model
4518    * 2 - if several scanners of the same model, use device name,
4519    *     replacing special chars
4520    */
4521   count=0;
4522   /* count models of the same names if several scanners attached */
4523     if(s_devices->size() > 1) {
4524         for (const auto& dev : *s_devices) {
4525             if (dev.vendorId == currdev->vendorId && dev.productId == currdev->productId) {
4526                 count++;
4527             }
4528         }
4529     }
4530   if(count>1)
4531     {
4532         std::snprintf(filename, sizeof(filename), "%s.cal", currdev->file_name.c_str());
4533       for(i=0;i<strlen(filename);i++)
4534         {
4535           if(filename[i]==':'||filename[i]==PATH_SEP)
4536             {
4537               filename[i]='_';
4538             }
4539         }
4540     }
4541   else
4542     {
4543       snprintf(filename,sizeof(filename),"%s.cal",currdev->model->name);
4544     }
4545 
4546   /* build final final name : store dir + filename */
4547     if (ptr == nullptr) {
4548         int size = std::snprintf(&ret.front(), ret.size(), "%s", filename);
4549         ret.resize(size);
4550     }
4551   else
4552     {
4553         int size = 0;
4554 #ifdef HAVE_MKDIR
4555         /* make sure .sane directory exists in existing store dir */
4556         size = std::snprintf(&ret.front(), ret.size(), "%s%c.sane", ptr, PATH_SEP);
4557         ret.resize(size);
4558         mkdir(ret.c_str(), 0700);
4559 
4560         ret.resize(PATH_MAX);
4561 #endif
4562         size = std::snprintf(&ret.front(), ret.size(), "%s%c.sane%c%s",
4563                              ptr, PATH_SEP, PATH_SEP, filename);
4564         ret.resize(size);
4565     }
4566 
4567     DBG(DBG_info, "%s: calibration filename >%s<\n", __func__, ret.c_str());
4568 
4569     return ret;
4570 }
4571 
set_resolution_option_values(Genesys_Scanner & s,bool reset_resolution_value)4572 static void set_resolution_option_values(Genesys_Scanner& s, bool reset_resolution_value)
4573 {
4574     auto resolutions = s.dev->model->get_resolutions(s.scan_method);
4575 
4576     s.opt_resolution_values.resize(resolutions.size() + 1, 0);
4577     s.opt_resolution_values[0] = resolutions.size();
4578     std::copy(resolutions.begin(), resolutions.end(), s.opt_resolution_values.begin() + 1);
4579 
4580     s.opt[OPT_RESOLUTION].constraint.word_list = s.opt_resolution_values.data();
4581 
4582     if (reset_resolution_value) {
4583         s.resolution = *std::min_element(resolutions.begin(), resolutions.end());
4584     }
4585 }
4586 
set_xy_range_option_values(Genesys_Scanner & s)4587 static void set_xy_range_option_values(Genesys_Scanner& s)
4588 {
4589     if (s.scan_method == ScanMethod::FLATBED)
4590     {
4591         s.opt_x_range = create_range(s.dev->model->x_size);
4592         s.opt_y_range = create_range(s.dev->model->y_size);
4593     }
4594   else
4595     {
4596         s.opt_x_range = create_range(s.dev->model->x_size_ta);
4597         s.opt_y_range = create_range(s.dev->model->y_size_ta);
4598     }
4599 
4600     s.opt[OPT_TL_X].constraint.range = &s.opt_x_range;
4601     s.opt[OPT_TL_Y].constraint.range = &s.opt_y_range;
4602     s.opt[OPT_BR_X].constraint.range = &s.opt_x_range;
4603     s.opt[OPT_BR_Y].constraint.range = &s.opt_y_range;
4604 
4605     s.pos_top_left_x = 0;
4606     s.pos_top_left_y = 0;
4607     s.pos_bottom_right_x = s.opt_x_range.max;
4608     s.pos_bottom_right_y = s.opt_y_range.max;
4609 }
4610 
init_options(Genesys_Scanner * s)4611 static void init_options(Genesys_Scanner* s)
4612 {
4613     DBG_HELPER(dbg);
4614   SANE_Int option;
4615     const Genesys_Model* model = s->dev->model;
4616 
4617   memset (s->opt, 0, sizeof (s->opt));
4618 
4619   for (option = 0; option < NUM_OPTIONS; ++option)
4620     {
4621       s->opt[option].size = sizeof (SANE_Word);
4622       s->opt[option].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
4623     }
4624   s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
4625   s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
4626   s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
4627   s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
4628   s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
4629 
4630   /* "Mode" group: */
4631   s->opt[OPT_MODE_GROUP].name = "scanmode-group";
4632   s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
4633   s->opt[OPT_MODE_GROUP].desc = "";
4634   s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
4635   s->opt[OPT_MODE_GROUP].size = 0;
4636   s->opt[OPT_MODE_GROUP].cap = 0;
4637   s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
4638 
4639   /* scan mode */
4640   s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
4641   s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
4642   s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
4643   s->opt[OPT_MODE].type = SANE_TYPE_STRING;
4644   s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
4645   s->opt[OPT_MODE].size = max_string_size (mode_list);
4646   s->opt[OPT_MODE].constraint.string_list = mode_list;
4647   s->mode = SANE_VALUE_SCAN_MODE_GRAY;
4648 
4649   /* scan source */
4650     s->opt_source_values.clear();
4651     for (const auto& resolution_setting : model->resolutions) {
4652         for (auto method : resolution_setting.methods) {
4653             s->opt_source_values.push_back(scan_method_to_option_string(method));
4654         }
4655     }
4656     s->opt_source_values.push_back(nullptr);
4657 
4658   s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
4659   s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
4660   s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
4661   s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
4662   s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
4663     s->opt[OPT_SOURCE].size = max_string_size(s->opt_source_values);
4664     s->opt[OPT_SOURCE].constraint.string_list = s->opt_source_values.data();
4665     if (s->opt_source_values.size() < 2) {
4666         throw SaneException("No scan methods specified for scanner");
4667     }
4668     s->scan_method = model->default_method;
4669 
4670   /* preview */
4671   s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
4672   s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
4673   s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
4674   s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
4675   s->opt[OPT_PREVIEW].unit = SANE_UNIT_NONE;
4676   s->opt[OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE;
4677   s->preview = false;
4678 
4679   /* bit depth */
4680   s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH;
4681   s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH;
4682   s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH;
4683   s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT;
4684   s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST;
4685   s->opt[OPT_BIT_DEPTH].size = sizeof (SANE_Word);
4686   s->opt[OPT_BIT_DEPTH].constraint.word_list = s->bpp_list;
4687   create_bpp_list (s, model->bpp_gray_values);
4688     s->bit_depth = model->bpp_gray_values[0];
4689 
4690     // resolution
4691   s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
4692   s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
4693   s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
4694   s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
4695   s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
4696   s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
4697     set_resolution_option_values(*s, true);
4698 
4699   /* "Geometry" group: */
4700   s->opt[OPT_GEOMETRY_GROUP].name = SANE_NAME_GEOMETRY;
4701   s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
4702   s->opt[OPT_GEOMETRY_GROUP].desc = "";
4703   s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
4704   s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
4705   s->opt[OPT_GEOMETRY_GROUP].size = 0;
4706   s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
4707 
4708     s->opt_x_range = create_range(model->x_size);
4709     s->opt_y_range = create_range(model->y_size);
4710 
4711     // scan area
4712   s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
4713   s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
4714   s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
4715   s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
4716   s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
4717   s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
4718 
4719   s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
4720   s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
4721   s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
4722   s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
4723   s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
4724   s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
4725 
4726   s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
4727   s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
4728   s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
4729   s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
4730   s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
4731   s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
4732 
4733   s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
4734   s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
4735   s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
4736   s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
4737   s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
4738   s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
4739 
4740     set_xy_range_option_values(*s);
4741 
4742   /* "Enhancement" group: */
4743   s->opt[OPT_ENHANCEMENT_GROUP].name = SANE_NAME_ENHANCEMENT;
4744   s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
4745   s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
4746   s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
4747   s->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED;
4748   s->opt[OPT_ENHANCEMENT_GROUP].size = 0;
4749   s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
4750 
4751   /* custom-gamma table */
4752   s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
4753   s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
4754   s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
4755   s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
4756   s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_ADVANCED;
4757   s->custom_gamma = false;
4758 
4759   /* grayscale gamma vector */
4760   s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR;
4761   s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR;
4762   s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR;
4763   init_gamma_vector_option (s, OPT_GAMMA_VECTOR);
4764 
4765   /* red gamma vector */
4766   s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R;
4767   s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R;
4768   s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R;
4769   init_gamma_vector_option (s, OPT_GAMMA_VECTOR_R);
4770 
4771   /* green gamma vector */
4772   s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G;
4773   s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G;
4774   s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G;
4775   init_gamma_vector_option (s, OPT_GAMMA_VECTOR_G);
4776 
4777   /* blue gamma vector */
4778   s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B;
4779   s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B;
4780   s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B;
4781   init_gamma_vector_option (s, OPT_GAMMA_VECTOR_B);
4782 
4783   /* currently, there are only gamma table options in this group,
4784    * so if the scanner doesn't support gamma table, disable the
4785    * whole group */
4786     if (!has_flag(model->flags, ModelFlag::CUSTOM_GAMMA)) {
4787       s->opt[OPT_ENHANCEMENT_GROUP].cap |= SANE_CAP_INACTIVE;
4788       s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
4789       DBG(DBG_info, "%s: custom gamma disabled\n", __func__);
4790     }
4791 
4792   /* software base image enhancements, these are consuming as many
4793    * memory than used by the full scanned image and may fail at high
4794    * resolution
4795    */
4796 
4797   /* Software brightness */
4798   s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
4799   s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
4800   s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
4801   s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
4802   s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
4803   s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
4804   s->opt[OPT_BRIGHTNESS].constraint.range = &(enhance_range);
4805   s->opt[OPT_BRIGHTNESS].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
4806   s->brightness = 0;    // disable by default
4807 
4808   /* Sowftware contrast */
4809   s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
4810   s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
4811   s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
4812   s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
4813   s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
4814   s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
4815   s->opt[OPT_CONTRAST].constraint.range = &(enhance_range);
4816   s->opt[OPT_CONTRAST].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
4817   s->contrast = 0;  // disable by default
4818 
4819   /* "Extras" group: */
4820   s->opt[OPT_EXTRAS_GROUP].name = "extras-group";
4821   s->opt[OPT_EXTRAS_GROUP].title = SANE_I18N ("Extras");
4822   s->opt[OPT_EXTRAS_GROUP].desc = "";
4823   s->opt[OPT_EXTRAS_GROUP].type = SANE_TYPE_GROUP;
4824   s->opt[OPT_EXTRAS_GROUP].cap = SANE_CAP_ADVANCED;
4825   s->opt[OPT_EXTRAS_GROUP].size = 0;
4826   s->opt[OPT_EXTRAS_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
4827 
4828   /* color filter */
4829   s->opt[OPT_COLOR_FILTER].name = "color-filter";
4830   s->opt[OPT_COLOR_FILTER].title = SANE_I18N ("Color filter");
4831   s->opt[OPT_COLOR_FILTER].desc =
4832     SANE_I18N
4833     ("When using gray or lineart this option selects the used color.");
4834   s->opt[OPT_COLOR_FILTER].type = SANE_TYPE_STRING;
4835   s->opt[OPT_COLOR_FILTER].constraint_type = SANE_CONSTRAINT_STRING_LIST;
4836   /* true gray not yet supported for GL847 and GL124 scanners */
4837     if (!model->is_cis || model->asic_type==AsicType::GL847 || model->asic_type==AsicType::GL124) {
4838       s->opt[OPT_COLOR_FILTER].size = max_string_size (color_filter_list);
4839       s->opt[OPT_COLOR_FILTER].constraint.string_list = color_filter_list;
4840       s->color_filter = s->opt[OPT_COLOR_FILTER].constraint.string_list[1];
4841     }
4842   else
4843     {
4844       s->opt[OPT_COLOR_FILTER].size = max_string_size (cis_color_filter_list);
4845       s->opt[OPT_COLOR_FILTER].constraint.string_list = cis_color_filter_list;
4846       /* default to "None" ie true gray */
4847       s->color_filter = s->opt[OPT_COLOR_FILTER].constraint.string_list[3];
4848     }
4849 
4850     // no support for color filter for cis+gl646 scanners
4851     if (model->asic_type == AsicType::GL646 && model->is_cis) {
4852       DISABLE (OPT_COLOR_FILTER);
4853     }
4854 
4855   /* calibration store file name */
4856   s->opt[OPT_CALIBRATION_FILE].name = "calibration-file";
4857   s->opt[OPT_CALIBRATION_FILE].title = SANE_I18N ("Calibration file");
4858   s->opt[OPT_CALIBRATION_FILE].desc = SANE_I18N ("Specify the calibration file to use");
4859   s->opt[OPT_CALIBRATION_FILE].type = SANE_TYPE_STRING;
4860   s->opt[OPT_CALIBRATION_FILE].unit = SANE_UNIT_NONE;
4861   s->opt[OPT_CALIBRATION_FILE].size = PATH_MAX;
4862   s->opt[OPT_CALIBRATION_FILE].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
4863   s->opt[OPT_CALIBRATION_FILE].constraint_type = SANE_CONSTRAINT_NONE;
4864   s->calibration_file.clear();
4865   /* disable option if run as root */
4866 #ifdef HAVE_GETUID
4867   if(geteuid()==0)
4868     {
4869       DISABLE (OPT_CALIBRATION_FILE);
4870     }
4871 #endif
4872 
4873   /* expiration time for calibration cache entries */
4874   s->opt[OPT_EXPIRATION_TIME].name = "expiration-time";
4875   s->opt[OPT_EXPIRATION_TIME].title = SANE_I18N ("Calibration cache expiration time");
4876   s->opt[OPT_EXPIRATION_TIME].desc = SANE_I18N ("Time (in minutes) before a cached calibration expires. "
4877      "A value of 0 means cache is not used. A negative value means cache never expires.");
4878   s->opt[OPT_EXPIRATION_TIME].type = SANE_TYPE_INT;
4879   s->opt[OPT_EXPIRATION_TIME].unit = SANE_UNIT_NONE;
4880   s->opt[OPT_EXPIRATION_TIME].constraint_type = SANE_CONSTRAINT_RANGE;
4881   s->opt[OPT_EXPIRATION_TIME].constraint.range = &expiration_range;
4882   s->expiration_time = 60;  // 60 minutes by default
4883 
4884   /* Powersave time (turn lamp off) */
4885   s->opt[OPT_LAMP_OFF_TIME].name = "lamp-off-time";
4886   s->opt[OPT_LAMP_OFF_TIME].title = SANE_I18N ("Lamp off time");
4887   s->opt[OPT_LAMP_OFF_TIME].desc =
4888     SANE_I18N
4889     ("The lamp will be turned off after the given time (in minutes). "
4890      "A value of 0 means, that the lamp won't be turned off.");
4891   s->opt[OPT_LAMP_OFF_TIME].type = SANE_TYPE_INT;
4892   s->opt[OPT_LAMP_OFF_TIME].unit = SANE_UNIT_NONE;
4893   s->opt[OPT_LAMP_OFF_TIME].constraint_type = SANE_CONSTRAINT_RANGE;
4894   s->opt[OPT_LAMP_OFF_TIME].constraint.range = &time_range;
4895   s->lamp_off_time = 15;    // 15 minutes
4896 
4897   /* turn lamp off during scan */
4898   s->opt[OPT_LAMP_OFF].name = "lamp-off-scan";
4899   s->opt[OPT_LAMP_OFF].title = SANE_I18N ("Lamp off during scan");
4900   s->opt[OPT_LAMP_OFF].desc = SANE_I18N ("The lamp will be turned off during scan. ");
4901   s->opt[OPT_LAMP_OFF].type = SANE_TYPE_BOOL;
4902   s->opt[OPT_LAMP_OFF].unit = SANE_UNIT_NONE;
4903   s->opt[OPT_LAMP_OFF].constraint_type = SANE_CONSTRAINT_NONE;
4904   s->lamp_off = false;
4905 
4906   s->opt[OPT_SENSOR_GROUP].name = SANE_NAME_SENSORS;
4907   s->opt[OPT_SENSOR_GROUP].title = SANE_TITLE_SENSORS;
4908   s->opt[OPT_SENSOR_GROUP].desc = SANE_DESC_SENSORS;
4909   s->opt[OPT_SENSOR_GROUP].type = SANE_TYPE_GROUP;
4910   s->opt[OPT_SENSOR_GROUP].cap = SANE_CAP_ADVANCED;
4911   s->opt[OPT_SENSOR_GROUP].size = 0;
4912   s->opt[OPT_SENSOR_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
4913 
4914   s->opt[OPT_SCAN_SW].name = SANE_NAME_SCAN;
4915   s->opt[OPT_SCAN_SW].title = SANE_TITLE_SCAN;
4916   s->opt[OPT_SCAN_SW].desc = SANE_DESC_SCAN;
4917   s->opt[OPT_SCAN_SW].type = SANE_TYPE_BOOL;
4918   s->opt[OPT_SCAN_SW].unit = SANE_UNIT_NONE;
4919   if (model->buttons & GENESYS_HAS_SCAN_SW)
4920     s->opt[OPT_SCAN_SW].cap =
4921       SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4922   else
4923     s->opt[OPT_SCAN_SW].cap = SANE_CAP_INACTIVE;
4924 
4925   /* SANE_NAME_FILE is not for buttons */
4926   s->opt[OPT_FILE_SW].name = "file";
4927   s->opt[OPT_FILE_SW].title = SANE_I18N ("File button");
4928   s->opt[OPT_FILE_SW].desc = SANE_I18N ("File button");
4929   s->opt[OPT_FILE_SW].type = SANE_TYPE_BOOL;
4930   s->opt[OPT_FILE_SW].unit = SANE_UNIT_NONE;
4931   if (model->buttons & GENESYS_HAS_FILE_SW)
4932     s->opt[OPT_FILE_SW].cap =
4933       SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4934   else
4935     s->opt[OPT_FILE_SW].cap = SANE_CAP_INACTIVE;
4936 
4937   s->opt[OPT_EMAIL_SW].name = SANE_NAME_EMAIL;
4938   s->opt[OPT_EMAIL_SW].title = SANE_TITLE_EMAIL;
4939   s->opt[OPT_EMAIL_SW].desc = SANE_DESC_EMAIL;
4940   s->opt[OPT_EMAIL_SW].type = SANE_TYPE_BOOL;
4941   s->opt[OPT_EMAIL_SW].unit = SANE_UNIT_NONE;
4942   if (model->buttons & GENESYS_HAS_EMAIL_SW)
4943     s->opt[OPT_EMAIL_SW].cap =
4944       SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4945   else
4946     s->opt[OPT_EMAIL_SW].cap = SANE_CAP_INACTIVE;
4947 
4948   s->opt[OPT_COPY_SW].name = SANE_NAME_COPY;
4949   s->opt[OPT_COPY_SW].title = SANE_TITLE_COPY;
4950   s->opt[OPT_COPY_SW].desc = SANE_DESC_COPY;
4951   s->opt[OPT_COPY_SW].type = SANE_TYPE_BOOL;
4952   s->opt[OPT_COPY_SW].unit = SANE_UNIT_NONE;
4953   if (model->buttons & GENESYS_HAS_COPY_SW)
4954     s->opt[OPT_COPY_SW].cap =
4955       SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4956   else
4957     s->opt[OPT_COPY_SW].cap = SANE_CAP_INACTIVE;
4958 
4959   s->opt[OPT_PAGE_LOADED_SW].name = SANE_NAME_PAGE_LOADED;
4960   s->opt[OPT_PAGE_LOADED_SW].title = SANE_TITLE_PAGE_LOADED;
4961   s->opt[OPT_PAGE_LOADED_SW].desc = SANE_DESC_PAGE_LOADED;
4962   s->opt[OPT_PAGE_LOADED_SW].type = SANE_TYPE_BOOL;
4963   s->opt[OPT_PAGE_LOADED_SW].unit = SANE_UNIT_NONE;
4964   if (model->buttons & GENESYS_HAS_PAGE_LOADED_SW)
4965     s->opt[OPT_PAGE_LOADED_SW].cap =
4966       SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4967   else
4968     s->opt[OPT_PAGE_LOADED_SW].cap = SANE_CAP_INACTIVE;
4969 
4970   /* OCR button */
4971   s->opt[OPT_OCR_SW].name = "ocr";
4972   s->opt[OPT_OCR_SW].title = SANE_I18N ("OCR button");
4973   s->opt[OPT_OCR_SW].desc = SANE_I18N ("OCR button");
4974   s->opt[OPT_OCR_SW].type = SANE_TYPE_BOOL;
4975   s->opt[OPT_OCR_SW].unit = SANE_UNIT_NONE;
4976   if (model->buttons & GENESYS_HAS_OCR_SW)
4977     s->opt[OPT_OCR_SW].cap =
4978       SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4979   else
4980     s->opt[OPT_OCR_SW].cap = SANE_CAP_INACTIVE;
4981 
4982   /* power button */
4983   s->opt[OPT_POWER_SW].name = "power";
4984   s->opt[OPT_POWER_SW].title = SANE_I18N ("Power button");
4985   s->opt[OPT_POWER_SW].desc = SANE_I18N ("Power button");
4986   s->opt[OPT_POWER_SW].type = SANE_TYPE_BOOL;
4987   s->opt[OPT_POWER_SW].unit = SANE_UNIT_NONE;
4988   if (model->buttons & GENESYS_HAS_POWER_SW)
4989     s->opt[OPT_POWER_SW].cap =
4990       SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
4991   else
4992     s->opt[OPT_POWER_SW].cap = SANE_CAP_INACTIVE;
4993 
4994   /* extra button */
4995   s->opt[OPT_EXTRA_SW].name = "extra";
4996   s->opt[OPT_EXTRA_SW].title = SANE_I18N ("Extra button");
4997   s->opt[OPT_EXTRA_SW].desc = SANE_I18N ("Extra button");
4998   s->opt[OPT_EXTRA_SW].type = SANE_TYPE_BOOL;
4999   s->opt[OPT_EXTRA_SW].unit = SANE_UNIT_NONE;
5000   if (model->buttons & GENESYS_HAS_EXTRA_SW)
5001     s->opt[OPT_EXTRA_SW].cap =
5002       SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
5003   else
5004     s->opt[OPT_EXTRA_SW].cap = SANE_CAP_INACTIVE;
5005 
5006   /* calibration needed */
5007   s->opt[OPT_NEED_CALIBRATION_SW].name = "need-calibration";
5008   s->opt[OPT_NEED_CALIBRATION_SW].title = SANE_I18N ("Needs calibration");
5009   s->opt[OPT_NEED_CALIBRATION_SW].desc = SANE_I18N ("The scanner needs calibration for the current settings");
5010   s->opt[OPT_NEED_CALIBRATION_SW].type = SANE_TYPE_BOOL;
5011   s->opt[OPT_NEED_CALIBRATION_SW].unit = SANE_UNIT_NONE;
5012   if (model->buttons & GENESYS_HAS_CALIBRATE)
5013     s->opt[OPT_NEED_CALIBRATION_SW].cap =
5014       SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
5015   else
5016     s->opt[OPT_NEED_CALIBRATION_SW].cap = SANE_CAP_INACTIVE;
5017 
5018   /* button group */
5019   s->opt[OPT_BUTTON_GROUP].name = "buttons";
5020   s->opt[OPT_BUTTON_GROUP].title = SANE_I18N ("Buttons");
5021   s->opt[OPT_BUTTON_GROUP].desc = "";
5022   s->opt[OPT_BUTTON_GROUP].type = SANE_TYPE_GROUP;
5023   s->opt[OPT_BUTTON_GROUP].cap = SANE_CAP_ADVANCED;
5024   s->opt[OPT_BUTTON_GROUP].size = 0;
5025   s->opt[OPT_BUTTON_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
5026 
5027   /* calibrate button */
5028   s->opt[OPT_CALIBRATE].name = "calibrate";
5029   s->opt[OPT_CALIBRATE].title = SANE_I18N ("Calibrate");
5030   s->opt[OPT_CALIBRATE].desc =
5031     SANE_I18N ("Start calibration using special sheet");
5032   s->opt[OPT_CALIBRATE].type = SANE_TYPE_BUTTON;
5033   s->opt[OPT_CALIBRATE].unit = SANE_UNIT_NONE;
5034   if (model->buttons & GENESYS_HAS_CALIBRATE)
5035     s->opt[OPT_CALIBRATE].cap =
5036       SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED |
5037       SANE_CAP_AUTOMATIC;
5038   else
5039     s->opt[OPT_CALIBRATE].cap = SANE_CAP_INACTIVE;
5040 
5041   /* clear calibration cache button */
5042   s->opt[OPT_CLEAR_CALIBRATION].name = "clear-calibration";
5043   s->opt[OPT_CLEAR_CALIBRATION].title = SANE_I18N ("Clear calibration");
5044   s->opt[OPT_CLEAR_CALIBRATION].desc = SANE_I18N ("Clear calibration cache");
5045   s->opt[OPT_CLEAR_CALIBRATION].type = SANE_TYPE_BUTTON;
5046   s->opt[OPT_CLEAR_CALIBRATION].unit = SANE_UNIT_NONE;
5047   s->opt[OPT_CLEAR_CALIBRATION].size = 0;
5048   s->opt[OPT_CLEAR_CALIBRATION].constraint_type = SANE_CONSTRAINT_NONE;
5049   s->opt[OPT_CLEAR_CALIBRATION].cap =
5050     SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
5051 
5052   /* force calibration cache button */
5053   s->opt[OPT_FORCE_CALIBRATION].name = "force-calibration";
5054   s->opt[OPT_FORCE_CALIBRATION].title = SANE_I18N("Force calibration");
5055   s->opt[OPT_FORCE_CALIBRATION].desc = SANE_I18N("Force calibration ignoring all and any calibration caches");
5056   s->opt[OPT_FORCE_CALIBRATION].type = SANE_TYPE_BUTTON;
5057   s->opt[OPT_FORCE_CALIBRATION].unit = SANE_UNIT_NONE;
5058   s->opt[OPT_FORCE_CALIBRATION].size = 0;
5059   s->opt[OPT_FORCE_CALIBRATION].constraint_type = SANE_CONSTRAINT_NONE;
5060   s->opt[OPT_FORCE_CALIBRATION].cap =
5061     SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED;
5062 
5063     // ignore offsets option
5064     s->opt[OPT_IGNORE_OFFSETS].name = "ignore-internal-offsets";
5065     s->opt[OPT_IGNORE_OFFSETS].title = SANE_I18N("Ignore internal offsets");
5066     s->opt[OPT_IGNORE_OFFSETS].desc =
5067         SANE_I18N("Acquires the image including the internal calibration areas of the scanner");
5068     s->opt[OPT_IGNORE_OFFSETS].type = SANE_TYPE_BUTTON;
5069     s->opt[OPT_IGNORE_OFFSETS].unit = SANE_UNIT_NONE;
5070     s->opt[OPT_IGNORE_OFFSETS].size = 0;
5071     s->opt[OPT_IGNORE_OFFSETS].constraint_type = SANE_CONSTRAINT_NONE;
5072     s->opt[OPT_IGNORE_OFFSETS].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT |
5073                                      SANE_CAP_ADVANCED;
5074 
5075     calc_parameters(s);
5076 }
5077 
5078 static bool present;
5079 
5080 // this function is passed to C API, it must not throw
5081 static SANE_Status
check_present(SANE_String_Const devname)5082 check_present (SANE_String_Const devname) noexcept
5083 {
5084     DBG_HELPER_ARGS(dbg, "%s detected.", devname);
5085     present = true;
5086   return SANE_STATUS_GOOD;
5087 }
5088 
get_matching_usb_dev(std::uint16_t vendor_id,std::uint16_t product_id,std::uint16_t bcd_device)5089 const UsbDeviceEntry& get_matching_usb_dev(std::uint16_t vendor_id, std::uint16_t product_id,
5090                                            std::uint16_t bcd_device)
5091 {
5092     for (auto& usb_dev : *s_usb_devices) {
5093         if (usb_dev.matches(vendor_id, product_id, bcd_device)) {
5094             return usb_dev;
5095         }
5096     }
5097 
5098     throw SaneException("vendor 0x%x product 0x%x (bcdDevice 0x%x) "
5099                         "is not supported by this backend",
5100                         vendor_id, product_id, bcd_device);
5101 }
5102 
attach_usb_device(const char * devname,std::uint16_t vendor_id,std::uint16_t product_id,std::uint16_t bcd_device)5103 static Genesys_Device* attach_usb_device(const char* devname,
5104                                          std::uint16_t vendor_id, std::uint16_t product_id,
5105                                          std::uint16_t bcd_device)
5106 {
5107     const auto& usb_dev = get_matching_usb_dev(vendor_id, product_id, bcd_device);
5108 
5109     s_devices->emplace_back();
5110     Genesys_Device* dev = &s_devices->back();
5111     dev->file_name = devname;
5112     dev->vendorId = vendor_id;
5113     dev->productId = product_id;
5114     dev->model = &usb_dev.model();
5115     dev->usb_mode = 0; // i.e. unset
5116     dev->already_initialized = false;
5117     return dev;
5118 }
5119 
5120 static bool s_attach_device_by_name_evaluate_bcd_device = false;
5121 
attach_device_by_name(SANE_String_Const devname,bool may_wait)5122 static Genesys_Device* attach_device_by_name(SANE_String_Const devname, bool may_wait)
5123 {
5124     DBG_HELPER_ARGS(dbg, " devname: %s, may_wait = %d", devname, may_wait);
5125 
5126     if (!devname) {
5127         throw SaneException("devname must not be nullptr");
5128     }
5129 
5130     for (auto& dev : *s_devices) {
5131         if (dev.file_name == devname) {
5132             DBG(DBG_info, "%s: device `%s' was already in device list\n", __func__, devname);
5133             return &dev;
5134         }
5135     }
5136 
5137   DBG(DBG_info, "%s: trying to open device `%s'\n", __func__, devname);
5138 
5139     UsbDevice usb_dev;
5140 
5141     usb_dev.open(devname);
5142     DBG(DBG_info, "%s: device `%s' successfully opened\n", __func__, devname);
5143 
5144     auto vendor_id = usb_dev.get_vendor_id();
5145     auto product_id = usb_dev.get_product_id();
5146     auto bcd_device = UsbDeviceEntry::BCD_DEVICE_NOT_SET;
5147     if (s_attach_device_by_name_evaluate_bcd_device) {
5148         // when the device is already known before scanning, we don't want to call get_bcd_device()
5149         // when iterating devices, as that will interfere with record/replay during testing.
5150         bcd_device = usb_dev.get_bcd_device();
5151     }
5152     usb_dev.close();
5153 
5154   /* KV-SS080 is an auxiliary device which requires a master device to be here */
5155     if (vendor_id == 0x04da && product_id == 0x100f) {
5156         present = false;
5157         sanei_usb_find_devices(vendor_id, 0x1006, check_present);
5158         sanei_usb_find_devices(vendor_id, 0x1007, check_present);
5159         sanei_usb_find_devices(vendor_id, 0x1010, check_present);
5160         if (present == false) {
5161             throw SaneException("master device not present");
5162         }
5163     }
5164 
5165     Genesys_Device* dev = attach_usb_device(devname, vendor_id, product_id, bcd_device);
5166 
5167     DBG(DBG_info, "%s: found %u flatbed scanner %u at %s\n", __func__, vendor_id, product_id,
5168         dev->file_name.c_str());
5169 
5170     return dev;
5171 }
5172 
5173 // this function is passed to C API and must not throw
attach_one_device(SANE_String_Const devname)5174 static SANE_Status attach_one_device(SANE_String_Const devname) noexcept
5175 {
5176     DBG_HELPER(dbg);
5177     return wrap_exceptions_to_status_code(__func__, [=]()
5178     {
5179         attach_device_by_name(devname, false);
5180     });
5181 }
5182 
5183 /* configuration framework functions */
5184 
5185 // this function is passed to C API, it must not throw
5186 static SANE_Status
config_attach_genesys(SANEI_Config __sane_unused__ * config,const char * devname,void __sane_unused__ * data)5187 config_attach_genesys(SANEI_Config __sane_unused__ *config, const char *devname,
5188                       void __sane_unused__ *data) noexcept
5189 {
5190   /* the devname has been processed and is ready to be used
5191    * directly. Since the backend is an USB only one, we can
5192    * call sanei_usb_attach_matching_devices straight */
5193   sanei_usb_attach_matching_devices (devname, attach_one_device);
5194 
5195   return SANE_STATUS_GOOD;
5196 }
5197 
5198 /* probes for scanner to attach to the backend */
probe_genesys_devices()5199 static void probe_genesys_devices()
5200 {
5201     DBG_HELPER(dbg);
5202     if (is_testing_mode()) {
5203         attach_usb_device(get_testing_device_name().c_str(),
5204                           get_testing_vendor_id(), get_testing_product_id(),
5205                           get_testing_bcd_device());
5206         return;
5207     }
5208 
5209   SANEI_Config config;
5210 
5211     // set configuration options structure : no option for this backend
5212     config.descriptors = nullptr;
5213     config.values = nullptr;
5214   config.count = 0;
5215 
5216     auto status = sanei_configure_attach(GENESYS_CONFIG_FILE, &config,
5217                                          config_attach_genesys, NULL);
5218     if (status == SANE_STATUS_ACCESS_DENIED) {
5219         dbg.vlog(DBG_error0, "Critical error: Couldn't access configuration file '%s'",
5220                  GENESYS_CONFIG_FILE);
5221     }
5222     TIE(status);
5223 
5224     DBG(DBG_info, "%s: %zu devices currently attached\n", __func__, s_devices->size());
5225 }
5226 
5227 /**
5228  * This should be changed if one of the substructures of
5229    Genesys_Calibration_Cache change, but it must be changed if there are
5230    changes that don't change size -- at least for now, as we store most
5231    of Genesys_Calibration_Cache as is.
5232 */
5233 static const char* CALIBRATION_IDENT = "sane_genesys";
5234 static const int CALIBRATION_VERSION = 31;
5235 
read_calibration(std::istream & str,Genesys_Device::Calibration & calibration,const std::string & path)5236 bool read_calibration(std::istream& str, Genesys_Device::Calibration& calibration,
5237                       const std::string& path)
5238 {
5239     DBG_HELPER(dbg);
5240 
5241     std::string ident;
5242     serialize(str, ident);
5243 
5244     if (ident != CALIBRATION_IDENT) {
5245         DBG(DBG_info, "%s: Incorrect calibration file '%s' header\n", __func__, path.c_str());
5246         return false;
5247     }
5248 
5249     size_t version;
5250     serialize(str, version);
5251 
5252     if (version != CALIBRATION_VERSION) {
5253         DBG(DBG_info, "%s: Incorrect calibration file '%s' version\n", __func__, path.c_str());
5254         return false;
5255     }
5256 
5257     calibration.clear();
5258     serialize(str, calibration);
5259     return true;
5260 }
5261 
5262 /**
5263  * reads previously cached calibration data
5264  * from file defined in dev->calib_file
5265  */
sanei_genesys_read_calibration(Genesys_Device::Calibration & calibration,const std::string & path)5266 static bool sanei_genesys_read_calibration(Genesys_Device::Calibration& calibration,
5267                                            const std::string& path)
5268 {
5269     DBG_HELPER(dbg);
5270 
5271     std::ifstream str;
5272     str.open(path);
5273     if (!str.is_open()) {
5274         DBG(DBG_info, "%s: Cannot open %s\n", __func__, path.c_str());
5275         return false;
5276     }
5277 
5278     return read_calibration(str, calibration, path);
5279 }
5280 
write_calibration(std::ostream & str,Genesys_Device::Calibration & calibration)5281 void write_calibration(std::ostream& str, Genesys_Device::Calibration& calibration)
5282 {
5283     std::string ident = CALIBRATION_IDENT;
5284     serialize(str, ident);
5285     size_t version = CALIBRATION_VERSION;
5286     serialize(str, version);
5287     serialize_newline(str);
5288     serialize(str, calibration);
5289 }
5290 
write_calibration(Genesys_Device::Calibration & calibration,const std::string & path)5291 static void write_calibration(Genesys_Device::Calibration& calibration, const std::string& path)
5292 {
5293     DBG_HELPER(dbg);
5294 
5295     std::ofstream str;
5296     str.open(path);
5297     if (!str.is_open()) {
5298         throw SaneException("Cannot open calibration for writing");
5299     }
5300     write_calibration(str, calibration);
5301 }
5302 
5303 /* -------------------------- SANE API functions ------------------------- */
5304 
sane_init_impl(SANE_Int * version_code,SANE_Auth_Callback authorize)5305 void sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize)
5306 {
5307   DBG_INIT ();
5308     DBG_HELPER_ARGS(dbg, "authorize %s null", authorize ? "!=" : "==");
5309     DBG(DBG_init, "SANE Genesys backend from %s\n", PACKAGE_STRING);
5310 
5311     if (!is_testing_mode()) {
5312 #ifdef HAVE_LIBUSB
5313         DBG(DBG_init, "SANE Genesys backend built with libusb-1.0\n");
5314 #endif
5315 #ifdef HAVE_LIBUSB_LEGACY
5316         DBG(DBG_init, "SANE Genesys backend built with libusb\n");
5317 #endif
5318     }
5319 
5320     if (version_code) {
5321         *version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, 0);
5322     }
5323 
5324     if (!is_testing_mode()) {
5325         sanei_usb_init();
5326     }
5327 
5328   s_scanners.init();
5329   s_devices.init();
5330   s_sane_devices.init();
5331     s_sane_devices_data.init();
5332   s_sane_devices_ptrs.init();
5333   genesys_init_sensor_tables();
5334   genesys_init_frontend_tables();
5335     genesys_init_gpo_tables();
5336     genesys_init_memory_layout_tables();
5337     genesys_init_motor_tables();
5338     genesys_init_usb_device_tables();
5339 
5340 
5341   DBG(DBG_info, "%s: %s endian machine\n", __func__,
5342 #ifdef WORDS_BIGENDIAN
5343        "big"
5344 #else
5345        "little"
5346 #endif
5347     );
5348 
5349     // cold-plug case :detection of already connected scanners
5350     s_attach_device_by_name_evaluate_bcd_device = false;
5351     probe_genesys_devices();
5352 }
5353 
5354 
5355 SANE_GENESYS_API_LINKAGE
sane_init(SANE_Int * version_code,SANE_Auth_Callback authorize)5356 SANE_Status sane_init(SANE_Int * version_code, SANE_Auth_Callback authorize)
5357 {
5358     return wrap_exceptions_to_status_code(__func__, [=]()
5359     {
5360         sane_init_impl(version_code, authorize);
5361     });
5362 }
5363 
5364 void
sane_exit_impl(void)5365 sane_exit_impl(void)
5366 {
5367     DBG_HELPER(dbg);
5368 
5369     if (!is_testing_mode()) {
5370         sanei_usb_exit();
5371     }
5372 
5373   run_functions_at_backend_exit();
5374 }
5375 
5376 SANE_GENESYS_API_LINKAGE
sane_exit()5377 void sane_exit()
5378 {
5379     catch_all_exceptions(__func__, [](){ sane_exit_impl(); });
5380 }
5381 
sane_get_devices_impl(const SANE_Device *** device_list,SANE_Bool local_only)5382 void sane_get_devices_impl(const SANE_Device *** device_list, SANE_Bool local_only)
5383 {
5384     DBG_HELPER_ARGS(dbg, "local_only = %s", local_only ? "true" : "false");
5385 
5386     if (!is_testing_mode()) {
5387         // hot-plug case : detection of newly connected scanners */
5388         sanei_usb_scan_devices();
5389     }
5390     s_attach_device_by_name_evaluate_bcd_device = true;
5391     probe_genesys_devices();
5392 
5393     s_sane_devices->clear();
5394     s_sane_devices_data->clear();
5395     s_sane_devices_ptrs->clear();
5396     s_sane_devices->reserve(s_devices->size());
5397     s_sane_devices_data->reserve(s_devices->size());
5398     s_sane_devices_ptrs->reserve(s_devices->size() + 1);
5399 
5400     for (auto dev_it = s_devices->begin(); dev_it != s_devices->end();) {
5401 
5402         if (is_testing_mode()) {
5403             present = true;
5404         } else {
5405             present = false;
5406             sanei_usb_find_devices(dev_it->vendorId, dev_it->productId, check_present);
5407         }
5408 
5409         if (present) {
5410             s_sane_devices->emplace_back();
5411             s_sane_devices_data->emplace_back();
5412             auto& sane_device = s_sane_devices->back();
5413             auto& sane_device_data = s_sane_devices_data->back();
5414             sane_device_data.name = dev_it->file_name;
5415             sane_device.name = sane_device_data.name.c_str();
5416             sane_device.vendor = dev_it->model->vendor;
5417             sane_device.model = dev_it->model->model;
5418             sane_device.type = "flatbed scanner";
5419             s_sane_devices_ptrs->push_back(&sane_device);
5420             dev_it++;
5421         } else {
5422             dev_it = s_devices->erase(dev_it);
5423         }
5424     }
5425     s_sane_devices_ptrs->push_back(nullptr);
5426 
5427     *const_cast<SANE_Device***>(device_list) = s_sane_devices_ptrs->data();
5428 }
5429 
5430 SANE_GENESYS_API_LINKAGE
sane_get_devices(const SANE_Device *** device_list,SANE_Bool local_only)5431 SANE_Status sane_get_devices(const SANE_Device *** device_list, SANE_Bool local_only)
5432 {
5433     return wrap_exceptions_to_status_code(__func__, [=]()
5434     {
5435         sane_get_devices_impl(device_list, local_only);
5436     });
5437 }
5438 
sane_open_impl(SANE_String_Const devicename,SANE_Handle * handle)5439 static void sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle)
5440 {
5441     DBG_HELPER_ARGS(dbg, "devicename = %s", devicename);
5442     Genesys_Device* dev = nullptr;
5443 
5444   /* devicename="" or devicename="genesys" are default values that use
5445    * first available device
5446    */
5447     if (devicename[0] && strcmp ("genesys", devicename) != 0) {
5448       /* search for the given devicename in the device list */
5449         for (auto& d : *s_devices) {
5450             if (d.file_name == devicename) {
5451                 dev = &d;
5452                 break;
5453             }
5454         }
5455 
5456         if (dev) {
5457             DBG(DBG_info, "%s: found `%s' in devlist\n", __func__, dev->file_name.c_str());
5458         } else if (is_testing_mode()) {
5459             DBG(DBG_info, "%s: couldn't find `%s' in devlist, not attaching", __func__, devicename);
5460         } else {
5461             DBG(DBG_info, "%s: couldn't find `%s' in devlist, trying attach\n", __func__,
5462                 devicename);
5463             dbg.status("attach_device_by_name");
5464             dev = attach_device_by_name(devicename, true);
5465             dbg.clear();
5466         }
5467     } else {
5468         // empty devicename or "genesys" -> use first device
5469         if (!s_devices->empty()) {
5470             dev = &s_devices->front();
5471             DBG(DBG_info, "%s: empty devicename, trying `%s'\n", __func__, dev->file_name.c_str());
5472         }
5473     }
5474 
5475     if (!dev) {
5476         throw SaneException("could not find the device to open: %s", devicename);
5477     }
5478 
5479     if (is_testing_mode()) {
5480         // during testing we need to initialize dev->model before test scanner interface is created
5481         // as that it needs to know what type of chip it needs to mimic.
5482         auto vendor_id = get_testing_vendor_id();
5483         auto product_id = get_testing_product_id();
5484         auto bcd_device = get_testing_bcd_device();
5485 
5486         dev->model = &get_matching_usb_dev(vendor_id, product_id, bcd_device).model();
5487 
5488         auto interface = std::unique_ptr<TestScannerInterface>{
5489                 new TestScannerInterface{dev, vendor_id, product_id, bcd_device}};
5490         interface->set_checkpoint_callback(get_testing_checkpoint_callback());
5491         dev->interface = std::move(interface);
5492 
5493         dev->interface->get_usb_device().open(dev->file_name.c_str());
5494     } else {
5495         dev->interface = std::unique_ptr<ScannerInterfaceUsb>{new ScannerInterfaceUsb{dev}};
5496 
5497         dbg.vstatus("open device '%s'", dev->file_name.c_str());
5498         dev->interface->get_usb_device().open(dev->file_name.c_str());
5499         dbg.clear();
5500 
5501         auto bcd_device = dev->interface->get_usb_device().get_bcd_device();
5502 
5503         dev->model = &get_matching_usb_dev(dev->vendorId, dev->productId, bcd_device).model();
5504     }
5505 
5506     dbg.vlog(DBG_info, "Opened device %s", dev->model->name);
5507 
5508     if (has_flag(dev->model->flags, ModelFlag::UNTESTED)) {
5509         DBG(DBG_error0, "WARNING: Your scanner is not fully supported or at least \n");
5510         DBG(DBG_error0, "         had only limited testing. Please be careful and \n");
5511         DBG(DBG_error0, "         report any failure/success to \n");
5512         DBG(DBG_error0, "         sane-devel@alioth-lists.debian.net. Please provide as many\n");
5513         DBG(DBG_error0, "         details as possible, e.g. the exact name of your\n");
5514         DBG(DBG_error0, "         scanner and what does (not) work.\n");
5515     }
5516 
5517   s_scanners->push_back(Genesys_Scanner());
5518   auto* s = &s_scanners->back();
5519 
5520     s->dev = dev;
5521     s->scanning = false;
5522     dev->parking = false;
5523     dev->read_active = false;
5524     dev->force_calibration = 0;
5525     dev->line_count = 0;
5526 
5527   *handle = s;
5528 
5529     if (!dev->already_initialized) {
5530         sanei_genesys_init_structs (dev);
5531     }
5532 
5533     dev->cmd_set = create_cmd_set(dev->model->asic_type);
5534 
5535     init_options(s);
5536 
5537     DBG_INIT();
5538 
5539     // FIXME: we create sensor tables for the sensor, this should happen when we know which sensor
5540     // we will select
5541     dev->cmd_set->init(dev);
5542 
5543     // some hardware capabilities are detected through sensors
5544     dev->cmd_set->update_hardware_sensors (s);
5545 }
5546 
5547 SANE_GENESYS_API_LINKAGE
sane_open(SANE_String_Const devicename,SANE_Handle * handle)5548 SANE_Status sane_open(SANE_String_Const devicename, SANE_Handle* handle)
5549 {
5550     return wrap_exceptions_to_status_code(__func__, [=]()
5551     {
5552         sane_open_impl(devicename, handle);
5553     });
5554 }
5555 
5556 void
sane_close_impl(SANE_Handle handle)5557 sane_close_impl(SANE_Handle handle)
5558 {
5559     DBG_HELPER(dbg);
5560 
5561   /* remove handle from list of open handles: */
5562   auto it = s_scanners->end();
5563   for (auto it2 = s_scanners->begin(); it2 != s_scanners->end(); it2++)
5564     {
5565       if (&*it2 == handle) {
5566           it = it2;
5567           break;
5568         }
5569     }
5570   if (it == s_scanners->end())
5571     {
5572       DBG(DBG_error, "%s: invalid handle %p\n", __func__, handle);
5573       return;			/* oops, not a handle we know about */
5574     }
5575 
5576     auto* dev = it->dev;
5577 
5578     // eject document for sheetfed scanners
5579     if (dev->model->is_sheetfed) {
5580         catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
5581     } else {
5582         // in case scanner is parking, wait for the head to reach home position
5583         if (dev->parking) {
5584             sanei_genesys_wait_for_home(dev);
5585         }
5586     }
5587 
5588     // enable power saving before leaving
5589     dev->cmd_set->save_power(dev, true);
5590 
5591     // here is the place to store calibration cache
5592     if (dev->force_calibration == 0 && !is_testing_mode()) {
5593         catch_all_exceptions(__func__, [&](){ write_calibration(dev->calibration_cache,
5594                                                                 dev->calib_file); });
5595     }
5596 
5597     dev->already_initialized = false;
5598     dev->clear();
5599 
5600     // LAMP OFF : same register across all the ASICs */
5601     dev->interface->write_register(0x03, 0x00);
5602 
5603     catch_all_exceptions(__func__, [&](){ dev->interface->get_usb_device().clear_halt(); });
5604 
5605     // we need this to avoid these ASIC getting stuck in bulk writes
5606     catch_all_exceptions(__func__, [&](){ dev->interface->get_usb_device().reset(); });
5607 
5608     // not freeing dev because it's in the dev list
5609     catch_all_exceptions(__func__, [&](){ dev->interface->get_usb_device().close(); });
5610 
5611     s_scanners->erase(it);
5612 }
5613 
5614 SANE_GENESYS_API_LINKAGE
sane_close(SANE_Handle handle)5615 void sane_close(SANE_Handle handle)
5616 {
5617     catch_all_exceptions(__func__, [=]()
5618     {
5619         sane_close_impl(handle);
5620     });
5621 }
5622 
5623 const SANE_Option_Descriptor *
sane_get_option_descriptor_impl(SANE_Handle handle,SANE_Int option)5624 sane_get_option_descriptor_impl(SANE_Handle handle, SANE_Int option)
5625 {
5626     DBG_HELPER(dbg);
5627     Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
5628 
5629     if (static_cast<unsigned>(option) >= NUM_OPTIONS) {
5630         return nullptr;
5631     }
5632 
5633   DBG(DBG_io2, "%s: option = %s (%d)\n", __func__, s->opt[option].name, option);
5634   return s->opt + option;
5635 }
5636 
5637 
5638 SANE_GENESYS_API_LINKAGE
sane_get_option_descriptor(SANE_Handle handle,SANE_Int option)5639 const SANE_Option_Descriptor* sane_get_option_descriptor(SANE_Handle handle, SANE_Int option)
5640 {
5641     const SANE_Option_Descriptor* ret = nullptr;
5642     catch_all_exceptions(__func__, [&]()
5643     {
5644         ret = sane_get_option_descriptor_impl(handle, option);
5645     });
5646     return ret;
5647 }
5648 
print_option(DebugMessageHelper & dbg,const Genesys_Scanner & s,int option,void * val)5649 static void print_option(DebugMessageHelper& dbg, const Genesys_Scanner& s, int option, void* val)
5650 {
5651     switch (s.opt[option].type) {
5652         case SANE_TYPE_INT: {
5653             dbg.vlog(DBG_proc, "value: %d", *reinterpret_cast<SANE_Word*>(val));
5654             return;
5655         }
5656         case SANE_TYPE_BOOL: {
5657             dbg.vlog(DBG_proc, "value: %s", *reinterpret_cast<SANE_Bool*>(val) ? "true" : "false");
5658             return;
5659         }
5660         case SANE_TYPE_FIXED: {
5661             dbg.vlog(DBG_proc, "value: %f", fixed_to_float(*reinterpret_cast<SANE_Word*>(val)));
5662             return;
5663         }
5664         case SANE_TYPE_STRING: {
5665             dbg.vlog(DBG_proc, "value: %s", reinterpret_cast<char*>(val));
5666             return;
5667         }
5668         default: break;
5669     }
5670     dbg.log(DBG_proc, "value: (non-printable)");
5671 }
5672 
get_option_value(Genesys_Scanner * s,int option,void * val)5673 static void get_option_value(Genesys_Scanner* s, int option, void* val)
5674 {
5675     DBG_HELPER_ARGS(dbg, "option: %s (%d)", s->opt[option].name, option);
5676     auto* dev = s->dev;
5677   unsigned int i;
5678     SANE_Word* table = nullptr;
5679   std::vector<uint16_t> gamma_table;
5680   unsigned option_size = 0;
5681 
5682     const Genesys_Sensor* sensor = nullptr;
5683     if (sanei_genesys_has_sensor(dev, dev->settings.xres, dev->settings.get_channels(),
5684                                  dev->settings.scan_method))
5685     {
5686         sensor = &sanei_genesys_find_sensor(dev, dev->settings.xres,
5687                                             dev->settings.get_channels(),
5688                                             dev->settings.scan_method);
5689     }
5690 
5691   switch (option)
5692     {
5693       /* geometry */
5694     case OPT_TL_X:
5695         *reinterpret_cast<SANE_Word*>(val) = s->pos_top_left_x;
5696         break;
5697     case OPT_TL_Y:
5698         *reinterpret_cast<SANE_Word*>(val) = s->pos_top_left_y;
5699         break;
5700     case OPT_BR_X:
5701         *reinterpret_cast<SANE_Word*>(val) = s->pos_bottom_right_x;
5702         break;
5703     case OPT_BR_Y:
5704         *reinterpret_cast<SANE_Word*>(val) = s->pos_bottom_right_y;
5705         break;
5706       /* word options: */
5707     case OPT_NUM_OPTS:
5708         *reinterpret_cast<SANE_Word*>(val) = NUM_OPTIONS;
5709         break;
5710     case OPT_RESOLUTION:
5711         *reinterpret_cast<SANE_Word*>(val) = s->resolution;
5712         break;
5713     case OPT_BIT_DEPTH:
5714         *reinterpret_cast<SANE_Word*>(val) = s->bit_depth;
5715         break;
5716     case OPT_PREVIEW:
5717         *reinterpret_cast<SANE_Word*>(val) = s->preview;
5718         break;
5719     case OPT_LAMP_OFF:
5720         *reinterpret_cast<SANE_Word*>(val) = s->lamp_off;
5721         break;
5722     case OPT_LAMP_OFF_TIME:
5723         *reinterpret_cast<SANE_Word*>(val) = s->lamp_off_time;
5724         break;
5725     case OPT_CONTRAST:
5726         *reinterpret_cast<SANE_Word*>(val) = s->contrast;
5727         break;
5728     case OPT_BRIGHTNESS:
5729         *reinterpret_cast<SANE_Word*>(val) = s->brightness;
5730         break;
5731     case OPT_EXPIRATION_TIME:
5732         *reinterpret_cast<SANE_Word*>(val) = s->expiration_time;
5733         break;
5734     case OPT_CUSTOM_GAMMA:
5735         *reinterpret_cast<SANE_Word*>(val) = s->custom_gamma;
5736         break;
5737 
5738       /* string options: */
5739     case OPT_MODE:
5740         std::strcpy(reinterpret_cast<char*>(val), s->mode.c_str());
5741         break;
5742     case OPT_COLOR_FILTER:
5743         std::strcpy(reinterpret_cast<char*>(val), s->color_filter.c_str());
5744         break;
5745     case OPT_CALIBRATION_FILE:
5746         std::strcpy(reinterpret_cast<char*>(val), s->calibration_file.c_str());
5747         break;
5748     case OPT_SOURCE:
5749         std::strcpy(reinterpret_cast<char*>(val), scan_method_to_option_string(s->scan_method));
5750         break;
5751 
5752       /* word array options */
5753     case OPT_GAMMA_VECTOR:
5754         if (!sensor)
5755             throw SaneException("Unsupported scanner mode selected");
5756 
5757         table = reinterpret_cast<SANE_Word*>(val);
5758             if (s->color_filter == "Red") {
5759                 gamma_table = get_gamma_table(dev, *sensor, GENESYS_RED);
5760             } else if (s->color_filter == "Blue") {
5761                 gamma_table = get_gamma_table(dev, *sensor, GENESYS_BLUE);
5762             } else {
5763                 gamma_table = get_gamma_table(dev, *sensor, GENESYS_GREEN);
5764             }
5765         option_size = s->opt[option].size / sizeof (SANE_Word);
5766         if (gamma_table.size() != option_size) {
5767             throw std::runtime_error("The size of the gamma tables does not match");
5768         }
5769         for (i = 0; i < option_size; i++) {
5770             table[i] = gamma_table[i];
5771         }
5772         break;
5773     case OPT_GAMMA_VECTOR_R:
5774         if (!sensor)
5775             throw SaneException("Unsupported scanner mode selected");
5776 
5777         table = reinterpret_cast<SANE_Word*>(val);
5778             gamma_table = get_gamma_table(dev, *sensor, GENESYS_RED);
5779         option_size = s->opt[option].size / sizeof (SANE_Word);
5780         if (gamma_table.size() != option_size) {
5781             throw std::runtime_error("The size of the gamma tables does not match");
5782         }
5783         for (i = 0; i < option_size; i++) {
5784             table[i] = gamma_table[i];
5785 	}
5786       break;
5787     case OPT_GAMMA_VECTOR_G:
5788         if (!sensor)
5789             throw SaneException("Unsupported scanner mode selected");
5790 
5791         table = reinterpret_cast<SANE_Word*>(val);
5792             gamma_table = get_gamma_table(dev, *sensor, GENESYS_GREEN);
5793         option_size = s->opt[option].size / sizeof (SANE_Word);
5794         if (gamma_table.size() != option_size) {
5795             throw std::runtime_error("The size of the gamma tables does not match");
5796         }
5797         for (i = 0; i < option_size; i++) {
5798             table[i] = gamma_table[i];
5799         }
5800       break;
5801     case OPT_GAMMA_VECTOR_B:
5802         if (!sensor)
5803             throw SaneException("Unsupported scanner mode selected");
5804 
5805         table = reinterpret_cast<SANE_Word*>(val);
5806             gamma_table = get_gamma_table(dev, *sensor, GENESYS_BLUE);
5807         option_size = s->opt[option].size / sizeof (SANE_Word);
5808         if (gamma_table.size() != option_size) {
5809             throw std::runtime_error("The size of the gamma tables does not match");
5810         }
5811         for (i = 0; i < option_size; i++) {
5812             table[i] = gamma_table[i];
5813         }
5814       break;
5815       /* sensors */
5816     case OPT_SCAN_SW:
5817     case OPT_FILE_SW:
5818     case OPT_EMAIL_SW:
5819     case OPT_COPY_SW:
5820     case OPT_PAGE_LOADED_SW:
5821     case OPT_OCR_SW:
5822     case OPT_POWER_SW:
5823     case OPT_EXTRA_SW:
5824         s->dev->cmd_set->update_hardware_sensors(s);
5825         *reinterpret_cast<SANE_Bool*>(val) = s->buttons[genesys_option_to_button(option)].read();
5826         break;
5827 
5828         case OPT_NEED_CALIBRATION_SW: {
5829             if (!sensor) {
5830                 throw SaneException("Unsupported scanner mode selected");
5831             }
5832 
5833             // scanner needs calibration for current mode unless a matching calibration cache is
5834             // found
5835 
5836             bool result = true;
5837 
5838             auto session = dev->cmd_set->calculate_scan_session(dev, *sensor, dev->settings);
5839 
5840             for (auto& cache : dev->calibration_cache) {
5841                 if (sanei_genesys_is_compatible_calibration(dev, session, &cache, false)) {
5842                     *reinterpret_cast<SANE_Bool*>(val) = SANE_FALSE;
5843                 }
5844             }
5845             *reinterpret_cast<SANE_Bool*>(val) = result;
5846             break;
5847         }
5848     default:
5849       DBG(DBG_warn, "%s: can't get unknown option %d\n", __func__, option);
5850     }
5851     print_option(dbg, *s, option, val);
5852 }
5853 
5854 /** @brief set calibration file value
5855  * Set calibration file value. Load new cache values from file if it exists,
5856  * else creates the file*/
set_calibration_value(Genesys_Scanner * s,const char * val)5857 static void set_calibration_value(Genesys_Scanner* s, const char* val)
5858 {
5859     DBG_HELPER(dbg);
5860     auto dev = s->dev;
5861 
5862     std::string new_calib_path = val;
5863     Genesys_Device::Calibration new_calibration;
5864 
5865     bool is_calib_success = false;
5866     catch_all_exceptions(__func__, [&]()
5867     {
5868         is_calib_success = sanei_genesys_read_calibration(new_calibration, new_calib_path);
5869     });
5870 
5871     if (!is_calib_success) {
5872         return;
5873     }
5874 
5875     dev->calibration_cache = std::move(new_calibration);
5876     dev->calib_file = new_calib_path;
5877     s->calibration_file = new_calib_path;
5878     DBG(DBG_info, "%s: Calibration filename set to '%s':\n", __func__, new_calib_path.c_str());
5879 }
5880 
5881 /* sets an option , called by sane_control_option */
set_option_value(Genesys_Scanner * s,int option,void * val,SANE_Int * myinfo)5882 static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int* myinfo)
5883 {
5884     DBG_HELPER_ARGS(dbg, "option: %s (%d)", s->opt[option].name, option);
5885     print_option(dbg, *s, option, val);
5886 
5887     auto* dev = s->dev;
5888 
5889   SANE_Word *table;
5890   unsigned int i;
5891   unsigned option_size = 0;
5892 
5893     switch (option) {
5894     case OPT_TL_X:
5895         s->pos_top_left_x = *reinterpret_cast<SANE_Word*>(val);
5896         calc_parameters(s);
5897         *myinfo |= SANE_INFO_RELOAD_PARAMS;
5898         break;
5899     case OPT_TL_Y:
5900         s->pos_top_left_y = *reinterpret_cast<SANE_Word*>(val);
5901         calc_parameters(s);
5902         *myinfo |= SANE_INFO_RELOAD_PARAMS;
5903         break;
5904     case OPT_BR_X:
5905         s->pos_bottom_right_x = *reinterpret_cast<SANE_Word*>(val);
5906         calc_parameters(s);
5907         *myinfo |= SANE_INFO_RELOAD_PARAMS;
5908         break;
5909     case OPT_BR_Y:
5910         s->pos_bottom_right_y = *reinterpret_cast<SANE_Word*>(val);
5911         calc_parameters(s);
5912         *myinfo |= SANE_INFO_RELOAD_PARAMS;
5913         break;
5914     case OPT_RESOLUTION:
5915         s->resolution = *reinterpret_cast<SANE_Word*>(val);
5916         calc_parameters(s);
5917         *myinfo |= SANE_INFO_RELOAD_PARAMS;
5918         break;
5919     case OPT_LAMP_OFF:
5920         s->lamp_off = *reinterpret_cast<SANE_Word*>(val);
5921         calc_parameters(s);
5922         *myinfo |= SANE_INFO_RELOAD_PARAMS;
5923         break;
5924     case OPT_PREVIEW:
5925         s->preview = *reinterpret_cast<SANE_Word*>(val);
5926         calc_parameters(s);
5927         *myinfo |= SANE_INFO_RELOAD_PARAMS;
5928         break;
5929     case OPT_BRIGHTNESS:
5930         s->brightness = *reinterpret_cast<SANE_Word*>(val);
5931         calc_parameters(s);
5932         *myinfo |= SANE_INFO_RELOAD_PARAMS;
5933         break;
5934     case OPT_CONTRAST:
5935         s->contrast = *reinterpret_cast<SANE_Word*>(val);
5936         calc_parameters(s);
5937         *myinfo |= SANE_INFO_RELOAD_PARAMS;
5938         break;
5939     /* software enhancement functions only apply to 8 or 1 bits data */
5940     case OPT_BIT_DEPTH:
5941         s->bit_depth = *reinterpret_cast<SANE_Word*>(val);
5942         if(s->bit_depth>8)
5943         {
5944           DISABLE(OPT_CONTRAST);
5945           DISABLE(OPT_BRIGHTNESS);
5946             } else {
5947           ENABLE(OPT_CONTRAST);
5948           ENABLE(OPT_BRIGHTNESS);
5949         }
5950         calc_parameters(s);
5951       *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
5952       break;
5953     case OPT_SOURCE: {
5954         auto scan_method = option_string_to_scan_method(reinterpret_cast<const char*>(val));
5955         if (s->scan_method != scan_method) {
5956             s->scan_method = scan_method;
5957 
5958             set_xy_range_option_values(*s);
5959             set_resolution_option_values(*s, false);
5960 
5961             *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
5962         }
5963         break;
5964     }
5965         case OPT_MODE: {
5966             s->mode = reinterpret_cast<const char*>(val);
5967 
5968             if (s->mode == SANE_VALUE_SCAN_MODE_GRAY) {
5969                 if (dev->model->asic_type != AsicType::GL646 || !dev->model->is_cis) {
5970                     ENABLE(OPT_COLOR_FILTER);
5971                 }
5972                 create_bpp_list(s, dev->model->bpp_gray_values);
5973                 s->bit_depth = dev->model->bpp_gray_values[0];
5974             } else {
5975                 DISABLE(OPT_COLOR_FILTER);
5976                 create_bpp_list(s, dev->model->bpp_color_values);
5977                 s->bit_depth = dev->model->bpp_color_values[0];
5978             }
5979 
5980             calc_parameters(s);
5981 
5982       /* if custom gamma, toggle gamma table options according to the mode */
5983       if (s->custom_gamma)
5984 	{
5985           if (s->mode == SANE_VALUE_SCAN_MODE_COLOR)
5986 	    {
5987 	      DISABLE (OPT_GAMMA_VECTOR);
5988 	      ENABLE (OPT_GAMMA_VECTOR_R);
5989 	      ENABLE (OPT_GAMMA_VECTOR_G);
5990 	      ENABLE (OPT_GAMMA_VECTOR_B);
5991 	    }
5992 	  else
5993 	    {
5994 	      ENABLE (OPT_GAMMA_VECTOR);
5995 	      DISABLE (OPT_GAMMA_VECTOR_R);
5996 	      DISABLE (OPT_GAMMA_VECTOR_G);
5997 	      DISABLE (OPT_GAMMA_VECTOR_B);
5998 	    }
5999 	}
6000 
6001       *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
6002       break;
6003         }
6004     case OPT_COLOR_FILTER:
6005       s->color_filter = reinterpret_cast<const char*>(val);
6006         calc_parameters(s);
6007       break;
6008         case OPT_CALIBRATION_FILE: {
6009             if (dev->force_calibration == 0) {
6010                 set_calibration_value(s, reinterpret_cast<const char*>(val));
6011             }
6012             break;
6013         }
6014         case OPT_LAMP_OFF_TIME: {
6015             if (*reinterpret_cast<SANE_Word*>(val) != s->lamp_off_time) {
6016                 s->lamp_off_time = *reinterpret_cast<SANE_Word*>(val);
6017                     dev->cmd_set->set_powersaving(dev, s->lamp_off_time);
6018             }
6019             break;
6020         }
6021         case OPT_EXPIRATION_TIME: {
6022             if (*reinterpret_cast<SANE_Word*>(val) != s->expiration_time) {
6023                 s->expiration_time = *reinterpret_cast<SANE_Word*>(val);
6024             }
6025             break;
6026         }
6027         case OPT_CUSTOM_GAMMA: {
6028       *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
6029         s->custom_gamma = *reinterpret_cast<SANE_Bool*>(val);
6030 
6031         if (s->custom_gamma) {
6032           if (s->mode == SANE_VALUE_SCAN_MODE_COLOR)
6033 	    {
6034 	      DISABLE (OPT_GAMMA_VECTOR);
6035 	      ENABLE (OPT_GAMMA_VECTOR_R);
6036 	      ENABLE (OPT_GAMMA_VECTOR_G);
6037 	      ENABLE (OPT_GAMMA_VECTOR_B);
6038 	    }
6039 	  else
6040 	    {
6041 	      ENABLE (OPT_GAMMA_VECTOR);
6042 	      DISABLE (OPT_GAMMA_VECTOR_R);
6043 	      DISABLE (OPT_GAMMA_VECTOR_G);
6044 	      DISABLE (OPT_GAMMA_VECTOR_B);
6045 	    }
6046 	}
6047       else
6048 	{
6049 	  DISABLE (OPT_GAMMA_VECTOR);
6050 	  DISABLE (OPT_GAMMA_VECTOR_R);
6051 	  DISABLE (OPT_GAMMA_VECTOR_G);
6052 	  DISABLE (OPT_GAMMA_VECTOR_B);
6053                 for (auto& table : dev->gamma_override_tables) {
6054                     table.clear();
6055                 }
6056             }
6057             break;
6058         }
6059 
6060         case OPT_GAMMA_VECTOR: {
6061             table = reinterpret_cast<SANE_Word*>(val);
6062             option_size = s->opt[option].size / sizeof (SANE_Word);
6063 
6064             dev->gamma_override_tables[GENESYS_RED].resize(option_size);
6065             dev->gamma_override_tables[GENESYS_GREEN].resize(option_size);
6066             dev->gamma_override_tables[GENESYS_BLUE].resize(option_size);
6067             for (i = 0; i < option_size; i++) {
6068                 dev->gamma_override_tables[GENESYS_RED][i] = table[i];
6069                 dev->gamma_override_tables[GENESYS_GREEN][i] = table[i];
6070                 dev->gamma_override_tables[GENESYS_BLUE][i] = table[i];
6071             }
6072             break;
6073         }
6074         case OPT_GAMMA_VECTOR_R: {
6075             table = reinterpret_cast<SANE_Word*>(val);
6076             option_size = s->opt[option].size / sizeof (SANE_Word);
6077             dev->gamma_override_tables[GENESYS_RED].resize(option_size);
6078             for (i = 0; i < option_size; i++) {
6079                 dev->gamma_override_tables[GENESYS_RED][i] = table[i];
6080             }
6081             break;
6082         }
6083         case OPT_GAMMA_VECTOR_G: {
6084             table = reinterpret_cast<SANE_Word*>(val);
6085             option_size = s->opt[option].size / sizeof (SANE_Word);
6086             dev->gamma_override_tables[GENESYS_GREEN].resize(option_size);
6087             for (i = 0; i < option_size; i++) {
6088                 dev->gamma_override_tables[GENESYS_GREEN][i] = table[i];
6089             }
6090             break;
6091         }
6092         case OPT_GAMMA_VECTOR_B: {
6093             table = reinterpret_cast<SANE_Word*>(val);
6094             option_size = s->opt[option].size / sizeof (SANE_Word);
6095             dev->gamma_override_tables[GENESYS_BLUE].resize(option_size);
6096             for (i = 0; i < option_size; i++) {
6097                 dev->gamma_override_tables[GENESYS_BLUE][i] = table[i];
6098             }
6099             break;
6100         }
6101         case OPT_CALIBRATE: {
6102             auto& sensor = sanei_genesys_find_sensor_for_write(dev, dev->settings.xres,
6103                                                                dev->settings.get_channels(),
6104                                                                dev->settings.scan_method);
6105             catch_all_exceptions(__func__, [&]()
6106             {
6107             dev->cmd_set->save_power(dev, false);
6108             genesys_scanner_calibration(dev, sensor);
6109             });
6110             catch_all_exceptions(__func__, [&]()
6111             {
6112             dev->cmd_set->save_power(dev, true);
6113             });
6114             *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
6115             break;
6116         }
6117         case OPT_CLEAR_CALIBRATION: {
6118             dev->calibration_cache.clear();
6119 
6120             // remove file
6121             unlink(dev->calib_file.c_str());
6122             // signals that sensors will have to be read again
6123             *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
6124             break;
6125         }
6126         case OPT_FORCE_CALIBRATION: {
6127             dev->force_calibration = 1;
6128             dev->calibration_cache.clear();
6129             dev->calib_file.clear();
6130 
6131             // signals that sensors will have to be read again
6132             *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
6133             break;
6134         }
6135 
6136         case OPT_IGNORE_OFFSETS: {
6137             dev->ignore_offsets = true;
6138             break;
6139         }
6140         default: {
6141             DBG(DBG_warn, "%s: can't set unknown option %d\n", __func__, option);
6142         }
6143     }
6144 }
6145 
6146 
6147 /* sets and gets scanner option values */
sane_control_option_impl(SANE_Handle handle,SANE_Int option,SANE_Action action,void * val,SANE_Int * info)6148 void sane_control_option_impl(SANE_Handle handle, SANE_Int option,
6149                               SANE_Action action, void *val, SANE_Int * info)
6150 {
6151     Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
6152     auto action_str = (action == SANE_ACTION_GET_VALUE) ? "get" :
6153                       (action == SANE_ACTION_SET_VALUE) ? "set" :
6154                       (action == SANE_ACTION_SET_AUTO) ? "set_auto" : "unknown";
6155     DBG_HELPER_ARGS(dbg, "action = %s, option = %s (%d)", action_str,
6156                     s->opt[option].name, option);
6157 
6158   SANE_Word cap;
6159   SANE_Int myinfo = 0;
6160 
6161     if (info) {
6162         *info = 0;
6163     }
6164 
6165     if (s->scanning) {
6166         throw SaneException(SANE_STATUS_DEVICE_BUSY,
6167                             "don't call this function while scanning (option = %s (%d))",
6168                             s->opt[option].name, option);
6169     }
6170     if (option >= NUM_OPTIONS || option < 0) {
6171         throw SaneException("option %d >= NUM_OPTIONS || option < 0", option);
6172     }
6173 
6174   cap = s->opt[option].cap;
6175 
6176     if (!SANE_OPTION_IS_ACTIVE (cap)) {
6177         throw SaneException("option %d is inactive", option);
6178     }
6179 
6180     switch (action) {
6181         case SANE_ACTION_GET_VALUE:
6182             get_option_value(s, option, val);
6183             break;
6184 
6185         case SANE_ACTION_SET_VALUE:
6186             if (!SANE_OPTION_IS_SETTABLE (cap)) {
6187                 throw SaneException("option %d is not settable", option);
6188             }
6189 
6190             TIE(sanei_constrain_value(s->opt + option, val, &myinfo));
6191 
6192             set_option_value(s, option, val, &myinfo);
6193             break;
6194 
6195         case SANE_ACTION_SET_AUTO:
6196             throw SaneException("SANE_ACTION_SET_AUTO unsupported since no option "
6197                                 "has SANE_CAP_AUTOMATIC");
6198         default:
6199             throw SaneException("unknown action %d for option %d", action, option);
6200     }
6201 
6202   if (info)
6203     *info = myinfo;
6204 }
6205 
6206 SANE_GENESYS_API_LINKAGE
sane_control_option(SANE_Handle handle,SANE_Int option,SANE_Action action,void * val,SANE_Int * info)6207 SANE_Status sane_control_option(SANE_Handle handle, SANE_Int option,
6208                                            SANE_Action action, void *val, SANE_Int * info)
6209 {
6210     return wrap_exceptions_to_status_code(__func__, [=]()
6211     {
6212         sane_control_option_impl(handle, option, action, val, info);
6213     });
6214 }
6215 
sane_get_parameters_impl(SANE_Handle handle,SANE_Parameters * params)6216 void sane_get_parameters_impl(SANE_Handle handle, SANE_Parameters* params)
6217 {
6218     DBG_HELPER(dbg);
6219     Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
6220     auto* dev = s->dev;
6221 
6222   /* don't recompute parameters once data reading is active, ie during scan */
6223     if (!dev->read_active) {
6224         calc_parameters(s);
6225     }
6226     if (params) {
6227       *params = s->params;
6228 
6229       /* in the case of a sheetfed scanner, when full height is specified
6230        * we override the computed line number with -1 to signal that we
6231        * don't know the real document height.
6232        * We don't do that doing buffering image for digital processing
6233        */
6234         if (dev->model->is_sheetfed &&
6235             s->pos_bottom_right_y == s->opt[OPT_BR_Y].constraint.range->max)
6236         {
6237             params->lines = -1;
6238         }
6239     }
6240     debug_dump(DBG_proc, *params);
6241 }
6242 
6243 SANE_GENESYS_API_LINKAGE
sane_get_parameters(SANE_Handle handle,SANE_Parameters * params)6244 SANE_Status sane_get_parameters(SANE_Handle handle, SANE_Parameters* params)
6245 {
6246     return wrap_exceptions_to_status_code(__func__, [=]()
6247     {
6248         sane_get_parameters_impl(handle, params);
6249     });
6250 }
6251 
sane_start_impl(SANE_Handle handle)6252 void sane_start_impl(SANE_Handle handle)
6253 {
6254     DBG_HELPER(dbg);
6255     Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
6256     auto* dev = s->dev;
6257 
6258     if (s->pos_top_left_x >= s->pos_bottom_right_x) {
6259         throw SaneException("top left x >= bottom right x");
6260     }
6261     if (s->pos_top_left_y >= s->pos_bottom_right_y) {
6262         throw SaneException("top left y >= bottom right y");
6263     }
6264 
6265     // fetch stored calibration
6266     if (dev->force_calibration == 0) {
6267         auto path = calibration_filename(dev);
6268         s->calibration_file = path;
6269         dev->calib_file = path;
6270         DBG(DBG_info, "%s: Calibration filename set to:\n", __func__);
6271         DBG(DBG_info, "%s: >%s<\n", __func__, dev->calib_file.c_str());
6272 
6273         catch_all_exceptions(__func__, [&]()
6274         {
6275             sanei_genesys_read_calibration(dev->calibration_cache, dev->calib_file);
6276         });
6277     }
6278 
6279     // First make sure we have a current parameter set.  Some of the
6280     // parameters will be overwritten below, but that's OK.
6281 
6282     calc_parameters(s);
6283     genesys_start_scan(dev, s->lamp_off);
6284 
6285     s->scanning = true;
6286 }
6287 
6288 SANE_GENESYS_API_LINKAGE
sane_start(SANE_Handle handle)6289 SANE_Status sane_start(SANE_Handle handle)
6290 {
6291     return wrap_exceptions_to_status_code(__func__, [=]()
6292     {
6293         sane_start_impl(handle);
6294     });
6295 }
6296 
6297 // returns SANE_STATUS_GOOD if there are more data, SANE_STATUS_EOF otherwise
sane_read_impl(SANE_Handle handle,SANE_Byte * buf,SANE_Int max_len,SANE_Int * len)6298 SANE_Status sane_read_impl(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int* len)
6299 {
6300     DBG_HELPER(dbg);
6301     Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
6302   size_t local_len;
6303 
6304     if (!s) {
6305         throw SaneException("handle is nullptr");
6306     }
6307 
6308     auto* dev = s->dev;
6309     if (!dev) {
6310         throw SaneException("dev is nullptr");
6311     }
6312 
6313     if (!buf) {
6314         throw SaneException("buf is nullptr");
6315     }
6316 
6317     if (!len) {
6318         throw SaneException("len is nullptr");
6319     }
6320 
6321   *len = 0;
6322 
6323     if (!s->scanning) {
6324         throw SaneException(SANE_STATUS_CANCELLED,
6325                             "scan was cancelled, is over or has not been initiated yet");
6326     }
6327 
6328   DBG(DBG_proc, "%s: start, %d maximum bytes required\n", __func__, max_len);
6329     DBG(DBG_io2, "%s: bytes_to_read=%zu, total_bytes_read=%zu\n", __func__,
6330         dev->total_bytes_to_read, dev->total_bytes_read);
6331 
6332   if(dev->total_bytes_read>=dev->total_bytes_to_read)
6333     {
6334       DBG(DBG_proc, "%s: nothing more to scan: EOF\n", __func__);
6335 
6336       /* issue park command immediately in case scanner can handle it
6337        * so we save time */
6338         if (!dev->model->is_sheetfed && !has_flag(dev->model->flags, ModelFlag::MUST_WAIT) &&
6339             !dev->parking)
6340         {
6341             dev->cmd_set->move_back_home(dev, false);
6342             dev->parking = true;
6343         }
6344         return SANE_STATUS_EOF;
6345     }
6346 
6347   local_len = max_len;
6348 
6349     genesys_read_ordered_data(dev, buf, &local_len);
6350 
6351   *len = local_len;
6352     if (local_len > static_cast<std::size_t>(max_len)) {
6353         dbg.log(DBG_error, "error: returning incorrect length");
6354     }
6355   DBG(DBG_proc, "%s: %d bytes returned\n", __func__, *len);
6356     return SANE_STATUS_GOOD;
6357 }
6358 
6359 SANE_GENESYS_API_LINKAGE
sane_read(SANE_Handle handle,SANE_Byte * buf,SANE_Int max_len,SANE_Int * len)6360 SANE_Status sane_read(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int* len)
6361 {
6362     return wrap_exceptions_to_status_code_return(__func__, [=]()
6363     {
6364         return sane_read_impl(handle, buf, max_len, len);
6365     });
6366 }
6367 
sane_cancel_impl(SANE_Handle handle)6368 void sane_cancel_impl(SANE_Handle handle)
6369 {
6370     DBG_HELPER(dbg);
6371     Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
6372     auto* dev = s->dev;
6373 
6374     s->scanning = false;
6375     dev->read_active = false;
6376 
6377     // no need to end scan if we are parking the head
6378     if (!dev->parking) {
6379         dev->cmd_set->end_scan(dev, &dev->reg, true);
6380     }
6381 
6382     // park head if flatbed scanner
6383     if (!dev->model->is_sheetfed) {
6384         if (!dev->parking) {
6385             dev->cmd_set->move_back_home(dev, has_flag(dev->model->flags, ModelFlag::MUST_WAIT));
6386             dev->parking = !has_flag(dev->model->flags, ModelFlag::MUST_WAIT);
6387         }
6388     } else {
6389         // in case of sheetfed scanners, we have to eject the document if still present
6390         dev->cmd_set->eject_document(dev);
6391     }
6392 
6393     // enable power saving mode unless we are parking ....
6394     if (!dev->parking) {
6395         dev->cmd_set->save_power(dev, true);
6396     }
6397 }
6398 
6399 SANE_GENESYS_API_LINKAGE
sane_cancel(SANE_Handle handle)6400 void sane_cancel(SANE_Handle handle)
6401 {
6402     catch_all_exceptions(__func__, [=]() { sane_cancel_impl(handle); });
6403 }
6404 
sane_set_io_mode_impl(SANE_Handle handle,SANE_Bool non_blocking)6405 void sane_set_io_mode_impl(SANE_Handle handle, SANE_Bool non_blocking)
6406 {
6407     DBG_HELPER_ARGS(dbg, "handle = %p, non_blocking = %s", handle,
6408                     non_blocking == SANE_TRUE ? "true" : "false");
6409     Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
6410 
6411     if (!s->scanning) {
6412         throw SaneException("not scanning");
6413     }
6414     if (non_blocking) {
6415         throw SaneException(SANE_STATUS_UNSUPPORTED);
6416     }
6417 }
6418 
6419 SANE_GENESYS_API_LINKAGE
sane_set_io_mode(SANE_Handle handle,SANE_Bool non_blocking)6420 SANE_Status sane_set_io_mode(SANE_Handle handle, SANE_Bool non_blocking)
6421 {
6422     return wrap_exceptions_to_status_code(__func__, [=]()
6423     {
6424         sane_set_io_mode_impl(handle, non_blocking);
6425     });
6426 }
6427 
sane_get_select_fd_impl(SANE_Handle handle,SANE_Int * fd)6428 void sane_get_select_fd_impl(SANE_Handle handle, SANE_Int* fd)
6429 {
6430     DBG_HELPER_ARGS(dbg, "handle = %p, fd = %p", handle, reinterpret_cast<void*>(fd));
6431     Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
6432 
6433     if (!s->scanning) {
6434         throw SaneException("not scanning");
6435     }
6436     throw SaneException(SANE_STATUS_UNSUPPORTED);
6437 }
6438 
6439 SANE_GENESYS_API_LINKAGE
sane_get_select_fd(SANE_Handle handle,SANE_Int * fd)6440 SANE_Status sane_get_select_fd(SANE_Handle handle, SANE_Int* fd)
6441 {
6442     return wrap_exceptions_to_status_code(__func__, [=]()
6443     {
6444         sane_get_select_fd_impl(handle, fd);
6445     });
6446 }
6447 
genesys_option_to_button(int option)6448 GenesysButtonName genesys_option_to_button(int option)
6449 {
6450     switch (option) {
6451     case OPT_SCAN_SW: return BUTTON_SCAN_SW;
6452     case OPT_FILE_SW: return BUTTON_FILE_SW;
6453     case OPT_EMAIL_SW: return BUTTON_EMAIL_SW;
6454     case OPT_COPY_SW: return BUTTON_COPY_SW;
6455     case OPT_PAGE_LOADED_SW: return BUTTON_PAGE_LOADED_SW;
6456     case OPT_OCR_SW: return BUTTON_OCR_SW;
6457     case OPT_POWER_SW: return BUTTON_POWER_SW;
6458     case OPT_EXTRA_SW: return BUTTON_EXTRA_SW;
6459     default: throw std::runtime_error("Unknown option to convert to button index");
6460     }
6461 }
6462 
6463 } // namespace genesys
6464