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