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