1 /* sane - Scanner Access Now Easy.
2 
3    Copyright (C) 2003 Oliver Rauch
4    Copyright (C) 2003, 2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
5    Copyright (C) 2004 Gerhard Jaeger <gerhard@gjaeger.de>
6    Copyright (C) 2004-2013 Stéphane Voltz <stef.dev@free.fr>
7    Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
8    Copyright (C) 2007 Luke <iceyfor@gmail.com>
9    Copyright (C) 2011 Alexey Osipov <simba@lerlan.ru> for HP2400 description
10                       and tuning
11 
12    This file is part of the SANE package.
13 
14    This program is free software; you can redistribute it and/or
15    modify it under the terms of the GNU General Public License as
16    published by the Free Software Foundation; either version 2 of the
17    License, or (at your option) any later version.
18 
19    This program is distributed in the hope that it will be useful, but
20    WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22    General Public License for more details.
23 
24    You should have received a copy of the GNU General Public License
25    along with this program.  If not, see <https://www.gnu.org/licenses/>.
26 */
27 
28 #define DEBUG_DECLARE_ONLY
29 
30 #include "gl646.h"
31 #include "gl646_registers.h"
32 #include "test_settings.h"
33 
34 #include <vector>
35 
36 namespace genesys {
37 namespace gl646 {
38 
39 namespace {
40 constexpr unsigned CALIBRATION_LINES = 10;
41 } // namespace
42 
43 static void write_control(Genesys_Device* dev, const Genesys_Sensor& sensor, int resolution);
44 
45 
46 static void gl646_set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set,
47                          int dpi);
48 
49 static void simple_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
50                         const ScanSession& session, bool move,
51                         std::vector<std::uint8_t>& data, const char* test_identifier);
52 /**
53  * Send the stop scan command
54  * */
55 static void end_scan_impl(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop,
56                           bool eject);
57 
58 /**
59  * master motor settings table entry
60  */
61 struct Motor_Master
62 {
63     MotorId motor_id;
64     unsigned dpi;
65     unsigned channels;
66 
67     // settings
68     StepType steptype;
69     bool fastmod; // fast scanning
70     bool fastfed; // fast fed slope tables
71     SANE_Int mtrpwm;
72     MotorSlope slope1;
73     MotorSlope slope2;
74     SANE_Int fwdbwd; // forward/backward steps
75 };
76 
77 /**
78  * master motor settings, for a given motor and dpi,
79  * it gives steps and speed information
80  */
81 static Motor_Master motor_master[] = {
82     /* HP3670 motor settings */
83     {MotorId::HP3670, 50, 3, StepType::HALF, false, true, 1,
84      MotorSlope::create_from_steps(2329, 120, 229),
85      MotorSlope::create_from_steps(3399, 337, 192), 192},
86 
87     {MotorId::HP3670, 75, 3, StepType::FULL, false, true, 1,
88      MotorSlope::create_from_steps(3429, 305, 200),
89      MotorSlope::create_from_steps(3399, 337, 192), 192},
90 
91     {MotorId::HP3670, 100, 3, StepType::HALF, false, true, 1,
92      MotorSlope::create_from_steps(2905, 187, 143),
93      MotorSlope::create_from_steps(3399, 337, 192), 192},
94 
95     {MotorId::HP3670, 150, 3, StepType::HALF, false, true, 1,
96      MotorSlope::create_from_steps(3429, 305, 73),
97      MotorSlope::create_from_steps(3399, 337, 192), 192},
98 
99     {MotorId::HP3670, 300, 3, StepType::HALF, false, true, 1,
100      MotorSlope::create_from_steps(1055, 563, 11),
101      MotorSlope::create_from_steps(3399, 337, 192), 192},
102 
103     {MotorId::HP3670, 600, 3, StepType::FULL, false, true, 0,
104      MotorSlope::create_from_steps(10687, 5126, 3),
105      MotorSlope::create_from_steps(3399, 337, 192), 192},
106 
107     {MotorId::HP3670,1200, 3, StepType::HALF, false, true, 0,
108      MotorSlope::create_from_steps(15937, 6375, 3),
109      MotorSlope::create_from_steps(3399, 337, 192), 192},
110 
111     {MotorId::HP3670, 50, 1, StepType::HALF, false, true, 1,
112      MotorSlope::create_from_steps(2329, 120, 229),
113      MotorSlope::create_from_steps(3399, 337, 192), 192},
114 
115     {MotorId::HP3670, 75, 1, StepType::FULL, false, true, 1,
116      MotorSlope::create_from_steps(3429, 305, 200),
117      MotorSlope::create_from_steps(3399, 337, 192), 192},
118 
119     {MotorId::HP3670, 100, 1, StepType::HALF, false, true, 1,
120      MotorSlope::create_from_steps(2905, 187, 143),
121      MotorSlope::create_from_steps(3399, 337, 192), 192},
122 
123     {MotorId::HP3670, 150, 1, StepType::HALF, false, true, 1,
124      MotorSlope::create_from_steps(3429, 305, 73),
125      MotorSlope::create_from_steps(3399, 337, 192), 192},
126 
127     {MotorId::HP3670, 300, 1, StepType::HALF, false, true, 1,
128      MotorSlope::create_from_steps(1055, 563, 11),
129      MotorSlope::create_from_steps(3399, 337, 192), 192},
130 
131     {MotorId::HP3670, 600, 1, StepType::FULL, false, true, 0,
132      MotorSlope::create_from_steps(10687, 5126, 3),
133      MotorSlope::create_from_steps(3399, 337, 192), 192},
134 
135     {MotorId::HP3670,1200, 1, StepType::HALF, false, true, 0,
136      MotorSlope::create_from_steps(15937, 6375, 3),
137      MotorSlope::create_from_steps(3399, 337, 192), 192},
138 
139     /* HP2400/G2410 motor settings base motor dpi = 600 */
140     {MotorId::HP2400, 50, 3, StepType::FULL, false, true, 63,
141      MotorSlope::create_from_steps(8736, 601, 120),
142      MotorSlope::create_from_steps(4905, 337, 192), 192},
143 
144     {MotorId::HP2400, 100, 3, StepType::HALF, false, true, 63,
145      MotorSlope::create_from_steps(8736, 601, 120),
146      MotorSlope::create_from_steps(4905, 337, 192), 192},
147 
148     {MotorId::HP2400, 150, 3, StepType::HALF, false, true, 63,
149      MotorSlope::create_from_steps(15902, 902, 67),
150      MotorSlope::create_from_steps(4905, 337, 192), 192},
151 
152     {MotorId::HP2400, 300, 3, StepType::HALF, false, true, 63,
153      MotorSlope::create_from_steps(16703, 2188, 32),
154      MotorSlope::create_from_steps(4905, 337, 192), 192},
155 
156     {MotorId::HP2400, 600, 3, StepType::FULL, false, true, 63,
157      MotorSlope::create_from_steps(18761, 18761, 3),
158      MotorSlope::create_from_steps(4905, 627, 192), 192},
159 
160     {MotorId::HP2400,1200, 3, StepType::HALF, false, true, 63,
161      MotorSlope::create_from_steps(43501, 43501, 3),
162      MotorSlope::create_from_steps(4905, 627, 192), 192},
163 
164     {MotorId::HP2400, 50, 1, StepType::FULL, false, true, 63,
165      MotorSlope::create_from_steps(8736, 601, 120),
166      MotorSlope::create_from_steps(4905, 337, 192), 192},
167 
168     {MotorId::HP2400, 100, 1, StepType::HALF, false, true, 63,
169      MotorSlope::create_from_steps(8736, 601, 120),
170      MotorSlope::create_from_steps(4905, 337, 192), 192},
171 
172     {MotorId::HP2400, 150, 1, StepType::HALF, false, true, 63,
173      MotorSlope::create_from_steps(15902, 902, 67),
174      MotorSlope::create_from_steps(4905, 337, 192), 192},
175 
176     {MotorId::HP2400, 300, 1, StepType::HALF, false, true, 63,
177      MotorSlope::create_from_steps(16703, 2188, 32),
178      MotorSlope::create_from_steps(4905, 337, 192), 192},
179 
180     {MotorId::HP2400, 600, 1, StepType::FULL, false, true, 63,
181      MotorSlope::create_from_steps(18761, 18761, 3),
182      MotorSlope::create_from_steps(4905, 337, 192), 192},
183 
184     {MotorId::HP2400,1200, 1, StepType::HALF, false, true, 63,
185      MotorSlope::create_from_steps(43501, 43501, 3),
186      MotorSlope::create_from_steps(4905, 337, 192), 192},
187 
188     /* XP 200 motor settings */
189     {MotorId::XP200, 75, 3, StepType::HALF, true, false, 0,
190      MotorSlope::create_from_steps(6000, 2136, 4),
191      MotorSlope::create_from_steps(12000, 1200, 8), 1},
192 
193     {MotorId::XP200, 100, 3, StepType::HALF, true, false, 0,
194      MotorSlope::create_from_steps(6000, 2850, 4),
195      MotorSlope::create_from_steps(12000, 1200, 8), 1},
196 
197     {MotorId::XP200, 200, 3, StepType::HALF, true, false, 0,
198      MotorSlope::create_from_steps(6999, 5700, 4),
199      MotorSlope::create_from_steps(12000, 1200, 8), 1},
200 
201     {MotorId::XP200, 250, 3, StepType::HALF, true, false, 0,
202      MotorSlope::create_from_steps(6999, 6999, 4),
203      MotorSlope::create_from_steps(12000, 1200, 8), 1},
204 
205     {MotorId::XP200, 300, 3, StepType::HALF, true, false, 0,
206      MotorSlope::create_from_steps(13500, 13500, 4),
207      MotorSlope::create_from_steps(12000, 1200, 8), 1},
208 
209     {MotorId::XP200, 600, 3, StepType::HALF, true, true, 0,
210      MotorSlope::create_from_steps(31998, 31998, 4),
211      MotorSlope::create_from_steps(12000, 1200, 2), 1},
212 
213     {MotorId::XP200, 75, 1, StepType::HALF, true, false, 0,
214      MotorSlope::create_from_steps(6000, 2000, 4),
215      MotorSlope::create_from_steps(12000, 1200, 8), 1},
216 
217     {MotorId::XP200, 100, 1, StepType::HALF, true, false, 0,
218      MotorSlope::create_from_steps(6000, 1300, 4),
219      MotorSlope::create_from_steps(12000, 1200, 8), 1},
220 
221     {MotorId::XP200, 200, 1, StepType::HALF, true, true, 0,
222      MotorSlope::create_from_steps(6000, 3666, 4),
223      MotorSlope::create_from_steps(12000, 1200, 8), 1},
224 
225     {MotorId::XP200, 300, 1, StepType::HALF, true, false, 0,
226      MotorSlope::create_from_steps(6500, 6500, 4),
227      MotorSlope::create_from_steps(12000, 1200, 8), 1},
228 
229     {MotorId::XP200, 600, 1, StepType::HALF, true, true, 0,
230      MotorSlope::create_from_steps(24000, 24000, 4),
231      MotorSlope::create_from_steps(12000, 1200, 2), 1},
232 
233     /* HP scanjet 2300c */
234     {MotorId::HP2300, 75, 3, StepType::FULL, false, true, 63,
235      MotorSlope::create_from_steps(8139, 560, 120),
236      MotorSlope::create_from_steps(4905, 337, 120), 16},
237 
238     {MotorId::HP2300, 150, 3, StepType::HALF, false, true, 63,
239      MotorSlope::create_from_steps(7903, 543, 67),
240      MotorSlope::create_from_steps(4905, 337, 120), 16},
241 
242     {MotorId::HP2300, 300, 3, StepType::HALF, false, true, 63,
243      MotorSlope::create_from_steps(2175, 1087, 3),
244      MotorSlope::create_from_steps(4905, 337, 120), 16},
245 
246     {MotorId::HP2300, 600, 3, StepType::HALF, false, true, 63,
247      MotorSlope::create_from_steps(8700, 4350, 3),
248      MotorSlope::create_from_steps(4905, 337, 120), 16},
249 
250     {MotorId::HP2300,1200, 3, StepType::HALF, false, true, 63,
251      MotorSlope::create_from_steps(17400, 8700, 3),
252      MotorSlope::create_from_steps(4905, 337, 120), 16},
253 
254     {MotorId::HP2300, 75, 1, StepType::FULL, false, true, 63,
255      MotorSlope::create_from_steps(8139, 560, 120),
256      MotorSlope::create_from_steps(4905, 337, 120), 16},
257 
258     {MotorId::HP2300, 150, 1, StepType::HALF, false, true, 63,
259      MotorSlope::create_from_steps(7903, 543, 67),
260      MotorSlope::create_from_steps(4905, 337, 120), 16},
261 
262     {MotorId::HP2300, 300, 1, StepType::HALF, false, true, 63,
263      MotorSlope::create_from_steps(2175, 1087, 3),
264      MotorSlope::create_from_steps(4905, 337, 120), 16},
265 
266     {MotorId::HP2300, 600, 1, StepType::HALF, false, true, 63,
267      MotorSlope::create_from_steps(8700, 4350, 3),
268      MotorSlope::create_from_steps(4905, 337, 120), 16},
269 
270     {MotorId::HP2300,1200, 1, StepType::HALF, false, true, 63,
271      MotorSlope::create_from_steps(17400, 8700, 3),
272      MotorSlope::create_from_steps(4905, 337, 120), 16},
273 
274     /* non half ccd settings for 300 dpi
275     {MotorId::HP2300, 300, 3, StepType::HALF, false, true, 63,
276      MotorSlope::create_from_steps(5386, 2175, 44),
277      MotorSlope::create_from_steps(4905, 337, 120), 16},
278 
279     {MotorId::HP2300, 300, 1, StepType::HALF, false, true, 63,
280      MotorSlope::create_from_steps(5386, 2175, 44),
281      MotorSlope::create_from_steps(4905, 337, 120), 16},
282     */
283 
284     /* MD5345/6471 motor settings */
285     /* vfinal=(exposure/(1200/dpi))/step_type */
286     {MotorId::MD_5345, 50, 3, StepType::HALF, false, true, 2,
287      MotorSlope::create_from_steps(2500, 250, 255),
288      MotorSlope::create_from_steps(2000, 300, 255), 64},
289 
290     {MotorId::MD_5345, 75, 3, StepType::HALF, false, true, 2,
291      MotorSlope::create_from_steps(2500, 343, 255),
292      MotorSlope::create_from_steps(2000, 300, 255), 64},
293 
294     {MotorId::MD_5345, 100, 3, StepType::HALF, false, true, 2,
295      MotorSlope::create_from_steps(2500, 458, 255),
296      MotorSlope::create_from_steps(2000, 300, 255), 64},
297 
298     {MotorId::MD_5345, 150, 3, StepType::HALF, false, true, 2,
299      MotorSlope::create_from_steps(2500, 687, 255),
300      MotorSlope::create_from_steps(2000, 300, 255), 64},
301 
302     {MotorId::MD_5345, 200, 3, StepType::HALF, false, true, 2,
303      MotorSlope::create_from_steps(2500, 916, 255),
304      MotorSlope::create_from_steps(2000, 300, 255), 64},
305 
306     {MotorId::MD_5345, 300, 3, StepType::HALF, false, true, 2,
307      MotorSlope::create_from_steps(2500, 1375, 255),
308      MotorSlope::create_from_steps(2000, 300, 255), 64},
309 
310     {MotorId::MD_5345, 400, 3, StepType::HALF, false, true, 0,
311      MotorSlope::create_from_steps(2000, 1833, 32),
312      MotorSlope::create_from_steps(2000, 300, 255), 32},
313 
314     {MotorId::MD_5345, 500, 3, StepType::HALF, false, true, 0,
315      MotorSlope::create_from_steps(2291, 2291, 32),
316      MotorSlope::create_from_steps(2000, 300, 255), 32},
317 
318     {MotorId::MD_5345, 600, 3, StepType::HALF, false, true, 0,
319      MotorSlope::create_from_steps(2750, 2750, 32),
320      MotorSlope::create_from_steps(2000, 300, 255), 32},
321 
322     {MotorId::MD_5345, 1200, 3, StepType::QUARTER, false, true, 0,
323      MotorSlope::create_from_steps(2750, 2750, 16),
324      MotorSlope::create_from_steps(2000, 300, 255), 146},
325 
326     {MotorId::MD_5345, 2400, 3, StepType::QUARTER, false, true, 0,
327      MotorSlope::create_from_steps(5500, 5500, 16),
328      MotorSlope::create_from_steps(2000, 300, 255), 146},
329 
330     {MotorId::MD_5345, 50, 1, StepType::HALF, false, true, 2,
331      MotorSlope::create_from_steps(2500, 250, 255),
332      MotorSlope::create_from_steps(2000, 300, 255), 64},
333 
334     {MotorId::MD_5345, 75, 1, StepType::HALF, false, true, 2,
335      MotorSlope::create_from_steps(2500, 343, 255),
336      MotorSlope::create_from_steps(2000, 300, 255), 64},
337 
338     {MotorId::MD_5345, 100, 1, StepType::HALF, false, true, 2,
339      MotorSlope::create_from_steps(2500, 458, 255),
340      MotorSlope::create_from_steps(2000, 300, 255), 64},
341 
342     {MotorId::MD_5345, 150, 1, StepType::HALF, false, true, 2,
343      MotorSlope::create_from_steps(2500, 687, 255),
344      MotorSlope::create_from_steps(2000, 300, 255), 64},
345 
346     {MotorId::MD_5345, 200, 1, StepType::HALF, false, true, 2,
347      MotorSlope::create_from_steps(2500, 916, 255),
348      MotorSlope::create_from_steps(2000, 300, 255), 64},
349 
350     {MotorId::MD_5345, 300, 1, StepType::HALF, false, true, 2,
351      MotorSlope::create_from_steps(2500, 1375, 255),
352      MotorSlope::create_from_steps(2000, 300, 255), 64},
353 
354     {MotorId::MD_5345, 400, 1, StepType::HALF, false, true, 0,
355      MotorSlope::create_from_steps(2000, 1833, 32),
356      MotorSlope::create_from_steps(2000, 300, 255), 32},
357 
358     {MotorId::MD_5345, 500, 1, StepType::HALF, false, true, 0,
359      MotorSlope::create_from_steps(2291, 2291, 32),
360      MotorSlope::create_from_steps(2000, 300, 255), 32},
361 
362     {MotorId::MD_5345, 600, 1, StepType::HALF, false, true, 0,
363      MotorSlope::create_from_steps(2750, 2750, 32),
364      MotorSlope::create_from_steps(2000, 300, 255), 32},
365 
366     {MotorId::MD_5345, 1200, 1, StepType::QUARTER, false, true, 0,
367      MotorSlope::create_from_steps(2750, 2750, 16),
368      MotorSlope::create_from_steps(2000, 300, 255), 146},
369 
370     {MotorId::MD_5345, 2400, 1, StepType::QUARTER, false, true, 0,
371      MotorSlope::create_from_steps(5500, 5500, 16),
372      MotorSlope::create_from_steps(2000, 300, 255), 146}, /* 5500 guessed */
373 };
374 
375 /**
376  * reads value from gpio endpoint
377  */
gl646_gpio_read(IUsbDevice& usb_dev, std::uint8_t* value)378 static void gl646_gpio_read(IUsbDevice& usb_dev, std::uint8_t* value)
379 {
380     DBG_HELPER(dbg);
381     usb_dev.control_msg(REQUEST_TYPE_IN, REQUEST_REGISTER, GPIO_READ, INDEX, 1, value);
382 }
383 
384 /**
385  * writes the given value to gpio endpoint
386  */
gl646_gpio_write(IUsbDevice& usb_dev, std::uint8_t value)387 static void gl646_gpio_write(IUsbDevice& usb_dev, std::uint8_t value)
388 {
389     DBG_HELPER_ARGS(dbg, "(0x%02x)", value);
390     usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, GPIO_WRITE, INDEX, 1, &value);
391 }
392 
393 /**
394  * writes the given value to gpio output enable endpoint
395  */
gl646_gpio_output_enable(IUsbDevice& usb_dev, std::uint8_t value)396 static void gl646_gpio_output_enable(IUsbDevice& usb_dev, std::uint8_t value)
397 {
398     DBG_HELPER_ARGS(dbg, "(0x%02x)", value);
399     usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, GPIO_OUTPUT_ENABLE, INDEX, 1, &value);
400 }
401 
402 /**
403  * stop scanner's motor
404  * @param dev scanner's device
405  */
gl646_stop_motor(Genesys_Device* dev)406 static void gl646_stop_motor(Genesys_Device* dev)
407 {
408     DBG_HELPER(dbg);
409     dev->interface->write_register(0x0f, 0x00);
410 }
411 
412 /**
413  * Returns the cksel values used by the required scan mode.
414  * @param sensor id of the sensor
415  * @param required required resolution
416  * @param color true is color mode
417  * @return cksel value for mode
418  */
get_cksel(SensorId sensor_id, int required, unsigned channels)419 static int get_cksel(SensorId sensor_id, int required, unsigned channels)
420 {
421     for (const auto& sensor : *s_sensors) {
422         // exit on perfect match
423         if (sensor.sensor_id == sensor_id && sensor.resolutions.matches(required) &&
424             sensor.matches_channel_count(channels))
425         {
426             unsigned cksel = sensor.ccd_pixels_per_system_pixel();
427             return cksel;
428         }
429     }
430   DBG(DBG_error, "%s: failed to find match for %d dpi\n", __func__, required);
431   /* fail safe fallback */
432   return 1;
433 }
434 
init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* regs, const ScanSession& session) const435 void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,
436                                                  Genesys_Register_Set* regs,
437                                                  const ScanSession& session) const
438 {
439     DBG_HELPER(dbg);
440     session.assert_computed();
441 
442     debug_dump(DBG_info, sensor);
443 
444     std::uint32_t move = session.params.starty;
445 
446   Motor_Master *motor = nullptr;
447     std::uint32_t z1, z2;
448   int feedl;
449 
450 
451   /* for the given resolution, search for master
452    * motor mode setting */
453     for (unsigned i = 0; i < sizeof (motor_master) / sizeof (Motor_Master); ++i) {
454         if (dev->model->motor_id == motor_master[i].motor_id &&
455             motor_master[i].dpi == session.params.yres &&
456             motor_master[i].channels == session.params.channels)
457         {
458             motor = &motor_master[i];
459         }
460     }
461     if (motor == nullptr) {
462         throw SaneException("unable to find settings for motor %d at %d dpi, color=%d",
463                             static_cast<unsigned>(dev->model->motor_id),
464                             session.params.yres, session.params.channels);
465     }
466 
467     scanner_setup_sensor(*dev, sensor, *regs);
468 
469   /* now generate slope tables : we are not using generate_slope_table3 yet */
470     auto slope_table1 = create_slope_table_for_speed(motor->slope1, motor->slope1.max_speed_w,
471                                                      StepType::FULL, 1, 4,
472                                                      get_slope_table_max_size(AsicType::GL646));
473     auto slope_table2 = create_slope_table_for_speed(motor->slope2, motor->slope2.max_speed_w,
474                                                      StepType::FULL, 1, 4,
475                                                      get_slope_table_max_size(AsicType::GL646));
476 
477   /* R01 */
478   /* now setup other registers for final scan (ie with shading enabled) */
479   /* watch dog + shading + scan enable */
480     regs->find_reg(0x01).value |= REG_0x01_DOGENB | REG_0x01_SCAN;
481     if (dev->model->is_cis) {
482         regs->find_reg(0x01).value |= REG_0x01_CISSET;
483     } else {
484         regs->find_reg(0x01).value &= ~REG_0x01_CISSET;
485     }
486 
487     // if device has no calibration, don't enable shading correction
488     if (has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) ||
489         has_flag(session.params.flags, ScanFlag::DISABLE_SHADING))
490     {
491         regs->find_reg(0x01).value &= ~REG_0x01_DVDSET;
492     } else {
493         regs->find_reg(0x01).value |= REG_0x01_DVDSET;
494     }
495 
496     regs->find_reg(0x01).value &= ~REG_0x01_FASTMOD;
497     if (motor->fastmod) {
498         regs->find_reg(0x01).value |= REG_0x01_FASTMOD;
499     }
500 
501   /* R02 */
502   /* allow moving when buffer full by default */
503     if (!dev->model->is_sheetfed) {
504         dev->reg.find_reg(0x02).value &= ~REG_0x02_ACDCDIS;
505     } else {
506         dev->reg.find_reg(0x02).value |= REG_0x02_ACDCDIS;
507     }
508 
509   /* setup motor power and direction */
510   sanei_genesys_set_motor_power(*regs, true);
511 
512     if (has_flag(session.params.flags, ScanFlag::REVERSE)) {
513         regs->find_reg(0x02).value |= REG_0x02_MTRREV;
514     } else {
515         regs->find_reg(0x02).value &= ~REG_0x02_MTRREV;
516     }
517 
518   /* fastfed enabled (2 motor slope tables) */
519     if (motor->fastfed) {
520         regs->find_reg(0x02).value |= REG_0x02_FASTFED;
521     } else {
522         regs->find_reg(0x02).value &= ~REG_0x02_FASTFED;
523     }
524 
525   /* step type */
526     regs->find_reg(0x02).value &= ~REG_0x02_STEPSEL;
527   switch (motor->steptype)
528     {
529     case StepType::FULL:
530       break;
531     case StepType::HALF:
532       regs->find_reg(0x02).value |= 1;
533       break;
534     case StepType::QUARTER:
535       regs->find_reg(0x02).value |= 2;
536       break;
537     default:
538       regs->find_reg(0x02).value |= 3;
539       break;
540     }
541 
542     if (dev->model->is_sheetfed || !has_flag(session.params.flags, ScanFlag::AUTO_GO_HOME)) {
543         regs->find_reg(0x02).value &= ~REG_0x02_AGOHOME;
544     } else {
545         regs->find_reg(0x02).value |= REG_0x02_AGOHOME;
546     }
547 
548   /* R03 */
549     regs->find_reg(0x03).value &= ~REG_0x03_AVEENB;
550     // regs->find_reg(0x03).value |= REG_0x03_AVEENB;
551     regs->find_reg(0x03).value &= ~REG_0x03_LAMPDOG;
552 
553   /* select XPA */
554     regs->find_reg(0x03).value &= ~REG_0x03_XPASEL;
555     if ((session.params.flags & ScanFlag::USE_XPA) != ScanFlag::NONE) {
556         regs->find_reg(0x03).value |= REG_0x03_XPASEL;
557     }
558     regs->state.is_xpa_on = (session.params.flags & ScanFlag::USE_XPA) != ScanFlag::NONE;
559 
560   /* R04 */
561   /* monochrome / color scan */
562     switch (session.params.depth) {
563     case 8:
564             regs->find_reg(0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET);
565             break;
566     case 16:
567             regs->find_reg(0x04).value &= ~REG_0x04_LINEART;
568             regs->find_reg(0x04).value |= REG_0x04_BITSET;
569             break;
570     }
571 
572     sanei_genesys_set_dpihw(*regs, sensor.full_resolution);
573 
574   /* gamma enable for scans */
575     if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) {
576         regs->find_reg(0x05).value |= REG_0x05_GMM14BIT;
577     }
578 
579     if (!has_flag(session.params.flags, ScanFlag::DISABLE_GAMMA) &&
580         session.params.depth < 16)
581     {
582         regs->find_reg(REG_0x05).value |= REG_0x05_GMMENB;
583     } else {
584         regs->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB;
585     }
586 
587   /* true CIS gray if needed */
588     if (dev->model->is_cis && session.params.channels == 1 &&
589         session.params.color_filter == ColorFilter::NONE)
590     {
591         regs->find_reg(0x05).value |= REG_0x05_LEDADD;
592     } else {
593         regs->find_reg(0x05).value &= ~REG_0x05_LEDADD;
594     }
595 
596   /* HP2400 1200dpi mode tuning */
597 
598     if (dev->model->sensor_id == SensorId::CCD_HP2400) {
599       /* reset count of dummy lines to zero */
600         regs->find_reg(0x1e).value &= ~REG_0x1E_LINESEL;
601         if (session.params.xres >= 1200) {
602           /* there must be one dummy line */
603             regs->find_reg(0x1e).value |= 1 & REG_0x1E_LINESEL;
604 
605           /* GPO12 need to be set to zero */
606           regs->find_reg(0x66).value &= ~0x20;
607         }
608         else
609         {
610           /* set GPO12 back to one */
611           regs->find_reg(0x66).value |= 0x20;
612         }
613     }
614 
615   /* motor steps used */
616     unsigned forward_steps = motor->fwdbwd;
617     unsigned backward_steps = motor->fwdbwd;
618 
619     // the steps count must be different by at most 128, otherwise it's impossible to construct
620     // a proper backtracking curve. We're using slightly lower limit to allow at least a minimum
621     // distance between accelerations (forward_steps, backward_steps)
622     if (slope_table1.table.size() > slope_table2.table.size() + 100) {
623         slope_table2.expand_table(slope_table1.table.size() - 100, 1);
624     }
625     if (slope_table2.table.size() > slope_table1.table.size() + 100) {
626         slope_table1.expand_table(slope_table2.table.size() - 100, 1);
627     }
628 
629     if (slope_table1.table.size() >= slope_table2.table.size()) {
630         backward_steps += (slope_table1.table.size() - slope_table2.table.size()) * 2;
631     } else {
632         forward_steps += (slope_table2.table.size() - slope_table1.table.size()) * 2;
633     }
634 
635     if (forward_steps > 255) {
636         if (backward_steps < (forward_steps - 255)) {
637             throw SaneException("Can't set backtracking parameters without skipping image");
638         }
639         backward_steps -= forward_steps - 255;
640     }
641     if (backward_steps > 255) {
642         if (forward_steps < (backward_steps - 255)) {
643             throw SaneException("Can't set backtracking parameters without skipping image");
644         }
645         forward_steps -= backward_steps - 255;
646     }
647 
648     regs->find_reg(0x21).value = slope_table1.table.size();
649     regs->find_reg(0x24).value = slope_table2.table.size();
650     regs->find_reg(0x22).value = forward_steps;
651     regs->find_reg(0x23).value = backward_steps;
652 
653   /* CIS scanners read one line per color channel
654    * since gray mode use 'add' we also read 3 channels even not in
655    * color mode */
656     if (dev->model->is_cis) {
657         regs->set24(REG_LINCNT, session.output_line_count * 3);
658     } else {
659         regs->set24(REG_LINCNT, session.output_line_count);
660     }
661 
662     regs->set16(REG_STRPIXEL, session.pixel_startx);
663     regs->set16(REG_ENDPIXEL, session.pixel_endx);
664 
665     regs->set24(REG_MAXWD, session.output_line_bytes);
666 
667     // FIXME: the incoming sensor is selected for incorrect resolution
668     const auto& dpiset_sensor = sanei_genesys_find_sensor(dev, session.params.xres,
669                                                           session.params.channels,
670                                                           session.params.scan_method);
671     regs->set16(REG_DPISET, dpiset_sensor.register_dpiset);
672     regs->set16(REG_LPERIOD, sensor.exposure_lperiod);
673 
674   /* move distance must be adjusted to take into account the extra lines
675    * read to reorder data */
676   feedl = move;
677 
678     if (session.num_staggered_lines + session.max_color_shift_lines > 0 && feedl != 0) {
679         unsigned total_lines = session.max_color_shift_lines + session.num_staggered_lines;
680         int feed_offset = (total_lines * dev->motor.base_ydpi) / motor->dpi;
681         if (feedl > feed_offset) {
682             feedl = feedl - feed_offset;
683         }
684     }
685 
686   /* we assume all scans are done with 2 tables */
687   /*
688      feedl = feed_steps - fast_slope_steps*2 -
689      (slow_slope_steps >> scan_step_type); */
690   /* but head has moved due to shading calibration => dev->scanhead_position_primary */
691   if (feedl > 0)
692     {
693       /* TODO clean up this when I'll fully understand.
694        * for now, special casing each motor */
695         switch (dev->model->motor_id) {
696             case MotorId::MD_5345:
697                     switch (motor->dpi) {
698 	    case 200:
699 	      feedl -= 70;
700 	      break;
701 	    case 300:
702 	      feedl -= 70;
703 	      break;
704 	    case 400:
705 	      feedl += 130;
706 	      break;
707 	    case 600:
708 	      feedl += 160;
709 	      break;
710 	    case 1200:
711 	      feedl += 160;
712 	      break;
713 	    case 2400:
714 	      feedl += 180;
715 	      break;
716 	    default:
717 	      break;
718 	    }
719 	  break;
720             case MotorId::HP2300:
721                     switch (motor->dpi) {
722 	    case 75:
723 	      feedl -= 180;
724 	      break;
725 	    case 150:
726 	      feedl += 0;
727 	      break;
728 	    case 300:
729 	      feedl += 30;
730 	      break;
731 	    case 600:
732 	      feedl += 35;
733 	      break;
734 	    case 1200:
735 	      feedl += 45;
736 	      break;
737 	    default:
738 	      break;
739 	    }
740 	  break;
741             case MotorId::HP2400:
742                     switch (motor->dpi) {
743 	    case 150:
744 	      feedl += 150;
745 	      break;
746 	    case 300:
747 	      feedl += 220;
748 	      break;
749 	    case 600:
750 	      feedl += 260;
751 	      break;
752 	    case 1200:
753 	      feedl += 280; /* 300 */
754 	      break;
755 	    case 50:
756 	      feedl += 0;
757 	      break;
758 	    case 100:
759 	      feedl += 100;
760 	      break;
761 	    default:
762 	      break;
763 	    }
764 	  break;
765 
766 	  /* theorical value */
767         default: {
768             unsigned step_shift = static_cast<unsigned>(motor->steptype);
769 
770 	  if (motor->fastfed)
771         {
772                 feedl = feedl - 2 * slope_table2.table.size() -
773                         (slope_table1.table.size() >> step_shift);
774 	    }
775 	  else
776 	    {
777                 feedl = feedl - (slope_table1.table.size() >> step_shift);
778 	    }
779 	  break;
780         }
781 	}
782       /* security */
783       if (feedl < 0)
784 	feedl = 0;
785     }
786 
787     regs->set24(REG_FEEDL, feedl);
788 
789   regs->find_reg(0x65).value = motor->mtrpwm;
790 
791     sanei_genesys_calculate_zmod(regs->find_reg(0x02).value & REG_0x02_FASTFED,
792                                  sensor.exposure_lperiod,
793                                  slope_table1.table,
794                                  slope_table1.table.size(),
795                                   move, motor->fwdbwd, &z1, &z2);
796 
797   /* no z1/z2 for sheetfed scanners */
798     if (dev->model->is_sheetfed) {
799       z1 = 0;
800       z2 = 0;
801     }
802     regs->set16(REG_Z1MOD, z1);
803     regs->set16(REG_Z2MOD, z2);
804     regs->find_reg(0x6b).value = slope_table2.table.size();
805   regs->find_reg(0x6c).value =
806     (regs->find_reg(0x6c).value & REG_0x6C_TGTIME) | ((z1 >> 13) & 0x38) | ((z2 >> 16)
807 								   & 0x07);
808 
809     write_control(dev, sensor, session.output_resolution);
810 
811     // setup analog frontend
812     gl646_set_fe(dev, sensor, AFE_SET, session.output_resolution);
813 
814     setup_image_pipeline(*dev, session);
815 
816     dev->read_active = true;
817 
818     dev->session = session;
819 
820     dev->total_bytes_read = 0;
821     dev->total_bytes_to_read = (size_t) session.output_line_bytes_requested
822           * (size_t) session.params.lines;
823 
824     /* select color filter based on settings */
825     regs->find_reg(0x04).value &= ~REG_0x04_FILTER;
826     if (session.params.channels == 1) {
827         switch (session.params.color_filter) {
828             case ColorFilter::RED:
829                 regs->find_reg(0x04).value |= 0x04;
830                 break;
831             case ColorFilter::GREEN:
832                 regs->find_reg(0x04).value |= 0x08;
833                 break;
834             case ColorFilter::BLUE:
835                 regs->find_reg(0x04).value |= 0x0c;
836                 break;
837             default:
838                 break;
839         }
840     }
841 
842     scanner_send_slope_table(dev, sensor, 0, slope_table1.table);
843     scanner_send_slope_table(dev, sensor, 1, slope_table2.table);
844 }
845 
846 /**
847  * Set all registers to default values after init
848  * @param dev scannerr's device to set
849  */
850 static void
gl646_init_regs(Genesys_Device * dev)851 gl646_init_regs (Genesys_Device * dev)
852 {
853   int addr;
854 
855   DBG(DBG_proc, "%s\n", __func__);
856 
857     dev->reg.clear();
858 
859     for (addr = 1; addr <= 0x0b; addr++)
860         dev->reg.init_reg(addr, 0);
861     for (addr = 0x10; addr <= 0x29; addr++)
862         dev->reg.init_reg(addr, 0);
863     for (addr = 0x2c; addr <= 0x39; addr++)
864         dev->reg.init_reg(addr, 0);
865     for (addr = 0x3d; addr <= 0x3f; addr++)
866         dev->reg.init_reg(addr, 0);
867     for (addr = 0x52; addr <= 0x5e; addr++)
868         dev->reg.init_reg(addr, 0);
869     for (addr = 0x60; addr <= 0x6d; addr++)
870         dev->reg.init_reg(addr, 0);
871 
872     dev->reg.find_reg(0x01).value = 0x20 /*0x22 */ ;	/* enable shading, CCD, color, 1M */
873     dev->reg.find_reg(0x02).value = 0x30 /*0x38 */ ;	/* auto home, one-table-move, full step */
874     if (dev->model->motor_id == MotorId::MD_5345) {
875         dev->reg.find_reg(0x02).value |= 0x01; // half-step
876     }
877     switch (dev->model->motor_id) {
878         case MotorId::MD_5345:
879       dev->reg.find_reg(0x02).value |= 0x01;	/* half-step */
880       break;
881         case MotorId::XP200:
882       /* for this sheetfed scanner, no AGOHOME, nor backtracking */
883       dev->reg.find_reg(0x02).value = 0x50;
884       break;
885         default:
886       break;
887     }
888     dev->reg.find_reg(0x03).value = 0x1f /*0x17 */ ;	/* lamp on */
889     dev->reg.find_reg(0x04).value = 0x13 /*0x03 */ ;	/* 8 bits data, 16 bits A/D, color, Wolfson fe *//* todo: according to spec, 0x0 is reserved? */
890   switch (dev->model->adc_id)
891     {
892     case AdcId::AD_XP200:
893       dev->reg.find_reg(0x04).value = 0x12;
894       break;
895     default:
896       /* Wolfson frontend */
897       dev->reg.find_reg(0x04).value = 0x13;
898       break;
899     }
900 
901   const auto& sensor = sanei_genesys_find_sensor_any(dev);
902 
903   dev->reg.find_reg(0x05).value = 0x00;	/* 12 bits gamma, disable gamma, 24 clocks/pixel */
904     sanei_genesys_set_dpihw(dev->reg, sensor.full_resolution);
905 
906     if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) {
907         dev->reg.find_reg(0x05).value |= REG_0x05_GMM14BIT;
908     }
909     if (dev->model->adc_id == AdcId::AD_XP200) {
910         dev->reg.find_reg(0x05).value |= 0x01;	/* 12 clocks/pixel */
911     }
912 
913     if (dev->model->sensor_id == SensorId::CCD_HP2300) {
914         dev->reg.find_reg(0x06).value = 0x00; // PWRBIT off, shading gain=4, normal AFE image capture
915     } else {
916         dev->reg.find_reg(0x06).value = 0x18; // PWRBIT on, shading gain=8, normal AFE image capture
917     }
918 
919     scanner_setup_sensor(*dev, sensor, dev->reg);
920 
921   dev->reg.find_reg(0x1e).value = 0xf0;	/* watch-dog time */
922 
923   switch (dev->model->sensor_id)
924     {
925     case SensorId::CCD_HP2300:
926       dev->reg.find_reg(0x1e).value = 0xf0;
927       dev->reg.find_reg(0x1f).value = 0x10;
928       dev->reg.find_reg(0x20).value = 0x20;
929       break;
930     case SensorId::CCD_HP2400:
931       dev->reg.find_reg(0x1e).value = 0x80;
932       dev->reg.find_reg(0x1f).value = 0x10;
933       dev->reg.find_reg(0x20).value = 0x20;
934       break;
935     case SensorId::CCD_HP3670:
936       dev->reg.find_reg(0x19).value = 0x2a;
937       dev->reg.find_reg(0x1e).value = 0x80;
938       dev->reg.find_reg(0x1f).value = 0x10;
939       dev->reg.find_reg(0x20).value = 0x20;
940       break;
941     case SensorId::CIS_XP200:
942       dev->reg.find_reg(0x1e).value = 0x10;
943       dev->reg.find_reg(0x1f).value = 0x01;
944       dev->reg.find_reg(0x20).value = 0x50;
945       break;
946     default:
947       dev->reg.find_reg(0x1f).value = 0x01;
948       dev->reg.find_reg(0x20).value = 0x50;
949       break;
950     }
951 
952   dev->reg.find_reg(0x21).value = 0x08 /*0x20 */ ;	/* table one steps number for forward slope curve of the acc/dec */
953   dev->reg.find_reg(0x22).value = 0x10 /*0x08 */ ;	/* steps number of the forward steps for start/stop */
954   dev->reg.find_reg(0x23).value = 0x10 /*0x08 */ ;	/* steps number of the backward steps for start/stop */
955   dev->reg.find_reg(0x24).value = 0x08 /*0x20 */ ;	/* table one steps number backward slope curve of the acc/dec */
956   dev->reg.find_reg(0x25).value = 0x00;	/* scan line numbers (7000) */
957   dev->reg.find_reg(0x26).value = 0x00 /*0x1b */ ;
958   dev->reg.find_reg(0x27).value = 0xd4 /*0x58 */ ;
959   dev->reg.find_reg(0x28).value = 0x01;	/* PWM duty for lamp control */
960   dev->reg.find_reg(0x29).value = 0xff;
961 
962   dev->reg.find_reg(0x2c).value = 0x02;	/* set resolution (600 DPI) */
963   dev->reg.find_reg(0x2d).value = 0x58;
964   dev->reg.find_reg(0x2e).value = 0x78;	/* set black&white threshold high level */
965   dev->reg.find_reg(0x2f).value = 0x7f;	/* set black&white threshold low level */
966 
967   dev->reg.find_reg(0x30).value = 0x00;	/* begin pixel position (16) */
968   dev->reg.find_reg(0x31).value = sensor.dummy_pixel /*0x10 */ ;	/* TGW + 2*TG_SHLD + x  */
969   dev->reg.find_reg(0x32).value = 0x2a /*0x15 */ ;	/* end pixel position (5390) */
970   dev->reg.find_reg(0x33).value = 0xf8 /*0x0e */ ;	/* TGW + 2*TG_SHLD + y   */
971   dev->reg.find_reg(0x34).value = sensor.dummy_pixel;
972   dev->reg.find_reg(0x35).value = 0x01 /*0x00 */ ;	/* set maximum word size per line, for buffer full control (10800) */
973   dev->reg.find_reg(0x36).value = 0x00 /*0x2a */ ;
974   dev->reg.find_reg(0x37).value = 0x00 /*0x30 */ ;
975   dev->reg.find_reg(0x38).value = 0x2a; // line period (exposure time = 11000 pixels) */
976   dev->reg.find_reg(0x39).value = 0xf8;
977   dev->reg.find_reg(0x3d).value = 0x00;	/* set feed steps number of motor move */
978   dev->reg.find_reg(0x3e).value = 0x00;
979   dev->reg.find_reg(0x3f).value = 0x01 /*0x00 */ ;
980 
981   dev->reg.find_reg(0x60).value = 0x00;	/* Z1MOD, 60h:61h:(6D b5:b3), remainder for start/stop */
982   dev->reg.find_reg(0x61).value = 0x00;	/* (21h+22h)/LPeriod */
983   dev->reg.find_reg(0x62).value = 0x00;	/* Z2MODE, 62h:63h:(6D b2:b0), remainder for start scan */
984   dev->reg.find_reg(0x63).value = 0x00;	/* (3Dh+3Eh+3Fh)/LPeriod for one-table mode,(21h+1Fh)/LPeriod */
985   dev->reg.find_reg(0x64).value = 0x00;	/* motor PWM frequency */
986   dev->reg.find_reg(0x65).value = 0x00;	/* PWM duty cycle for table one motor phase (63 = max) */
987     if (dev->model->motor_id == MotorId::MD_5345) {
988         // PWM duty cycle for table one motor phase (63 = max)
989         dev->reg.find_reg(0x65).value = 0x02;
990     }
991 
992     for (const auto& reg : dev->gpo.regs) {
993         dev->reg.set8(reg.address, reg.value);
994     }
995 
996     switch (dev->model->motor_id) {
997         case MotorId::HP2300:
998         case MotorId::HP2400:
999       dev->reg.find_reg(0x6a).value = 0x7f;	/* table two steps number for acc/dec */
1000       dev->reg.find_reg(0x6b).value = 0x78;	/* table two steps number for acc/dec */
1001       dev->reg.find_reg(0x6d).value = 0x7f;
1002       break;
1003         case MotorId::MD_5345:
1004       dev->reg.find_reg(0x6a).value = 0x42;	/* table two fast moving step type, PWM duty for table two */
1005       dev->reg.find_reg(0x6b).value = 0xff;	/* table two steps number for acc/dec */
1006       dev->reg.find_reg(0x6d).value = 0x41;	/* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */
1007       break;
1008         case MotorId::XP200:
1009       dev->reg.find_reg(0x6a).value = 0x7f;	/* table two fast moving step type, PWM duty for table two */
1010       dev->reg.find_reg(0x6b).value = 0x08;	/* table two steps number for acc/dec */
1011       dev->reg.find_reg(0x6d).value = 0x01;	/* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */
1012       break;
1013         case MotorId::HP3670:
1014       dev->reg.find_reg(0x6a).value = 0x41;	/* table two steps number for acc/dec */
1015       dev->reg.find_reg(0x6b).value = 0xc8;	/* table two steps number for acc/dec */
1016       dev->reg.find_reg(0x6d).value = 0x7f;
1017       break;
1018         default:
1019       dev->reg.find_reg(0x6a).value = 0x40;	/* table two fast moving step type, PWM duty for table two */
1020       dev->reg.find_reg(0x6b).value = 0xff;	/* table two steps number for acc/dec */
1021       dev->reg.find_reg(0x6d).value = 0x01;	/* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */
1022       break;
1023     }
1024   dev->reg.find_reg(0x6c).value = 0x00;	/* period times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE (one period time) */
1025 }
1026 
1027 // Set values of Analog Device type frontend
gl646_set_ad_fe(Genesys_Device* dev, std::uint8_t set)1028 static void gl646_set_ad_fe(Genesys_Device* dev, std::uint8_t set)
1029 {
1030     DBG_HELPER(dbg);
1031   int i;
1032 
1033     if (set == AFE_INIT) {
1034 
1035         dev->frontend = dev->frontend_initial;
1036 
1037         // write them to analog frontend
1038         dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00));
1039         dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01));
1040     }
1041   if (set == AFE_SET)
1042     {
1043         for (i = 0; i < 3; i++) {
1044             dev->interface->write_fe_register(0x02 + i, dev->frontend.get_gain(i));
1045         }
1046         for (i = 0; i < 3; i++) {
1047             dev->interface->write_fe_register(0x05 + i, dev->frontend.get_offset(i));
1048         }
1049     }
1050   /*
1051      if (set == AFE_POWER_SAVE)
1052      {
1053         dev->interface->write_fe_register(0x00, dev->frontend.reg[0] | 0x04);
1054      } */
1055 }
1056 
1057 /** set up analog frontend
1058  * set up analog frontend
1059  * @param dev device to set up
1060  * @param set action from AFE_SET, AFE_INIT and AFE_POWERSAVE
1061  * @param dpi resolution of the scan since it affects settings
1062  */
gl646_wm_hp3670(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set, unsigned dpi)1063 static void gl646_wm_hp3670(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set,
1064                             unsigned dpi)
1065 {
1066     DBG_HELPER(dbg);
1067   int i;
1068 
1069   switch (set)
1070     {
1071     case AFE_INIT:
1072         dev->interface->write_fe_register(0x04, 0x80);
1073         dev->interface->sleep_ms(200);
1074     dev->interface->write_register(0x50, 0x00);
1075       dev->frontend = dev->frontend_initial;
1076         dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01));
1077         dev->interface->write_fe_register(0x02, dev->frontend.regs.get_value(0x02));
1078         gl646_gpio_output_enable(dev->interface->get_usb_device(), 0x07);
1079       break;
1080     case AFE_POWER_SAVE:
1081         dev->interface->write_fe_register(0x01, 0x06);
1082         dev->interface->write_fe_register(0x06, 0x0f);
1083             return;
1084       break;
1085     default:			/* AFE_SET */
1086       /* mode setup */
1087       i = dev->frontend.regs.get_value(0x03);
1088             if (dpi > sensor.full_resolution / 2) {
1089       /* fe_reg_0x03 must be 0x12 for 1200 dpi in WOLFSON_HP3670.
1090        * WOLFSON_HP2400 in 1200 dpi mode works well with
1091 	   * fe_reg_0x03 set to 0x32 or 0x12 but not to 0x02 */
1092 	  i = 0x12;
1093 	}
1094         dev->interface->write_fe_register(0x03, i);
1095       /* offset and sign (or msb/lsb ?) */
1096         for (i = 0; i < 3; i++) {
1097             dev->interface->write_fe_register(0x20 + i, dev->frontend.get_offset(i));
1098             dev->interface->write_fe_register(0x24 + i, dev->frontend.regs.get_value(0x24 + i));
1099         }
1100 
1101         // gain
1102         for (i = 0; i < 3; i++) {
1103             dev->interface->write_fe_register(0x28 + i, dev->frontend.get_gain(i));
1104         }
1105     }
1106 }
1107 
1108 /** Set values of analog frontend
1109  * @param dev device to set
1110  * @param set action to execute
1111  * @param dpi dpi to setup the AFE
1112  */
gl646_set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set, int dpi)1113 static void gl646_set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set,
1114                          int dpi)
1115 {
1116     DBG_HELPER_ARGS(dbg, "%s,%d", set == AFE_INIT ? "init" :
1117                                   set == AFE_SET ? "set" :
1118                                   set == AFE_POWER_SAVE ? "powersave" : "huh?", dpi);
1119   int i;
1120     std::uint8_t val;
1121 
1122   /* Analog Device type frontend */
1123     std::uint8_t frontend_type = dev->reg.find_reg(0x04).value & REG_0x04_FESET;
1124     if (frontend_type == 0x02) {
1125         gl646_set_ad_fe(dev, set);
1126         return;
1127     }
1128 
1129   /* Wolfson type frontend */
1130     if (frontend_type != 0x03) {
1131         throw SaneException("unsupported frontend type %d", frontend_type);
1132     }
1133 
1134   /* per frontend function to keep code clean */
1135   switch (dev->model->adc_id)
1136     {
1137     case AdcId::WOLFSON_HP3670:
1138     case AdcId::WOLFSON_HP2400:
1139             gl646_wm_hp3670(dev, sensor, set, dpi);
1140             return;
1141     default:
1142       DBG(DBG_proc, "%s(): using old method\n", __func__);
1143       break;
1144     }
1145 
1146   /* initialize analog frontend */
1147     if (set == AFE_INIT) {
1148         dev->frontend = dev->frontend_initial;
1149 
1150         // reset only done on init
1151         dev->interface->write_fe_register(0x04, 0x80);
1152 
1153       /* enable GPIO for some models */
1154         if (dev->model->sensor_id == SensorId::CCD_HP2300) {
1155 	  val = 0x07;
1156             gl646_gpio_output_enable(dev->interface->get_usb_device(), val);
1157 	}
1158         return;
1159     }
1160 
1161     // set fontend to power saving mode
1162     if (set == AFE_POWER_SAVE) {
1163         dev->interface->write_fe_register(0x01, 0x02);
1164         return;
1165     }
1166 
1167   /* here starts AFE_SET */
1168   /* TODO :  base this test on cfg reg3 or a CCD family flag to be created */
1169   /* if (dev->model->ccd_type != SensorId::CCD_HP2300
1170      && dev->model->ccd_type != SensorId::CCD_HP3670
1171      && dev->model->ccd_type != SensorId::CCD_HP2400) */
1172   {
1173         dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00));
1174         dev->interface->write_fe_register(0x02, dev->frontend.regs.get_value(0x02));
1175   }
1176 
1177     // start with reg3
1178     dev->interface->write_fe_register(0x03, dev->frontend.regs.get_value(0x03));
1179 
1180   switch (dev->model->sensor_id)
1181     {
1182     default:
1183             for (i = 0; i < 3; i++) {
1184                 dev->interface->write_fe_register(0x24 + i, dev->frontend.regs.get_value(0x24 + i));
1185                 dev->interface->write_fe_register(0x28 + i, dev->frontend.get_gain(i));
1186                 dev->interface->write_fe_register(0x20 + i, dev->frontend.get_offset(i));
1187             }
1188       break;
1189       /* just can't have it to work ....
1190          case SensorId::CCD_HP2300:
1191          case SensorId::CCD_HP2400:
1192          case SensorId::CCD_HP3670:
1193 
1194         dev->interface->write_fe_register(0x23, dev->frontend.get_offset(1));
1195         dev->interface->write_fe_register(0x28, dev->frontend.get_gain(1));
1196          break; */
1197     }
1198 
1199     // end with reg1
1200     dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01));
1201 }
1202 
1203 /** Set values of analog frontend
1204  * this this the public interface, the gl646 as to use one more
1205  * parameter to work effectively, hence the redirection
1206  * @param dev device to set
1207  * @param set action to execute
1208  */
set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set) const1209 void CommandSetGl646::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor,
1210                              std::uint8_t set) const
1211 {
1212     gl646_set_fe(dev, sensor, set, dev->settings.yres);
1213 }
1214 
1215 /**
1216  * enters or leaves power saving mode
1217  * limited to AFE for now.
1218  * @param dev scanner's device
1219  * @param enable true to enable power saving, false to leave it
1220  */
save_power(Genesys_Device* dev, bool enable) const1221 void CommandSetGl646::save_power(Genesys_Device* dev, bool enable) const
1222 {
1223     DBG_HELPER_ARGS(dbg, "enable = %d", enable);
1224 
1225   const auto& sensor = sanei_genesys_find_sensor_any(dev);
1226 
1227   if (enable)
1228     {
1229         // gl646_set_fe(dev, sensor, AFE_POWER_SAVE);
1230     }
1231   else
1232     {
1233       gl646_set_fe(dev, sensor, AFE_INIT, 0);
1234     }
1235 }
1236 
set_powersaving(Genesys_Device* dev, int delay ) const1237 void CommandSetGl646::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const
1238 {
1239     DBG_HELPER_ARGS(dbg, "delay = %d", delay);
1240   Genesys_Register_Set local_reg(Genesys_Register_Set::SEQUENTIAL);
1241   int rate, exposure_time, tgtime, time;
1242 
1243   local_reg.init_reg(0x01, dev->reg.get8(0x01));	// disable fastmode
1244   local_reg.init_reg(0x03, dev->reg.get8(0x03));        // Lamp power control
1245     local_reg.init_reg(0x05, dev->reg.get8(0x05) & ~REG_0x05_BASESEL);   // 24 clocks/pixel
1246   local_reg.init_reg(0x38, 0x00); // line period low
1247   local_reg.init_reg(0x39, 0x00); //line period high
1248   local_reg.init_reg(0x6c, 0x00); // period times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE
1249 
1250   if (!delay)
1251     local_reg.find_reg(0x03).value &= 0xf0;	/* disable lampdog and set lamptime = 0 */
1252   else if (delay < 20)
1253     local_reg.find_reg(0x03).value = (local_reg.get8(0x03) & 0xf0) | 0x09;	/* enable lampdog and set lamptime = 1 */
1254   else
1255     local_reg.find_reg(0x03).value = (local_reg.get8(0x03) & 0xf0) | 0x0f;	/* enable lampdog and set lamptime = 7 */
1256 
1257   time = delay * 1000 * 60;	/* -> msec */
1258     exposure_time = static_cast<std::uint32_t>((time * 32000.0 /
1259                 (24.0 * 64.0 * (local_reg.get8(0x03) & REG_0x03_LAMPTIM) *
1260          1024.0) + 0.5));
1261   /* 32000 = system clock, 24 = clocks per pixel */
1262   rate = (exposure_time + 65536) / 65536;
1263   if (rate > 4)
1264     {
1265       rate = 8;
1266       tgtime = 3;
1267     }
1268   else if (rate > 2)
1269     {
1270       rate = 4;
1271       tgtime = 2;
1272     }
1273   else if (rate > 1)
1274     {
1275       rate = 2;
1276       tgtime = 1;
1277     }
1278   else
1279     {
1280       rate = 1;
1281       tgtime = 0;
1282     }
1283 
1284   local_reg.find_reg(0x6c).value |= tgtime << 6;
1285   exposure_time /= rate;
1286 
1287   if (exposure_time > 65535)
1288     exposure_time = 65535;
1289 
1290   local_reg.find_reg(0x38).value = exposure_time / 256;
1291   local_reg.find_reg(0x39).value = exposure_time & 255;
1292 
1293     dev->interface->write_registers(local_reg);
1294 }
1295 
1296 
1297 /**
1298  * loads document into scanner
1299  * currently only used by XP200
1300  * bit2 (0x04) of gpio is paper event (document in/out) on XP200
1301  * HOMESNR is set if no document in front of sensor, the sequence of events is
1302  * paper event -> document is in the sheet feeder
1303  * HOMESNR becomes 0 -> document reach sensor
1304  * HOMESNR becomes 1 ->document left sensor
1305  * paper event -> document is out
1306  */
load_document(Genesys_Device* dev) const1307 void CommandSetGl646::load_document(Genesys_Device* dev) const
1308 {
1309     DBG_HELPER(dbg);
1310 
1311   // FIXME: sequential not really needed in this case
1312   Genesys_Register_Set regs(Genesys_Register_Set::SEQUENTIAL);
1313     unsigned count;
1314 
1315   /* no need to load document is flatbed scanner */
1316     if (!dev->model->is_sheetfed) {
1317       DBG(DBG_proc, "%s: nothing to load\n", __func__);
1318       DBG(DBG_proc, "%s: end\n", __func__);
1319       return;
1320     }
1321 
1322     auto status = scanner_read_status(*dev);
1323 
1324     // home sensor is set if a document is inserted
1325     if (status.is_at_home) {
1326       /* if no document, waits for a paper event to start loading */
1327       /* with a 60 seconde minutes timeout                        */
1328       count = 0;
1329         std::uint8_t val = 0;
1330         do {
1331             gl646_gpio_read(dev->interface->get_usb_device(), &val);
1332 
1333 	  DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, val);
1334 	  if ((val & 0x04) != 0x04)
1335 	    {
1336               DBG(DBG_warn, "%s: no paper detected\n", __func__);
1337 	    }
1338             dev->interface->sleep_ms(200);
1339             count++;
1340         }
1341       while (((val & 0x04) != 0x04) && (count < 300));	/* 1 min time out */
1342       if (count == 300)
1343 	{
1344         throw SaneException(SANE_STATUS_NO_DOCS, "timeout waiting for document");
1345     }
1346     }
1347 
1348   /* set up to fast move before scan then move until document is detected */
1349   regs.init_reg(0x01, 0x90);
1350 
1351   /* AGOME, 2 slopes motor moving */
1352   regs.init_reg(0x02, 0x79);
1353 
1354   /* motor feeding steps to 0 */
1355   regs.init_reg(0x3d, 0);
1356   regs.init_reg(0x3e, 0);
1357   regs.init_reg(0x3f, 0);
1358 
1359   /* 50 fast moving steps */
1360   regs.init_reg(0x6b, 50);
1361 
1362   /* set GPO */
1363   regs.init_reg(0x66, 0x30);
1364 
1365   /* stesp NO */
1366   regs.init_reg(0x21, 4);
1367   regs.init_reg(0x22, 1);
1368   regs.init_reg(0x23, 1);
1369   regs.init_reg(0x24, 4);
1370 
1371   /* generate slope table 2 */
1372     auto slope_table = create_slope_table_for_speed(MotorSlope::create_from_steps(6000, 2400, 50),
1373                                                     2400, StepType::FULL, 1, 4,
1374                                                     get_slope_table_max_size(AsicType::GL646));
1375     // document loading:
1376     // send regs
1377     // start motor
1378     // wait e1 status to become e0
1379     const auto& sensor = sanei_genesys_find_sensor_any(dev);
1380     scanner_send_slope_table(dev, sensor, 1, slope_table.table);
1381 
1382     dev->interface->write_registers(regs);
1383 
1384     scanner_start_action(*dev, true);
1385 
1386   count = 0;
1387   do
1388     {
1389         status = scanner_read_status(*dev);
1390         dev->interface->sleep_ms(200);
1391       count++;
1392     } while (status.is_motor_enabled && (count < 300));
1393 
1394   if (count == 300)
1395     {
1396       throw SaneException(SANE_STATUS_JAMMED, "can't load document");
1397     }
1398 
1399   /* when loading OK, document is here */
1400     dev->document = true;
1401 
1402   /* set up to idle */
1403   regs.set8(0x02, 0x71);
1404   regs.set8(0x3f, 1);
1405   regs.set8(0x6b, 8);
1406     dev->interface->write_registers(regs);
1407 }
1408 
1409 /**
1410  * detects end of document and adjust current scan
1411  * to take it into account
1412  * used by sheetfed scanners
1413  */
detect_document_end(Genesys_Device* dev) const1414 void CommandSetGl646::detect_document_end(Genesys_Device* dev) const
1415 {
1416     DBG_HELPER(dbg);
1417     std::uint8_t gpio;
1418     unsigned int bytes_left;
1419 
1420     // test for document presence
1421     scanner_read_print_status(*dev);
1422 
1423     gl646_gpio_read(dev->interface->get_usb_device(), &gpio);
1424   DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, gpio);
1425 
1426   /* detect document event. There one event when the document go in,
1427    * then another when it leaves */
1428     if (dev->document && (gpio & 0x04) && (dev->total_bytes_read > 0)) {
1429       DBG(DBG_info, "%s: no more document\n", __func__);
1430         dev->document = false;
1431 
1432       /* adjust number of bytes to read:
1433        * total_bytes_to_read is the number of byte to send to frontend
1434        * total_bytes_read is the number of bytes sent to frontend
1435        * read_bytes_left is the number of bytes to read from the scanner
1436        */
1437       DBG(DBG_io, "%s: total_bytes_to_read=%zu\n", __func__, dev->total_bytes_to_read);
1438       DBG(DBG_io, "%s: total_bytes_read   =%zu\n", __func__, dev->total_bytes_read);
1439 
1440         // amount of data available from scanner is what to scan
1441         sanei_genesys_read_valid_words(dev, &bytes_left);
1442 
1443         unsigned lines_in_buffer = bytes_left / dev->session.output_line_bytes_raw;
1444 
1445         // we add the number of lines needed to read the last part of the document in
1446         unsigned lines_offset = static_cast<unsigned>(
1447                 (dev->model->y_offset * dev->session.params.yres) / MM_PER_INCH);
1448 
1449         unsigned remaining_lines = lines_in_buffer + lines_offset;
1450 
1451         bytes_left = remaining_lines * dev->session.output_line_bytes_raw;
1452 
1453         if (bytes_left < dev->get_pipeline_source().remaining_bytes()) {
1454             dev->get_pipeline_source().set_remaining_bytes(bytes_left);
1455             dev->total_bytes_to_read = dev->total_bytes_read + bytes_left;
1456         }
1457       DBG(DBG_io, "%s: total_bytes_to_read=%zu\n", __func__, dev->total_bytes_to_read);
1458       DBG(DBG_io, "%s: total_bytes_read   =%zu\n", __func__, dev->total_bytes_read);
1459     }
1460 }
1461 
1462 /**
1463  * eject document from the feeder
1464  * currently only used by XP200
1465  * TODO we currently rely on AGOHOME not being set for sheetfed scanners,
1466  * maybe check this flag in eject to let the document being eject automatically
1467  */
eject_document(Genesys_Device* dev) const1468 void CommandSetGl646::eject_document(Genesys_Device* dev) const
1469 {
1470     DBG_HELPER(dbg);
1471 
1472   // FIXME: SEQUENTIAL not really needed in this case
1473   Genesys_Register_Set regs((Genesys_Register_Set::SEQUENTIAL));
1474     unsigned count;
1475     std::uint8_t gpio;
1476 
1477   /* at the end there will be no more document */
1478     dev->document = false;
1479 
1480     // first check for document event
1481     gl646_gpio_read(dev->interface->get_usb_device(), &gpio);
1482 
1483   DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, gpio);
1484 
1485     // test status : paper event + HOMESNR -> no more doc ?
1486     auto status = scanner_read_status(*dev);
1487 
1488     // home sensor is set when document is inserted
1489     if (status.is_at_home) {
1490         dev->document = false;
1491         DBG(DBG_info, "%s: no more document to eject\n", __func__);
1492         return;
1493     }
1494 
1495     // there is a document inserted, eject it
1496     dev->interface->write_register(0x01, 0xb0);
1497 
1498   /* wait for motor to stop */
1499     do {
1500         dev->interface->sleep_ms(200);
1501         status = scanner_read_status(*dev);
1502     }
1503     while (status.is_motor_enabled);
1504 
1505   /* set up to fast move before scan then move until document is detected */
1506   regs.init_reg(0x01, 0xb0);
1507 
1508   /* AGOME, 2 slopes motor moving , eject 'backward' */
1509   regs.init_reg(0x02, 0x5d);
1510 
1511   /* motor feeding steps to 119880 */
1512   regs.init_reg(0x3d, 1);
1513   regs.init_reg(0x3e, 0xd4);
1514   regs.init_reg(0x3f, 0x48);
1515 
1516   /* 60 fast moving steps */
1517   regs.init_reg(0x6b, 60);
1518 
1519   /* set GPO */
1520   regs.init_reg(0x66, 0x30);
1521 
1522   /* stesp NO */
1523   regs.init_reg(0x21, 4);
1524   regs.init_reg(0x22, 1);
1525   regs.init_reg(0x23, 1);
1526   regs.init_reg(0x24, 4);
1527 
1528   /* generate slope table 2 */
1529     auto slope_table = create_slope_table_for_speed(MotorSlope::create_from_steps(10000, 1600, 60),
1530                                                     1600, StepType::FULL, 1, 4,
1531                                                     get_slope_table_max_size(AsicType::GL646));
1532     // document eject:
1533     // send regs
1534     // start motor
1535     // wait c1 status to become c8 : HOMESNR and ~MOTFLAG
1536     // FIXME: sensor is not used.
1537     const auto& sensor = sanei_genesys_find_sensor_any(dev);
1538     scanner_send_slope_table(dev, sensor, 1, slope_table.table);
1539 
1540     dev->interface->write_registers(regs);
1541 
1542     scanner_start_action(*dev, true);
1543 
1544   /* loop until paper sensor tells paper is out, and till motor is running */
1545   /* use a 30 timeout */
1546   count = 0;
1547     do {
1548         status = scanner_read_status(*dev);
1549 
1550         dev->interface->sleep_ms(200);
1551       count++;
1552     } while (!status.is_at_home && (count < 150));
1553 
1554     // read GPIO on exit
1555     gl646_gpio_read(dev->interface->get_usb_device(), &gpio);
1556 
1557   DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, gpio);
1558 }
1559 
1560 // Send the low-level scan command
begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, bool start_motor) const1561 void CommandSetGl646::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
1562                                  Genesys_Register_Set* reg, bool start_motor) const
1563 {
1564     DBG_HELPER(dbg);
1565     (void) sensor;
1566   // FIXME: SEQUENTIAL not really needed in this case
1567   Genesys_Register_Set local_reg(Genesys_Register_Set::SEQUENTIAL);
1568 
1569     local_reg.init_reg(0x03, reg->get8(0x03));
1570     local_reg.init_reg(0x01, reg->get8(0x01) | REG_0x01_SCAN);
1571 
1572     if (start_motor) {
1573         local_reg.init_reg(0x0f, 0x01);
1574     } else {
1575         local_reg.init_reg(0x0f, 0x00); // do not start motor yet
1576     }
1577 
1578     dev->interface->write_registers(local_reg);
1579 
1580     dev->advance_head_pos_by_session(ScanHeadId::PRIMARY);
1581 }
1582 
1583 
1584 // Send the stop scan command
end_scan_impl(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop, bool eject)1585 static void end_scan_impl(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop,
1586                           bool eject)
1587 {
1588     DBG_HELPER_ARGS(dbg, "check_stop = %d, eject = %d", check_stop, eject);
1589 
1590     scanner_stop_action_no_move(*dev, *reg);
1591 
1592     unsigned wait_limit_seconds = 30;
1593 
1594   /* for sheetfed scanners, we may have to eject document */
1595     if (dev->model->is_sheetfed) {
1596         if (eject && dev->document) {
1597             dev->cmd_set->eject_document(dev);
1598         }
1599         wait_limit_seconds = 3;
1600     }
1601 
1602     if (is_testing_mode()) {
1603         return;
1604     }
1605 
1606     dev->interface->sleep_ms(100);
1607 
1608     if (check_stop) {
1609         for (unsigned i = 0; i < wait_limit_seconds * 10; i++) {
1610             if (scanner_is_motor_stopped(*dev)) {
1611                 return;
1612             }
1613 
1614             dev->interface->sleep_ms(100);
1615         }
1616         throw SaneException(SANE_STATUS_IO_ERROR, "could not stop motor");
1617     }
1618 }
1619 
1620 // Send the stop scan command
end_scan(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop) const1621 void CommandSetGl646::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg,
1622                                bool check_stop) const
1623 {
1624     end_scan_impl(dev, reg, check_stop, false);
1625 }
1626 
1627 /**
1628  * parks head
1629  * @param dev scanner's device
1630  * @param wait_until_home true if the function waits until head parked
1631  */
move_back_home(Genesys_Device* dev, bool wait_until_home) const1632 void CommandSetGl646::move_back_home(Genesys_Device* dev, bool wait_until_home) const
1633 {
1634     DBG_HELPER_ARGS(dbg, "wait_until_home = %d\n", wait_until_home);
1635   int i;
1636   int loop = 0;
1637 
1638     auto status = scanner_read_status(*dev);
1639 
1640     if (status.is_at_home) {
1641       DBG(DBG_info, "%s: end since already at home\n", __func__);
1642         dev->set_head_pos_zero(ScanHeadId::PRIMARY);
1643         return;
1644     }
1645 
1646   /* stop motor if needed */
1647     if (status.is_motor_enabled) {
1648         gl646_stop_motor(dev);
1649         dev->interface->sleep_ms(200);
1650     }
1651 
1652   /* when scanhead is moving then wait until scanhead stops or timeout */
1653   DBG(DBG_info, "%s: ensuring that motor is off\n", __func__);
1654     for (i = 400; i > 0; i--) {
1655         // do not wait longer than 40 seconds, count down to get i = 0 when busy
1656 
1657         status = scanner_read_status(*dev);
1658 
1659         if (!status.is_motor_enabled && status.is_at_home) {
1660             DBG(DBG_info, "%s: already at home and not moving\n", __func__);
1661             dev->set_head_pos_zero(ScanHeadId::PRIMARY);
1662             return;
1663         }
1664         if (!status.is_motor_enabled) {
1665             break;
1666         }
1667 
1668         dev->interface->sleep_ms(100);
1669     }
1670 
1671   if (!i)			/* the loop counted down to 0, scanner still is busy */
1672     {
1673         dev->set_head_pos_unknown(ScanHeadId::PRIMARY | ScanHeadId::SECONDARY);
1674         throw SaneException(SANE_STATUS_DEVICE_BUSY, "motor is still on: device busy");
1675     }
1676 
1677     // setup for a backward scan of 65535 steps, with no actual data reading
1678     auto resolution = sanei_genesys_get_lowest_dpi(dev);
1679 
1680     const auto& sensor = sanei_genesys_find_sensor(dev, resolution, 3,
1681                                                    dev->model->default_method);
1682 
1683     ScanSession session;
1684     session.params.xres = resolution;
1685     session.params.yres = resolution;
1686     session.params.startx = 0;
1687     session.params.starty = 65535;
1688     session.params.pixels = 600;
1689     session.params.lines = 1;
1690     session.params.depth = 8;
1691     session.params.channels = 3;
1692     session.params.scan_method = dev->model->default_method;
1693     session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
1694     session.params.color_filter = ColorFilter::RED;
1695     session.params.contrast_adjustment = dev->settings.contrast;
1696     session.params.brightness_adjustment = dev->settings.brightness;
1697     session.params.flags = ScanFlag::REVERSE |
1698                            ScanFlag::AUTO_GO_HOME |
1699                            ScanFlag::DISABLE_GAMMA;
1700     if (dev->model->default_method == ScanMethod::TRANSPARENCY) {
1701         session.params.flags |= ScanFlag::USE_XPA;
1702     }
1703     compute_session(dev, session, sensor);
1704 
1705     init_regs_for_scan_session(dev, sensor, &dev->reg, session);
1706 
1707   /* backward , no actual data scanned TODO more setup flags to avoid this register manipulations ? */
1708     regs_set_optical_off(dev->model->asic_type, dev->reg);
1709 
1710     // sets frontend
1711     gl646_set_fe(dev, sensor, AFE_SET, resolution);
1712 
1713   /* write scan registers */
1714     try {
1715         dev->interface->write_registers(dev->reg);
1716     } catch (...) {
1717         DBG(DBG_error, "%s: failed to bulk write registers\n", __func__);
1718     }
1719 
1720   /* registers are restored to an iddl state, give up if no head to park */
1721     if (dev->model->is_sheetfed) {
1722         return;
1723     }
1724 
1725     // starts scan
1726     {
1727         // this is effectively the same as dev->cmd_set->begin_scan(dev, sensor, &dev->reg, true);
1728         // except that we don't modify the head position calculations
1729 
1730         // FIXME: SEQUENTIAL not really needed in this case
1731         Genesys_Register_Set scan_local_reg(Genesys_Register_Set::SEQUENTIAL);
1732 
1733         scan_local_reg.init_reg(0x03, dev->reg.get8(0x03));
1734         scan_local_reg.init_reg(0x01, dev->reg.get8(0x01) | REG_0x01_SCAN);
1735         scan_local_reg.init_reg(0x0f, 0x01);
1736 
1737         dev->interface->write_registers(scan_local_reg);
1738     }
1739 
1740     if (is_testing_mode()) {
1741         dev->interface->test_checkpoint("move_back_home");
1742         dev->set_head_pos_zero(ScanHeadId::PRIMARY);
1743         return;
1744     }
1745 
1746   /* loop until head parked */
1747   if (wait_until_home)
1748     {
1749       while (loop < 300)		/* do not wait longer then 30 seconds */
1750 	{
1751             auto status = scanner_read_status(*dev);
1752 
1753             if (status.is_at_home) {
1754 	      DBG(DBG_info, "%s: reached home position\n", __func__);
1755                 dev->interface->sleep_ms(500);
1756                 dev->set_head_pos_zero(ScanHeadId::PRIMARY);
1757                 return;
1758             }
1759             dev->interface->sleep_ms(100);
1760             ++loop;
1761         }
1762 
1763         // when we come here then the scanner needed too much time for this, so we better
1764         // stop the motor
1765         catch_all_exceptions(__func__, [&](){ gl646_stop_motor (dev); });
1766         catch_all_exceptions(__func__, [&](){ end_scan_impl(dev, &dev->reg, true, false); });
1767         dev->set_head_pos_unknown(ScanHeadId::PRIMARY | ScanHeadId::SECONDARY);
1768         throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for scanhead to go home");
1769     }
1770 
1771 
1772   DBG(DBG_info, "%s: scanhead is still moving\n", __func__);
1773 }
1774 
1775 /**
1776  * init registers for shading calibration
1777  * we assume that scanner's head is on an area suiting shading calibration.
1778  * We scan a full scan width area by the shading line number for the device
1779  */
init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const1780 void CommandSetGl646::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
1781                                             Genesys_Register_Set& regs) const
1782 {
1783     DBG_HELPER(dbg);
1784     (void) regs;
1785 
1786   /* fill settings for scan : always a color scan */
1787   int channels = 3;
1788 
1789     unsigned cksel = get_cksel(dev->model->sensor_id, dev->settings.xres, channels);
1790 
1791     unsigned resolution = sensor.get_optical_resolution() / cksel;
1792     // FIXME: we select wrong calibration sensor
1793     const auto& calib_sensor = sanei_genesys_find_sensor(dev, dev->settings.xres, channels,
1794                                                          dev->settings.scan_method);
1795 
1796     auto pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
1797 
1798     unsigned calib_lines =
1799             static_cast<unsigned>(dev->model->y_size_calib_mm * resolution / MM_PER_INCH);
1800 
1801     ScanSession session;
1802     session.params.xres = resolution;
1803     session.params.yres = resolution;
1804     session.params.startx = 0;
1805     session.params.starty = 0;
1806     session.params.pixels = pixels;
1807     session.params.lines = calib_lines;
1808     session.params.depth = 16;
1809     session.params.channels = channels;
1810     session.params.scan_method = dev->settings.scan_method;
1811     session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
1812     session.params.color_filter = dev->settings.color_filter;
1813     session.params.contrast_adjustment = dev->settings.contrast;
1814     session.params.brightness_adjustment = dev->settings.brightness;
1815     session.params.flags = ScanFlag::DISABLE_SHADING |
1816                            ScanFlag::DISABLE_GAMMA |
1817                            ScanFlag::IGNORE_COLOR_OFFSET |
1818                            ScanFlag::IGNORE_STAGGER_OFFSET;
1819     if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) {
1820         session.params.flags |= ScanFlag::USE_XPA;
1821     }
1822     compute_session(dev, session, calib_sensor);
1823 
1824     dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session);
1825 
1826     dev->calib_session = session;
1827 
1828   /* no shading */
1829     dev->reg.find_reg(0x02).value |= REG_0x02_ACDCDIS;	/* ease backtracking */
1830     dev->reg.find_reg(0x02).value &= ~REG_0x02_FASTFED;
1831   sanei_genesys_set_motor_power(dev->reg, false);
1832 }
1833 
needs_home_before_init_regs_for_scan(Genesys_Device* dev) const1834 bool CommandSetGl646::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const
1835 {
1836     return dev->is_head_pos_known(ScanHeadId::PRIMARY) &&
1837             dev->head_pos(ScanHeadId::PRIMARY) &&
1838             dev->settings.scan_method == ScanMethod::FLATBED;
1839 }
1840 
1841 /**
1842  * this function send gamma table to ASIC
1843  */
send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const1844 void CommandSetGl646::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const
1845 {
1846     DBG_HELPER(dbg);
1847   int size;
1848   int address;
1849   int bits;
1850 
1851      if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) {
1852       size = 16384;
1853       bits = 14;
1854     }
1855   else
1856     {
1857       size = 4096;
1858       bits = 12;
1859     }
1860 
1861     auto gamma = generate_gamma_buffer(dev, sensor, bits, size-1, size);
1862 
1863   /* table address */
1864   switch (dev->reg.find_reg(0x05).value >> 6)
1865     {
1866     case 0:			/* 600 dpi */
1867       address = 0x09000;
1868       break;
1869     case 1:			/* 1200 dpi */
1870       address = 0x11000;
1871       break;
1872     case 2:			/* 2400 dpi */
1873       address = 0x20000;
1874       break;
1875     default:
1876             throw SaneException("invalid dpi");
1877     }
1878 
1879     dev->interface->write_buffer(0x3c, address, gamma.data(), size * 2 * 3);
1880 }
1881 
1882 /** @brief this function does the led calibration.
1883  * this function does the led calibration by scanning one line of the calibration
1884  * area below scanner's top on white strip. The scope of this function is
1885  * currently limited to the XP200
1886  */
led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const1887 SensorExposure CommandSetGl646::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
1888                                                 Genesys_Register_Set& regs) const
1889 {
1890     DBG_HELPER(dbg);
1891     (void) regs;
1892   unsigned int i, j;
1893   int val;
1894   int avg[3], avga, avge;
1895   int turn;
1896     std::uint16_t expr, expg, expb;
1897 
1898     unsigned channels = dev->settings.get_channels();
1899 
1900     ScanColorMode scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
1901     if (dev->settings.scan_mode != ScanColorMode::COLOR_SINGLE_PASS) {
1902         scan_mode = ScanColorMode::GRAY;
1903     }
1904 
1905     // offset calibration is always done in color mode
1906     unsigned pixels = dev->model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH;
1907 
1908     ScanSession session;
1909     session.params.xres = sensor.full_resolution;
1910     session.params.yres = sensor.full_resolution;
1911     session.params.startx = 0;
1912     session.params.starty = 0;
1913     session.params.pixels = pixels;
1914     session.params.lines = 1;
1915     session.params.depth = 16;
1916     session.params.channels = channels;
1917     session.params.scan_method = dev->settings.scan_method;
1918     session.params.scan_mode = scan_mode;
1919     session.params.color_filter = ColorFilter::RED;
1920     session.params.contrast_adjustment = dev->settings.contrast;
1921     session.params.brightness_adjustment = dev->settings.brightness;
1922     session.params.flags = ScanFlag::DISABLE_SHADING;
1923     if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) {
1924         session.params.flags |= ScanFlag::USE_XPA;
1925     }
1926     compute_session(dev, session, sensor);
1927 
1928     // colors * bytes_per_color * scan lines
1929     unsigned total_size = pixels * channels * 2 * 1;
1930 
1931     std::vector<std::uint8_t> line(total_size);
1932 
1933 /*
1934    we try to get equal bright leds here:
1935 
1936    loop:
1937      average per color
1938      adjust exposure times
1939  */
1940   expr = sensor.exposure.red;
1941   expg = sensor.exposure.green;
1942   expb = sensor.exposure.blue;
1943 
1944   turn = 0;
1945 
1946     auto calib_sensor = sensor;
1947 
1948     bool acceptable = false;
1949     do {
1950         calib_sensor.exposure.red = expr;
1951         calib_sensor.exposure.green = expg;
1952         calib_sensor.exposure.blue = expb;
1953 
1954       DBG(DBG_info, "%s: starting first line reading\n", __func__);
1955 
1956         dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session);
1957         simple_scan(dev, calib_sensor, session, false, line, "led_calibration");
1958 
1959         if (is_testing_mode()) {
1960             return calib_sensor.exposure;
1961         }
1962 
1963         if (dbg_log_image_data()) {
1964             char fn[30];
1965             std::snprintf(fn, 30, "gl646_led_%02d.tiff", turn);
1966             write_tiff_file(fn, line.data(), 16, channels, pixels, 1);
1967         }
1968 
1969         acceptable = true;
1970 
1971       for (j = 0; j < channels; j++)
1972 	{
1973 	  avg[j] = 0;
1974             for (i = 0; i < pixels; i++) {
1975                 if (dev->model->is_cis) {
1976                     val = line[i * 2 + j * 2 * pixels + 1] * 256 + line[i * 2 + j * 2 * pixels];
1977                 } else {
1978                     val = line[i * 2 * channels + 2 * j + 1] * 256 + line[i * 2 * channels + 2 * j];
1979                 }
1980             avg[j] += val;
1981 	    }
1982 
1983       avg[j] /= pixels;
1984 	}
1985 
1986       DBG(DBG_info, "%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]);
1987 
1988         acceptable = true;
1989 
1990       if (!acceptable)
1991 	{
1992 	  avga = (avg[0] + avg[1] + avg[2]) / 3;
1993 	  expr = (expr * avga) / avg[0];
1994 	  expg = (expg * avga) / avg[1];
1995 	  expb = (expb * avga) / avg[2];
1996 
1997 	  /* keep exposure time in a working window */
1998 	  avge = (expr + expg + expb) / 3;
1999 	  if (avge > 0x2000)
2000 	    {
2001 	      expr = (expr * 0x2000) / avge;
2002 	      expg = (expg * 0x2000) / avge;
2003 	      expb = (expb * 0x2000) / avge;
2004 	    }
2005 	  if (avge < 0x400)
2006 	    {
2007 	      expr = (expr * 0x400) / avge;
2008 	      expg = (expg * 0x400) / avge;
2009 	      expb = (expb * 0x400) / avge;
2010 	    }
2011 	}
2012 
2013       turn++;
2014 
2015     }
2016   while (!acceptable && turn < 100);
2017 
2018   DBG(DBG_info,"%s: acceptable exposure: 0x%04x,0x%04x,0x%04x\n", __func__, expr, expg, expb);
2019     // BUG: we don't store the result of the last iteration to the sensor
2020     return calib_sensor.exposure;
2021 }
2022 
2023 /**
2024  * average dark pixels of a scan
2025  */
dark_average(std::uint8_t * data, unsigned int pixels, unsigned int lines, unsigned int channels, unsigned int black)2026 static int dark_average(std::uint8_t * data, unsigned int pixels, unsigned int lines,
2027                         unsigned int channels, unsigned int black)
2028 {
2029   unsigned int i, j, k, average, count;
2030   unsigned int avg[3];
2031     std::uint8_t val;
2032 
2033   /* computes average value on black margin */
2034   for (k = 0; k < channels; k++)
2035     {
2036       avg[k] = 0;
2037       count = 0;
2038       for (i = 0; i < lines; i++)
2039 	{
2040 	  for (j = 0; j < black; j++)
2041 	    {
2042 	      val = data[i * channels * pixels + j + k];
2043 	      avg[k] += val;
2044 	      count++;
2045 	    }
2046 	}
2047       if (count)
2048 	avg[k] /= count;
2049       DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, k, avg[k]);
2050     }
2051   average = 0;
2052   for (i = 0; i < channels; i++)
2053     average += avg[i];
2054   average /= channels;
2055   DBG(DBG_info, "%s: average = %d\n", __func__, average);
2056   return average;
2057 }
2058 
2059 
2060 /** @brief calibration for AD frontend devices
2061  * we do simple scan until all black_pixels are higher than 0,
2062  * raising offset at each turn.
2063  */
ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor)2064 static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor)
2065 {
2066     DBG_HELPER(dbg);
2067     (void) sensor;
2068 
2069   unsigned int channels;
2070   int pass = 0;
2071     unsigned adr, min;
2072   unsigned int bottom, black_pixels;
2073 
2074   channels = 3;
2075 
2076     // FIXME: maybe reuse `sensor`
2077     const auto& calib_sensor = sanei_genesys_find_sensor(dev, sensor.full_resolution, 3,
2078                                                          ScanMethod::FLATBED);
2079     black_pixels = (calib_sensor.black_pixels * sensor.full_resolution) / calib_sensor.full_resolution;
2080 
2081     unsigned pixels = dev->model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH;
2082     unsigned lines = CALIBRATION_LINES;
2083 
2084     if (dev->model->is_cis) {
2085         lines = ((lines + 2) / 3) * 3;
2086     }
2087 
2088     ScanSession session;
2089     session.params.xres = sensor.full_resolution;
2090     session.params.yres = sensor.full_resolution;
2091     session.params.startx = 0;
2092     session.params.starty = 0;
2093     session.params.pixels = pixels;
2094     session.params.lines = lines;
2095     session.params.depth = 8;
2096     session.params.channels = 3;
2097     session.params.scan_method = dev->settings.scan_method;
2098     session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
2099     session.params.color_filter = ColorFilter::RED;
2100     session.params.contrast_adjustment = dev->settings.contrast;
2101     session.params.brightness_adjustment = dev->settings.brightness;
2102     session.params.flags = ScanFlag::DISABLE_SHADING;
2103     if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) {
2104         session.params.flags |= ScanFlag::USE_XPA;
2105     }
2106     compute_session(dev, session, calib_sensor);
2107 
2108   /* scan first line of data with no gain */
2109   dev->frontend.set_gain(0, 0);
2110   dev->frontend.set_gain(1, 0);
2111   dev->frontend.set_gain(2, 0);
2112 
2113     std::vector<std::uint8_t> line;
2114 
2115   /* scan with no move */
2116   bottom = 1;
2117   do
2118     {
2119       pass++;
2120       dev->frontend.set_offset(0, bottom);
2121       dev->frontend.set_offset(1, bottom);
2122       dev->frontend.set_offset(2, bottom);
2123 
2124         dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session);
2125         simple_scan(dev, calib_sensor, session, false, line, "ad_fe_offset_calibration");
2126 
2127         if (is_testing_mode()) {
2128             return;
2129         }
2130 
2131         if (dbg_log_image_data()) {
2132             char title[30];
2133             std::snprintf(title, 30, "gl646_offset%03d.tiff", static_cast<int>(bottom));
2134             write_tiff_file(title, line.data(), 8, channels, pixels, lines);
2135         }
2136 
2137       min = 0;
2138         for (unsigned y = 0; y < lines; y++) {
2139             for (unsigned x = 0; x < black_pixels; x++) {
2140                 adr = (x + y * pixels) * channels;
2141 	      if (line[adr] > min)
2142 		min = line[adr];
2143 	      if (line[adr + 1] > min)
2144 		min = line[adr + 1];
2145 	      if (line[adr + 2] > min)
2146 		min = line[adr + 2];
2147 	    }
2148 	}
2149 
2150       DBG(DBG_info, "%s: pass=%d, min=%d\n", __func__, pass, min);
2151       bottom++;
2152     }
2153   while (pass < 128 && min == 0);
2154   if (pass == 128)
2155     {
2156         throw SaneException(SANE_STATUS_INVAL, "failed to find correct offset");
2157     }
2158 
2159   DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
2160       dev->frontend.get_offset(0),
2161       dev->frontend.get_offset(1),
2162       dev->frontend.get_offset(2));
2163 }
2164 
2165 /**
2166  * This function does the offset calibration by scanning one line of the calibration
2167  * area below scanner's top. There is a black margin and the remaining is white.
2168  * genesys_search_start() must have been called so that the offsets and margins
2169  * are already known.
2170  * @param dev scanner's device
2171 */
offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const2172 void CommandSetGl646::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
2173                                          Genesys_Register_Set& regs) const
2174 {
2175     DBG_HELPER(dbg);
2176     (void) regs;
2177 
2178   int pass = 0, avg;
2179   int topavg, bottomavg;
2180   int top, bottom, black_pixels;
2181 
2182     if (dev->model->adc_id == AdcId::AD_XP200) {
2183         ad_fe_offset_calibration(dev, sensor);
2184         return;
2185     }
2186 
2187   /* setup for a RGB scan, one full sensor's width line */
2188   /* resolution is the one from the final scan          */
2189     unsigned resolution = dev->settings.xres;
2190     unsigned channels = 3;
2191 
2192     const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
2193                                                          ScanMethod::FLATBED);
2194     black_pixels = (calib_sensor.black_pixels * resolution) / calib_sensor.full_resolution;
2195 
2196     unsigned pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
2197     unsigned lines = CALIBRATION_LINES;
2198     if (dev->model->is_cis) {
2199         lines = ((lines + 2) / 3) * 3;
2200     }
2201 
2202     ScanSession session;
2203     session.params.xres = resolution;
2204     session.params.yres = resolution;
2205     session.params.startx = 0;
2206     session.params.starty = 0;
2207     session.params.pixels = pixels;
2208     session.params.lines = lines;
2209     session.params.depth = 8;
2210     session.params.channels = channels;
2211     session.params.scan_method = dev->settings.scan_method;
2212     session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
2213     session.params.color_filter = ColorFilter::RED;
2214     session.params.contrast_adjustment = dev->settings.contrast;
2215     session.params.brightness_adjustment = dev->settings.brightness;
2216     session.params.flags = ScanFlag::DISABLE_SHADING;
2217     if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) {
2218         session.params.flags |= ScanFlag::USE_XPA;
2219     }
2220     compute_session(dev, session, sensor);
2221 
2222   /* scan first line of data with no gain, but with offset from
2223    * last calibration */
2224   dev->frontend.set_gain(0, 0);
2225   dev->frontend.set_gain(1, 0);
2226   dev->frontend.set_gain(2, 0);
2227 
2228   /* scan with no move */
2229   bottom = 90;
2230   dev->frontend.set_offset(0, bottom);
2231   dev->frontend.set_offset(1, bottom);
2232   dev->frontend.set_offset(2, bottom);
2233 
2234     std::vector<std::uint8_t> first_line, second_line;
2235 
2236     dev->cmd_set->init_regs_for_scan_session(dev, sensor, &dev->reg, session);
2237     simple_scan(dev, calib_sensor, session, false, first_line, "offset_first_line");
2238 
2239     if (dbg_log_image_data()) {
2240         char title[30];
2241         std::snprintf(title, 30, "gl646_offset%03d.tiff", bottom);
2242         write_tiff_file(title, first_line.data(), 8, channels, pixels, lines);
2243     }
2244     bottomavg = dark_average(first_line.data(), pixels, lines, channels, black_pixels);
2245     DBG(DBG_info, "%s: bottom avg=%d\n", __func__, bottomavg);
2246 
2247   /* now top value */
2248   top = 231;
2249   dev->frontend.set_offset(0, top);
2250   dev->frontend.set_offset(1, top);
2251   dev->frontend.set_offset(2, top);
2252     dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session);
2253     simple_scan(dev, calib_sensor, session, false, second_line, "offset_second_line");
2254 
2255     if (dbg_log_image_data()) {
2256         char title[30];
2257         std::snprintf(title, 30, "gl646_offset%03d.tiff", top);
2258         write_tiff_file(title, second_line.data(), 8, channels, pixels, lines);
2259     }
2260     topavg = dark_average(second_line.data(), pixels, lines, channels, black_pixels);
2261     DBG(DBG_info, "%s: top avg=%d\n", __func__, topavg);
2262 
2263     if (is_testing_mode()) {
2264         return;
2265     }
2266 
2267   /* loop until acceptable level */
2268   while ((pass < 32) && (top - bottom > 1))
2269     {
2270       pass++;
2271 
2272       /* settings for new scan */
2273       dev->frontend.set_offset(0, (top + bottom) / 2);
2274       dev->frontend.set_offset(1, (top + bottom) / 2);
2275       dev->frontend.set_offset(2, (top + bottom) / 2);
2276 
2277         // scan with no move
2278         dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session);
2279         simple_scan(dev, calib_sensor, session, false, second_line,
2280                     "offset_calibration_i");
2281 
2282         if (dbg_log_image_data()) {
2283             char title[30];
2284             std::snprintf(title, 30, "gl646_offset%03d.tiff", dev->frontend.get_offset(1));
2285             write_tiff_file(title, second_line.data(), 8, channels, pixels, lines);
2286         }
2287 
2288         avg = dark_average(second_line.data(), pixels, lines, channels, black_pixels);
2289       DBG(DBG_info, "%s: avg=%d offset=%d\n", __func__, avg, dev->frontend.get_offset(1));
2290 
2291       /* compute new boundaries */
2292       if (topavg == avg)
2293 	{
2294 	  topavg = avg;
2295           top = dev->frontend.get_offset(1);
2296 	}
2297       else
2298 	{
2299 	  bottomavg = avg;
2300           bottom = dev->frontend.get_offset(1);
2301 	}
2302     }
2303 
2304   DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
2305       dev->frontend.get_offset(0),
2306       dev->frontend.get_offset(1),
2307       dev->frontend.get_offset(2));
2308 }
2309 
2310 /**
2311  * Alternative coarse gain calibration
2312  * this on uses the settings from offset_calibration. First scan moves so
2313  * we can go to calibration area for XPA.
2314  * @param dev device for scan
2315  * @param dpi resolutnio to calibrate at
2316  */
coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, int dpi) const2317 void CommandSetGl646::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
2318                                               Genesys_Register_Set& regs, int dpi) const
2319 {
2320     DBG_HELPER(dbg);
2321     (void) dpi;
2322     (void) sensor;
2323     (void) regs;
2324 
2325   float average[3];
2326   char title[32];
2327 
2328   /* setup for a RGB scan, one full sensor's width line */
2329   /* resolution is the one from the final scan          */
2330     unsigned channels = 3;
2331 
2332     // BUG: the following comment is incorrect
2333     // we are searching a sensor resolution */
2334     const auto& calib_sensor = sanei_genesys_find_sensor(dev, dev->settings.xres, channels,
2335                                                          ScanMethod::FLATBED);
2336 
2337     unsigned pixels = 0;
2338     float start = 0;
2339     if (dev->settings.scan_method == ScanMethod::FLATBED) {
2340         pixels = dev->model->x_size_calib_mm * dev->settings.xres / MM_PER_INCH;
2341     } else {
2342         start = dev->model->x_offset_ta;
2343         pixels = static_cast<unsigned>(
2344                               (dev->model->x_size_ta * dev->settings.xres) / MM_PER_INCH);
2345     }
2346 
2347     unsigned lines = CALIBRATION_LINES;
2348     // round up to multiple of 3 in case of CIS scanner
2349     if (dev->model->is_cis) {
2350         lines = ((lines + 2) / 3) * 3;
2351     }
2352 
2353     start = static_cast<float>((start * dev->settings.xres) / MM_PER_INCH);
2354 
2355     ScanSession session;
2356     session.params.xres = dev->settings.xres;
2357     session.params.yres = dev->settings.xres;
2358     session.params.startx = static_cast<unsigned>(start);
2359     session.params.starty = 0;
2360     session.params.pixels = pixels;
2361     session.params.lines = lines;
2362     session.params.depth = 8;
2363     session.params.channels = channels;
2364     session.params.scan_method = dev->settings.scan_method;
2365     session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
2366     session.params.color_filter = ColorFilter::RED;
2367     session.params.contrast_adjustment = dev->settings.contrast;
2368     session.params.brightness_adjustment = dev->settings.brightness;
2369     session.params.flags = ScanFlag::DISABLE_SHADING;
2370     if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) {
2371         session.params.flags |= ScanFlag::USE_XPA;
2372     }
2373     compute_session(dev, session, calib_sensor);
2374 
2375   /* start gain value */
2376   dev->frontend.set_gain(0, 1);
2377   dev->frontend.set_gain(1, 1);
2378   dev->frontend.set_gain(2, 1);
2379 
2380     average[0] = 0;
2381     average[1] = 0;
2382     average[2] = 0;
2383 
2384     unsigned pass = 0;
2385 
2386     std::vector<std::uint8_t> line;
2387 
2388   /* loop until each channel raises to acceptable level */
2389     while (((average[0] < calib_sensor.gain_white_ref) ||
2390             (average[1] < calib_sensor.gain_white_ref) ||
2391             (average[2] < calib_sensor.gain_white_ref)) && (pass < 30))
2392     {
2393         // scan with no move
2394         dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session);
2395         simple_scan(dev, calib_sensor, session, false, line, "coarse_gain_calibration");
2396 
2397         if (dbg_log_image_data()) {
2398             std::sprintf(title, "gl646_gain%02d.tiff", pass);
2399             write_tiff_file(title, line.data(), 8, channels, pixels, lines);
2400         }
2401         pass++;
2402 
2403         // average high level for each channel and compute gain to reach the target code
2404         // we only use the central half of the CCD data
2405         for (unsigned k = 0; k < channels; k++) {
2406 
2407             // we find the maximum white value, so we can deduce a threshold
2408             // to average white values
2409             unsigned maximum = 0;
2410             for (unsigned i = 0; i < lines; i++) {
2411                 for (unsigned j = 0; j < pixels; j++) {
2412                     unsigned val = line[i * channels * pixels + j + k];
2413                     maximum = std::max(maximum, val);
2414                 }
2415             }
2416 
2417             maximum = static_cast<int>(maximum * 0.9);
2418 
2419             // computes white average
2420             average[k] = 0;
2421             unsigned count = 0;
2422             for (unsigned i = 0; i < lines; i++) {
2423                 for (unsigned j = 0; j < pixels; j++) {
2424                     // averaging only white points allow us not to care about dark margins
2425                     unsigned val = line[i * channels * pixels + j + k];
2426                     if (val > maximum) {
2427                         average[k] += val;
2428                         count++;
2429                     }
2430                 }
2431             }
2432             average[k] = average[k] / count;
2433 
2434             // adjusts gain for the channel
2435             if (average[k] < calib_sensor.gain_white_ref) {
2436                 dev->frontend.set_gain(k, dev->frontend.get_gain(k) + 1);
2437             }
2438 
2439             DBG(DBG_info, "%s: channel %d, average = %.2f, gain = %d\n", __func__, k, average[k],
2440                 dev->frontend.get_gain(k));
2441         }
2442     }
2443 
2444     DBG(DBG_info, "%s: gains=(%d,%d,%d)\n", __func__,
2445         dev->frontend.get_gain(0),
2446         dev->frontend.get_gain(1),
2447         dev->frontend.get_gain(2));
2448 }
2449 
2450 /**
2451  * sets up the scanner's register for warming up. We scan 2 lines without moving.
2452  *
2453  */
init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* local_reg) const2454 void CommandSetGl646::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor,
2455                                            Genesys_Register_Set* local_reg) const
2456 {
2457     DBG_HELPER(dbg);
2458     (void) sensor;
2459 
2460   dev->frontend = dev->frontend_initial;
2461 
2462     unsigned resolution = 300;
2463     const auto& local_sensor = sanei_genesys_find_sensor(dev, resolution, 1,
2464                                                          dev->settings.scan_method);
2465 
2466     // set up for a full width 2 lines gray scan without moving
2467     unsigned pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
2468 
2469     ScanSession session;
2470     session.params.xres = resolution;
2471     session.params.yres = resolution;
2472     session.params.startx = 0;
2473     session.params.starty = 0;
2474     session.params.pixels = pixels;
2475     session.params.lines = 2;
2476     session.params.depth = dev->model->bpp_gray_values.front();
2477     session.params.channels = 1;
2478     session.params.scan_method = dev->settings.scan_method;
2479     session.params.scan_mode = ScanColorMode::GRAY;
2480     session.params.color_filter =  ColorFilter::RED;
2481     session.params.contrast_adjustment = 0;
2482     session.params.brightness_adjustment = 0;
2483     session.params.flags = ScanFlag::DISABLE_SHADING |
2484                            ScanFlag::DISABLE_GAMMA;
2485     if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) {
2486         session.params.flags |= ScanFlag::USE_XPA;
2487     }
2488     compute_session(dev, session, local_sensor);
2489 
2490     dev->cmd_set->init_regs_for_scan_session(dev, local_sensor, &dev->reg, session);
2491 
2492   /* we are not going to move, so clear these bits */
2493     dev->reg.find_reg(0x02).value &= ~REG_0x02_FASTFED;
2494 
2495   /* copy to local_reg */
2496   *local_reg = dev->reg;
2497 
2498   /* turn off motor during this scan */
2499   sanei_genesys_set_motor_power(*local_reg, false);
2500 
2501     // now registers are ok, write them to scanner
2502     gl646_set_fe(dev, local_sensor, AFE_SET, session.params.xres);
2503 }
2504 
2505 /* *
2506  * initialize ASIC : registers, motor tables, and gamma tables
2507  * then ensure scanner's head is at home
2508  * @param dev device description of the scanner to initialize
2509  */
init(Genesys_Device* dev) const2510 void CommandSetGl646::init(Genesys_Device* dev) const
2511 {
2512     DBG_INIT();
2513     DBG_HELPER(dbg);
2514 
2515     std::uint8_t val = 0;
2516     std::uint32_t addr = 0xdead;
2517   size_t len;
2518 
2519     // to detect real power up condition, we write to REG_0x41 with pwrbit set, then read it back.
2520     // When scanner is cold (just replugged) PWRBIT will be set in the returned value
2521     auto status = scanner_read_status(*dev);
2522     if (status.is_replugged) {
2523       DBG(DBG_info, "%s: device is cold\n", __func__);
2524     } else {
2525       DBG(DBG_info, "%s: device is hot\n", __func__);
2526     }
2527 
2528   const auto& sensor = sanei_genesys_find_sensor_any(dev);
2529 
2530   /* if scanning session hasn't been initialized, set it up */
2531   if (!dev->already_initialized)
2532     {
2533       dev->dark_average_data.clear();
2534       dev->white_average_data.clear();
2535 
2536       dev->settings.color_filter = ColorFilter::GREEN;
2537 
2538       /* Set default values for registers */
2539       gl646_init_regs (dev);
2540 
2541         // Init shading data
2542         sanei_genesys_init_shading_data(dev, sensor,
2543                                         dev->model->x_size_calib_mm * sensor.full_resolution /
2544                                             MM_PER_INCH);
2545 
2546         dev->initial_regs = dev->reg;
2547     }
2548 
2549     // execute physical unit init only if cold
2550     if (status.is_replugged)
2551     {
2552       DBG(DBG_info, "%s: device is cold\n", __func__);
2553 
2554         val = 0x04;
2555         dev->interface->get_usb_device().control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER,
2556                                                      VALUE_INIT, INDEX, 1, &val);
2557 
2558         // ASIC reset
2559         dev->interface->write_register(0x0e, 0x00);
2560         dev->interface->sleep_ms(100);
2561 
2562         // Write initial registers
2563         dev->interface->write_registers(dev->reg);
2564 
2565         // send gamma tables if needed
2566         dev->cmd_set->send_gamma_table(dev, sensor);
2567 
2568         // Set powersaving(default = 15 minutes)
2569         dev->cmd_set->set_powersaving(dev, 15);
2570     }
2571 
2572     // Set analog frontend
2573     gl646_set_fe(dev, sensor, AFE_INIT, 0);
2574 
2575   /* GPO enabling for XP200 */
2576     if (dev->model->sensor_id == SensorId::CIS_XP200) {
2577         dev->interface->write_register(0x68, dev->gpo.regs.get_value(0x68));
2578         dev->interface->write_register(0x69, dev->gpo.regs.get_value(0x69));
2579 
2580         // enable GPIO
2581         gl646_gpio_output_enable(dev->interface->get_usb_device(), 6);
2582 
2583         // writes 0 to GPIO
2584         gl646_gpio_write(dev->interface->get_usb_device(), 0);
2585 
2586         // clear GPIO enable
2587         gl646_gpio_output_enable(dev->interface->get_usb_device(), 0);
2588 
2589         dev->interface->write_register(0x66, 0x10);
2590         dev->interface->write_register(0x66, 0x00);
2591         dev->interface->write_register(0x66, 0x10);
2592     }
2593 
2594   /* MD6471/G2410 and XP200 read/write data from an undocumented memory area which
2595    * is after the second slope table */
2596     if (dev->model->gpio_id != GpioId::HP3670 &&
2597         dev->model->gpio_id != GpioId::HP2400)
2598     {
2599       switch (sensor.full_resolution)
2600 	{
2601 	case 600:
2602 	  addr = 0x08200;
2603 	  break;
2604 	case 1200:
2605 	  addr = 0x10200;
2606 	  break;
2607 	case 2400:
2608 	  addr = 0x1fa00;
2609 	  break;
2610 	}
2611     sanei_genesys_set_buffer_address(dev, addr);
2612 
2613       sanei_usb_set_timeout (2 * 1000);
2614       len = 6;
2615         // for some reason, read fails here for MD6471, HP2300 and XP200 one time out of
2616         // 2 scanimage launches
2617         try {
2618             dev->interface->bulk_read_data(0x45, dev->control, len);
2619         } catch (...) {
2620             dev->interface->bulk_read_data(0x45, dev->control, len);
2621         }
2622       sanei_usb_set_timeout (30 * 1000);
2623     }
2624   else
2625     /* HP2400 and HP3670 case */
2626     {
2627       dev->control[0] = 0x00;
2628       dev->control[1] = 0x00;
2629       dev->control[2] = 0x01;
2630       dev->control[3] = 0x00;
2631       dev->control[4] = 0x00;
2632       dev->control[5] = 0x00;
2633     }
2634 
2635   /* ensure head is correctly parked, and check lock */
2636     if (!dev->model->is_sheetfed) {
2637         move_back_home(dev, true);
2638     }
2639 
2640   /* here session and device are initialized */
2641     dev->already_initialized = true;
2642 }
2643 
simple_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, const ScanSession& session, bool move, std::vector<std::uint8_t>& data, const char* scan_identifier)2644 static void simple_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
2645                         const ScanSession& session, bool move,
2646                         std::vector<std::uint8_t>& data, const char* scan_identifier)
2647 {
2648     unsigned lines = session.output_line_count;
2649     if (!dev->model->is_cis) {
2650         lines++;
2651     }
2652 
2653     std::size_t size = lines * session.params.pixels;
2654     unsigned bpp = session.params.depth == 16 ? 2 : 1;
2655 
2656     size *= bpp * session.params.channels;
2657   data.clear();
2658   data.resize(size);
2659 
2660     // initialize frontend
2661     gl646_set_fe(dev, sensor, AFE_SET, session.params.xres);
2662 
2663     // no watch dog for simple scan
2664     dev->reg.find_reg(0x01).value &= ~REG_0x01_DOGENB;
2665 
2666   /* one table movement for simple scan */
2667     dev->reg.find_reg(0x02).value &= ~REG_0x02_FASTFED;
2668 
2669     if (!move) {
2670         sanei_genesys_set_motor_power(dev->reg, false);
2671     }
2672 
2673   /* no automatic go home when using XPA */
2674     if (session.params.scan_method == ScanMethod::TRANSPARENCY) {
2675         dev->reg.find_reg(0x02).value &= ~REG_0x02_AGOHOME;
2676     }
2677 
2678     // write scan registers
2679     dev->interface->write_registers(dev->reg);
2680 
2681     // starts scan
2682     dev->cmd_set->begin_scan(dev, sensor, &dev->reg, move);
2683 
2684     if (is_testing_mode()) {
2685         dev->interface->test_checkpoint(scan_identifier);
2686         return;
2687     }
2688 
2689     wait_until_buffer_non_empty(dev, true);
2690 
2691     // now we're on target, we can read data
2692     sanei_genesys_read_data_from_scanner(dev, data.data(), size);
2693 
2694   /* in case of CIS scanner, we must reorder data */
2695     if (dev->model->is_cis && session.params.scan_mode == ScanColorMode::COLOR_SINGLE_PASS) {
2696         auto pixels_count = session.params.pixels;
2697 
2698         std::vector<std::uint8_t> buffer(pixels_count * 3 * bpp);
2699 
2700         if (bpp == 1) {
2701             for (unsigned y = 0; y < lines; y++) {
2702                 // reorder line
2703                 for (unsigned x = 0; x < pixels_count; x++) {
2704                     buffer[x * 3] = data[y * pixels_count * 3 + x];
2705                     buffer[x * 3 + 1] = data[y * pixels_count * 3 + pixels_count + x];
2706                     buffer[x * 3 + 2] = data[y * pixels_count * 3 + 2 * pixels_count + x];
2707                 }
2708                 // copy line back
2709                 std::memcpy(data.data() + pixels_count * 3 * y, buffer.data(), pixels_count * 3);
2710             }
2711         } else {
2712             for (unsigned y = 0; y < lines; y++) {
2713                 // reorder line
2714                 auto pixels_count = session.params.pixels;
2715                 for (unsigned x = 0; x < pixels_count; x++) {
2716                     buffer[x * 6] = data[y * pixels_count * 6 + x * 2];
2717                     buffer[x * 6 + 1] = data[y * pixels_count * 6 + x * 2 + 1];
2718                     buffer[x * 6 + 2] = data[y * pixels_count * 6 + 2 * pixels_count + x * 2];
2719                     buffer[x * 6 + 3] = data[y * pixels_count * 6 + 2 * pixels_count + x * 2 + 1];
2720                     buffer[x * 6 + 4] = data[y * pixels_count * 6 + 4 * pixels_count + x * 2];
2721                     buffer[x * 6 + 5] = data[y * pixels_count * 6 + 4 * pixels_count + x * 2 + 1];
2722                 }
2723                 // copy line back
2724                 std::memcpy(data.data() + pixels_count * 6 * y, buffer.data(),pixels_count * 6);
2725             }
2726         }
2727     }
2728 
2729     // end scan , waiting the motor to stop if needed (if moving), but without ejecting doc
2730     end_scan_impl(dev, &dev->reg, true, false);
2731 }
2732 
2733 /**
2734  * update the status of the required sensor in the scanner session
2735  * the button fields are used to make events 'sticky'
2736  */
update_hardware_sensors(Genesys_Scanner* session) const2737 void CommandSetGl646::update_hardware_sensors(Genesys_Scanner* session) const
2738 {
2739     DBG_HELPER(dbg);
2740   Genesys_Device *dev = session->dev;
2741     std::uint8_t value;
2742 
2743     // do what is needed to get a new set of events, but try to not loose any of them.
2744     gl646_gpio_read(dev->interface->get_usb_device(), &value);
2745     DBG(DBG_io, "%s: GPIO=0x%02x\n", __func__, value);
2746 
2747     // scan button
2748     if (dev->model->buttons & GENESYS_HAS_SCAN_SW) {
2749         switch (dev->model->gpio_id) {
2750         case GpioId::XP200:
2751             session->buttons[BUTTON_SCAN_SW].write((value & 0x02) != 0);
2752             break;
2753         case GpioId::MD_5345:
2754             session->buttons[BUTTON_SCAN_SW].write(value == 0x16);
2755             break;
2756         case GpioId::HP2300:
2757             session->buttons[BUTTON_SCAN_SW].write(value == 0x6c);
2758             break;
2759         case GpioId::HP3670:
2760         case GpioId::HP2400:
2761             session->buttons[BUTTON_SCAN_SW].write((value & 0x20) == 0);
2762             break;
2763         default:
2764                 throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
2765 	}
2766     }
2767 
2768     // email button
2769     if (dev->model->buttons & GENESYS_HAS_EMAIL_SW) {
2770         switch (dev->model->gpio_id) {
2771         case GpioId::MD_5345:
2772             session->buttons[BUTTON_EMAIL_SW].write(value == 0x12);
2773             break;
2774         case GpioId::HP3670:
2775         case GpioId::HP2400:
2776             session->buttons[BUTTON_EMAIL_SW].write((value & 0x08) == 0);
2777             break;
2778         default:
2779                 throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
2780     }
2781     }
2782 
2783     // copy button
2784     if (dev->model->buttons & GENESYS_HAS_COPY_SW) {
2785         switch (dev->model->gpio_id) {
2786         case GpioId::MD_5345:
2787             session->buttons[BUTTON_COPY_SW].write(value == 0x11);
2788             break;
2789         case GpioId::HP2300:
2790             session->buttons[BUTTON_COPY_SW].write(value == 0x5c);
2791             break;
2792         case GpioId::HP3670:
2793         case GpioId::HP2400:
2794             session->buttons[BUTTON_COPY_SW].write((value & 0x10) == 0);
2795             break;
2796         default:
2797                 throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
2798     }
2799     }
2800 
2801     // power button
2802     if (dev->model->buttons & GENESYS_HAS_POWER_SW) {
2803         switch (dev->model->gpio_id) {
2804         case GpioId::MD_5345:
2805             session->buttons[BUTTON_POWER_SW].write(value == 0x14);
2806             break;
2807         default:
2808                 throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
2809     }
2810     }
2811 
2812     // ocr button
2813     if (dev->model->buttons & GENESYS_HAS_OCR_SW) {
2814         switch (dev->model->gpio_id) {
2815     case GpioId::MD_5345:
2816             session->buttons[BUTTON_OCR_SW].write(value == 0x13);
2817             break;
2818 	default:
2819                 throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
2820     }
2821     }
2822 
2823     // document detection
2824     if (dev->model->buttons & GENESYS_HAS_PAGE_LOADED_SW) {
2825         switch (dev->model->gpio_id) {
2826         case GpioId::XP200:
2827             session->buttons[BUTTON_PAGE_LOADED_SW].write((value & 0x04) != 0);
2828             break;
2829         default:
2830                 throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
2831     }
2832     }
2833 
2834   /* XPA detection */
2835     if (dev->model->has_method(ScanMethod::TRANSPARENCY)) {
2836         switch (dev->model->gpio_id) {
2837             case GpioId::HP3670:
2838             case GpioId::HP2400:
2839 	  /* test if XPA is plugged-in */
2840             if ((value & 0x40) == 0) {
2841                 session->opt[OPT_SOURCE].cap &= ~SANE_CAP_INACTIVE;
2842             } else {
2843                 session->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE;
2844             }
2845       break;
2846             default:
2847                 throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
2848     }
2849     }
2850 }
2851 
update_home_sensor_gpio(Genesys_Device& dev) const2852 void CommandSetGl646::update_home_sensor_gpio(Genesys_Device& dev) const
2853 {
2854     DBG_HELPER(dbg);
2855     (void) dev;
2856 }
2857 
write_control(Genesys_Device* dev, const Genesys_Sensor& sensor, int resolution)2858 static void write_control(Genesys_Device* dev, const Genesys_Sensor& sensor, int resolution)
2859 {
2860     DBG_HELPER(dbg);
2861     std::uint8_t control[4];
2862     std::uint32_t addr = 0xdead;
2863 
2864   /* 2300 does not write to 'control' */
2865     if (dev->model->motor_id == MotorId::HP2300) {
2866         return;
2867     }
2868 
2869   /* MD6471/G2410/HP2300 and XP200 read/write data from an undocumented memory area which
2870    * is after the second slope table */
2871   switch (sensor.full_resolution)
2872     {
2873     case 600:
2874       addr = 0x08200;
2875       break;
2876     case 1200:
2877       addr = 0x10200;
2878       break;
2879     case 2400:
2880       addr = 0x1fa00;
2881       break;
2882     default:
2883         throw SaneException("failed to compute control address");
2884     }
2885 
2886   /* XP200 sets dpi, what other scanner put is unknown yet */
2887   switch (dev->model->motor_id)
2888     {
2889         case MotorId::XP200:
2890       /* we put scan's dpi, not motor one */
2891             control[0] = resolution & 0xff;
2892             control[1] = (resolution >> 8) & 0xff;
2893       control[2] = dev->control[4];
2894       control[3] = dev->control[5];
2895       break;
2896         case MotorId::HP3670:
2897         case MotorId::HP2400:
2898         case MotorId::MD_5345:
2899         default:
2900       control[0] = dev->control[2];
2901       control[1] = dev->control[3];
2902       control[2] = dev->control[4];
2903       control[3] = dev->control[5];
2904       break;
2905     }
2906 
2907     dev->interface->write_buffer(0x3c, addr, control, 4);
2908 }
2909 
wait_for_motor_stop(Genesys_Device* dev) const2910 void CommandSetGl646::wait_for_motor_stop(Genesys_Device* dev) const
2911 {
2912     (void) dev;
2913 }
2914 
send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t* data, int size) const2915 void CommandSetGl646::send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor,
2916                                         std::uint8_t* data, int size) const
2917 {
2918     (void) dev;
2919     (void) sensor;
2920     (void) data;
2921     (void) size;
2922     throw SaneException("not implemented");
2923 }
2924 
calculate_scan_session(const Genesys_Device* dev, const Genesys_Sensor& sensor, const Genesys_Settings& settings) const2925 ScanSession CommandSetGl646::calculate_scan_session(const Genesys_Device* dev,
2926                                                     const Genesys_Sensor& sensor,
2927                                                     const Genesys_Settings& settings) const
2928 {
2929     // compute distance to move
2930     float move = 0;
2931     if (!dev->model->is_sheetfed) {
2932         move = dev->model->y_offset;
2933         // add tl_y to base movement
2934     }
2935     move += settings.tl_y;
2936 
2937     if (move < 0) {
2938         DBG(DBG_error, "%s: overriding negative move value %f\n", __func__, move);
2939         move = 0;
2940     }
2941 
2942     move = static_cast<float>((move * dev->motor.base_ydpi) / MM_PER_INCH);
2943     float start = settings.tl_x;
2944     if (settings.scan_method == ScanMethod::FLATBED) {
2945         start += dev->model->x_offset;
2946     } else {
2947         start += dev->model->x_offset_ta;
2948     }
2949     start = static_cast<float>((start * settings.xres) / MM_PER_INCH);
2950 
2951     ScanSession session;
2952     session.params.xres = settings.xres;
2953     session.params.yres = settings.yres;
2954     session.params.startx = static_cast<unsigned>(start);
2955     session.params.starty = static_cast<unsigned>(move);
2956     session.params.pixels = settings.pixels;
2957     session.params.requested_pixels = settings.requested_pixels;
2958     session.params.lines = settings.lines;
2959     session.params.depth = settings.depth;
2960     session.params.channels = settings.get_channels();
2961     session.params.scan_method = dev->settings.scan_method;
2962     session.params.scan_mode = settings.scan_mode;
2963     session.params.color_filter = settings.color_filter;
2964     session.params.contrast_adjustment = settings.contrast;
2965     session.params.brightness_adjustment = settings.brightness;
2966     session.params.flags = ScanFlag::AUTO_GO_HOME;
2967     if (settings.scan_method == ScanMethod::TRANSPARENCY) {
2968         session.params.flags |= ScanFlag::USE_XPA;
2969     }
2970     compute_session(dev, session, sensor);
2971 
2972     return session;
2973 }
2974 
asic_boot(Genesys_Device *dev, bool cold) const2975 void CommandSetGl646::asic_boot(Genesys_Device *dev, bool cold) const
2976 {
2977     (void) dev;
2978     (void) cold;
2979     throw SaneException("not implemented");
2980 }
2981 
2982 } // namespace gl646
2983 } // namespace genesys
2984