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