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