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