1141cc406Sopenharmony_ci/* sane - Scanner Access Now Easy.
2141cc406Sopenharmony_ci
3141cc406Sopenharmony_ci   Copyright (C) 2003 Oliver Rauch
4141cc406Sopenharmony_ci   Copyright (C) 2003, 2004 Henning Meier-Geinitz <henning@meier-geinitz.de>
5141cc406Sopenharmony_ci   Copyright (C) 2004 Gerhard Jaeger <gerhard@gjaeger.de>
6141cc406Sopenharmony_ci   Copyright (C) 2004-2013 Stéphane Voltz <stef.dev@free.fr>
7141cc406Sopenharmony_ci   Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org>
8141cc406Sopenharmony_ci   Copyright (C) 2007 Luke <iceyfor@gmail.com>
9141cc406Sopenharmony_ci   Copyright (C) 2011 Alexey Osipov <simba@lerlan.ru> for HP2400 description
10141cc406Sopenharmony_ci                      and tuning
11141cc406Sopenharmony_ci
12141cc406Sopenharmony_ci   This file is part of the SANE package.
13141cc406Sopenharmony_ci
14141cc406Sopenharmony_ci   This program is free software; you can redistribute it and/or
15141cc406Sopenharmony_ci   modify it under the terms of the GNU General Public License as
16141cc406Sopenharmony_ci   published by the Free Software Foundation; either version 2 of the
17141cc406Sopenharmony_ci   License, or (at your option) any later version.
18141cc406Sopenharmony_ci
19141cc406Sopenharmony_ci   This program is distributed in the hope that it will be useful, but
20141cc406Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
21141cc406Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22141cc406Sopenharmony_ci   General Public License for more details.
23141cc406Sopenharmony_ci
24141cc406Sopenharmony_ci   You should have received a copy of the GNU General Public License
25141cc406Sopenharmony_ci   along with this program.  If not, see <https://www.gnu.org/licenses/>.
26141cc406Sopenharmony_ci*/
27141cc406Sopenharmony_ci
28141cc406Sopenharmony_ci#define DEBUG_DECLARE_ONLY
29141cc406Sopenharmony_ci
30141cc406Sopenharmony_ci#include "gl646.h"
31141cc406Sopenharmony_ci#include "gl646_registers.h"
32141cc406Sopenharmony_ci#include "test_settings.h"
33141cc406Sopenharmony_ci
34141cc406Sopenharmony_ci#include <vector>
35141cc406Sopenharmony_ci
36141cc406Sopenharmony_cinamespace genesys {
37141cc406Sopenharmony_cinamespace gl646 {
38141cc406Sopenharmony_ci
39141cc406Sopenharmony_cinamespace {
40141cc406Sopenharmony_ciconstexpr unsigned CALIBRATION_LINES = 10;
41141cc406Sopenharmony_ci} // namespace
42141cc406Sopenharmony_ci
43141cc406Sopenharmony_cistatic void write_control(Genesys_Device* dev, const Genesys_Sensor& sensor, int resolution);
44141cc406Sopenharmony_ci
45141cc406Sopenharmony_ci
46141cc406Sopenharmony_cistatic void gl646_set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set,
47141cc406Sopenharmony_ci                         int dpi);
48141cc406Sopenharmony_ci
49141cc406Sopenharmony_cistatic void simple_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
50141cc406Sopenharmony_ci                        const ScanSession& session, bool move,
51141cc406Sopenharmony_ci                        std::vector<std::uint8_t>& data, const char* test_identifier);
52141cc406Sopenharmony_ci/**
53141cc406Sopenharmony_ci * Send the stop scan command
54141cc406Sopenharmony_ci * */
55141cc406Sopenharmony_cistatic void end_scan_impl(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop,
56141cc406Sopenharmony_ci                          bool eject);
57141cc406Sopenharmony_ci
58141cc406Sopenharmony_ci/**
59141cc406Sopenharmony_ci * master motor settings table entry
60141cc406Sopenharmony_ci */
61141cc406Sopenharmony_cistruct Motor_Master
62141cc406Sopenharmony_ci{
63141cc406Sopenharmony_ci    MotorId motor_id;
64141cc406Sopenharmony_ci    unsigned dpi;
65141cc406Sopenharmony_ci    unsigned channels;
66141cc406Sopenharmony_ci
67141cc406Sopenharmony_ci    // settings
68141cc406Sopenharmony_ci    StepType steptype;
69141cc406Sopenharmony_ci    bool fastmod; // fast scanning
70141cc406Sopenharmony_ci    bool fastfed; // fast fed slope tables
71141cc406Sopenharmony_ci    SANE_Int mtrpwm;
72141cc406Sopenharmony_ci    MotorSlope slope1;
73141cc406Sopenharmony_ci    MotorSlope slope2;
74141cc406Sopenharmony_ci    SANE_Int fwdbwd; // forward/backward steps
75141cc406Sopenharmony_ci};
76141cc406Sopenharmony_ci
77141cc406Sopenharmony_ci/**
78141cc406Sopenharmony_ci * master motor settings, for a given motor and dpi,
79141cc406Sopenharmony_ci * it gives steps and speed information
80141cc406Sopenharmony_ci */
81141cc406Sopenharmony_cistatic Motor_Master motor_master[] = {
82141cc406Sopenharmony_ci    /* HP3670 motor settings */
83141cc406Sopenharmony_ci    {MotorId::HP3670, 50, 3, StepType::HALF, false, true, 1,
84141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2329, 120, 229),
85141cc406Sopenharmony_ci     MotorSlope::create_from_steps(3399, 337, 192), 192},
86141cc406Sopenharmony_ci
87141cc406Sopenharmony_ci    {MotorId::HP3670, 75, 3, StepType::FULL, false, true, 1,
88141cc406Sopenharmony_ci     MotorSlope::create_from_steps(3429, 305, 200),
89141cc406Sopenharmony_ci     MotorSlope::create_from_steps(3399, 337, 192), 192},
90141cc406Sopenharmony_ci
91141cc406Sopenharmony_ci    {MotorId::HP3670, 100, 3, StepType::HALF, false, true, 1,
92141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2905, 187, 143),
93141cc406Sopenharmony_ci     MotorSlope::create_from_steps(3399, 337, 192), 192},
94141cc406Sopenharmony_ci
95141cc406Sopenharmony_ci    {MotorId::HP3670, 150, 3, StepType::HALF, false, true, 1,
96141cc406Sopenharmony_ci     MotorSlope::create_from_steps(3429, 305, 73),
97141cc406Sopenharmony_ci     MotorSlope::create_from_steps(3399, 337, 192), 192},
98141cc406Sopenharmony_ci
99141cc406Sopenharmony_ci    {MotorId::HP3670, 300, 3, StepType::HALF, false, true, 1,
100141cc406Sopenharmony_ci     MotorSlope::create_from_steps(1055, 563, 11),
101141cc406Sopenharmony_ci     MotorSlope::create_from_steps(3399, 337, 192), 192},
102141cc406Sopenharmony_ci
103141cc406Sopenharmony_ci    {MotorId::HP3670, 600, 3, StepType::FULL, false, true, 0,
104141cc406Sopenharmony_ci     MotorSlope::create_from_steps(10687, 5126, 3),
105141cc406Sopenharmony_ci     MotorSlope::create_from_steps(3399, 337, 192), 192},
106141cc406Sopenharmony_ci
107141cc406Sopenharmony_ci    {MotorId::HP3670,1200, 3, StepType::HALF, false, true, 0,
108141cc406Sopenharmony_ci     MotorSlope::create_from_steps(15937, 6375, 3),
109141cc406Sopenharmony_ci     MotorSlope::create_from_steps(3399, 337, 192), 192},
110141cc406Sopenharmony_ci
111141cc406Sopenharmony_ci    {MotorId::HP3670, 50, 1, StepType::HALF, false, true, 1,
112141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2329, 120, 229),
113141cc406Sopenharmony_ci     MotorSlope::create_from_steps(3399, 337, 192), 192},
114141cc406Sopenharmony_ci
115141cc406Sopenharmony_ci    {MotorId::HP3670, 75, 1, StepType::FULL, false, true, 1,
116141cc406Sopenharmony_ci     MotorSlope::create_from_steps(3429, 305, 200),
117141cc406Sopenharmony_ci     MotorSlope::create_from_steps(3399, 337, 192), 192},
118141cc406Sopenharmony_ci
119141cc406Sopenharmony_ci    {MotorId::HP3670, 100, 1, StepType::HALF, false, true, 1,
120141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2905, 187, 143),
121141cc406Sopenharmony_ci     MotorSlope::create_from_steps(3399, 337, 192), 192},
122141cc406Sopenharmony_ci
123141cc406Sopenharmony_ci    {MotorId::HP3670, 150, 1, StepType::HALF, false, true, 1,
124141cc406Sopenharmony_ci     MotorSlope::create_from_steps(3429, 305, 73),
125141cc406Sopenharmony_ci     MotorSlope::create_from_steps(3399, 337, 192), 192},
126141cc406Sopenharmony_ci
127141cc406Sopenharmony_ci    {MotorId::HP3670, 300, 1, StepType::HALF, false, true, 1,
128141cc406Sopenharmony_ci     MotorSlope::create_from_steps(1055, 563, 11),
129141cc406Sopenharmony_ci     MotorSlope::create_from_steps(3399, 337, 192), 192},
130141cc406Sopenharmony_ci
131141cc406Sopenharmony_ci    {MotorId::HP3670, 600, 1, StepType::FULL, false, true, 0,
132141cc406Sopenharmony_ci     MotorSlope::create_from_steps(10687, 5126, 3),
133141cc406Sopenharmony_ci     MotorSlope::create_from_steps(3399, 337, 192), 192},
134141cc406Sopenharmony_ci
135141cc406Sopenharmony_ci    {MotorId::HP3670,1200, 1, StepType::HALF, false, true, 0,
136141cc406Sopenharmony_ci     MotorSlope::create_from_steps(15937, 6375, 3),
137141cc406Sopenharmony_ci     MotorSlope::create_from_steps(3399, 337, 192), 192},
138141cc406Sopenharmony_ci
139141cc406Sopenharmony_ci    /* HP2400/G2410 motor settings base motor dpi = 600 */
140141cc406Sopenharmony_ci    {MotorId::HP2400, 50, 3, StepType::FULL, false, true, 63,
141141cc406Sopenharmony_ci     MotorSlope::create_from_steps(8736, 601, 120),
142141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 192), 192},
143141cc406Sopenharmony_ci
144141cc406Sopenharmony_ci    {MotorId::HP2400, 100, 3, StepType::HALF, false, true, 63,
145141cc406Sopenharmony_ci     MotorSlope::create_from_steps(8736, 601, 120),
146141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 192), 192},
147141cc406Sopenharmony_ci
148141cc406Sopenharmony_ci    {MotorId::HP2400, 150, 3, StepType::HALF, false, true, 63,
149141cc406Sopenharmony_ci     MotorSlope::create_from_steps(15902, 902, 67),
150141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 192), 192},
151141cc406Sopenharmony_ci
152141cc406Sopenharmony_ci    {MotorId::HP2400, 300, 3, StepType::HALF, false, true, 63,
153141cc406Sopenharmony_ci     MotorSlope::create_from_steps(16703, 2188, 32),
154141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 192), 192},
155141cc406Sopenharmony_ci
156141cc406Sopenharmony_ci    {MotorId::HP2400, 600, 3, StepType::FULL, false, true, 63,
157141cc406Sopenharmony_ci     MotorSlope::create_from_steps(18761, 18761, 3),
158141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 627, 192), 192},
159141cc406Sopenharmony_ci
160141cc406Sopenharmony_ci    {MotorId::HP2400,1200, 3, StepType::HALF, false, true, 63,
161141cc406Sopenharmony_ci     MotorSlope::create_from_steps(43501, 43501, 3),
162141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 627, 192), 192},
163141cc406Sopenharmony_ci
164141cc406Sopenharmony_ci    {MotorId::HP2400, 50, 1, StepType::FULL, false, true, 63,
165141cc406Sopenharmony_ci     MotorSlope::create_from_steps(8736, 601, 120),
166141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 192), 192},
167141cc406Sopenharmony_ci
168141cc406Sopenharmony_ci    {MotorId::HP2400, 100, 1, StepType::HALF, false, true, 63,
169141cc406Sopenharmony_ci     MotorSlope::create_from_steps(8736, 601, 120),
170141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 192), 192},
171141cc406Sopenharmony_ci
172141cc406Sopenharmony_ci    {MotorId::HP2400, 150, 1, StepType::HALF, false, true, 63,
173141cc406Sopenharmony_ci     MotorSlope::create_from_steps(15902, 902, 67),
174141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 192), 192},
175141cc406Sopenharmony_ci
176141cc406Sopenharmony_ci    {MotorId::HP2400, 300, 1, StepType::HALF, false, true, 63,
177141cc406Sopenharmony_ci     MotorSlope::create_from_steps(16703, 2188, 32),
178141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 192), 192},
179141cc406Sopenharmony_ci
180141cc406Sopenharmony_ci    {MotorId::HP2400, 600, 1, StepType::FULL, false, true, 63,
181141cc406Sopenharmony_ci     MotorSlope::create_from_steps(18761, 18761, 3),
182141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 192), 192},
183141cc406Sopenharmony_ci
184141cc406Sopenharmony_ci    {MotorId::HP2400,1200, 1, StepType::HALF, false, true, 63,
185141cc406Sopenharmony_ci     MotorSlope::create_from_steps(43501, 43501, 3),
186141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 192), 192},
187141cc406Sopenharmony_ci
188141cc406Sopenharmony_ci    /* XP 200 motor settings */
189141cc406Sopenharmony_ci    {MotorId::XP200, 75, 3, StepType::HALF, true, false, 0,
190141cc406Sopenharmony_ci     MotorSlope::create_from_steps(6000, 2136, 4),
191141cc406Sopenharmony_ci     MotorSlope::create_from_steps(12000, 1200, 8), 1},
192141cc406Sopenharmony_ci
193141cc406Sopenharmony_ci    {MotorId::XP200, 100, 3, StepType::HALF, true, false, 0,
194141cc406Sopenharmony_ci     MotorSlope::create_from_steps(6000, 2850, 4),
195141cc406Sopenharmony_ci     MotorSlope::create_from_steps(12000, 1200, 8), 1},
196141cc406Sopenharmony_ci
197141cc406Sopenharmony_ci    {MotorId::XP200, 200, 3, StepType::HALF, true, false, 0,
198141cc406Sopenharmony_ci     MotorSlope::create_from_steps(6999, 5700, 4),
199141cc406Sopenharmony_ci     MotorSlope::create_from_steps(12000, 1200, 8), 1},
200141cc406Sopenharmony_ci
201141cc406Sopenharmony_ci    {MotorId::XP200, 250, 3, StepType::HALF, true, false, 0,
202141cc406Sopenharmony_ci     MotorSlope::create_from_steps(6999, 6999, 4),
203141cc406Sopenharmony_ci     MotorSlope::create_from_steps(12000, 1200, 8), 1},
204141cc406Sopenharmony_ci
205141cc406Sopenharmony_ci    {MotorId::XP200, 300, 3, StepType::HALF, true, false, 0,
206141cc406Sopenharmony_ci     MotorSlope::create_from_steps(13500, 13500, 4),
207141cc406Sopenharmony_ci     MotorSlope::create_from_steps(12000, 1200, 8), 1},
208141cc406Sopenharmony_ci
209141cc406Sopenharmony_ci    {MotorId::XP200, 600, 3, StepType::HALF, true, true, 0,
210141cc406Sopenharmony_ci     MotorSlope::create_from_steps(31998, 31998, 4),
211141cc406Sopenharmony_ci     MotorSlope::create_from_steps(12000, 1200, 2), 1},
212141cc406Sopenharmony_ci
213141cc406Sopenharmony_ci    {MotorId::XP200, 75, 1, StepType::HALF, true, false, 0,
214141cc406Sopenharmony_ci     MotorSlope::create_from_steps(6000, 2000, 4),
215141cc406Sopenharmony_ci     MotorSlope::create_from_steps(12000, 1200, 8), 1},
216141cc406Sopenharmony_ci
217141cc406Sopenharmony_ci    {MotorId::XP200, 100, 1, StepType::HALF, true, false, 0,
218141cc406Sopenharmony_ci     MotorSlope::create_from_steps(6000, 1300, 4),
219141cc406Sopenharmony_ci     MotorSlope::create_from_steps(12000, 1200, 8), 1},
220141cc406Sopenharmony_ci
221141cc406Sopenharmony_ci    {MotorId::XP200, 200, 1, StepType::HALF, true, true, 0,
222141cc406Sopenharmony_ci     MotorSlope::create_from_steps(6000, 3666, 4),
223141cc406Sopenharmony_ci     MotorSlope::create_from_steps(12000, 1200, 8), 1},
224141cc406Sopenharmony_ci
225141cc406Sopenharmony_ci    {MotorId::XP200, 300, 1, StepType::HALF, true, false, 0,
226141cc406Sopenharmony_ci     MotorSlope::create_from_steps(6500, 6500, 4),
227141cc406Sopenharmony_ci     MotorSlope::create_from_steps(12000, 1200, 8), 1},
228141cc406Sopenharmony_ci
229141cc406Sopenharmony_ci    {MotorId::XP200, 600, 1, StepType::HALF, true, true, 0,
230141cc406Sopenharmony_ci     MotorSlope::create_from_steps(24000, 24000, 4),
231141cc406Sopenharmony_ci     MotorSlope::create_from_steps(12000, 1200, 2), 1},
232141cc406Sopenharmony_ci
233141cc406Sopenharmony_ci    /* HP scanjet 2300c */
234141cc406Sopenharmony_ci    {MotorId::HP2300, 75, 3, StepType::FULL, false, true, 63,
235141cc406Sopenharmony_ci     MotorSlope::create_from_steps(8139, 560, 120),
236141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 120), 16},
237141cc406Sopenharmony_ci
238141cc406Sopenharmony_ci    {MotorId::HP2300, 150, 3, StepType::HALF, false, true, 63,
239141cc406Sopenharmony_ci     MotorSlope::create_from_steps(7903, 543, 67),
240141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 120), 16},
241141cc406Sopenharmony_ci
242141cc406Sopenharmony_ci    {MotorId::HP2300, 300, 3, StepType::HALF, false, true, 63,
243141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2175, 1087, 3),
244141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 120), 16},
245141cc406Sopenharmony_ci
246141cc406Sopenharmony_ci    {MotorId::HP2300, 600, 3, StepType::HALF, false, true, 63,
247141cc406Sopenharmony_ci     MotorSlope::create_from_steps(8700, 4350, 3),
248141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 120), 16},
249141cc406Sopenharmony_ci
250141cc406Sopenharmony_ci    {MotorId::HP2300,1200, 3, StepType::HALF, false, true, 63,
251141cc406Sopenharmony_ci     MotorSlope::create_from_steps(17400, 8700, 3),
252141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 120), 16},
253141cc406Sopenharmony_ci
254141cc406Sopenharmony_ci    {MotorId::HP2300, 75, 1, StepType::FULL, false, true, 63,
255141cc406Sopenharmony_ci     MotorSlope::create_from_steps(8139, 560, 120),
256141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 120), 16},
257141cc406Sopenharmony_ci
258141cc406Sopenharmony_ci    {MotorId::HP2300, 150, 1, StepType::HALF, false, true, 63,
259141cc406Sopenharmony_ci     MotorSlope::create_from_steps(7903, 543, 67),
260141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 120), 16},
261141cc406Sopenharmony_ci
262141cc406Sopenharmony_ci    {MotorId::HP2300, 300, 1, StepType::HALF, false, true, 63,
263141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2175, 1087, 3),
264141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 120), 16},
265141cc406Sopenharmony_ci
266141cc406Sopenharmony_ci    {MotorId::HP2300, 600, 1, StepType::HALF, false, true, 63,
267141cc406Sopenharmony_ci     MotorSlope::create_from_steps(8700, 4350, 3),
268141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 120), 16},
269141cc406Sopenharmony_ci
270141cc406Sopenharmony_ci    {MotorId::HP2300,1200, 1, StepType::HALF, false, true, 63,
271141cc406Sopenharmony_ci     MotorSlope::create_from_steps(17400, 8700, 3),
272141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 120), 16},
273141cc406Sopenharmony_ci
274141cc406Sopenharmony_ci    /* non half ccd settings for 300 dpi
275141cc406Sopenharmony_ci    {MotorId::HP2300, 300, 3, StepType::HALF, false, true, 63,
276141cc406Sopenharmony_ci     MotorSlope::create_from_steps(5386, 2175, 44),
277141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 120), 16},
278141cc406Sopenharmony_ci
279141cc406Sopenharmony_ci    {MotorId::HP2300, 300, 1, StepType::HALF, false, true, 63,
280141cc406Sopenharmony_ci     MotorSlope::create_from_steps(5386, 2175, 44),
281141cc406Sopenharmony_ci     MotorSlope::create_from_steps(4905, 337, 120), 16},
282141cc406Sopenharmony_ci    */
283141cc406Sopenharmony_ci
284141cc406Sopenharmony_ci    /* MD5345/6471 motor settings */
285141cc406Sopenharmony_ci    /* vfinal=(exposure/(1200/dpi))/step_type */
286141cc406Sopenharmony_ci    {MotorId::MD_5345, 50, 3, StepType::HALF, false, true, 2,
287141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2500, 250, 255),
288141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 64},
289141cc406Sopenharmony_ci
290141cc406Sopenharmony_ci    {MotorId::MD_5345, 75, 3, StepType::HALF, false, true, 2,
291141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2500, 343, 255),
292141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 64},
293141cc406Sopenharmony_ci
294141cc406Sopenharmony_ci    {MotorId::MD_5345, 100, 3, StepType::HALF, false, true, 2,
295141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2500, 458, 255),
296141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 64},
297141cc406Sopenharmony_ci
298141cc406Sopenharmony_ci    {MotorId::MD_5345, 150, 3, StepType::HALF, false, true, 2,
299141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2500, 687, 255),
300141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 64},
301141cc406Sopenharmony_ci
302141cc406Sopenharmony_ci    {MotorId::MD_5345, 200, 3, StepType::HALF, false, true, 2,
303141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2500, 916, 255),
304141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 64},
305141cc406Sopenharmony_ci
306141cc406Sopenharmony_ci    {MotorId::MD_5345, 300, 3, StepType::HALF, false, true, 2,
307141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2500, 1375, 255),
308141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 64},
309141cc406Sopenharmony_ci
310141cc406Sopenharmony_ci    {MotorId::MD_5345, 400, 3, StepType::HALF, false, true, 0,
311141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 1833, 32),
312141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 32},
313141cc406Sopenharmony_ci
314141cc406Sopenharmony_ci    {MotorId::MD_5345, 500, 3, StepType::HALF, false, true, 0,
315141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2291, 2291, 32),
316141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 32},
317141cc406Sopenharmony_ci
318141cc406Sopenharmony_ci    {MotorId::MD_5345, 600, 3, StepType::HALF, false, true, 0,
319141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2750, 2750, 32),
320141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 32},
321141cc406Sopenharmony_ci
322141cc406Sopenharmony_ci    {MotorId::MD_5345, 1200, 3, StepType::QUARTER, false, true, 0,
323141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2750, 2750, 16),
324141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 146},
325141cc406Sopenharmony_ci
326141cc406Sopenharmony_ci    {MotorId::MD_5345, 2400, 3, StepType::QUARTER, false, true, 0,
327141cc406Sopenharmony_ci     MotorSlope::create_from_steps(5500, 5500, 16),
328141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 146},
329141cc406Sopenharmony_ci
330141cc406Sopenharmony_ci    {MotorId::MD_5345, 50, 1, StepType::HALF, false, true, 2,
331141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2500, 250, 255),
332141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 64},
333141cc406Sopenharmony_ci
334141cc406Sopenharmony_ci    {MotorId::MD_5345, 75, 1, StepType::HALF, false, true, 2,
335141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2500, 343, 255),
336141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 64},
337141cc406Sopenharmony_ci
338141cc406Sopenharmony_ci    {MotorId::MD_5345, 100, 1, StepType::HALF, false, true, 2,
339141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2500, 458, 255),
340141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 64},
341141cc406Sopenharmony_ci
342141cc406Sopenharmony_ci    {MotorId::MD_5345, 150, 1, StepType::HALF, false, true, 2,
343141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2500, 687, 255),
344141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 64},
345141cc406Sopenharmony_ci
346141cc406Sopenharmony_ci    {MotorId::MD_5345, 200, 1, StepType::HALF, false, true, 2,
347141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2500, 916, 255),
348141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 64},
349141cc406Sopenharmony_ci
350141cc406Sopenharmony_ci    {MotorId::MD_5345, 300, 1, StepType::HALF, false, true, 2,
351141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2500, 1375, 255),
352141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 64},
353141cc406Sopenharmony_ci
354141cc406Sopenharmony_ci    {MotorId::MD_5345, 400, 1, StepType::HALF, false, true, 0,
355141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 1833, 32),
356141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 32},
357141cc406Sopenharmony_ci
358141cc406Sopenharmony_ci    {MotorId::MD_5345, 500, 1, StepType::HALF, false, true, 0,
359141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2291, 2291, 32),
360141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 32},
361141cc406Sopenharmony_ci
362141cc406Sopenharmony_ci    {MotorId::MD_5345, 600, 1, StepType::HALF, false, true, 0,
363141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2750, 2750, 32),
364141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 32},
365141cc406Sopenharmony_ci
366141cc406Sopenharmony_ci    {MotorId::MD_5345, 1200, 1, StepType::QUARTER, false, true, 0,
367141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2750, 2750, 16),
368141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 146},
369141cc406Sopenharmony_ci
370141cc406Sopenharmony_ci    {MotorId::MD_5345, 2400, 1, StepType::QUARTER, false, true, 0,
371141cc406Sopenharmony_ci     MotorSlope::create_from_steps(5500, 5500, 16),
372141cc406Sopenharmony_ci     MotorSlope::create_from_steps(2000, 300, 255), 146}, /* 5500 guessed */
373141cc406Sopenharmony_ci};
374141cc406Sopenharmony_ci
375141cc406Sopenharmony_ci/**
376141cc406Sopenharmony_ci * reads value from gpio endpoint
377141cc406Sopenharmony_ci */
378141cc406Sopenharmony_cistatic void gl646_gpio_read(IUsbDevice& usb_dev, std::uint8_t* value)
379141cc406Sopenharmony_ci{
380141cc406Sopenharmony_ci    DBG_HELPER(dbg);
381141cc406Sopenharmony_ci    usb_dev.control_msg(REQUEST_TYPE_IN, REQUEST_REGISTER, GPIO_READ, INDEX, 1, value);
382141cc406Sopenharmony_ci}
383141cc406Sopenharmony_ci
384141cc406Sopenharmony_ci/**
385141cc406Sopenharmony_ci * writes the given value to gpio endpoint
386141cc406Sopenharmony_ci */
387141cc406Sopenharmony_cistatic void gl646_gpio_write(IUsbDevice& usb_dev, std::uint8_t value)
388141cc406Sopenharmony_ci{
389141cc406Sopenharmony_ci    DBG_HELPER_ARGS(dbg, "(0x%02x)", value);
390141cc406Sopenharmony_ci    usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, GPIO_WRITE, INDEX, 1, &value);
391141cc406Sopenharmony_ci}
392141cc406Sopenharmony_ci
393141cc406Sopenharmony_ci/**
394141cc406Sopenharmony_ci * writes the given value to gpio output enable endpoint
395141cc406Sopenharmony_ci */
396141cc406Sopenharmony_cistatic void gl646_gpio_output_enable(IUsbDevice& usb_dev, std::uint8_t value)
397141cc406Sopenharmony_ci{
398141cc406Sopenharmony_ci    DBG_HELPER_ARGS(dbg, "(0x%02x)", value);
399141cc406Sopenharmony_ci    usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, GPIO_OUTPUT_ENABLE, INDEX, 1, &value);
400141cc406Sopenharmony_ci}
401141cc406Sopenharmony_ci
402141cc406Sopenharmony_ci/**
403141cc406Sopenharmony_ci * stop scanner's motor
404141cc406Sopenharmony_ci * @param dev scanner's device
405141cc406Sopenharmony_ci */
406141cc406Sopenharmony_cistatic void gl646_stop_motor(Genesys_Device* dev)
407141cc406Sopenharmony_ci{
408141cc406Sopenharmony_ci    DBG_HELPER(dbg);
409141cc406Sopenharmony_ci    dev->interface->write_register(0x0f, 0x00);
410141cc406Sopenharmony_ci}
411141cc406Sopenharmony_ci
412141cc406Sopenharmony_ci/**
413141cc406Sopenharmony_ci * Returns the cksel values used by the required scan mode.
414141cc406Sopenharmony_ci * @param sensor id of the sensor
415141cc406Sopenharmony_ci * @param required required resolution
416141cc406Sopenharmony_ci * @param color true is color mode
417141cc406Sopenharmony_ci * @return cksel value for mode
418141cc406Sopenharmony_ci */
419141cc406Sopenharmony_cistatic int get_cksel(SensorId sensor_id, int required, unsigned channels)
420141cc406Sopenharmony_ci{
421141cc406Sopenharmony_ci    for (const auto& sensor : *s_sensors) {
422141cc406Sopenharmony_ci        // exit on perfect match
423141cc406Sopenharmony_ci        if (sensor.sensor_id == sensor_id && sensor.resolutions.matches(required) &&
424141cc406Sopenharmony_ci            sensor.matches_channel_count(channels))
425141cc406Sopenharmony_ci        {
426141cc406Sopenharmony_ci            unsigned cksel = sensor.ccd_pixels_per_system_pixel();
427141cc406Sopenharmony_ci            return cksel;
428141cc406Sopenharmony_ci        }
429141cc406Sopenharmony_ci    }
430141cc406Sopenharmony_ci  DBG(DBG_error, "%s: failed to find match for %d dpi\n", __func__, required);
431141cc406Sopenharmony_ci  /* fail safe fallback */
432141cc406Sopenharmony_ci  return 1;
433141cc406Sopenharmony_ci}
434141cc406Sopenharmony_ci
435141cc406Sopenharmony_civoid CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,
436141cc406Sopenharmony_ci                                                 Genesys_Register_Set* regs,
437141cc406Sopenharmony_ci                                                 const ScanSession& session) const
438141cc406Sopenharmony_ci{
439141cc406Sopenharmony_ci    DBG_HELPER(dbg);
440141cc406Sopenharmony_ci    session.assert_computed();
441141cc406Sopenharmony_ci
442141cc406Sopenharmony_ci    debug_dump(DBG_info, sensor);
443141cc406Sopenharmony_ci
444141cc406Sopenharmony_ci    std::uint32_t move = session.params.starty;
445141cc406Sopenharmony_ci
446141cc406Sopenharmony_ci  Motor_Master *motor = nullptr;
447141cc406Sopenharmony_ci    std::uint32_t z1, z2;
448141cc406Sopenharmony_ci  int feedl;
449141cc406Sopenharmony_ci
450141cc406Sopenharmony_ci
451141cc406Sopenharmony_ci  /* for the given resolution, search for master
452141cc406Sopenharmony_ci   * motor mode setting */
453141cc406Sopenharmony_ci    for (unsigned i = 0; i < sizeof (motor_master) / sizeof (Motor_Master); ++i) {
454141cc406Sopenharmony_ci        if (dev->model->motor_id == motor_master[i].motor_id &&
455141cc406Sopenharmony_ci            motor_master[i].dpi == session.params.yres &&
456141cc406Sopenharmony_ci            motor_master[i].channels == session.params.channels)
457141cc406Sopenharmony_ci        {
458141cc406Sopenharmony_ci            motor = &motor_master[i];
459141cc406Sopenharmony_ci        }
460141cc406Sopenharmony_ci    }
461141cc406Sopenharmony_ci    if (motor == nullptr) {
462141cc406Sopenharmony_ci        throw SaneException("unable to find settings for motor %d at %d dpi, color=%d",
463141cc406Sopenharmony_ci                            static_cast<unsigned>(dev->model->motor_id),
464141cc406Sopenharmony_ci                            session.params.yres, session.params.channels);
465141cc406Sopenharmony_ci    }
466141cc406Sopenharmony_ci
467141cc406Sopenharmony_ci    scanner_setup_sensor(*dev, sensor, *regs);
468141cc406Sopenharmony_ci
469141cc406Sopenharmony_ci  /* now generate slope tables : we are not using generate_slope_table3 yet */
470141cc406Sopenharmony_ci    auto slope_table1 = create_slope_table_for_speed(motor->slope1, motor->slope1.max_speed_w,
471141cc406Sopenharmony_ci                                                     StepType::FULL, 1, 4,
472141cc406Sopenharmony_ci                                                     get_slope_table_max_size(AsicType::GL646));
473141cc406Sopenharmony_ci    auto slope_table2 = create_slope_table_for_speed(motor->slope2, motor->slope2.max_speed_w,
474141cc406Sopenharmony_ci                                                     StepType::FULL, 1, 4,
475141cc406Sopenharmony_ci                                                     get_slope_table_max_size(AsicType::GL646));
476141cc406Sopenharmony_ci
477141cc406Sopenharmony_ci  /* R01 */
478141cc406Sopenharmony_ci  /* now setup other registers for final scan (ie with shading enabled) */
479141cc406Sopenharmony_ci  /* watch dog + shading + scan enable */
480141cc406Sopenharmony_ci    regs->find_reg(0x01).value |= REG_0x01_DOGENB | REG_0x01_SCAN;
481141cc406Sopenharmony_ci    if (dev->model->is_cis) {
482141cc406Sopenharmony_ci        regs->find_reg(0x01).value |= REG_0x01_CISSET;
483141cc406Sopenharmony_ci    } else {
484141cc406Sopenharmony_ci        regs->find_reg(0x01).value &= ~REG_0x01_CISSET;
485141cc406Sopenharmony_ci    }
486141cc406Sopenharmony_ci
487141cc406Sopenharmony_ci    // if device has no calibration, don't enable shading correction
488141cc406Sopenharmony_ci    if (has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) ||
489141cc406Sopenharmony_ci        has_flag(session.params.flags, ScanFlag::DISABLE_SHADING))
490141cc406Sopenharmony_ci    {
491141cc406Sopenharmony_ci        regs->find_reg(0x01).value &= ~REG_0x01_DVDSET;
492141cc406Sopenharmony_ci    } else {
493141cc406Sopenharmony_ci        regs->find_reg(0x01).value |= REG_0x01_DVDSET;
494141cc406Sopenharmony_ci    }
495141cc406Sopenharmony_ci
496141cc406Sopenharmony_ci    regs->find_reg(0x01).value &= ~REG_0x01_FASTMOD;
497141cc406Sopenharmony_ci    if (motor->fastmod) {
498141cc406Sopenharmony_ci        regs->find_reg(0x01).value |= REG_0x01_FASTMOD;
499141cc406Sopenharmony_ci    }
500141cc406Sopenharmony_ci
501141cc406Sopenharmony_ci  /* R02 */
502141cc406Sopenharmony_ci  /* allow moving when buffer full by default */
503141cc406Sopenharmony_ci    if (!dev->model->is_sheetfed) {
504141cc406Sopenharmony_ci        dev->reg.find_reg(0x02).value &= ~REG_0x02_ACDCDIS;
505141cc406Sopenharmony_ci    } else {
506141cc406Sopenharmony_ci        dev->reg.find_reg(0x02).value |= REG_0x02_ACDCDIS;
507141cc406Sopenharmony_ci    }
508141cc406Sopenharmony_ci
509141cc406Sopenharmony_ci  /* setup motor power and direction */
510141cc406Sopenharmony_ci  sanei_genesys_set_motor_power(*regs, true);
511141cc406Sopenharmony_ci
512141cc406Sopenharmony_ci    if (has_flag(session.params.flags, ScanFlag::REVERSE)) {
513141cc406Sopenharmony_ci        regs->find_reg(0x02).value |= REG_0x02_MTRREV;
514141cc406Sopenharmony_ci    } else {
515141cc406Sopenharmony_ci        regs->find_reg(0x02).value &= ~REG_0x02_MTRREV;
516141cc406Sopenharmony_ci    }
517141cc406Sopenharmony_ci
518141cc406Sopenharmony_ci  /* fastfed enabled (2 motor slope tables) */
519141cc406Sopenharmony_ci    if (motor->fastfed) {
520141cc406Sopenharmony_ci        regs->find_reg(0x02).value |= REG_0x02_FASTFED;
521141cc406Sopenharmony_ci    } else {
522141cc406Sopenharmony_ci        regs->find_reg(0x02).value &= ~REG_0x02_FASTFED;
523141cc406Sopenharmony_ci    }
524141cc406Sopenharmony_ci
525141cc406Sopenharmony_ci  /* step type */
526141cc406Sopenharmony_ci    regs->find_reg(0x02).value &= ~REG_0x02_STEPSEL;
527141cc406Sopenharmony_ci  switch (motor->steptype)
528141cc406Sopenharmony_ci    {
529141cc406Sopenharmony_ci    case StepType::FULL:
530141cc406Sopenharmony_ci      break;
531141cc406Sopenharmony_ci    case StepType::HALF:
532141cc406Sopenharmony_ci      regs->find_reg(0x02).value |= 1;
533141cc406Sopenharmony_ci      break;
534141cc406Sopenharmony_ci    case StepType::QUARTER:
535141cc406Sopenharmony_ci      regs->find_reg(0x02).value |= 2;
536141cc406Sopenharmony_ci      break;
537141cc406Sopenharmony_ci    default:
538141cc406Sopenharmony_ci      regs->find_reg(0x02).value |= 3;
539141cc406Sopenharmony_ci      break;
540141cc406Sopenharmony_ci    }
541141cc406Sopenharmony_ci
542141cc406Sopenharmony_ci    if (dev->model->is_sheetfed || !has_flag(session.params.flags, ScanFlag::AUTO_GO_HOME)) {
543141cc406Sopenharmony_ci        regs->find_reg(0x02).value &= ~REG_0x02_AGOHOME;
544141cc406Sopenharmony_ci    } else {
545141cc406Sopenharmony_ci        regs->find_reg(0x02).value |= REG_0x02_AGOHOME;
546141cc406Sopenharmony_ci    }
547141cc406Sopenharmony_ci
548141cc406Sopenharmony_ci  /* R03 */
549141cc406Sopenharmony_ci    regs->find_reg(0x03).value &= ~REG_0x03_AVEENB;
550141cc406Sopenharmony_ci    // regs->find_reg(0x03).value |= REG_0x03_AVEENB;
551141cc406Sopenharmony_ci    regs->find_reg(0x03).value &= ~REG_0x03_LAMPDOG;
552141cc406Sopenharmony_ci
553141cc406Sopenharmony_ci  /* select XPA */
554141cc406Sopenharmony_ci    regs->find_reg(0x03).value &= ~REG_0x03_XPASEL;
555141cc406Sopenharmony_ci    if ((session.params.flags & ScanFlag::USE_XPA) != ScanFlag::NONE) {
556141cc406Sopenharmony_ci        regs->find_reg(0x03).value |= REG_0x03_XPASEL;
557141cc406Sopenharmony_ci    }
558141cc406Sopenharmony_ci    regs->state.is_xpa_on = (session.params.flags & ScanFlag::USE_XPA) != ScanFlag::NONE;
559141cc406Sopenharmony_ci
560141cc406Sopenharmony_ci  /* R04 */
561141cc406Sopenharmony_ci  /* monochrome / color scan */
562141cc406Sopenharmony_ci    switch (session.params.depth) {
563141cc406Sopenharmony_ci    case 8:
564141cc406Sopenharmony_ci            regs->find_reg(0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET);
565141cc406Sopenharmony_ci            break;
566141cc406Sopenharmony_ci    case 16:
567141cc406Sopenharmony_ci            regs->find_reg(0x04).value &= ~REG_0x04_LINEART;
568141cc406Sopenharmony_ci            regs->find_reg(0x04).value |= REG_0x04_BITSET;
569141cc406Sopenharmony_ci            break;
570141cc406Sopenharmony_ci    }
571141cc406Sopenharmony_ci
572141cc406Sopenharmony_ci    sanei_genesys_set_dpihw(*regs, sensor.full_resolution);
573141cc406Sopenharmony_ci
574141cc406Sopenharmony_ci  /* gamma enable for scans */
575141cc406Sopenharmony_ci    if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) {
576141cc406Sopenharmony_ci        regs->find_reg(0x05).value |= REG_0x05_GMM14BIT;
577141cc406Sopenharmony_ci    }
578141cc406Sopenharmony_ci
579141cc406Sopenharmony_ci    if (!has_flag(session.params.flags, ScanFlag::DISABLE_GAMMA) &&
580141cc406Sopenharmony_ci        session.params.depth < 16)
581141cc406Sopenharmony_ci    {
582141cc406Sopenharmony_ci        regs->find_reg(REG_0x05).value |= REG_0x05_GMMENB;
583141cc406Sopenharmony_ci    } else {
584141cc406Sopenharmony_ci        regs->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB;
585141cc406Sopenharmony_ci    }
586141cc406Sopenharmony_ci
587141cc406Sopenharmony_ci  /* true CIS gray if needed */
588141cc406Sopenharmony_ci    if (dev->model->is_cis && session.params.channels == 1 &&
589141cc406Sopenharmony_ci        session.params.color_filter == ColorFilter::NONE)
590141cc406Sopenharmony_ci    {
591141cc406Sopenharmony_ci        regs->find_reg(0x05).value |= REG_0x05_LEDADD;
592141cc406Sopenharmony_ci    } else {
593141cc406Sopenharmony_ci        regs->find_reg(0x05).value &= ~REG_0x05_LEDADD;
594141cc406Sopenharmony_ci    }
595141cc406Sopenharmony_ci
596141cc406Sopenharmony_ci  /* HP2400 1200dpi mode tuning */
597141cc406Sopenharmony_ci
598141cc406Sopenharmony_ci    if (dev->model->sensor_id == SensorId::CCD_HP2400) {
599141cc406Sopenharmony_ci      /* reset count of dummy lines to zero */
600141cc406Sopenharmony_ci        regs->find_reg(0x1e).value &= ~REG_0x1E_LINESEL;
601141cc406Sopenharmony_ci        if (session.params.xres >= 1200) {
602141cc406Sopenharmony_ci          /* there must be one dummy line */
603141cc406Sopenharmony_ci            regs->find_reg(0x1e).value |= 1 & REG_0x1E_LINESEL;
604141cc406Sopenharmony_ci
605141cc406Sopenharmony_ci          /* GPO12 need to be set to zero */
606141cc406Sopenharmony_ci          regs->find_reg(0x66).value &= ~0x20;
607141cc406Sopenharmony_ci        }
608141cc406Sopenharmony_ci        else
609141cc406Sopenharmony_ci        {
610141cc406Sopenharmony_ci          /* set GPO12 back to one */
611141cc406Sopenharmony_ci          regs->find_reg(0x66).value |= 0x20;
612141cc406Sopenharmony_ci        }
613141cc406Sopenharmony_ci    }
614141cc406Sopenharmony_ci
615141cc406Sopenharmony_ci  /* motor steps used */
616141cc406Sopenharmony_ci    unsigned forward_steps = motor->fwdbwd;
617141cc406Sopenharmony_ci    unsigned backward_steps = motor->fwdbwd;
618141cc406Sopenharmony_ci
619141cc406Sopenharmony_ci    // the steps count must be different by at most 128, otherwise it's impossible to construct
620141cc406Sopenharmony_ci    // a proper backtracking curve. We're using slightly lower limit to allow at least a minimum
621141cc406Sopenharmony_ci    // distance between accelerations (forward_steps, backward_steps)
622141cc406Sopenharmony_ci    if (slope_table1.table.size() > slope_table2.table.size() + 100) {
623141cc406Sopenharmony_ci        slope_table2.expand_table(slope_table1.table.size() - 100, 1);
624141cc406Sopenharmony_ci    }
625141cc406Sopenharmony_ci    if (slope_table2.table.size() > slope_table1.table.size() + 100) {
626141cc406Sopenharmony_ci        slope_table1.expand_table(slope_table2.table.size() - 100, 1);
627141cc406Sopenharmony_ci    }
628141cc406Sopenharmony_ci
629141cc406Sopenharmony_ci    if (slope_table1.table.size() >= slope_table2.table.size()) {
630141cc406Sopenharmony_ci        backward_steps += (slope_table1.table.size() - slope_table2.table.size()) * 2;
631141cc406Sopenharmony_ci    } else {
632141cc406Sopenharmony_ci        forward_steps += (slope_table2.table.size() - slope_table1.table.size()) * 2;
633141cc406Sopenharmony_ci    }
634141cc406Sopenharmony_ci
635141cc406Sopenharmony_ci    if (forward_steps > 255) {
636141cc406Sopenharmony_ci        if (backward_steps < (forward_steps - 255)) {
637141cc406Sopenharmony_ci            throw SaneException("Can't set backtracking parameters without skipping image");
638141cc406Sopenharmony_ci        }
639141cc406Sopenharmony_ci        backward_steps -= forward_steps - 255;
640141cc406Sopenharmony_ci    }
641141cc406Sopenharmony_ci    if (backward_steps > 255) {
642141cc406Sopenharmony_ci        if (forward_steps < (backward_steps - 255)) {
643141cc406Sopenharmony_ci            throw SaneException("Can't set backtracking parameters without skipping image");
644141cc406Sopenharmony_ci        }
645141cc406Sopenharmony_ci        forward_steps -= backward_steps - 255;
646141cc406Sopenharmony_ci    }
647141cc406Sopenharmony_ci
648141cc406Sopenharmony_ci    regs->find_reg(0x21).value = slope_table1.table.size();
649141cc406Sopenharmony_ci    regs->find_reg(0x24).value = slope_table2.table.size();
650141cc406Sopenharmony_ci    regs->find_reg(0x22).value = forward_steps;
651141cc406Sopenharmony_ci    regs->find_reg(0x23).value = backward_steps;
652141cc406Sopenharmony_ci
653141cc406Sopenharmony_ci  /* CIS scanners read one line per color channel
654141cc406Sopenharmony_ci   * since gray mode use 'add' we also read 3 channels even not in
655141cc406Sopenharmony_ci   * color mode */
656141cc406Sopenharmony_ci    if (dev->model->is_cis) {
657141cc406Sopenharmony_ci        regs->set24(REG_LINCNT, session.output_line_count * 3);
658141cc406Sopenharmony_ci    } else {
659141cc406Sopenharmony_ci        regs->set24(REG_LINCNT, session.output_line_count);
660141cc406Sopenharmony_ci    }
661141cc406Sopenharmony_ci
662141cc406Sopenharmony_ci    regs->set16(REG_STRPIXEL, session.pixel_startx);
663141cc406Sopenharmony_ci    regs->set16(REG_ENDPIXEL, session.pixel_endx);
664141cc406Sopenharmony_ci
665141cc406Sopenharmony_ci    regs->set24(REG_MAXWD, session.output_line_bytes);
666141cc406Sopenharmony_ci
667141cc406Sopenharmony_ci    // FIXME: the incoming sensor is selected for incorrect resolution
668141cc406Sopenharmony_ci    const auto& dpiset_sensor = sanei_genesys_find_sensor(dev, session.params.xres,
669141cc406Sopenharmony_ci                                                          session.params.channels,
670141cc406Sopenharmony_ci                                                          session.params.scan_method);
671141cc406Sopenharmony_ci    regs->set16(REG_DPISET, dpiset_sensor.register_dpiset);
672141cc406Sopenharmony_ci    regs->set16(REG_LPERIOD, sensor.exposure_lperiod);
673141cc406Sopenharmony_ci
674141cc406Sopenharmony_ci  /* move distance must be adjusted to take into account the extra lines
675141cc406Sopenharmony_ci   * read to reorder data */
676141cc406Sopenharmony_ci  feedl = move;
677141cc406Sopenharmony_ci
678141cc406Sopenharmony_ci    if (session.num_staggered_lines + session.max_color_shift_lines > 0 && feedl != 0) {
679141cc406Sopenharmony_ci        unsigned total_lines = session.max_color_shift_lines + session.num_staggered_lines;
680141cc406Sopenharmony_ci        int feed_offset = (total_lines * dev->motor.base_ydpi) / motor->dpi;
681141cc406Sopenharmony_ci        if (feedl > feed_offset) {
682141cc406Sopenharmony_ci            feedl = feedl - feed_offset;
683141cc406Sopenharmony_ci        }
684141cc406Sopenharmony_ci    }
685141cc406Sopenharmony_ci
686141cc406Sopenharmony_ci  /* we assume all scans are done with 2 tables */
687141cc406Sopenharmony_ci  /*
688141cc406Sopenharmony_ci     feedl = feed_steps - fast_slope_steps*2 -
689141cc406Sopenharmony_ci     (slow_slope_steps >> scan_step_type); */
690141cc406Sopenharmony_ci  /* but head has moved due to shading calibration => dev->scanhead_position_primary */
691141cc406Sopenharmony_ci  if (feedl > 0)
692141cc406Sopenharmony_ci    {
693141cc406Sopenharmony_ci      /* TODO clean up this when I'll fully understand.
694141cc406Sopenharmony_ci       * for now, special casing each motor */
695141cc406Sopenharmony_ci        switch (dev->model->motor_id) {
696141cc406Sopenharmony_ci            case MotorId::MD_5345:
697141cc406Sopenharmony_ci                    switch (motor->dpi) {
698141cc406Sopenharmony_ci	    case 200:
699141cc406Sopenharmony_ci	      feedl -= 70;
700141cc406Sopenharmony_ci	      break;
701141cc406Sopenharmony_ci	    case 300:
702141cc406Sopenharmony_ci	      feedl -= 70;
703141cc406Sopenharmony_ci	      break;
704141cc406Sopenharmony_ci	    case 400:
705141cc406Sopenharmony_ci	      feedl += 130;
706141cc406Sopenharmony_ci	      break;
707141cc406Sopenharmony_ci	    case 600:
708141cc406Sopenharmony_ci	      feedl += 160;
709141cc406Sopenharmony_ci	      break;
710141cc406Sopenharmony_ci	    case 1200:
711141cc406Sopenharmony_ci	      feedl += 160;
712141cc406Sopenharmony_ci	      break;
713141cc406Sopenharmony_ci	    case 2400:
714141cc406Sopenharmony_ci	      feedl += 180;
715141cc406Sopenharmony_ci	      break;
716141cc406Sopenharmony_ci	    default:
717141cc406Sopenharmony_ci	      break;
718141cc406Sopenharmony_ci	    }
719141cc406Sopenharmony_ci	  break;
720141cc406Sopenharmony_ci            case MotorId::HP2300:
721141cc406Sopenharmony_ci                    switch (motor->dpi) {
722141cc406Sopenharmony_ci	    case 75:
723141cc406Sopenharmony_ci	      feedl -= 180;
724141cc406Sopenharmony_ci	      break;
725141cc406Sopenharmony_ci	    case 150:
726141cc406Sopenharmony_ci	      feedl += 0;
727141cc406Sopenharmony_ci	      break;
728141cc406Sopenharmony_ci	    case 300:
729141cc406Sopenharmony_ci	      feedl += 30;
730141cc406Sopenharmony_ci	      break;
731141cc406Sopenharmony_ci	    case 600:
732141cc406Sopenharmony_ci	      feedl += 35;
733141cc406Sopenharmony_ci	      break;
734141cc406Sopenharmony_ci	    case 1200:
735141cc406Sopenharmony_ci	      feedl += 45;
736141cc406Sopenharmony_ci	      break;
737141cc406Sopenharmony_ci	    default:
738141cc406Sopenharmony_ci	      break;
739141cc406Sopenharmony_ci	    }
740141cc406Sopenharmony_ci	  break;
741141cc406Sopenharmony_ci            case MotorId::HP2400:
742141cc406Sopenharmony_ci                    switch (motor->dpi) {
743141cc406Sopenharmony_ci	    case 150:
744141cc406Sopenharmony_ci	      feedl += 150;
745141cc406Sopenharmony_ci	      break;
746141cc406Sopenharmony_ci	    case 300:
747141cc406Sopenharmony_ci	      feedl += 220;
748141cc406Sopenharmony_ci	      break;
749141cc406Sopenharmony_ci	    case 600:
750141cc406Sopenharmony_ci	      feedl += 260;
751141cc406Sopenharmony_ci	      break;
752141cc406Sopenharmony_ci	    case 1200:
753141cc406Sopenharmony_ci	      feedl += 280; /* 300 */
754141cc406Sopenharmony_ci	      break;
755141cc406Sopenharmony_ci	    case 50:
756141cc406Sopenharmony_ci	      feedl += 0;
757141cc406Sopenharmony_ci	      break;
758141cc406Sopenharmony_ci	    case 100:
759141cc406Sopenharmony_ci	      feedl += 100;
760141cc406Sopenharmony_ci	      break;
761141cc406Sopenharmony_ci	    default:
762141cc406Sopenharmony_ci	      break;
763141cc406Sopenharmony_ci	    }
764141cc406Sopenharmony_ci	  break;
765141cc406Sopenharmony_ci
766141cc406Sopenharmony_ci	  /* theorical value */
767141cc406Sopenharmony_ci        default: {
768141cc406Sopenharmony_ci            unsigned step_shift = static_cast<unsigned>(motor->steptype);
769141cc406Sopenharmony_ci
770141cc406Sopenharmony_ci	  if (motor->fastfed)
771141cc406Sopenharmony_ci        {
772141cc406Sopenharmony_ci                feedl = feedl - 2 * slope_table2.table.size() -
773141cc406Sopenharmony_ci                        (slope_table1.table.size() >> step_shift);
774141cc406Sopenharmony_ci	    }
775141cc406Sopenharmony_ci	  else
776141cc406Sopenharmony_ci	    {
777141cc406Sopenharmony_ci                feedl = feedl - (slope_table1.table.size() >> step_shift);
778141cc406Sopenharmony_ci	    }
779141cc406Sopenharmony_ci	  break;
780141cc406Sopenharmony_ci        }
781141cc406Sopenharmony_ci	}
782141cc406Sopenharmony_ci      /* security */
783141cc406Sopenharmony_ci      if (feedl < 0)
784141cc406Sopenharmony_ci	feedl = 0;
785141cc406Sopenharmony_ci    }
786141cc406Sopenharmony_ci
787141cc406Sopenharmony_ci    regs->set24(REG_FEEDL, feedl);
788141cc406Sopenharmony_ci
789141cc406Sopenharmony_ci  regs->find_reg(0x65).value = motor->mtrpwm;
790141cc406Sopenharmony_ci
791141cc406Sopenharmony_ci    sanei_genesys_calculate_zmod(regs->find_reg(0x02).value & REG_0x02_FASTFED,
792141cc406Sopenharmony_ci                                 sensor.exposure_lperiod,
793141cc406Sopenharmony_ci                                 slope_table1.table,
794141cc406Sopenharmony_ci                                 slope_table1.table.size(),
795141cc406Sopenharmony_ci                                  move, motor->fwdbwd, &z1, &z2);
796141cc406Sopenharmony_ci
797141cc406Sopenharmony_ci  /* no z1/z2 for sheetfed scanners */
798141cc406Sopenharmony_ci    if (dev->model->is_sheetfed) {
799141cc406Sopenharmony_ci      z1 = 0;
800141cc406Sopenharmony_ci      z2 = 0;
801141cc406Sopenharmony_ci    }
802141cc406Sopenharmony_ci    regs->set16(REG_Z1MOD, z1);
803141cc406Sopenharmony_ci    regs->set16(REG_Z2MOD, z2);
804141cc406Sopenharmony_ci    regs->find_reg(0x6b).value = slope_table2.table.size();
805141cc406Sopenharmony_ci  regs->find_reg(0x6c).value =
806141cc406Sopenharmony_ci    (regs->find_reg(0x6c).value & REG_0x6C_TGTIME) | ((z1 >> 13) & 0x38) | ((z2 >> 16)
807141cc406Sopenharmony_ci								   & 0x07);
808141cc406Sopenharmony_ci
809141cc406Sopenharmony_ci    write_control(dev, sensor, session.output_resolution);
810141cc406Sopenharmony_ci
811141cc406Sopenharmony_ci    // setup analog frontend
812141cc406Sopenharmony_ci    gl646_set_fe(dev, sensor, AFE_SET, session.output_resolution);
813141cc406Sopenharmony_ci
814141cc406Sopenharmony_ci    setup_image_pipeline(*dev, session);
815141cc406Sopenharmony_ci
816141cc406Sopenharmony_ci    dev->read_active = true;
817141cc406Sopenharmony_ci
818141cc406Sopenharmony_ci    dev->session = session;
819141cc406Sopenharmony_ci
820141cc406Sopenharmony_ci    dev->total_bytes_read = 0;
821141cc406Sopenharmony_ci    dev->total_bytes_to_read = (size_t) session.output_line_bytes_requested
822141cc406Sopenharmony_ci          * (size_t) session.params.lines;
823141cc406Sopenharmony_ci
824141cc406Sopenharmony_ci    /* select color filter based on settings */
825141cc406Sopenharmony_ci    regs->find_reg(0x04).value &= ~REG_0x04_FILTER;
826141cc406Sopenharmony_ci    if (session.params.channels == 1) {
827141cc406Sopenharmony_ci        switch (session.params.color_filter) {
828141cc406Sopenharmony_ci            case ColorFilter::RED:
829141cc406Sopenharmony_ci                regs->find_reg(0x04).value |= 0x04;
830141cc406Sopenharmony_ci                break;
831141cc406Sopenharmony_ci            case ColorFilter::GREEN:
832141cc406Sopenharmony_ci                regs->find_reg(0x04).value |= 0x08;
833141cc406Sopenharmony_ci                break;
834141cc406Sopenharmony_ci            case ColorFilter::BLUE:
835141cc406Sopenharmony_ci                regs->find_reg(0x04).value |= 0x0c;
836141cc406Sopenharmony_ci                break;
837141cc406Sopenharmony_ci            default:
838141cc406Sopenharmony_ci                break;
839141cc406Sopenharmony_ci        }
840141cc406Sopenharmony_ci    }
841141cc406Sopenharmony_ci
842141cc406Sopenharmony_ci    scanner_send_slope_table(dev, sensor, 0, slope_table1.table);
843141cc406Sopenharmony_ci    scanner_send_slope_table(dev, sensor, 1, slope_table2.table);
844141cc406Sopenharmony_ci}
845141cc406Sopenharmony_ci
846141cc406Sopenharmony_ci/**
847141cc406Sopenharmony_ci * Set all registers to default values after init
848141cc406Sopenharmony_ci * @param dev scannerr's device to set
849141cc406Sopenharmony_ci */
850141cc406Sopenharmony_cistatic void
851141cc406Sopenharmony_cigl646_init_regs (Genesys_Device * dev)
852141cc406Sopenharmony_ci{
853141cc406Sopenharmony_ci  int addr;
854141cc406Sopenharmony_ci
855141cc406Sopenharmony_ci  DBG(DBG_proc, "%s\n", __func__);
856141cc406Sopenharmony_ci
857141cc406Sopenharmony_ci    dev->reg.clear();
858141cc406Sopenharmony_ci
859141cc406Sopenharmony_ci    for (addr = 1; addr <= 0x0b; addr++)
860141cc406Sopenharmony_ci        dev->reg.init_reg(addr, 0);
861141cc406Sopenharmony_ci    for (addr = 0x10; addr <= 0x29; addr++)
862141cc406Sopenharmony_ci        dev->reg.init_reg(addr, 0);
863141cc406Sopenharmony_ci    for (addr = 0x2c; addr <= 0x39; addr++)
864141cc406Sopenharmony_ci        dev->reg.init_reg(addr, 0);
865141cc406Sopenharmony_ci    for (addr = 0x3d; addr <= 0x3f; addr++)
866141cc406Sopenharmony_ci        dev->reg.init_reg(addr, 0);
867141cc406Sopenharmony_ci    for (addr = 0x52; addr <= 0x5e; addr++)
868141cc406Sopenharmony_ci        dev->reg.init_reg(addr, 0);
869141cc406Sopenharmony_ci    for (addr = 0x60; addr <= 0x6d; addr++)
870141cc406Sopenharmony_ci        dev->reg.init_reg(addr, 0);
871141cc406Sopenharmony_ci
872141cc406Sopenharmony_ci    dev->reg.find_reg(0x01).value = 0x20 /*0x22 */ ;	/* enable shading, CCD, color, 1M */
873141cc406Sopenharmony_ci    dev->reg.find_reg(0x02).value = 0x30 /*0x38 */ ;	/* auto home, one-table-move, full step */
874141cc406Sopenharmony_ci    if (dev->model->motor_id == MotorId::MD_5345) {
875141cc406Sopenharmony_ci        dev->reg.find_reg(0x02).value |= 0x01; // half-step
876141cc406Sopenharmony_ci    }
877141cc406Sopenharmony_ci    switch (dev->model->motor_id) {
878141cc406Sopenharmony_ci        case MotorId::MD_5345:
879141cc406Sopenharmony_ci      dev->reg.find_reg(0x02).value |= 0x01;	/* half-step */
880141cc406Sopenharmony_ci      break;
881141cc406Sopenharmony_ci        case MotorId::XP200:
882141cc406Sopenharmony_ci      /* for this sheetfed scanner, no AGOHOME, nor backtracking */
883141cc406Sopenharmony_ci      dev->reg.find_reg(0x02).value = 0x50;
884141cc406Sopenharmony_ci      break;
885141cc406Sopenharmony_ci        default:
886141cc406Sopenharmony_ci      break;
887141cc406Sopenharmony_ci    }
888141cc406Sopenharmony_ci    dev->reg.find_reg(0x03).value = 0x1f /*0x17 */ ;	/* lamp on */
889141cc406Sopenharmony_ci    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? */
890141cc406Sopenharmony_ci  switch (dev->model->adc_id)
891141cc406Sopenharmony_ci    {
892141cc406Sopenharmony_ci    case AdcId::AD_XP200:
893141cc406Sopenharmony_ci      dev->reg.find_reg(0x04).value = 0x12;
894141cc406Sopenharmony_ci      break;
895141cc406Sopenharmony_ci    default:
896141cc406Sopenharmony_ci      /* Wolfson frontend */
897141cc406Sopenharmony_ci      dev->reg.find_reg(0x04).value = 0x13;
898141cc406Sopenharmony_ci      break;
899141cc406Sopenharmony_ci    }
900141cc406Sopenharmony_ci
901141cc406Sopenharmony_ci  const auto& sensor = sanei_genesys_find_sensor_any(dev);
902141cc406Sopenharmony_ci
903141cc406Sopenharmony_ci  dev->reg.find_reg(0x05).value = 0x00;	/* 12 bits gamma, disable gamma, 24 clocks/pixel */
904141cc406Sopenharmony_ci    sanei_genesys_set_dpihw(dev->reg, sensor.full_resolution);
905141cc406Sopenharmony_ci
906141cc406Sopenharmony_ci    if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) {
907141cc406Sopenharmony_ci        dev->reg.find_reg(0x05).value |= REG_0x05_GMM14BIT;
908141cc406Sopenharmony_ci    }
909141cc406Sopenharmony_ci    if (dev->model->adc_id == AdcId::AD_XP200) {
910141cc406Sopenharmony_ci        dev->reg.find_reg(0x05).value |= 0x01;	/* 12 clocks/pixel */
911141cc406Sopenharmony_ci    }
912141cc406Sopenharmony_ci
913141cc406Sopenharmony_ci    if (dev->model->sensor_id == SensorId::CCD_HP2300) {
914141cc406Sopenharmony_ci        dev->reg.find_reg(0x06).value = 0x00; // PWRBIT off, shading gain=4, normal AFE image capture
915141cc406Sopenharmony_ci    } else {
916141cc406Sopenharmony_ci        dev->reg.find_reg(0x06).value = 0x18; // PWRBIT on, shading gain=8, normal AFE image capture
917141cc406Sopenharmony_ci    }
918141cc406Sopenharmony_ci
919141cc406Sopenharmony_ci    scanner_setup_sensor(*dev, sensor, dev->reg);
920141cc406Sopenharmony_ci
921141cc406Sopenharmony_ci  dev->reg.find_reg(0x1e).value = 0xf0;	/* watch-dog time */
922141cc406Sopenharmony_ci
923141cc406Sopenharmony_ci  switch (dev->model->sensor_id)
924141cc406Sopenharmony_ci    {
925141cc406Sopenharmony_ci    case SensorId::CCD_HP2300:
926141cc406Sopenharmony_ci      dev->reg.find_reg(0x1e).value = 0xf0;
927141cc406Sopenharmony_ci      dev->reg.find_reg(0x1f).value = 0x10;
928141cc406Sopenharmony_ci      dev->reg.find_reg(0x20).value = 0x20;
929141cc406Sopenharmony_ci      break;
930141cc406Sopenharmony_ci    case SensorId::CCD_HP2400:
931141cc406Sopenharmony_ci      dev->reg.find_reg(0x1e).value = 0x80;
932141cc406Sopenharmony_ci      dev->reg.find_reg(0x1f).value = 0x10;
933141cc406Sopenharmony_ci      dev->reg.find_reg(0x20).value = 0x20;
934141cc406Sopenharmony_ci      break;
935141cc406Sopenharmony_ci    case SensorId::CCD_HP3670:
936141cc406Sopenharmony_ci      dev->reg.find_reg(0x19).value = 0x2a;
937141cc406Sopenharmony_ci      dev->reg.find_reg(0x1e).value = 0x80;
938141cc406Sopenharmony_ci      dev->reg.find_reg(0x1f).value = 0x10;
939141cc406Sopenharmony_ci      dev->reg.find_reg(0x20).value = 0x20;
940141cc406Sopenharmony_ci      break;
941141cc406Sopenharmony_ci    case SensorId::CIS_XP200:
942141cc406Sopenharmony_ci      dev->reg.find_reg(0x1e).value = 0x10;
943141cc406Sopenharmony_ci      dev->reg.find_reg(0x1f).value = 0x01;
944141cc406Sopenharmony_ci      dev->reg.find_reg(0x20).value = 0x50;
945141cc406Sopenharmony_ci      break;
946141cc406Sopenharmony_ci    default:
947141cc406Sopenharmony_ci      dev->reg.find_reg(0x1f).value = 0x01;
948141cc406Sopenharmony_ci      dev->reg.find_reg(0x20).value = 0x50;
949141cc406Sopenharmony_ci      break;
950141cc406Sopenharmony_ci    }
951141cc406Sopenharmony_ci
952141cc406Sopenharmony_ci  dev->reg.find_reg(0x21).value = 0x08 /*0x20 */ ;	/* table one steps number for forward slope curve of the acc/dec */
953141cc406Sopenharmony_ci  dev->reg.find_reg(0x22).value = 0x10 /*0x08 */ ;	/* steps number of the forward steps for start/stop */
954141cc406Sopenharmony_ci  dev->reg.find_reg(0x23).value = 0x10 /*0x08 */ ;	/* steps number of the backward steps for start/stop */
955141cc406Sopenharmony_ci  dev->reg.find_reg(0x24).value = 0x08 /*0x20 */ ;	/* table one steps number backward slope curve of the acc/dec */
956141cc406Sopenharmony_ci  dev->reg.find_reg(0x25).value = 0x00;	/* scan line numbers (7000) */
957141cc406Sopenharmony_ci  dev->reg.find_reg(0x26).value = 0x00 /*0x1b */ ;
958141cc406Sopenharmony_ci  dev->reg.find_reg(0x27).value = 0xd4 /*0x58 */ ;
959141cc406Sopenharmony_ci  dev->reg.find_reg(0x28).value = 0x01;	/* PWM duty for lamp control */
960141cc406Sopenharmony_ci  dev->reg.find_reg(0x29).value = 0xff;
961141cc406Sopenharmony_ci
962141cc406Sopenharmony_ci  dev->reg.find_reg(0x2c).value = 0x02;	/* set resolution (600 DPI) */
963141cc406Sopenharmony_ci  dev->reg.find_reg(0x2d).value = 0x58;
964141cc406Sopenharmony_ci  dev->reg.find_reg(0x2e).value = 0x78;	/* set black&white threshold high level */
965141cc406Sopenharmony_ci  dev->reg.find_reg(0x2f).value = 0x7f;	/* set black&white threshold low level */
966141cc406Sopenharmony_ci
967141cc406Sopenharmony_ci  dev->reg.find_reg(0x30).value = 0x00;	/* begin pixel position (16) */
968141cc406Sopenharmony_ci  dev->reg.find_reg(0x31).value = sensor.dummy_pixel /*0x10 */ ;	/* TGW + 2*TG_SHLD + x  */
969141cc406Sopenharmony_ci  dev->reg.find_reg(0x32).value = 0x2a /*0x15 */ ;	/* end pixel position (5390) */
970141cc406Sopenharmony_ci  dev->reg.find_reg(0x33).value = 0xf8 /*0x0e */ ;	/* TGW + 2*TG_SHLD + y   */
971141cc406Sopenharmony_ci  dev->reg.find_reg(0x34).value = sensor.dummy_pixel;
972141cc406Sopenharmony_ci  dev->reg.find_reg(0x35).value = 0x01 /*0x00 */ ;	/* set maximum word size per line, for buffer full control (10800) */
973141cc406Sopenharmony_ci  dev->reg.find_reg(0x36).value = 0x00 /*0x2a */ ;
974141cc406Sopenharmony_ci  dev->reg.find_reg(0x37).value = 0x00 /*0x30 */ ;
975141cc406Sopenharmony_ci  dev->reg.find_reg(0x38).value = 0x2a; // line period (exposure time = 11000 pixels) */
976141cc406Sopenharmony_ci  dev->reg.find_reg(0x39).value = 0xf8;
977141cc406Sopenharmony_ci  dev->reg.find_reg(0x3d).value = 0x00;	/* set feed steps number of motor move */
978141cc406Sopenharmony_ci  dev->reg.find_reg(0x3e).value = 0x00;
979141cc406Sopenharmony_ci  dev->reg.find_reg(0x3f).value = 0x01 /*0x00 */ ;
980141cc406Sopenharmony_ci
981141cc406Sopenharmony_ci  dev->reg.find_reg(0x60).value = 0x00;	/* Z1MOD, 60h:61h:(6D b5:b3), remainder for start/stop */
982141cc406Sopenharmony_ci  dev->reg.find_reg(0x61).value = 0x00;	/* (21h+22h)/LPeriod */
983141cc406Sopenharmony_ci  dev->reg.find_reg(0x62).value = 0x00;	/* Z2MODE, 62h:63h:(6D b2:b0), remainder for start scan */
984141cc406Sopenharmony_ci  dev->reg.find_reg(0x63).value = 0x00;	/* (3Dh+3Eh+3Fh)/LPeriod for one-table mode,(21h+1Fh)/LPeriod */
985141cc406Sopenharmony_ci  dev->reg.find_reg(0x64).value = 0x00;	/* motor PWM frequency */
986141cc406Sopenharmony_ci  dev->reg.find_reg(0x65).value = 0x00;	/* PWM duty cycle for table one motor phase (63 = max) */
987141cc406Sopenharmony_ci    if (dev->model->motor_id == MotorId::MD_5345) {
988141cc406Sopenharmony_ci        // PWM duty cycle for table one motor phase (63 = max)
989141cc406Sopenharmony_ci        dev->reg.find_reg(0x65).value = 0x02;
990141cc406Sopenharmony_ci    }
991141cc406Sopenharmony_ci
992141cc406Sopenharmony_ci    for (const auto& reg : dev->gpo.regs) {
993141cc406Sopenharmony_ci        dev->reg.set8(reg.address, reg.value);
994141cc406Sopenharmony_ci    }
995141cc406Sopenharmony_ci
996141cc406Sopenharmony_ci    switch (dev->model->motor_id) {
997141cc406Sopenharmony_ci        case MotorId::HP2300:
998141cc406Sopenharmony_ci        case MotorId::HP2400:
999141cc406Sopenharmony_ci      dev->reg.find_reg(0x6a).value = 0x7f;	/* table two steps number for acc/dec */
1000141cc406Sopenharmony_ci      dev->reg.find_reg(0x6b).value = 0x78;	/* table two steps number for acc/dec */
1001141cc406Sopenharmony_ci      dev->reg.find_reg(0x6d).value = 0x7f;
1002141cc406Sopenharmony_ci      break;
1003141cc406Sopenharmony_ci        case MotorId::MD_5345:
1004141cc406Sopenharmony_ci      dev->reg.find_reg(0x6a).value = 0x42;	/* table two fast moving step type, PWM duty for table two */
1005141cc406Sopenharmony_ci      dev->reg.find_reg(0x6b).value = 0xff;	/* table two steps number for acc/dec */
1006141cc406Sopenharmony_ci      dev->reg.find_reg(0x6d).value = 0x41;	/* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */
1007141cc406Sopenharmony_ci      break;
1008141cc406Sopenharmony_ci        case MotorId::XP200:
1009141cc406Sopenharmony_ci      dev->reg.find_reg(0x6a).value = 0x7f;	/* table two fast moving step type, PWM duty for table two */
1010141cc406Sopenharmony_ci      dev->reg.find_reg(0x6b).value = 0x08;	/* table two steps number for acc/dec */
1011141cc406Sopenharmony_ci      dev->reg.find_reg(0x6d).value = 0x01;	/* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */
1012141cc406Sopenharmony_ci      break;
1013141cc406Sopenharmony_ci        case MotorId::HP3670:
1014141cc406Sopenharmony_ci      dev->reg.find_reg(0x6a).value = 0x41;	/* table two steps number for acc/dec */
1015141cc406Sopenharmony_ci      dev->reg.find_reg(0x6b).value = 0xc8;	/* table two steps number for acc/dec */
1016141cc406Sopenharmony_ci      dev->reg.find_reg(0x6d).value = 0x7f;
1017141cc406Sopenharmony_ci      break;
1018141cc406Sopenharmony_ci        default:
1019141cc406Sopenharmony_ci      dev->reg.find_reg(0x6a).value = 0x40;	/* table two fast moving step type, PWM duty for table two */
1020141cc406Sopenharmony_ci      dev->reg.find_reg(0x6b).value = 0xff;	/* table two steps number for acc/dec */
1021141cc406Sopenharmony_ci      dev->reg.find_reg(0x6d).value = 0x01;	/* select deceleration steps whenever go home (0), accel/decel stop time (31 * LPeriod) */
1022141cc406Sopenharmony_ci      break;
1023141cc406Sopenharmony_ci    }
1024141cc406Sopenharmony_ci  dev->reg.find_reg(0x6c).value = 0x00;	/* period times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE (one period time) */
1025141cc406Sopenharmony_ci}
1026141cc406Sopenharmony_ci
1027141cc406Sopenharmony_ci// Set values of Analog Device type frontend
1028141cc406Sopenharmony_cistatic void gl646_set_ad_fe(Genesys_Device* dev, std::uint8_t set)
1029141cc406Sopenharmony_ci{
1030141cc406Sopenharmony_ci    DBG_HELPER(dbg);
1031141cc406Sopenharmony_ci  int i;
1032141cc406Sopenharmony_ci
1033141cc406Sopenharmony_ci    if (set == AFE_INIT) {
1034141cc406Sopenharmony_ci
1035141cc406Sopenharmony_ci        dev->frontend = dev->frontend_initial;
1036141cc406Sopenharmony_ci
1037141cc406Sopenharmony_ci        // write them to analog frontend
1038141cc406Sopenharmony_ci        dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00));
1039141cc406Sopenharmony_ci        dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01));
1040141cc406Sopenharmony_ci    }
1041141cc406Sopenharmony_ci  if (set == AFE_SET)
1042141cc406Sopenharmony_ci    {
1043141cc406Sopenharmony_ci        for (i = 0; i < 3; i++) {
1044141cc406Sopenharmony_ci            dev->interface->write_fe_register(0x02 + i, dev->frontend.get_gain(i));
1045141cc406Sopenharmony_ci        }
1046141cc406Sopenharmony_ci        for (i = 0; i < 3; i++) {
1047141cc406Sopenharmony_ci            dev->interface->write_fe_register(0x05 + i, dev->frontend.get_offset(i));
1048141cc406Sopenharmony_ci        }
1049141cc406Sopenharmony_ci    }
1050141cc406Sopenharmony_ci  /*
1051141cc406Sopenharmony_ci     if (set == AFE_POWER_SAVE)
1052141cc406Sopenharmony_ci     {
1053141cc406Sopenharmony_ci        dev->interface->write_fe_register(0x00, dev->frontend.reg[0] | 0x04);
1054141cc406Sopenharmony_ci     } */
1055141cc406Sopenharmony_ci}
1056141cc406Sopenharmony_ci
1057141cc406Sopenharmony_ci/** set up analog frontend
1058141cc406Sopenharmony_ci * set up analog frontend
1059141cc406Sopenharmony_ci * @param dev device to set up
1060141cc406Sopenharmony_ci * @param set action from AFE_SET, AFE_INIT and AFE_POWERSAVE
1061141cc406Sopenharmony_ci * @param dpi resolution of the scan since it affects settings
1062141cc406Sopenharmony_ci */
1063141cc406Sopenharmony_cistatic void gl646_wm_hp3670(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set,
1064141cc406Sopenharmony_ci                            unsigned dpi)
1065141cc406Sopenharmony_ci{
1066141cc406Sopenharmony_ci    DBG_HELPER(dbg);
1067141cc406Sopenharmony_ci  int i;
1068141cc406Sopenharmony_ci
1069141cc406Sopenharmony_ci  switch (set)
1070141cc406Sopenharmony_ci    {
1071141cc406Sopenharmony_ci    case AFE_INIT:
1072141cc406Sopenharmony_ci        dev->interface->write_fe_register(0x04, 0x80);
1073141cc406Sopenharmony_ci        dev->interface->sleep_ms(200);
1074141cc406Sopenharmony_ci    dev->interface->write_register(0x50, 0x00);
1075141cc406Sopenharmony_ci      dev->frontend = dev->frontend_initial;
1076141cc406Sopenharmony_ci        dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01));
1077141cc406Sopenharmony_ci        dev->interface->write_fe_register(0x02, dev->frontend.regs.get_value(0x02));
1078141cc406Sopenharmony_ci        gl646_gpio_output_enable(dev->interface->get_usb_device(), 0x07);
1079141cc406Sopenharmony_ci      break;
1080141cc406Sopenharmony_ci    case AFE_POWER_SAVE:
1081141cc406Sopenharmony_ci        dev->interface->write_fe_register(0x01, 0x06);
1082141cc406Sopenharmony_ci        dev->interface->write_fe_register(0x06, 0x0f);
1083141cc406Sopenharmony_ci            return;
1084141cc406Sopenharmony_ci      break;
1085141cc406Sopenharmony_ci    default:			/* AFE_SET */
1086141cc406Sopenharmony_ci      /* mode setup */
1087141cc406Sopenharmony_ci      i = dev->frontend.regs.get_value(0x03);
1088141cc406Sopenharmony_ci            if (dpi > sensor.full_resolution / 2) {
1089141cc406Sopenharmony_ci      /* fe_reg_0x03 must be 0x12 for 1200 dpi in WOLFSON_HP3670.
1090141cc406Sopenharmony_ci       * WOLFSON_HP2400 in 1200 dpi mode works well with
1091141cc406Sopenharmony_ci	   * fe_reg_0x03 set to 0x32 or 0x12 but not to 0x02 */
1092141cc406Sopenharmony_ci	  i = 0x12;
1093141cc406Sopenharmony_ci	}
1094141cc406Sopenharmony_ci        dev->interface->write_fe_register(0x03, i);
1095141cc406Sopenharmony_ci      /* offset and sign (or msb/lsb ?) */
1096141cc406Sopenharmony_ci        for (i = 0; i < 3; i++) {
1097141cc406Sopenharmony_ci            dev->interface->write_fe_register(0x20 + i, dev->frontend.get_offset(i));
1098141cc406Sopenharmony_ci            dev->interface->write_fe_register(0x24 + i, dev->frontend.regs.get_value(0x24 + i));
1099141cc406Sopenharmony_ci        }
1100141cc406Sopenharmony_ci
1101141cc406Sopenharmony_ci        // gain
1102141cc406Sopenharmony_ci        for (i = 0; i < 3; i++) {
1103141cc406Sopenharmony_ci            dev->interface->write_fe_register(0x28 + i, dev->frontend.get_gain(i));
1104141cc406Sopenharmony_ci        }
1105141cc406Sopenharmony_ci    }
1106141cc406Sopenharmony_ci}
1107141cc406Sopenharmony_ci
1108141cc406Sopenharmony_ci/** Set values of analog frontend
1109141cc406Sopenharmony_ci * @param dev device to set
1110141cc406Sopenharmony_ci * @param set action to execute
1111141cc406Sopenharmony_ci * @param dpi dpi to setup the AFE
1112141cc406Sopenharmony_ci */
1113141cc406Sopenharmony_cistatic void gl646_set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set,
1114141cc406Sopenharmony_ci                         int dpi)
1115141cc406Sopenharmony_ci{
1116141cc406Sopenharmony_ci    DBG_HELPER_ARGS(dbg, "%s,%d", set == AFE_INIT ? "init" :
1117141cc406Sopenharmony_ci                                  set == AFE_SET ? "set" :
1118141cc406Sopenharmony_ci                                  set == AFE_POWER_SAVE ? "powersave" : "huh?", dpi);
1119141cc406Sopenharmony_ci  int i;
1120141cc406Sopenharmony_ci    std::uint8_t val;
1121141cc406Sopenharmony_ci
1122141cc406Sopenharmony_ci  /* Analog Device type frontend */
1123141cc406Sopenharmony_ci    std::uint8_t frontend_type = dev->reg.find_reg(0x04).value & REG_0x04_FESET;
1124141cc406Sopenharmony_ci    if (frontend_type == 0x02) {
1125141cc406Sopenharmony_ci        gl646_set_ad_fe(dev, set);
1126141cc406Sopenharmony_ci        return;
1127141cc406Sopenharmony_ci    }
1128141cc406Sopenharmony_ci
1129141cc406Sopenharmony_ci  /* Wolfson type frontend */
1130141cc406Sopenharmony_ci    if (frontend_type != 0x03) {
1131141cc406Sopenharmony_ci        throw SaneException("unsupported frontend type %d", frontend_type);
1132141cc406Sopenharmony_ci    }
1133141cc406Sopenharmony_ci
1134141cc406Sopenharmony_ci  /* per frontend function to keep code clean */
1135141cc406Sopenharmony_ci  switch (dev->model->adc_id)
1136141cc406Sopenharmony_ci    {
1137141cc406Sopenharmony_ci    case AdcId::WOLFSON_HP3670:
1138141cc406Sopenharmony_ci    case AdcId::WOLFSON_HP2400:
1139141cc406Sopenharmony_ci            gl646_wm_hp3670(dev, sensor, set, dpi);
1140141cc406Sopenharmony_ci            return;
1141141cc406Sopenharmony_ci    default:
1142141cc406Sopenharmony_ci      DBG(DBG_proc, "%s(): using old method\n", __func__);
1143141cc406Sopenharmony_ci      break;
1144141cc406Sopenharmony_ci    }
1145141cc406Sopenharmony_ci
1146141cc406Sopenharmony_ci  /* initialize analog frontend */
1147141cc406Sopenharmony_ci    if (set == AFE_INIT) {
1148141cc406Sopenharmony_ci        dev->frontend = dev->frontend_initial;
1149141cc406Sopenharmony_ci
1150141cc406Sopenharmony_ci        // reset only done on init
1151141cc406Sopenharmony_ci        dev->interface->write_fe_register(0x04, 0x80);
1152141cc406Sopenharmony_ci
1153141cc406Sopenharmony_ci      /* enable GPIO for some models */
1154141cc406Sopenharmony_ci        if (dev->model->sensor_id == SensorId::CCD_HP2300) {
1155141cc406Sopenharmony_ci	  val = 0x07;
1156141cc406Sopenharmony_ci            gl646_gpio_output_enable(dev->interface->get_usb_device(), val);
1157141cc406Sopenharmony_ci	}
1158141cc406Sopenharmony_ci        return;
1159141cc406Sopenharmony_ci    }
1160141cc406Sopenharmony_ci
1161141cc406Sopenharmony_ci    // set fontend to power saving mode
1162141cc406Sopenharmony_ci    if (set == AFE_POWER_SAVE) {
1163141cc406Sopenharmony_ci        dev->interface->write_fe_register(0x01, 0x02);
1164141cc406Sopenharmony_ci        return;
1165141cc406Sopenharmony_ci    }
1166141cc406Sopenharmony_ci
1167141cc406Sopenharmony_ci  /* here starts AFE_SET */
1168141cc406Sopenharmony_ci  /* TODO :  base this test on cfg reg3 or a CCD family flag to be created */
1169141cc406Sopenharmony_ci  /* if (dev->model->ccd_type != SensorId::CCD_HP2300
1170141cc406Sopenharmony_ci     && dev->model->ccd_type != SensorId::CCD_HP3670
1171141cc406Sopenharmony_ci     && dev->model->ccd_type != SensorId::CCD_HP2400) */
1172141cc406Sopenharmony_ci  {
1173141cc406Sopenharmony_ci        dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00));
1174141cc406Sopenharmony_ci        dev->interface->write_fe_register(0x02, dev->frontend.regs.get_value(0x02));
1175141cc406Sopenharmony_ci  }
1176141cc406Sopenharmony_ci
1177141cc406Sopenharmony_ci    // start with reg3
1178141cc406Sopenharmony_ci    dev->interface->write_fe_register(0x03, dev->frontend.regs.get_value(0x03));
1179141cc406Sopenharmony_ci
1180141cc406Sopenharmony_ci  switch (dev->model->sensor_id)
1181141cc406Sopenharmony_ci    {
1182141cc406Sopenharmony_ci    default:
1183141cc406Sopenharmony_ci            for (i = 0; i < 3; i++) {
1184141cc406Sopenharmony_ci                dev->interface->write_fe_register(0x24 + i, dev->frontend.regs.get_value(0x24 + i));
1185141cc406Sopenharmony_ci                dev->interface->write_fe_register(0x28 + i, dev->frontend.get_gain(i));
1186141cc406Sopenharmony_ci                dev->interface->write_fe_register(0x20 + i, dev->frontend.get_offset(i));
1187141cc406Sopenharmony_ci            }
1188141cc406Sopenharmony_ci      break;
1189141cc406Sopenharmony_ci      /* just can't have it to work ....
1190141cc406Sopenharmony_ci         case SensorId::CCD_HP2300:
1191141cc406Sopenharmony_ci         case SensorId::CCD_HP2400:
1192141cc406Sopenharmony_ci         case SensorId::CCD_HP3670:
1193141cc406Sopenharmony_ci
1194141cc406Sopenharmony_ci        dev->interface->write_fe_register(0x23, dev->frontend.get_offset(1));
1195141cc406Sopenharmony_ci        dev->interface->write_fe_register(0x28, dev->frontend.get_gain(1));
1196141cc406Sopenharmony_ci         break; */
1197141cc406Sopenharmony_ci    }
1198141cc406Sopenharmony_ci
1199141cc406Sopenharmony_ci    // end with reg1
1200141cc406Sopenharmony_ci    dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01));
1201141cc406Sopenharmony_ci}
1202141cc406Sopenharmony_ci
1203141cc406Sopenharmony_ci/** Set values of analog frontend
1204141cc406Sopenharmony_ci * this this the public interface, the gl646 as to use one more
1205141cc406Sopenharmony_ci * parameter to work effectively, hence the redirection
1206141cc406Sopenharmony_ci * @param dev device to set
1207141cc406Sopenharmony_ci * @param set action to execute
1208141cc406Sopenharmony_ci */
1209141cc406Sopenharmony_civoid CommandSetGl646::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor,
1210141cc406Sopenharmony_ci                             std::uint8_t set) const
1211141cc406Sopenharmony_ci{
1212141cc406Sopenharmony_ci    gl646_set_fe(dev, sensor, set, dev->settings.yres);
1213141cc406Sopenharmony_ci}
1214141cc406Sopenharmony_ci
1215141cc406Sopenharmony_ci/**
1216141cc406Sopenharmony_ci * enters or leaves power saving mode
1217141cc406Sopenharmony_ci * limited to AFE for now.
1218141cc406Sopenharmony_ci * @param dev scanner's device
1219141cc406Sopenharmony_ci * @param enable true to enable power saving, false to leave it
1220141cc406Sopenharmony_ci */
1221141cc406Sopenharmony_civoid CommandSetGl646::save_power(Genesys_Device* dev, bool enable) const
1222141cc406Sopenharmony_ci{
1223141cc406Sopenharmony_ci    DBG_HELPER_ARGS(dbg, "enable = %d", enable);
1224141cc406Sopenharmony_ci
1225141cc406Sopenharmony_ci  const auto& sensor = sanei_genesys_find_sensor_any(dev);
1226141cc406Sopenharmony_ci
1227141cc406Sopenharmony_ci  if (enable)
1228141cc406Sopenharmony_ci    {
1229141cc406Sopenharmony_ci        // gl646_set_fe(dev, sensor, AFE_POWER_SAVE);
1230141cc406Sopenharmony_ci    }
1231141cc406Sopenharmony_ci  else
1232141cc406Sopenharmony_ci    {
1233141cc406Sopenharmony_ci      gl646_set_fe(dev, sensor, AFE_INIT, 0);
1234141cc406Sopenharmony_ci    }
1235141cc406Sopenharmony_ci}
1236141cc406Sopenharmony_ci
1237141cc406Sopenharmony_civoid CommandSetGl646::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const
1238141cc406Sopenharmony_ci{
1239141cc406Sopenharmony_ci    DBG_HELPER_ARGS(dbg, "delay = %d", delay);
1240141cc406Sopenharmony_ci  Genesys_Register_Set local_reg(Genesys_Register_Set::SEQUENTIAL);
1241141cc406Sopenharmony_ci  int rate, exposure_time, tgtime, time;
1242141cc406Sopenharmony_ci
1243141cc406Sopenharmony_ci  local_reg.init_reg(0x01, dev->reg.get8(0x01));	// disable fastmode
1244141cc406Sopenharmony_ci  local_reg.init_reg(0x03, dev->reg.get8(0x03));        // Lamp power control
1245141cc406Sopenharmony_ci    local_reg.init_reg(0x05, dev->reg.get8(0x05) & ~REG_0x05_BASESEL);   // 24 clocks/pixel
1246141cc406Sopenharmony_ci  local_reg.init_reg(0x38, 0x00); // line period low
1247141cc406Sopenharmony_ci  local_reg.init_reg(0x39, 0x00); //line period high
1248141cc406Sopenharmony_ci  local_reg.init_reg(0x6c, 0x00); // period times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE
1249141cc406Sopenharmony_ci
1250141cc406Sopenharmony_ci  if (!delay)
1251141cc406Sopenharmony_ci    local_reg.find_reg(0x03).value &= 0xf0;	/* disable lampdog and set lamptime = 0 */
1252141cc406Sopenharmony_ci  else if (delay < 20)
1253141cc406Sopenharmony_ci    local_reg.find_reg(0x03).value = (local_reg.get8(0x03) & 0xf0) | 0x09;	/* enable lampdog and set lamptime = 1 */
1254141cc406Sopenharmony_ci  else
1255141cc406Sopenharmony_ci    local_reg.find_reg(0x03).value = (local_reg.get8(0x03) & 0xf0) | 0x0f;	/* enable lampdog and set lamptime = 7 */
1256141cc406Sopenharmony_ci
1257141cc406Sopenharmony_ci  time = delay * 1000 * 60;	/* -> msec */
1258141cc406Sopenharmony_ci    exposure_time = static_cast<std::uint32_t>((time * 32000.0 /
1259141cc406Sopenharmony_ci                (24.0 * 64.0 * (local_reg.get8(0x03) & REG_0x03_LAMPTIM) *
1260141cc406Sopenharmony_ci         1024.0) + 0.5));
1261141cc406Sopenharmony_ci  /* 32000 = system clock, 24 = clocks per pixel */
1262141cc406Sopenharmony_ci  rate = (exposure_time + 65536) / 65536;
1263141cc406Sopenharmony_ci  if (rate > 4)
1264141cc406Sopenharmony_ci    {
1265141cc406Sopenharmony_ci      rate = 8;
1266141cc406Sopenharmony_ci      tgtime = 3;
1267141cc406Sopenharmony_ci    }
1268141cc406Sopenharmony_ci  else if (rate > 2)
1269141cc406Sopenharmony_ci    {
1270141cc406Sopenharmony_ci      rate = 4;
1271141cc406Sopenharmony_ci      tgtime = 2;
1272141cc406Sopenharmony_ci    }
1273141cc406Sopenharmony_ci  else if (rate > 1)
1274141cc406Sopenharmony_ci    {
1275141cc406Sopenharmony_ci      rate = 2;
1276141cc406Sopenharmony_ci      tgtime = 1;
1277141cc406Sopenharmony_ci    }
1278141cc406Sopenharmony_ci  else
1279141cc406Sopenharmony_ci    {
1280141cc406Sopenharmony_ci      rate = 1;
1281141cc406Sopenharmony_ci      tgtime = 0;
1282141cc406Sopenharmony_ci    }
1283141cc406Sopenharmony_ci
1284141cc406Sopenharmony_ci  local_reg.find_reg(0x6c).value |= tgtime << 6;
1285141cc406Sopenharmony_ci  exposure_time /= rate;
1286141cc406Sopenharmony_ci
1287141cc406Sopenharmony_ci  if (exposure_time > 65535)
1288141cc406Sopenharmony_ci    exposure_time = 65535;
1289141cc406Sopenharmony_ci
1290141cc406Sopenharmony_ci  local_reg.find_reg(0x38).value = exposure_time / 256;
1291141cc406Sopenharmony_ci  local_reg.find_reg(0x39).value = exposure_time & 255;
1292141cc406Sopenharmony_ci
1293141cc406Sopenharmony_ci    dev->interface->write_registers(local_reg);
1294141cc406Sopenharmony_ci}
1295141cc406Sopenharmony_ci
1296141cc406Sopenharmony_ci
1297141cc406Sopenharmony_ci/**
1298141cc406Sopenharmony_ci * loads document into scanner
1299141cc406Sopenharmony_ci * currently only used by XP200
1300141cc406Sopenharmony_ci * bit2 (0x04) of gpio is paper event (document in/out) on XP200
1301141cc406Sopenharmony_ci * HOMESNR is set if no document in front of sensor, the sequence of events is
1302141cc406Sopenharmony_ci * paper event -> document is in the sheet feeder
1303141cc406Sopenharmony_ci * HOMESNR becomes 0 -> document reach sensor
1304141cc406Sopenharmony_ci * HOMESNR becomes 1 ->document left sensor
1305141cc406Sopenharmony_ci * paper event -> document is out
1306141cc406Sopenharmony_ci */
1307141cc406Sopenharmony_civoid CommandSetGl646::load_document(Genesys_Device* dev) const
1308141cc406Sopenharmony_ci{
1309141cc406Sopenharmony_ci    DBG_HELPER(dbg);
1310141cc406Sopenharmony_ci
1311141cc406Sopenharmony_ci  // FIXME: sequential not really needed in this case
1312141cc406Sopenharmony_ci  Genesys_Register_Set regs(Genesys_Register_Set::SEQUENTIAL);
1313141cc406Sopenharmony_ci    unsigned count;
1314141cc406Sopenharmony_ci
1315141cc406Sopenharmony_ci  /* no need to load document is flatbed scanner */
1316141cc406Sopenharmony_ci    if (!dev->model->is_sheetfed) {
1317141cc406Sopenharmony_ci      DBG(DBG_proc, "%s: nothing to load\n", __func__);
1318141cc406Sopenharmony_ci      DBG(DBG_proc, "%s: end\n", __func__);
1319141cc406Sopenharmony_ci      return;
1320141cc406Sopenharmony_ci    }
1321141cc406Sopenharmony_ci
1322141cc406Sopenharmony_ci    auto status = scanner_read_status(*dev);
1323141cc406Sopenharmony_ci
1324141cc406Sopenharmony_ci    // home sensor is set if a document is inserted
1325141cc406Sopenharmony_ci    if (status.is_at_home) {
1326141cc406Sopenharmony_ci      /* if no document, waits for a paper event to start loading */
1327141cc406Sopenharmony_ci      /* with a 60 seconde minutes timeout                        */
1328141cc406Sopenharmony_ci      count = 0;
1329141cc406Sopenharmony_ci        std::uint8_t val = 0;
1330141cc406Sopenharmony_ci        do {
1331141cc406Sopenharmony_ci            gl646_gpio_read(dev->interface->get_usb_device(), &val);
1332141cc406Sopenharmony_ci
1333141cc406Sopenharmony_ci	  DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, val);
1334141cc406Sopenharmony_ci	  if ((val & 0x04) != 0x04)
1335141cc406Sopenharmony_ci	    {
1336141cc406Sopenharmony_ci              DBG(DBG_warn, "%s: no paper detected\n", __func__);
1337141cc406Sopenharmony_ci	    }
1338141cc406Sopenharmony_ci            dev->interface->sleep_ms(200);
1339141cc406Sopenharmony_ci            count++;
1340141cc406Sopenharmony_ci        }
1341141cc406Sopenharmony_ci      while (((val & 0x04) != 0x04) && (count < 300));	/* 1 min time out */
1342141cc406Sopenharmony_ci      if (count == 300)
1343141cc406Sopenharmony_ci	{
1344141cc406Sopenharmony_ci        throw SaneException(SANE_STATUS_NO_DOCS, "timeout waiting for document");
1345141cc406Sopenharmony_ci    }
1346141cc406Sopenharmony_ci    }
1347141cc406Sopenharmony_ci
1348141cc406Sopenharmony_ci  /* set up to fast move before scan then move until document is detected */
1349141cc406Sopenharmony_ci  regs.init_reg(0x01, 0x90);
1350141cc406Sopenharmony_ci
1351141cc406Sopenharmony_ci  /* AGOME, 2 slopes motor moving */
1352141cc406Sopenharmony_ci  regs.init_reg(0x02, 0x79);
1353141cc406Sopenharmony_ci
1354141cc406Sopenharmony_ci  /* motor feeding steps to 0 */
1355141cc406Sopenharmony_ci  regs.init_reg(0x3d, 0);
1356141cc406Sopenharmony_ci  regs.init_reg(0x3e, 0);
1357141cc406Sopenharmony_ci  regs.init_reg(0x3f, 0);
1358141cc406Sopenharmony_ci
1359141cc406Sopenharmony_ci  /* 50 fast moving steps */
1360141cc406Sopenharmony_ci  regs.init_reg(0x6b, 50);
1361141cc406Sopenharmony_ci
1362141cc406Sopenharmony_ci  /* set GPO */
1363141cc406Sopenharmony_ci  regs.init_reg(0x66, 0x30);
1364141cc406Sopenharmony_ci
1365141cc406Sopenharmony_ci  /* stesp NO */
1366141cc406Sopenharmony_ci  regs.init_reg(0x21, 4);
1367141cc406Sopenharmony_ci  regs.init_reg(0x22, 1);
1368141cc406Sopenharmony_ci  regs.init_reg(0x23, 1);
1369141cc406Sopenharmony_ci  regs.init_reg(0x24, 4);
1370141cc406Sopenharmony_ci
1371141cc406Sopenharmony_ci  /* generate slope table 2 */
1372141cc406Sopenharmony_ci    auto slope_table = create_slope_table_for_speed(MotorSlope::create_from_steps(6000, 2400, 50),
1373141cc406Sopenharmony_ci                                                    2400, StepType::FULL, 1, 4,
1374141cc406Sopenharmony_ci                                                    get_slope_table_max_size(AsicType::GL646));
1375141cc406Sopenharmony_ci    // document loading:
1376141cc406Sopenharmony_ci    // send regs
1377141cc406Sopenharmony_ci    // start motor
1378141cc406Sopenharmony_ci    // wait e1 status to become e0
1379141cc406Sopenharmony_ci    const auto& sensor = sanei_genesys_find_sensor_any(dev);
1380141cc406Sopenharmony_ci    scanner_send_slope_table(dev, sensor, 1, slope_table.table);
1381141cc406Sopenharmony_ci
1382141cc406Sopenharmony_ci    dev->interface->write_registers(regs);
1383141cc406Sopenharmony_ci
1384141cc406Sopenharmony_ci    scanner_start_action(*dev, true);
1385141cc406Sopenharmony_ci
1386141cc406Sopenharmony_ci  count = 0;
1387141cc406Sopenharmony_ci  do
1388141cc406Sopenharmony_ci    {
1389141cc406Sopenharmony_ci        status = scanner_read_status(*dev);
1390141cc406Sopenharmony_ci        dev->interface->sleep_ms(200);
1391141cc406Sopenharmony_ci      count++;
1392141cc406Sopenharmony_ci    } while (status.is_motor_enabled && (count < 300));
1393141cc406Sopenharmony_ci
1394141cc406Sopenharmony_ci  if (count == 300)
1395141cc406Sopenharmony_ci    {
1396141cc406Sopenharmony_ci      throw SaneException(SANE_STATUS_JAMMED, "can't load document");
1397141cc406Sopenharmony_ci    }
1398141cc406Sopenharmony_ci
1399141cc406Sopenharmony_ci  /* when loading OK, document is here */
1400141cc406Sopenharmony_ci    dev->document = true;
1401141cc406Sopenharmony_ci
1402141cc406Sopenharmony_ci  /* set up to idle */
1403141cc406Sopenharmony_ci  regs.set8(0x02, 0x71);
1404141cc406Sopenharmony_ci  regs.set8(0x3f, 1);
1405141cc406Sopenharmony_ci  regs.set8(0x6b, 8);
1406141cc406Sopenharmony_ci    dev->interface->write_registers(regs);
1407141cc406Sopenharmony_ci}
1408141cc406Sopenharmony_ci
1409141cc406Sopenharmony_ci/**
1410141cc406Sopenharmony_ci * detects end of document and adjust current scan
1411141cc406Sopenharmony_ci * to take it into account
1412141cc406Sopenharmony_ci * used by sheetfed scanners
1413141cc406Sopenharmony_ci */
1414141cc406Sopenharmony_civoid CommandSetGl646::detect_document_end(Genesys_Device* dev) const
1415141cc406Sopenharmony_ci{
1416141cc406Sopenharmony_ci    DBG_HELPER(dbg);
1417141cc406Sopenharmony_ci    std::uint8_t gpio;
1418141cc406Sopenharmony_ci    unsigned int bytes_left;
1419141cc406Sopenharmony_ci
1420141cc406Sopenharmony_ci    // test for document presence
1421141cc406Sopenharmony_ci    scanner_read_print_status(*dev);
1422141cc406Sopenharmony_ci
1423141cc406Sopenharmony_ci    gl646_gpio_read(dev->interface->get_usb_device(), &gpio);
1424141cc406Sopenharmony_ci  DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, gpio);
1425141cc406Sopenharmony_ci
1426141cc406Sopenharmony_ci  /* detect document event. There one event when the document go in,
1427141cc406Sopenharmony_ci   * then another when it leaves */
1428141cc406Sopenharmony_ci    if (dev->document && (gpio & 0x04) && (dev->total_bytes_read > 0)) {
1429141cc406Sopenharmony_ci      DBG(DBG_info, "%s: no more document\n", __func__);
1430141cc406Sopenharmony_ci        dev->document = false;
1431141cc406Sopenharmony_ci
1432141cc406Sopenharmony_ci      /* adjust number of bytes to read:
1433141cc406Sopenharmony_ci       * total_bytes_to_read is the number of byte to send to frontend
1434141cc406Sopenharmony_ci       * total_bytes_read is the number of bytes sent to frontend
1435141cc406Sopenharmony_ci       * read_bytes_left is the number of bytes to read from the scanner
1436141cc406Sopenharmony_ci       */
1437141cc406Sopenharmony_ci      DBG(DBG_io, "%s: total_bytes_to_read=%zu\n", __func__, dev->total_bytes_to_read);
1438141cc406Sopenharmony_ci      DBG(DBG_io, "%s: total_bytes_read   =%zu\n", __func__, dev->total_bytes_read);
1439141cc406Sopenharmony_ci
1440141cc406Sopenharmony_ci        // amount of data available from scanner is what to scan
1441141cc406Sopenharmony_ci        sanei_genesys_read_valid_words(dev, &bytes_left);
1442141cc406Sopenharmony_ci
1443141cc406Sopenharmony_ci        unsigned lines_in_buffer = bytes_left / dev->session.output_line_bytes_raw;
1444141cc406Sopenharmony_ci
1445141cc406Sopenharmony_ci        // we add the number of lines needed to read the last part of the document in
1446141cc406Sopenharmony_ci        unsigned lines_offset = static_cast<unsigned>(
1447141cc406Sopenharmony_ci                (dev->model->y_offset * dev->session.params.yres) / MM_PER_INCH);
1448141cc406Sopenharmony_ci
1449141cc406Sopenharmony_ci        unsigned remaining_lines = lines_in_buffer + lines_offset;
1450141cc406Sopenharmony_ci
1451141cc406Sopenharmony_ci        bytes_left = remaining_lines * dev->session.output_line_bytes_raw;
1452141cc406Sopenharmony_ci
1453141cc406Sopenharmony_ci        if (bytes_left < dev->get_pipeline_source().remaining_bytes()) {
1454141cc406Sopenharmony_ci            dev->get_pipeline_source().set_remaining_bytes(bytes_left);
1455141cc406Sopenharmony_ci            dev->total_bytes_to_read = dev->total_bytes_read + bytes_left;
1456141cc406Sopenharmony_ci        }
1457141cc406Sopenharmony_ci      DBG(DBG_io, "%s: total_bytes_to_read=%zu\n", __func__, dev->total_bytes_to_read);
1458141cc406Sopenharmony_ci      DBG(DBG_io, "%s: total_bytes_read   =%zu\n", __func__, dev->total_bytes_read);
1459141cc406Sopenharmony_ci    }
1460141cc406Sopenharmony_ci}
1461141cc406Sopenharmony_ci
1462141cc406Sopenharmony_ci/**
1463141cc406Sopenharmony_ci * eject document from the feeder
1464141cc406Sopenharmony_ci * currently only used by XP200
1465141cc406Sopenharmony_ci * TODO we currently rely on AGOHOME not being set for sheetfed scanners,
1466141cc406Sopenharmony_ci * maybe check this flag in eject to let the document being eject automatically
1467141cc406Sopenharmony_ci */
1468141cc406Sopenharmony_civoid CommandSetGl646::eject_document(Genesys_Device* dev) const
1469141cc406Sopenharmony_ci{
1470141cc406Sopenharmony_ci    DBG_HELPER(dbg);
1471141cc406Sopenharmony_ci
1472141cc406Sopenharmony_ci  // FIXME: SEQUENTIAL not really needed in this case
1473141cc406Sopenharmony_ci  Genesys_Register_Set regs((Genesys_Register_Set::SEQUENTIAL));
1474141cc406Sopenharmony_ci    unsigned count;
1475141cc406Sopenharmony_ci    std::uint8_t gpio;
1476141cc406Sopenharmony_ci
1477141cc406Sopenharmony_ci  /* at the end there will be no more document */
1478141cc406Sopenharmony_ci    dev->document = false;
1479141cc406Sopenharmony_ci
1480141cc406Sopenharmony_ci    // first check for document event
1481141cc406Sopenharmony_ci    gl646_gpio_read(dev->interface->get_usb_device(), &gpio);
1482141cc406Sopenharmony_ci
1483141cc406Sopenharmony_ci  DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, gpio);
1484141cc406Sopenharmony_ci
1485141cc406Sopenharmony_ci    // test status : paper event + HOMESNR -> no more doc ?
1486141cc406Sopenharmony_ci    auto status = scanner_read_status(*dev);
1487141cc406Sopenharmony_ci
1488141cc406Sopenharmony_ci    // home sensor is set when document is inserted
1489141cc406Sopenharmony_ci    if (status.is_at_home) {
1490141cc406Sopenharmony_ci        dev->document = false;
1491141cc406Sopenharmony_ci        DBG(DBG_info, "%s: no more document to eject\n", __func__);
1492141cc406Sopenharmony_ci        return;
1493141cc406Sopenharmony_ci    }
1494141cc406Sopenharmony_ci
1495141cc406Sopenharmony_ci    // there is a document inserted, eject it
1496141cc406Sopenharmony_ci    dev->interface->write_register(0x01, 0xb0);
1497141cc406Sopenharmony_ci
1498141cc406Sopenharmony_ci  /* wait for motor to stop */
1499141cc406Sopenharmony_ci    do {
1500141cc406Sopenharmony_ci        dev->interface->sleep_ms(200);
1501141cc406Sopenharmony_ci        status = scanner_read_status(*dev);
1502141cc406Sopenharmony_ci    }
1503141cc406Sopenharmony_ci    while (status.is_motor_enabled);
1504141cc406Sopenharmony_ci
1505141cc406Sopenharmony_ci  /* set up to fast move before scan then move until document is detected */
1506141cc406Sopenharmony_ci  regs.init_reg(0x01, 0xb0);
1507141cc406Sopenharmony_ci
1508141cc406Sopenharmony_ci  /* AGOME, 2 slopes motor moving , eject 'backward' */
1509141cc406Sopenharmony_ci  regs.init_reg(0x02, 0x5d);
1510141cc406Sopenharmony_ci
1511141cc406Sopenharmony_ci  /* motor feeding steps to 119880 */
1512141cc406Sopenharmony_ci  regs.init_reg(0x3d, 1);
1513141cc406Sopenharmony_ci  regs.init_reg(0x3e, 0xd4);
1514141cc406Sopenharmony_ci  regs.init_reg(0x3f, 0x48);
1515141cc406Sopenharmony_ci
1516141cc406Sopenharmony_ci  /* 60 fast moving steps */
1517141cc406Sopenharmony_ci  regs.init_reg(0x6b, 60);
1518141cc406Sopenharmony_ci
1519141cc406Sopenharmony_ci  /* set GPO */
1520141cc406Sopenharmony_ci  regs.init_reg(0x66, 0x30);
1521141cc406Sopenharmony_ci
1522141cc406Sopenharmony_ci  /* stesp NO */
1523141cc406Sopenharmony_ci  regs.init_reg(0x21, 4);
1524141cc406Sopenharmony_ci  regs.init_reg(0x22, 1);
1525141cc406Sopenharmony_ci  regs.init_reg(0x23, 1);
1526141cc406Sopenharmony_ci  regs.init_reg(0x24, 4);
1527141cc406Sopenharmony_ci
1528141cc406Sopenharmony_ci  /* generate slope table 2 */
1529141cc406Sopenharmony_ci    auto slope_table = create_slope_table_for_speed(MotorSlope::create_from_steps(10000, 1600, 60),
1530141cc406Sopenharmony_ci                                                    1600, StepType::FULL, 1, 4,
1531141cc406Sopenharmony_ci                                                    get_slope_table_max_size(AsicType::GL646));
1532141cc406Sopenharmony_ci    // document eject:
1533141cc406Sopenharmony_ci    // send regs
1534141cc406Sopenharmony_ci    // start motor
1535141cc406Sopenharmony_ci    // wait c1 status to become c8 : HOMESNR and ~MOTFLAG
1536141cc406Sopenharmony_ci    // FIXME: sensor is not used.
1537141cc406Sopenharmony_ci    const auto& sensor = sanei_genesys_find_sensor_any(dev);
1538141cc406Sopenharmony_ci    scanner_send_slope_table(dev, sensor, 1, slope_table.table);
1539141cc406Sopenharmony_ci
1540141cc406Sopenharmony_ci    dev->interface->write_registers(regs);
1541141cc406Sopenharmony_ci
1542141cc406Sopenharmony_ci    scanner_start_action(*dev, true);
1543141cc406Sopenharmony_ci
1544141cc406Sopenharmony_ci  /* loop until paper sensor tells paper is out, and till motor is running */
1545141cc406Sopenharmony_ci  /* use a 30 timeout */
1546141cc406Sopenharmony_ci  count = 0;
1547141cc406Sopenharmony_ci    do {
1548141cc406Sopenharmony_ci        status = scanner_read_status(*dev);
1549141cc406Sopenharmony_ci
1550141cc406Sopenharmony_ci        dev->interface->sleep_ms(200);
1551141cc406Sopenharmony_ci      count++;
1552141cc406Sopenharmony_ci    } while (!status.is_at_home && (count < 150));
1553141cc406Sopenharmony_ci
1554141cc406Sopenharmony_ci    // read GPIO on exit
1555141cc406Sopenharmony_ci    gl646_gpio_read(dev->interface->get_usb_device(), &gpio);
1556141cc406Sopenharmony_ci
1557141cc406Sopenharmony_ci  DBG(DBG_info, "%s: GPIO=0x%02x\n", __func__, gpio);
1558141cc406Sopenharmony_ci}
1559141cc406Sopenharmony_ci
1560141cc406Sopenharmony_ci// Send the low-level scan command
1561141cc406Sopenharmony_civoid CommandSetGl646::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
1562141cc406Sopenharmony_ci                                 Genesys_Register_Set* reg, bool start_motor) const
1563141cc406Sopenharmony_ci{
1564141cc406Sopenharmony_ci    DBG_HELPER(dbg);
1565141cc406Sopenharmony_ci    (void) sensor;
1566141cc406Sopenharmony_ci  // FIXME: SEQUENTIAL not really needed in this case
1567141cc406Sopenharmony_ci  Genesys_Register_Set local_reg(Genesys_Register_Set::SEQUENTIAL);
1568141cc406Sopenharmony_ci
1569141cc406Sopenharmony_ci    local_reg.init_reg(0x03, reg->get8(0x03));
1570141cc406Sopenharmony_ci    local_reg.init_reg(0x01, reg->get8(0x01) | REG_0x01_SCAN);
1571141cc406Sopenharmony_ci
1572141cc406Sopenharmony_ci    if (start_motor) {
1573141cc406Sopenharmony_ci        local_reg.init_reg(0x0f, 0x01);
1574141cc406Sopenharmony_ci    } else {
1575141cc406Sopenharmony_ci        local_reg.init_reg(0x0f, 0x00); // do not start motor yet
1576141cc406Sopenharmony_ci    }
1577141cc406Sopenharmony_ci
1578141cc406Sopenharmony_ci    dev->interface->write_registers(local_reg);
1579141cc406Sopenharmony_ci
1580141cc406Sopenharmony_ci    dev->advance_head_pos_by_session(ScanHeadId::PRIMARY);
1581141cc406Sopenharmony_ci}
1582141cc406Sopenharmony_ci
1583141cc406Sopenharmony_ci
1584141cc406Sopenharmony_ci// Send the stop scan command
1585141cc406Sopenharmony_cistatic void end_scan_impl(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop,
1586141cc406Sopenharmony_ci                          bool eject)
1587141cc406Sopenharmony_ci{
1588141cc406Sopenharmony_ci    DBG_HELPER_ARGS(dbg, "check_stop = %d, eject = %d", check_stop, eject);
1589141cc406Sopenharmony_ci
1590141cc406Sopenharmony_ci    scanner_stop_action_no_move(*dev, *reg);
1591141cc406Sopenharmony_ci
1592141cc406Sopenharmony_ci    unsigned wait_limit_seconds = 30;
1593141cc406Sopenharmony_ci
1594141cc406Sopenharmony_ci  /* for sheetfed scanners, we may have to eject document */
1595141cc406Sopenharmony_ci    if (dev->model->is_sheetfed) {
1596141cc406Sopenharmony_ci        if (eject && dev->document) {
1597141cc406Sopenharmony_ci            dev->cmd_set->eject_document(dev);
1598141cc406Sopenharmony_ci        }
1599141cc406Sopenharmony_ci        wait_limit_seconds = 3;
1600141cc406Sopenharmony_ci    }
1601141cc406Sopenharmony_ci
1602141cc406Sopenharmony_ci    if (is_testing_mode()) {
1603141cc406Sopenharmony_ci        return;
1604141cc406Sopenharmony_ci    }
1605141cc406Sopenharmony_ci
1606141cc406Sopenharmony_ci    dev->interface->sleep_ms(100);
1607141cc406Sopenharmony_ci
1608141cc406Sopenharmony_ci    if (check_stop) {
1609141cc406Sopenharmony_ci        for (unsigned i = 0; i < wait_limit_seconds * 10; i++) {
1610141cc406Sopenharmony_ci            if (scanner_is_motor_stopped(*dev)) {
1611141cc406Sopenharmony_ci                return;
1612141cc406Sopenharmony_ci            }
1613141cc406Sopenharmony_ci
1614141cc406Sopenharmony_ci            dev->interface->sleep_ms(100);
1615141cc406Sopenharmony_ci        }
1616141cc406Sopenharmony_ci        throw SaneException(SANE_STATUS_IO_ERROR, "could not stop motor");
1617141cc406Sopenharmony_ci    }
1618141cc406Sopenharmony_ci}
1619141cc406Sopenharmony_ci
1620141cc406Sopenharmony_ci// Send the stop scan command
1621141cc406Sopenharmony_civoid CommandSetGl646::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg,
1622141cc406Sopenharmony_ci                               bool check_stop) const
1623141cc406Sopenharmony_ci{
1624141cc406Sopenharmony_ci    end_scan_impl(dev, reg, check_stop, false);
1625141cc406Sopenharmony_ci}
1626141cc406Sopenharmony_ci
1627141cc406Sopenharmony_ci/**
1628141cc406Sopenharmony_ci * parks head
1629141cc406Sopenharmony_ci * @param dev scanner's device
1630141cc406Sopenharmony_ci * @param wait_until_home true if the function waits until head parked
1631141cc406Sopenharmony_ci */
1632141cc406Sopenharmony_civoid CommandSetGl646::move_back_home(Genesys_Device* dev, bool wait_until_home) const
1633141cc406Sopenharmony_ci{
1634141cc406Sopenharmony_ci    DBG_HELPER_ARGS(dbg, "wait_until_home = %d\n", wait_until_home);
1635141cc406Sopenharmony_ci  int i;
1636141cc406Sopenharmony_ci  int loop = 0;
1637141cc406Sopenharmony_ci
1638141cc406Sopenharmony_ci    auto status = scanner_read_status(*dev);
1639141cc406Sopenharmony_ci
1640141cc406Sopenharmony_ci    if (status.is_at_home) {
1641141cc406Sopenharmony_ci      DBG(DBG_info, "%s: end since already at home\n", __func__);
1642141cc406Sopenharmony_ci        dev->set_head_pos_zero(ScanHeadId::PRIMARY);
1643141cc406Sopenharmony_ci        return;
1644141cc406Sopenharmony_ci    }
1645141cc406Sopenharmony_ci
1646141cc406Sopenharmony_ci  /* stop motor if needed */
1647141cc406Sopenharmony_ci    if (status.is_motor_enabled) {
1648141cc406Sopenharmony_ci        gl646_stop_motor(dev);
1649141cc406Sopenharmony_ci        dev->interface->sleep_ms(200);
1650141cc406Sopenharmony_ci    }
1651141cc406Sopenharmony_ci
1652141cc406Sopenharmony_ci  /* when scanhead is moving then wait until scanhead stops or timeout */
1653141cc406Sopenharmony_ci  DBG(DBG_info, "%s: ensuring that motor is off\n", __func__);
1654141cc406Sopenharmony_ci    for (i = 400; i > 0; i--) {
1655141cc406Sopenharmony_ci        // do not wait longer than 40 seconds, count down to get i = 0 when busy
1656141cc406Sopenharmony_ci
1657141cc406Sopenharmony_ci        status = scanner_read_status(*dev);
1658141cc406Sopenharmony_ci
1659141cc406Sopenharmony_ci        if (!status.is_motor_enabled && status.is_at_home) {
1660141cc406Sopenharmony_ci            DBG(DBG_info, "%s: already at home and not moving\n", __func__);
1661141cc406Sopenharmony_ci            dev->set_head_pos_zero(ScanHeadId::PRIMARY);
1662141cc406Sopenharmony_ci            return;
1663141cc406Sopenharmony_ci        }
1664141cc406Sopenharmony_ci        if (!status.is_motor_enabled) {
1665141cc406Sopenharmony_ci            break;
1666141cc406Sopenharmony_ci        }
1667141cc406Sopenharmony_ci
1668141cc406Sopenharmony_ci        dev->interface->sleep_ms(100);
1669141cc406Sopenharmony_ci    }
1670141cc406Sopenharmony_ci
1671141cc406Sopenharmony_ci  if (!i)			/* the loop counted down to 0, scanner still is busy */
1672141cc406Sopenharmony_ci    {
1673141cc406Sopenharmony_ci        dev->set_head_pos_unknown(ScanHeadId::PRIMARY | ScanHeadId::SECONDARY);
1674141cc406Sopenharmony_ci        throw SaneException(SANE_STATUS_DEVICE_BUSY, "motor is still on: device busy");
1675141cc406Sopenharmony_ci    }
1676141cc406Sopenharmony_ci
1677141cc406Sopenharmony_ci    // setup for a backward scan of 65535 steps, with no actual data reading
1678141cc406Sopenharmony_ci    auto resolution = sanei_genesys_get_lowest_dpi(dev);
1679141cc406Sopenharmony_ci
1680141cc406Sopenharmony_ci    const auto& sensor = sanei_genesys_find_sensor(dev, resolution, 3,
1681141cc406Sopenharmony_ci                                                   dev->model->default_method);
1682141cc406Sopenharmony_ci
1683141cc406Sopenharmony_ci    ScanSession session;
1684141cc406Sopenharmony_ci    session.params.xres = resolution;
1685141cc406Sopenharmony_ci    session.params.yres = resolution;
1686141cc406Sopenharmony_ci    session.params.startx = 0;
1687141cc406Sopenharmony_ci    session.params.starty = 65535;
1688141cc406Sopenharmony_ci    session.params.pixels = 600;
1689141cc406Sopenharmony_ci    session.params.lines = 1;
1690141cc406Sopenharmony_ci    session.params.depth = 8;
1691141cc406Sopenharmony_ci    session.params.channels = 3;
1692141cc406Sopenharmony_ci    session.params.scan_method = dev->model->default_method;
1693141cc406Sopenharmony_ci    session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
1694141cc406Sopenharmony_ci    session.params.color_filter = ColorFilter::RED;
1695141cc406Sopenharmony_ci    session.params.contrast_adjustment = dev->settings.contrast;
1696141cc406Sopenharmony_ci    session.params.brightness_adjustment = dev->settings.brightness;
1697141cc406Sopenharmony_ci    session.params.flags = ScanFlag::REVERSE |
1698141cc406Sopenharmony_ci                           ScanFlag::AUTO_GO_HOME |
1699141cc406Sopenharmony_ci                           ScanFlag::DISABLE_GAMMA;
1700141cc406Sopenharmony_ci    if (dev->model->default_method == ScanMethod::TRANSPARENCY) {
1701141cc406Sopenharmony_ci        session.params.flags |= ScanFlag::USE_XPA;
1702141cc406Sopenharmony_ci    }
1703141cc406Sopenharmony_ci    compute_session(dev, session, sensor);
1704141cc406Sopenharmony_ci
1705141cc406Sopenharmony_ci    init_regs_for_scan_session(dev, sensor, &dev->reg, session);
1706141cc406Sopenharmony_ci
1707141cc406Sopenharmony_ci  /* backward , no actual data scanned TODO more setup flags to avoid this register manipulations ? */
1708141cc406Sopenharmony_ci    regs_set_optical_off(dev->model->asic_type, dev->reg);
1709141cc406Sopenharmony_ci
1710141cc406Sopenharmony_ci    // sets frontend
1711141cc406Sopenharmony_ci    gl646_set_fe(dev, sensor, AFE_SET, resolution);
1712141cc406Sopenharmony_ci
1713141cc406Sopenharmony_ci  /* write scan registers */
1714141cc406Sopenharmony_ci    try {
1715141cc406Sopenharmony_ci        dev->interface->write_registers(dev->reg);
1716141cc406Sopenharmony_ci    } catch (...) {
1717141cc406Sopenharmony_ci        DBG(DBG_error, "%s: failed to bulk write registers\n", __func__);
1718141cc406Sopenharmony_ci    }
1719141cc406Sopenharmony_ci
1720141cc406Sopenharmony_ci  /* registers are restored to an iddl state, give up if no head to park */
1721141cc406Sopenharmony_ci    if (dev->model->is_sheetfed) {
1722141cc406Sopenharmony_ci        return;
1723141cc406Sopenharmony_ci    }
1724141cc406Sopenharmony_ci
1725141cc406Sopenharmony_ci    // starts scan
1726141cc406Sopenharmony_ci    {
1727141cc406Sopenharmony_ci        // this is effectively the same as dev->cmd_set->begin_scan(dev, sensor, &dev->reg, true);
1728141cc406Sopenharmony_ci        // except that we don't modify the head position calculations
1729141cc406Sopenharmony_ci
1730141cc406Sopenharmony_ci        // FIXME: SEQUENTIAL not really needed in this case
1731141cc406Sopenharmony_ci        Genesys_Register_Set scan_local_reg(Genesys_Register_Set::SEQUENTIAL);
1732141cc406Sopenharmony_ci
1733141cc406Sopenharmony_ci        scan_local_reg.init_reg(0x03, dev->reg.get8(0x03));
1734141cc406Sopenharmony_ci        scan_local_reg.init_reg(0x01, dev->reg.get8(0x01) | REG_0x01_SCAN);
1735141cc406Sopenharmony_ci        scan_local_reg.init_reg(0x0f, 0x01);
1736141cc406Sopenharmony_ci
1737141cc406Sopenharmony_ci        dev->interface->write_registers(scan_local_reg);
1738141cc406Sopenharmony_ci    }
1739141cc406Sopenharmony_ci
1740141cc406Sopenharmony_ci    if (is_testing_mode()) {
1741141cc406Sopenharmony_ci        dev->interface->test_checkpoint("move_back_home");
1742141cc406Sopenharmony_ci        dev->set_head_pos_zero(ScanHeadId::PRIMARY);
1743141cc406Sopenharmony_ci        return;
1744141cc406Sopenharmony_ci    }
1745141cc406Sopenharmony_ci
1746141cc406Sopenharmony_ci  /* loop until head parked */
1747141cc406Sopenharmony_ci  if (wait_until_home)
1748141cc406Sopenharmony_ci    {
1749141cc406Sopenharmony_ci      while (loop < 300)		/* do not wait longer then 30 seconds */
1750141cc406Sopenharmony_ci	{
1751141cc406Sopenharmony_ci            auto status = scanner_read_status(*dev);
1752141cc406Sopenharmony_ci
1753141cc406Sopenharmony_ci            if (status.is_at_home) {
1754141cc406Sopenharmony_ci	      DBG(DBG_info, "%s: reached home position\n", __func__);
1755141cc406Sopenharmony_ci                dev->interface->sleep_ms(500);
1756141cc406Sopenharmony_ci                dev->set_head_pos_zero(ScanHeadId::PRIMARY);
1757141cc406Sopenharmony_ci                return;
1758141cc406Sopenharmony_ci            }
1759141cc406Sopenharmony_ci            dev->interface->sleep_ms(100);
1760141cc406Sopenharmony_ci            ++loop;
1761141cc406Sopenharmony_ci        }
1762141cc406Sopenharmony_ci
1763141cc406Sopenharmony_ci        // when we come here then the scanner needed too much time for this, so we better
1764141cc406Sopenharmony_ci        // stop the motor
1765141cc406Sopenharmony_ci        catch_all_exceptions(__func__, [&](){ gl646_stop_motor (dev); });
1766141cc406Sopenharmony_ci        catch_all_exceptions(__func__, [&](){ end_scan_impl(dev, &dev->reg, true, false); });
1767141cc406Sopenharmony_ci        dev->set_head_pos_unknown(ScanHeadId::PRIMARY | ScanHeadId::SECONDARY);
1768141cc406Sopenharmony_ci        throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for scanhead to go home");
1769141cc406Sopenharmony_ci    }
1770141cc406Sopenharmony_ci
1771141cc406Sopenharmony_ci
1772141cc406Sopenharmony_ci  DBG(DBG_info, "%s: scanhead is still moving\n", __func__);
1773141cc406Sopenharmony_ci}
1774141cc406Sopenharmony_ci
1775141cc406Sopenharmony_ci/**
1776141cc406Sopenharmony_ci * init registers for shading calibration
1777141cc406Sopenharmony_ci * we assume that scanner's head is on an area suiting shading calibration.
1778141cc406Sopenharmony_ci * We scan a full scan width area by the shading line number for the device
1779141cc406Sopenharmony_ci */
1780141cc406Sopenharmony_civoid CommandSetGl646::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
1781141cc406Sopenharmony_ci                                            Genesys_Register_Set& regs) const
1782141cc406Sopenharmony_ci{
1783141cc406Sopenharmony_ci    DBG_HELPER(dbg);
1784141cc406Sopenharmony_ci    (void) regs;
1785141cc406Sopenharmony_ci
1786141cc406Sopenharmony_ci  /* fill settings for scan : always a color scan */
1787141cc406Sopenharmony_ci  int channels = 3;
1788141cc406Sopenharmony_ci
1789141cc406Sopenharmony_ci    unsigned cksel = get_cksel(dev->model->sensor_id, dev->settings.xres, channels);
1790141cc406Sopenharmony_ci
1791141cc406Sopenharmony_ci    unsigned resolution = sensor.get_optical_resolution() / cksel;
1792141cc406Sopenharmony_ci    // FIXME: we select wrong calibration sensor
1793141cc406Sopenharmony_ci    const auto& calib_sensor = sanei_genesys_find_sensor(dev, dev->settings.xres, channels,
1794141cc406Sopenharmony_ci                                                         dev->settings.scan_method);
1795141cc406Sopenharmony_ci
1796141cc406Sopenharmony_ci    auto pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
1797141cc406Sopenharmony_ci
1798141cc406Sopenharmony_ci    unsigned calib_lines =
1799141cc406Sopenharmony_ci            static_cast<unsigned>(dev->model->y_size_calib_mm * resolution / MM_PER_INCH);
1800141cc406Sopenharmony_ci
1801141cc406Sopenharmony_ci    ScanSession session;
1802141cc406Sopenharmony_ci    session.params.xres = resolution;
1803141cc406Sopenharmony_ci    session.params.yres = resolution;
1804141cc406Sopenharmony_ci    session.params.startx = 0;
1805141cc406Sopenharmony_ci    session.params.starty = 0;
1806141cc406Sopenharmony_ci    session.params.pixels = pixels;
1807141cc406Sopenharmony_ci    session.params.lines = calib_lines;
1808141cc406Sopenharmony_ci    session.params.depth = 16;
1809141cc406Sopenharmony_ci    session.params.channels = channels;
1810141cc406Sopenharmony_ci    session.params.scan_method = dev->settings.scan_method;
1811141cc406Sopenharmony_ci    session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
1812141cc406Sopenharmony_ci    session.params.color_filter = dev->settings.color_filter;
1813141cc406Sopenharmony_ci    session.params.contrast_adjustment = dev->settings.contrast;
1814141cc406Sopenharmony_ci    session.params.brightness_adjustment = dev->settings.brightness;
1815141cc406Sopenharmony_ci    session.params.flags = ScanFlag::DISABLE_SHADING |
1816141cc406Sopenharmony_ci                           ScanFlag::DISABLE_GAMMA |
1817141cc406Sopenharmony_ci                           ScanFlag::IGNORE_COLOR_OFFSET |
1818141cc406Sopenharmony_ci                           ScanFlag::IGNORE_STAGGER_OFFSET;
1819141cc406Sopenharmony_ci    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) {
1820141cc406Sopenharmony_ci        session.params.flags |= ScanFlag::USE_XPA;
1821141cc406Sopenharmony_ci    }
1822141cc406Sopenharmony_ci    compute_session(dev, session, calib_sensor);
1823141cc406Sopenharmony_ci
1824141cc406Sopenharmony_ci    dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session);
1825141cc406Sopenharmony_ci
1826141cc406Sopenharmony_ci    dev->calib_session = session;
1827141cc406Sopenharmony_ci
1828141cc406Sopenharmony_ci  /* no shading */
1829141cc406Sopenharmony_ci    dev->reg.find_reg(0x02).value |= REG_0x02_ACDCDIS;	/* ease backtracking */
1830141cc406Sopenharmony_ci    dev->reg.find_reg(0x02).value &= ~REG_0x02_FASTFED;
1831141cc406Sopenharmony_ci  sanei_genesys_set_motor_power(dev->reg, false);
1832141cc406Sopenharmony_ci}
1833141cc406Sopenharmony_ci
1834141cc406Sopenharmony_cibool CommandSetGl646::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const
1835141cc406Sopenharmony_ci{
1836141cc406Sopenharmony_ci    return dev->is_head_pos_known(ScanHeadId::PRIMARY) &&
1837141cc406Sopenharmony_ci            dev->head_pos(ScanHeadId::PRIMARY) &&
1838141cc406Sopenharmony_ci            dev->settings.scan_method == ScanMethod::FLATBED;
1839141cc406Sopenharmony_ci}
1840141cc406Sopenharmony_ci
1841141cc406Sopenharmony_ci/**
1842141cc406Sopenharmony_ci * this function send gamma table to ASIC
1843141cc406Sopenharmony_ci */
1844141cc406Sopenharmony_civoid CommandSetGl646::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const
1845141cc406Sopenharmony_ci{
1846141cc406Sopenharmony_ci    DBG_HELPER(dbg);
1847141cc406Sopenharmony_ci  int size;
1848141cc406Sopenharmony_ci  int address;
1849141cc406Sopenharmony_ci  int bits;
1850141cc406Sopenharmony_ci
1851141cc406Sopenharmony_ci     if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) {
1852141cc406Sopenharmony_ci      size = 16384;
1853141cc406Sopenharmony_ci      bits = 14;
1854141cc406Sopenharmony_ci    }
1855141cc406Sopenharmony_ci  else
1856141cc406Sopenharmony_ci    {
1857141cc406Sopenharmony_ci      size = 4096;
1858141cc406Sopenharmony_ci      bits = 12;
1859141cc406Sopenharmony_ci    }
1860141cc406Sopenharmony_ci
1861141cc406Sopenharmony_ci    auto gamma = generate_gamma_buffer(dev, sensor, bits, size-1, size);
1862141cc406Sopenharmony_ci
1863141cc406Sopenharmony_ci  /* table address */
1864141cc406Sopenharmony_ci  switch (dev->reg.find_reg(0x05).value >> 6)
1865141cc406Sopenharmony_ci    {
1866141cc406Sopenharmony_ci    case 0:			/* 600 dpi */
1867141cc406Sopenharmony_ci      address = 0x09000;
1868141cc406Sopenharmony_ci      break;
1869141cc406Sopenharmony_ci    case 1:			/* 1200 dpi */
1870141cc406Sopenharmony_ci      address = 0x11000;
1871141cc406Sopenharmony_ci      break;
1872141cc406Sopenharmony_ci    case 2:			/* 2400 dpi */
1873141cc406Sopenharmony_ci      address = 0x20000;
1874141cc406Sopenharmony_ci      break;
1875141cc406Sopenharmony_ci    default:
1876141cc406Sopenharmony_ci            throw SaneException("invalid dpi");
1877141cc406Sopenharmony_ci    }
1878141cc406Sopenharmony_ci
1879141cc406Sopenharmony_ci    dev->interface->write_buffer(0x3c, address, gamma.data(), size * 2 * 3);
1880141cc406Sopenharmony_ci}
1881141cc406Sopenharmony_ci
1882141cc406Sopenharmony_ci/** @brief this function does the led calibration.
1883141cc406Sopenharmony_ci * this function does the led calibration by scanning one line of the calibration
1884141cc406Sopenharmony_ci * area below scanner's top on white strip. The scope of this function is
1885141cc406Sopenharmony_ci * currently limited to the XP200
1886141cc406Sopenharmony_ci */
1887141cc406Sopenharmony_ciSensorExposure CommandSetGl646::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
1888141cc406Sopenharmony_ci                                                Genesys_Register_Set& regs) const
1889141cc406Sopenharmony_ci{
1890141cc406Sopenharmony_ci    DBG_HELPER(dbg);
1891141cc406Sopenharmony_ci    (void) regs;
1892141cc406Sopenharmony_ci  unsigned int i, j;
1893141cc406Sopenharmony_ci  int val;
1894141cc406Sopenharmony_ci  int avg[3], avga, avge;
1895141cc406Sopenharmony_ci  int turn;
1896141cc406Sopenharmony_ci    std::uint16_t expr, expg, expb;
1897141cc406Sopenharmony_ci
1898141cc406Sopenharmony_ci    unsigned channels = dev->settings.get_channels();
1899141cc406Sopenharmony_ci
1900141cc406Sopenharmony_ci    ScanColorMode scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
1901141cc406Sopenharmony_ci    if (dev->settings.scan_mode != ScanColorMode::COLOR_SINGLE_PASS) {
1902141cc406Sopenharmony_ci        scan_mode = ScanColorMode::GRAY;
1903141cc406Sopenharmony_ci    }
1904141cc406Sopenharmony_ci
1905141cc406Sopenharmony_ci    // offset calibration is always done in color mode
1906141cc406Sopenharmony_ci    unsigned pixels = dev->model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH;
1907141cc406Sopenharmony_ci
1908141cc406Sopenharmony_ci    ScanSession session;
1909141cc406Sopenharmony_ci    session.params.xres = sensor.full_resolution;
1910141cc406Sopenharmony_ci    session.params.yres = sensor.full_resolution;
1911141cc406Sopenharmony_ci    session.params.startx = 0;
1912141cc406Sopenharmony_ci    session.params.starty = 0;
1913141cc406Sopenharmony_ci    session.params.pixels = pixels;
1914141cc406Sopenharmony_ci    session.params.lines = 1;
1915141cc406Sopenharmony_ci    session.params.depth = 16;
1916141cc406Sopenharmony_ci    session.params.channels = channels;
1917141cc406Sopenharmony_ci    session.params.scan_method = dev->settings.scan_method;
1918141cc406Sopenharmony_ci    session.params.scan_mode = scan_mode;
1919141cc406Sopenharmony_ci    session.params.color_filter = ColorFilter::RED;
1920141cc406Sopenharmony_ci    session.params.contrast_adjustment = dev->settings.contrast;
1921141cc406Sopenharmony_ci    session.params.brightness_adjustment = dev->settings.brightness;
1922141cc406Sopenharmony_ci    session.params.flags = ScanFlag::DISABLE_SHADING;
1923141cc406Sopenharmony_ci    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) {
1924141cc406Sopenharmony_ci        session.params.flags |= ScanFlag::USE_XPA;
1925141cc406Sopenharmony_ci    }
1926141cc406Sopenharmony_ci    compute_session(dev, session, sensor);
1927141cc406Sopenharmony_ci
1928141cc406Sopenharmony_ci    // colors * bytes_per_color * scan lines
1929141cc406Sopenharmony_ci    unsigned total_size = pixels * channels * 2 * 1;
1930141cc406Sopenharmony_ci
1931141cc406Sopenharmony_ci    std::vector<std::uint8_t> line(total_size);
1932141cc406Sopenharmony_ci
1933141cc406Sopenharmony_ci/*
1934141cc406Sopenharmony_ci   we try to get equal bright leds here:
1935141cc406Sopenharmony_ci
1936141cc406Sopenharmony_ci   loop:
1937141cc406Sopenharmony_ci     average per color
1938141cc406Sopenharmony_ci     adjust exposure times
1939141cc406Sopenharmony_ci */
1940141cc406Sopenharmony_ci  expr = sensor.exposure.red;
1941141cc406Sopenharmony_ci  expg = sensor.exposure.green;
1942141cc406Sopenharmony_ci  expb = sensor.exposure.blue;
1943141cc406Sopenharmony_ci
1944141cc406Sopenharmony_ci  turn = 0;
1945141cc406Sopenharmony_ci
1946141cc406Sopenharmony_ci    auto calib_sensor = sensor;
1947141cc406Sopenharmony_ci
1948141cc406Sopenharmony_ci    bool acceptable = false;
1949141cc406Sopenharmony_ci    do {
1950141cc406Sopenharmony_ci        calib_sensor.exposure.red = expr;
1951141cc406Sopenharmony_ci        calib_sensor.exposure.green = expg;
1952141cc406Sopenharmony_ci        calib_sensor.exposure.blue = expb;
1953141cc406Sopenharmony_ci
1954141cc406Sopenharmony_ci      DBG(DBG_info, "%s: starting first line reading\n", __func__);
1955141cc406Sopenharmony_ci
1956141cc406Sopenharmony_ci        dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session);
1957141cc406Sopenharmony_ci        simple_scan(dev, calib_sensor, session, false, line, "led_calibration");
1958141cc406Sopenharmony_ci
1959141cc406Sopenharmony_ci        if (is_testing_mode()) {
1960141cc406Sopenharmony_ci            return calib_sensor.exposure;
1961141cc406Sopenharmony_ci        }
1962141cc406Sopenharmony_ci
1963141cc406Sopenharmony_ci        if (dbg_log_image_data()) {
1964141cc406Sopenharmony_ci            char fn[30];
1965141cc406Sopenharmony_ci            std::snprintf(fn, 30, "gl646_led_%02d.tiff", turn);
1966141cc406Sopenharmony_ci            write_tiff_file(fn, line.data(), 16, channels, pixels, 1);
1967141cc406Sopenharmony_ci        }
1968141cc406Sopenharmony_ci
1969141cc406Sopenharmony_ci        acceptable = true;
1970141cc406Sopenharmony_ci
1971141cc406Sopenharmony_ci      for (j = 0; j < channels; j++)
1972141cc406Sopenharmony_ci	{
1973141cc406Sopenharmony_ci	  avg[j] = 0;
1974141cc406Sopenharmony_ci            for (i = 0; i < pixels; i++) {
1975141cc406Sopenharmony_ci                if (dev->model->is_cis) {
1976141cc406Sopenharmony_ci                    val = line[i * 2 + j * 2 * pixels + 1] * 256 + line[i * 2 + j * 2 * pixels];
1977141cc406Sopenharmony_ci                } else {
1978141cc406Sopenharmony_ci                    val = line[i * 2 * channels + 2 * j + 1] * 256 + line[i * 2 * channels + 2 * j];
1979141cc406Sopenharmony_ci                }
1980141cc406Sopenharmony_ci            avg[j] += val;
1981141cc406Sopenharmony_ci	    }
1982141cc406Sopenharmony_ci
1983141cc406Sopenharmony_ci      avg[j] /= pixels;
1984141cc406Sopenharmony_ci	}
1985141cc406Sopenharmony_ci
1986141cc406Sopenharmony_ci      DBG(DBG_info, "%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]);
1987141cc406Sopenharmony_ci
1988141cc406Sopenharmony_ci        acceptable = true;
1989141cc406Sopenharmony_ci
1990141cc406Sopenharmony_ci      if (!acceptable)
1991141cc406Sopenharmony_ci	{
1992141cc406Sopenharmony_ci	  avga = (avg[0] + avg[1] + avg[2]) / 3;
1993141cc406Sopenharmony_ci	  expr = (expr * avga) / avg[0];
1994141cc406Sopenharmony_ci	  expg = (expg * avga) / avg[1];
1995141cc406Sopenharmony_ci	  expb = (expb * avga) / avg[2];
1996141cc406Sopenharmony_ci
1997141cc406Sopenharmony_ci	  /* keep exposure time in a working window */
1998141cc406Sopenharmony_ci	  avge = (expr + expg + expb) / 3;
1999141cc406Sopenharmony_ci	  if (avge > 0x2000)
2000141cc406Sopenharmony_ci	    {
2001141cc406Sopenharmony_ci	      expr = (expr * 0x2000) / avge;
2002141cc406Sopenharmony_ci	      expg = (expg * 0x2000) / avge;
2003141cc406Sopenharmony_ci	      expb = (expb * 0x2000) / avge;
2004141cc406Sopenharmony_ci	    }
2005141cc406Sopenharmony_ci	  if (avge < 0x400)
2006141cc406Sopenharmony_ci	    {
2007141cc406Sopenharmony_ci	      expr = (expr * 0x400) / avge;
2008141cc406Sopenharmony_ci	      expg = (expg * 0x400) / avge;
2009141cc406Sopenharmony_ci	      expb = (expb * 0x400) / avge;
2010141cc406Sopenharmony_ci	    }
2011141cc406Sopenharmony_ci	}
2012141cc406Sopenharmony_ci
2013141cc406Sopenharmony_ci      turn++;
2014141cc406Sopenharmony_ci
2015141cc406Sopenharmony_ci    }
2016141cc406Sopenharmony_ci  while (!acceptable && turn < 100);
2017141cc406Sopenharmony_ci
2018141cc406Sopenharmony_ci  DBG(DBG_info,"%s: acceptable exposure: 0x%04x,0x%04x,0x%04x\n", __func__, expr, expg, expb);
2019141cc406Sopenharmony_ci    // BUG: we don't store the result of the last iteration to the sensor
2020141cc406Sopenharmony_ci    return calib_sensor.exposure;
2021141cc406Sopenharmony_ci}
2022141cc406Sopenharmony_ci
2023141cc406Sopenharmony_ci/**
2024141cc406Sopenharmony_ci * average dark pixels of a scan
2025141cc406Sopenharmony_ci */
2026141cc406Sopenharmony_cistatic int dark_average(std::uint8_t * data, unsigned int pixels, unsigned int lines,
2027141cc406Sopenharmony_ci                        unsigned int channels, unsigned int black)
2028141cc406Sopenharmony_ci{
2029141cc406Sopenharmony_ci  unsigned int i, j, k, average, count;
2030141cc406Sopenharmony_ci  unsigned int avg[3];
2031141cc406Sopenharmony_ci    std::uint8_t val;
2032141cc406Sopenharmony_ci
2033141cc406Sopenharmony_ci  /* computes average value on black margin */
2034141cc406Sopenharmony_ci  for (k = 0; k < channels; k++)
2035141cc406Sopenharmony_ci    {
2036141cc406Sopenharmony_ci      avg[k] = 0;
2037141cc406Sopenharmony_ci      count = 0;
2038141cc406Sopenharmony_ci      for (i = 0; i < lines; i++)
2039141cc406Sopenharmony_ci	{
2040141cc406Sopenharmony_ci	  for (j = 0; j < black; j++)
2041141cc406Sopenharmony_ci	    {
2042141cc406Sopenharmony_ci	      val = data[i * channels * pixels + j + k];
2043141cc406Sopenharmony_ci	      avg[k] += val;
2044141cc406Sopenharmony_ci	      count++;
2045141cc406Sopenharmony_ci	    }
2046141cc406Sopenharmony_ci	}
2047141cc406Sopenharmony_ci      if (count)
2048141cc406Sopenharmony_ci	avg[k] /= count;
2049141cc406Sopenharmony_ci      DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, k, avg[k]);
2050141cc406Sopenharmony_ci    }
2051141cc406Sopenharmony_ci  average = 0;
2052141cc406Sopenharmony_ci  for (i = 0; i < channels; i++)
2053141cc406Sopenharmony_ci    average += avg[i];
2054141cc406Sopenharmony_ci  average /= channels;
2055141cc406Sopenharmony_ci  DBG(DBG_info, "%s: average = %d\n", __func__, average);
2056141cc406Sopenharmony_ci  return average;
2057141cc406Sopenharmony_ci}
2058141cc406Sopenharmony_ci
2059141cc406Sopenharmony_ci
2060141cc406Sopenharmony_ci/** @brief calibration for AD frontend devices
2061141cc406Sopenharmony_ci * we do simple scan until all black_pixels are higher than 0,
2062141cc406Sopenharmony_ci * raising offset at each turn.
2063141cc406Sopenharmony_ci */
2064141cc406Sopenharmony_cistatic void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor)
2065141cc406Sopenharmony_ci{
2066141cc406Sopenharmony_ci    DBG_HELPER(dbg);
2067141cc406Sopenharmony_ci    (void) sensor;
2068141cc406Sopenharmony_ci
2069141cc406Sopenharmony_ci  unsigned int channels;
2070141cc406Sopenharmony_ci  int pass = 0;
2071141cc406Sopenharmony_ci    unsigned adr, min;
2072141cc406Sopenharmony_ci  unsigned int bottom, black_pixels;
2073141cc406Sopenharmony_ci
2074141cc406Sopenharmony_ci  channels = 3;
2075141cc406Sopenharmony_ci
2076141cc406Sopenharmony_ci    // FIXME: maybe reuse `sensor`
2077141cc406Sopenharmony_ci    const auto& calib_sensor = sanei_genesys_find_sensor(dev, sensor.full_resolution, 3,
2078141cc406Sopenharmony_ci                                                         ScanMethod::FLATBED);
2079141cc406Sopenharmony_ci    black_pixels = (calib_sensor.black_pixels * sensor.full_resolution) / calib_sensor.full_resolution;
2080141cc406Sopenharmony_ci
2081141cc406Sopenharmony_ci    unsigned pixels = dev->model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH;
2082141cc406Sopenharmony_ci    unsigned lines = CALIBRATION_LINES;
2083141cc406Sopenharmony_ci
2084141cc406Sopenharmony_ci    if (dev->model->is_cis) {
2085141cc406Sopenharmony_ci        lines = ((lines + 2) / 3) * 3;
2086141cc406Sopenharmony_ci    }
2087141cc406Sopenharmony_ci
2088141cc406Sopenharmony_ci    ScanSession session;
2089141cc406Sopenharmony_ci    session.params.xres = sensor.full_resolution;
2090141cc406Sopenharmony_ci    session.params.yres = sensor.full_resolution;
2091141cc406Sopenharmony_ci    session.params.startx = 0;
2092141cc406Sopenharmony_ci    session.params.starty = 0;
2093141cc406Sopenharmony_ci    session.params.pixels = pixels;
2094141cc406Sopenharmony_ci    session.params.lines = lines;
2095141cc406Sopenharmony_ci    session.params.depth = 8;
2096141cc406Sopenharmony_ci    session.params.channels = 3;
2097141cc406Sopenharmony_ci    session.params.scan_method = dev->settings.scan_method;
2098141cc406Sopenharmony_ci    session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
2099141cc406Sopenharmony_ci    session.params.color_filter = ColorFilter::RED;
2100141cc406Sopenharmony_ci    session.params.contrast_adjustment = dev->settings.contrast;
2101141cc406Sopenharmony_ci    session.params.brightness_adjustment = dev->settings.brightness;
2102141cc406Sopenharmony_ci    session.params.flags = ScanFlag::DISABLE_SHADING;
2103141cc406Sopenharmony_ci    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) {
2104141cc406Sopenharmony_ci        session.params.flags |= ScanFlag::USE_XPA;
2105141cc406Sopenharmony_ci    }
2106141cc406Sopenharmony_ci    compute_session(dev, session, calib_sensor);
2107141cc406Sopenharmony_ci
2108141cc406Sopenharmony_ci  /* scan first line of data with no gain */
2109141cc406Sopenharmony_ci  dev->frontend.set_gain(0, 0);
2110141cc406Sopenharmony_ci  dev->frontend.set_gain(1, 0);
2111141cc406Sopenharmony_ci  dev->frontend.set_gain(2, 0);
2112141cc406Sopenharmony_ci
2113141cc406Sopenharmony_ci    std::vector<std::uint8_t> line;
2114141cc406Sopenharmony_ci
2115141cc406Sopenharmony_ci  /* scan with no move */
2116141cc406Sopenharmony_ci  bottom = 1;
2117141cc406Sopenharmony_ci  do
2118141cc406Sopenharmony_ci    {
2119141cc406Sopenharmony_ci      pass++;
2120141cc406Sopenharmony_ci      dev->frontend.set_offset(0, bottom);
2121141cc406Sopenharmony_ci      dev->frontend.set_offset(1, bottom);
2122141cc406Sopenharmony_ci      dev->frontend.set_offset(2, bottom);
2123141cc406Sopenharmony_ci
2124141cc406Sopenharmony_ci        dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session);
2125141cc406Sopenharmony_ci        simple_scan(dev, calib_sensor, session, false, line, "ad_fe_offset_calibration");
2126141cc406Sopenharmony_ci
2127141cc406Sopenharmony_ci        if (is_testing_mode()) {
2128141cc406Sopenharmony_ci            return;
2129141cc406Sopenharmony_ci        }
2130141cc406Sopenharmony_ci
2131141cc406Sopenharmony_ci        if (dbg_log_image_data()) {
2132141cc406Sopenharmony_ci            char title[30];
2133141cc406Sopenharmony_ci            std::snprintf(title, 30, "gl646_offset%03d.tiff", static_cast<int>(bottom));
2134141cc406Sopenharmony_ci            write_tiff_file(title, line.data(), 8, channels, pixels, lines);
2135141cc406Sopenharmony_ci        }
2136141cc406Sopenharmony_ci
2137141cc406Sopenharmony_ci      min = 0;
2138141cc406Sopenharmony_ci        for (unsigned y = 0; y < lines; y++) {
2139141cc406Sopenharmony_ci            for (unsigned x = 0; x < black_pixels; x++) {
2140141cc406Sopenharmony_ci                adr = (x + y * pixels) * channels;
2141141cc406Sopenharmony_ci	      if (line[adr] > min)
2142141cc406Sopenharmony_ci		min = line[adr];
2143141cc406Sopenharmony_ci	      if (line[adr + 1] > min)
2144141cc406Sopenharmony_ci		min = line[adr + 1];
2145141cc406Sopenharmony_ci	      if (line[adr + 2] > min)
2146141cc406Sopenharmony_ci		min = line[adr + 2];
2147141cc406Sopenharmony_ci	    }
2148141cc406Sopenharmony_ci	}
2149141cc406Sopenharmony_ci
2150141cc406Sopenharmony_ci      DBG(DBG_info, "%s: pass=%d, min=%d\n", __func__, pass, min);
2151141cc406Sopenharmony_ci      bottom++;
2152141cc406Sopenharmony_ci    }
2153141cc406Sopenharmony_ci  while (pass < 128 && min == 0);
2154141cc406Sopenharmony_ci  if (pass == 128)
2155141cc406Sopenharmony_ci    {
2156141cc406Sopenharmony_ci        throw SaneException(SANE_STATUS_INVAL, "failed to find correct offset");
2157141cc406Sopenharmony_ci    }
2158141cc406Sopenharmony_ci
2159141cc406Sopenharmony_ci  DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
2160141cc406Sopenharmony_ci      dev->frontend.get_offset(0),
2161141cc406Sopenharmony_ci      dev->frontend.get_offset(1),
2162141cc406Sopenharmony_ci      dev->frontend.get_offset(2));
2163141cc406Sopenharmony_ci}
2164141cc406Sopenharmony_ci
2165141cc406Sopenharmony_ci/**
2166141cc406Sopenharmony_ci * This function does the offset calibration by scanning one line of the calibration
2167141cc406Sopenharmony_ci * area below scanner's top. There is a black margin and the remaining is white.
2168141cc406Sopenharmony_ci * genesys_search_start() must have been called so that the offsets and margins
2169141cc406Sopenharmony_ci * are already known.
2170141cc406Sopenharmony_ci * @param dev scanner's device
2171141cc406Sopenharmony_ci*/
2172141cc406Sopenharmony_civoid CommandSetGl646::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
2173141cc406Sopenharmony_ci                                         Genesys_Register_Set& regs) const
2174141cc406Sopenharmony_ci{
2175141cc406Sopenharmony_ci    DBG_HELPER(dbg);
2176141cc406Sopenharmony_ci    (void) regs;
2177141cc406Sopenharmony_ci
2178141cc406Sopenharmony_ci  int pass = 0, avg;
2179141cc406Sopenharmony_ci  int topavg, bottomavg;
2180141cc406Sopenharmony_ci  int top, bottom, black_pixels;
2181141cc406Sopenharmony_ci
2182141cc406Sopenharmony_ci    if (dev->model->adc_id == AdcId::AD_XP200) {
2183141cc406Sopenharmony_ci        ad_fe_offset_calibration(dev, sensor);
2184141cc406Sopenharmony_ci        return;
2185141cc406Sopenharmony_ci    }
2186141cc406Sopenharmony_ci
2187141cc406Sopenharmony_ci  /* setup for a RGB scan, one full sensor's width line */
2188141cc406Sopenharmony_ci  /* resolution is the one from the final scan          */
2189141cc406Sopenharmony_ci    unsigned resolution = dev->settings.xres;
2190141cc406Sopenharmony_ci    unsigned channels = 3;
2191141cc406Sopenharmony_ci
2192141cc406Sopenharmony_ci    const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
2193141cc406Sopenharmony_ci                                                         ScanMethod::FLATBED);
2194141cc406Sopenharmony_ci    black_pixels = (calib_sensor.black_pixels * resolution) / calib_sensor.full_resolution;
2195141cc406Sopenharmony_ci
2196141cc406Sopenharmony_ci    unsigned pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
2197141cc406Sopenharmony_ci    unsigned lines = CALIBRATION_LINES;
2198141cc406Sopenharmony_ci    if (dev->model->is_cis) {
2199141cc406Sopenharmony_ci        lines = ((lines + 2) / 3) * 3;
2200141cc406Sopenharmony_ci    }
2201141cc406Sopenharmony_ci
2202141cc406Sopenharmony_ci    ScanSession session;
2203141cc406Sopenharmony_ci    session.params.xres = resolution;
2204141cc406Sopenharmony_ci    session.params.yres = resolution;
2205141cc406Sopenharmony_ci    session.params.startx = 0;
2206141cc406Sopenharmony_ci    session.params.starty = 0;
2207141cc406Sopenharmony_ci    session.params.pixels = pixels;
2208141cc406Sopenharmony_ci    session.params.lines = lines;
2209141cc406Sopenharmony_ci    session.params.depth = 8;
2210141cc406Sopenharmony_ci    session.params.channels = channels;
2211141cc406Sopenharmony_ci    session.params.scan_method = dev->settings.scan_method;
2212141cc406Sopenharmony_ci    session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
2213141cc406Sopenharmony_ci    session.params.color_filter = ColorFilter::RED;
2214141cc406Sopenharmony_ci    session.params.contrast_adjustment = dev->settings.contrast;
2215141cc406Sopenharmony_ci    session.params.brightness_adjustment = dev->settings.brightness;
2216141cc406Sopenharmony_ci    session.params.flags = ScanFlag::DISABLE_SHADING;
2217141cc406Sopenharmony_ci    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) {
2218141cc406Sopenharmony_ci        session.params.flags |= ScanFlag::USE_XPA;
2219141cc406Sopenharmony_ci    }
2220141cc406Sopenharmony_ci    compute_session(dev, session, sensor);
2221141cc406Sopenharmony_ci
2222141cc406Sopenharmony_ci  /* scan first line of data with no gain, but with offset from
2223141cc406Sopenharmony_ci   * last calibration */
2224141cc406Sopenharmony_ci  dev->frontend.set_gain(0, 0);
2225141cc406Sopenharmony_ci  dev->frontend.set_gain(1, 0);
2226141cc406Sopenharmony_ci  dev->frontend.set_gain(2, 0);
2227141cc406Sopenharmony_ci
2228141cc406Sopenharmony_ci  /* scan with no move */
2229141cc406Sopenharmony_ci  bottom = 90;
2230141cc406Sopenharmony_ci  dev->frontend.set_offset(0, bottom);
2231141cc406Sopenharmony_ci  dev->frontend.set_offset(1, bottom);
2232141cc406Sopenharmony_ci  dev->frontend.set_offset(2, bottom);
2233141cc406Sopenharmony_ci
2234141cc406Sopenharmony_ci    std::vector<std::uint8_t> first_line, second_line;
2235141cc406Sopenharmony_ci
2236141cc406Sopenharmony_ci    dev->cmd_set->init_regs_for_scan_session(dev, sensor, &dev->reg, session);
2237141cc406Sopenharmony_ci    simple_scan(dev, calib_sensor, session, false, first_line, "offset_first_line");
2238141cc406Sopenharmony_ci
2239141cc406Sopenharmony_ci    if (dbg_log_image_data()) {
2240141cc406Sopenharmony_ci        char title[30];
2241141cc406Sopenharmony_ci        std::snprintf(title, 30, "gl646_offset%03d.tiff", bottom);
2242141cc406Sopenharmony_ci        write_tiff_file(title, first_line.data(), 8, channels, pixels, lines);
2243141cc406Sopenharmony_ci    }
2244141cc406Sopenharmony_ci    bottomavg = dark_average(first_line.data(), pixels, lines, channels, black_pixels);
2245141cc406Sopenharmony_ci    DBG(DBG_info, "%s: bottom avg=%d\n", __func__, bottomavg);
2246141cc406Sopenharmony_ci
2247141cc406Sopenharmony_ci  /* now top value */
2248141cc406Sopenharmony_ci  top = 231;
2249141cc406Sopenharmony_ci  dev->frontend.set_offset(0, top);
2250141cc406Sopenharmony_ci  dev->frontend.set_offset(1, top);
2251141cc406Sopenharmony_ci  dev->frontend.set_offset(2, top);
2252141cc406Sopenharmony_ci    dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session);
2253141cc406Sopenharmony_ci    simple_scan(dev, calib_sensor, session, false, second_line, "offset_second_line");
2254141cc406Sopenharmony_ci
2255141cc406Sopenharmony_ci    if (dbg_log_image_data()) {
2256141cc406Sopenharmony_ci        char title[30];
2257141cc406Sopenharmony_ci        std::snprintf(title, 30, "gl646_offset%03d.tiff", top);
2258141cc406Sopenharmony_ci        write_tiff_file(title, second_line.data(), 8, channels, pixels, lines);
2259141cc406Sopenharmony_ci    }
2260141cc406Sopenharmony_ci    topavg = dark_average(second_line.data(), pixels, lines, channels, black_pixels);
2261141cc406Sopenharmony_ci    DBG(DBG_info, "%s: top avg=%d\n", __func__, topavg);
2262141cc406Sopenharmony_ci
2263141cc406Sopenharmony_ci    if (is_testing_mode()) {
2264141cc406Sopenharmony_ci        return;
2265141cc406Sopenharmony_ci    }
2266141cc406Sopenharmony_ci
2267141cc406Sopenharmony_ci  /* loop until acceptable level */
2268141cc406Sopenharmony_ci  while ((pass < 32) && (top - bottom > 1))
2269141cc406Sopenharmony_ci    {
2270141cc406Sopenharmony_ci      pass++;
2271141cc406Sopenharmony_ci
2272141cc406Sopenharmony_ci      /* settings for new scan */
2273141cc406Sopenharmony_ci      dev->frontend.set_offset(0, (top + bottom) / 2);
2274141cc406Sopenharmony_ci      dev->frontend.set_offset(1, (top + bottom) / 2);
2275141cc406Sopenharmony_ci      dev->frontend.set_offset(2, (top + bottom) / 2);
2276141cc406Sopenharmony_ci
2277141cc406Sopenharmony_ci        // scan with no move
2278141cc406Sopenharmony_ci        dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session);
2279141cc406Sopenharmony_ci        simple_scan(dev, calib_sensor, session, false, second_line,
2280141cc406Sopenharmony_ci                    "offset_calibration_i");
2281141cc406Sopenharmony_ci
2282141cc406Sopenharmony_ci        if (dbg_log_image_data()) {
2283141cc406Sopenharmony_ci            char title[30];
2284141cc406Sopenharmony_ci            std::snprintf(title, 30, "gl646_offset%03d.tiff", dev->frontend.get_offset(1));
2285141cc406Sopenharmony_ci            write_tiff_file(title, second_line.data(), 8, channels, pixels, lines);
2286141cc406Sopenharmony_ci        }
2287141cc406Sopenharmony_ci
2288141cc406Sopenharmony_ci        avg = dark_average(second_line.data(), pixels, lines, channels, black_pixels);
2289141cc406Sopenharmony_ci      DBG(DBG_info, "%s: avg=%d offset=%d\n", __func__, avg, dev->frontend.get_offset(1));
2290141cc406Sopenharmony_ci
2291141cc406Sopenharmony_ci      /* compute new boundaries */
2292141cc406Sopenharmony_ci      if (topavg == avg)
2293141cc406Sopenharmony_ci	{
2294141cc406Sopenharmony_ci	  topavg = avg;
2295141cc406Sopenharmony_ci          top = dev->frontend.get_offset(1);
2296141cc406Sopenharmony_ci	}
2297141cc406Sopenharmony_ci      else
2298141cc406Sopenharmony_ci	{
2299141cc406Sopenharmony_ci	  bottomavg = avg;
2300141cc406Sopenharmony_ci          bottom = dev->frontend.get_offset(1);
2301141cc406Sopenharmony_ci	}
2302141cc406Sopenharmony_ci    }
2303141cc406Sopenharmony_ci
2304141cc406Sopenharmony_ci  DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
2305141cc406Sopenharmony_ci      dev->frontend.get_offset(0),
2306141cc406Sopenharmony_ci      dev->frontend.get_offset(1),
2307141cc406Sopenharmony_ci      dev->frontend.get_offset(2));
2308141cc406Sopenharmony_ci}
2309141cc406Sopenharmony_ci
2310141cc406Sopenharmony_ci/**
2311141cc406Sopenharmony_ci * Alternative coarse gain calibration
2312141cc406Sopenharmony_ci * this on uses the settings from offset_calibration. First scan moves so
2313141cc406Sopenharmony_ci * we can go to calibration area for XPA.
2314141cc406Sopenharmony_ci * @param dev device for scan
2315141cc406Sopenharmony_ci * @param dpi resolutnio to calibrate at
2316141cc406Sopenharmony_ci */
2317141cc406Sopenharmony_civoid CommandSetGl646::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
2318141cc406Sopenharmony_ci                                              Genesys_Register_Set& regs, int dpi) const
2319141cc406Sopenharmony_ci{
2320141cc406Sopenharmony_ci    DBG_HELPER(dbg);
2321141cc406Sopenharmony_ci    (void) dpi;
2322141cc406Sopenharmony_ci    (void) sensor;
2323141cc406Sopenharmony_ci    (void) regs;
2324141cc406Sopenharmony_ci
2325141cc406Sopenharmony_ci  float average[3];
2326141cc406Sopenharmony_ci  char title[32];
2327141cc406Sopenharmony_ci
2328141cc406Sopenharmony_ci  /* setup for a RGB scan, one full sensor's width line */
2329141cc406Sopenharmony_ci  /* resolution is the one from the final scan          */
2330141cc406Sopenharmony_ci    unsigned channels = 3;
2331141cc406Sopenharmony_ci
2332141cc406Sopenharmony_ci    // BUG: the following comment is incorrect
2333141cc406Sopenharmony_ci    // we are searching a sensor resolution */
2334141cc406Sopenharmony_ci    const auto& calib_sensor = sanei_genesys_find_sensor(dev, dev->settings.xres, channels,
2335141cc406Sopenharmony_ci                                                         ScanMethod::FLATBED);
2336141cc406Sopenharmony_ci
2337141cc406Sopenharmony_ci    unsigned pixels = 0;
2338141cc406Sopenharmony_ci    float start = 0;
2339141cc406Sopenharmony_ci    if (dev->settings.scan_method == ScanMethod::FLATBED) {
2340141cc406Sopenharmony_ci        pixels = dev->model->x_size_calib_mm * dev->settings.xres / MM_PER_INCH;
2341141cc406Sopenharmony_ci    } else {
2342141cc406Sopenharmony_ci        start = dev->model->x_offset_ta;
2343141cc406Sopenharmony_ci        pixels = static_cast<unsigned>(
2344141cc406Sopenharmony_ci                              (dev->model->x_size_ta * dev->settings.xres) / MM_PER_INCH);
2345141cc406Sopenharmony_ci    }
2346141cc406Sopenharmony_ci
2347141cc406Sopenharmony_ci    unsigned lines = CALIBRATION_LINES;
2348141cc406Sopenharmony_ci    // round up to multiple of 3 in case of CIS scanner
2349141cc406Sopenharmony_ci    if (dev->model->is_cis) {
2350141cc406Sopenharmony_ci        lines = ((lines + 2) / 3) * 3;
2351141cc406Sopenharmony_ci    }
2352141cc406Sopenharmony_ci
2353141cc406Sopenharmony_ci    start = static_cast<float>((start * dev->settings.xres) / MM_PER_INCH);
2354141cc406Sopenharmony_ci
2355141cc406Sopenharmony_ci    ScanSession session;
2356141cc406Sopenharmony_ci    session.params.xres = dev->settings.xres;
2357141cc406Sopenharmony_ci    session.params.yres = dev->settings.xres;
2358141cc406Sopenharmony_ci    session.params.startx = static_cast<unsigned>(start);
2359141cc406Sopenharmony_ci    session.params.starty = 0;
2360141cc406Sopenharmony_ci    session.params.pixels = pixels;
2361141cc406Sopenharmony_ci    session.params.lines = lines;
2362141cc406Sopenharmony_ci    session.params.depth = 8;
2363141cc406Sopenharmony_ci    session.params.channels = channels;
2364141cc406Sopenharmony_ci    session.params.scan_method = dev->settings.scan_method;
2365141cc406Sopenharmony_ci    session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
2366141cc406Sopenharmony_ci    session.params.color_filter = ColorFilter::RED;
2367141cc406Sopenharmony_ci    session.params.contrast_adjustment = dev->settings.contrast;
2368141cc406Sopenharmony_ci    session.params.brightness_adjustment = dev->settings.brightness;
2369141cc406Sopenharmony_ci    session.params.flags = ScanFlag::DISABLE_SHADING;
2370141cc406Sopenharmony_ci    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) {
2371141cc406Sopenharmony_ci        session.params.flags |= ScanFlag::USE_XPA;
2372141cc406Sopenharmony_ci    }
2373141cc406Sopenharmony_ci    compute_session(dev, session, calib_sensor);
2374141cc406Sopenharmony_ci
2375141cc406Sopenharmony_ci  /* start gain value */
2376141cc406Sopenharmony_ci  dev->frontend.set_gain(0, 1);
2377141cc406Sopenharmony_ci  dev->frontend.set_gain(1, 1);
2378141cc406Sopenharmony_ci  dev->frontend.set_gain(2, 1);
2379141cc406Sopenharmony_ci
2380141cc406Sopenharmony_ci    average[0] = 0;
2381141cc406Sopenharmony_ci    average[1] = 0;
2382141cc406Sopenharmony_ci    average[2] = 0;
2383141cc406Sopenharmony_ci
2384141cc406Sopenharmony_ci    unsigned pass = 0;
2385141cc406Sopenharmony_ci
2386141cc406Sopenharmony_ci    std::vector<std::uint8_t> line;
2387141cc406Sopenharmony_ci
2388141cc406Sopenharmony_ci  /* loop until each channel raises to acceptable level */
2389141cc406Sopenharmony_ci    while (((average[0] < calib_sensor.gain_white_ref) ||
2390141cc406Sopenharmony_ci            (average[1] < calib_sensor.gain_white_ref) ||
2391141cc406Sopenharmony_ci            (average[2] < calib_sensor.gain_white_ref)) && (pass < 30))
2392141cc406Sopenharmony_ci    {
2393141cc406Sopenharmony_ci        // scan with no move
2394141cc406Sopenharmony_ci        dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session);
2395141cc406Sopenharmony_ci        simple_scan(dev, calib_sensor, session, false, line, "coarse_gain_calibration");
2396141cc406Sopenharmony_ci
2397141cc406Sopenharmony_ci        if (dbg_log_image_data()) {
2398141cc406Sopenharmony_ci            std::sprintf(title, "gl646_gain%02d.tiff", pass);
2399141cc406Sopenharmony_ci            write_tiff_file(title, line.data(), 8, channels, pixels, lines);
2400141cc406Sopenharmony_ci        }
2401141cc406Sopenharmony_ci        pass++;
2402141cc406Sopenharmony_ci
2403141cc406Sopenharmony_ci        // average high level for each channel and compute gain to reach the target code
2404141cc406Sopenharmony_ci        // we only use the central half of the CCD data
2405141cc406Sopenharmony_ci        for (unsigned k = 0; k < channels; k++) {
2406141cc406Sopenharmony_ci
2407141cc406Sopenharmony_ci            // we find the maximum white value, so we can deduce a threshold
2408141cc406Sopenharmony_ci            // to average white values
2409141cc406Sopenharmony_ci            unsigned maximum = 0;
2410141cc406Sopenharmony_ci            for (unsigned i = 0; i < lines; i++) {
2411141cc406Sopenharmony_ci                for (unsigned j = 0; j < pixels; j++) {
2412141cc406Sopenharmony_ci                    unsigned val = line[i * channels * pixels + j + k];
2413141cc406Sopenharmony_ci                    maximum = std::max(maximum, val);
2414141cc406Sopenharmony_ci                }
2415141cc406Sopenharmony_ci            }
2416141cc406Sopenharmony_ci
2417141cc406Sopenharmony_ci            maximum = static_cast<int>(maximum * 0.9);
2418141cc406Sopenharmony_ci
2419141cc406Sopenharmony_ci            // computes white average
2420141cc406Sopenharmony_ci            average[k] = 0;
2421141cc406Sopenharmony_ci            unsigned count = 0;
2422141cc406Sopenharmony_ci            for (unsigned i = 0; i < lines; i++) {
2423141cc406Sopenharmony_ci                for (unsigned j = 0; j < pixels; j++) {
2424141cc406Sopenharmony_ci                    // averaging only white points allow us not to care about dark margins
2425141cc406Sopenharmony_ci                    unsigned val = line[i * channels * pixels + j + k];
2426141cc406Sopenharmony_ci                    if (val > maximum) {
2427141cc406Sopenharmony_ci                        average[k] += val;
2428141cc406Sopenharmony_ci                        count++;
2429141cc406Sopenharmony_ci                    }
2430141cc406Sopenharmony_ci                }
2431141cc406Sopenharmony_ci            }
2432141cc406Sopenharmony_ci            average[k] = average[k] / count;
2433141cc406Sopenharmony_ci
2434141cc406Sopenharmony_ci            // adjusts gain for the channel
2435141cc406Sopenharmony_ci            if (average[k] < calib_sensor.gain_white_ref) {
2436141cc406Sopenharmony_ci                dev->frontend.set_gain(k, dev->frontend.get_gain(k) + 1);
2437141cc406Sopenharmony_ci            }
2438141cc406Sopenharmony_ci
2439141cc406Sopenharmony_ci            DBG(DBG_info, "%s: channel %d, average = %.2f, gain = %d\n", __func__, k, average[k],
2440141cc406Sopenharmony_ci                dev->frontend.get_gain(k));
2441141cc406Sopenharmony_ci        }
2442141cc406Sopenharmony_ci    }
2443141cc406Sopenharmony_ci
2444141cc406Sopenharmony_ci    DBG(DBG_info, "%s: gains=(%d,%d,%d)\n", __func__,
2445141cc406Sopenharmony_ci        dev->frontend.get_gain(0),
2446141cc406Sopenharmony_ci        dev->frontend.get_gain(1),
2447141cc406Sopenharmony_ci        dev->frontend.get_gain(2));
2448141cc406Sopenharmony_ci}
2449141cc406Sopenharmony_ci
2450141cc406Sopenharmony_ci/**
2451141cc406Sopenharmony_ci * sets up the scanner's register for warming up. We scan 2 lines without moving.
2452141cc406Sopenharmony_ci *
2453141cc406Sopenharmony_ci */
2454141cc406Sopenharmony_civoid CommandSetGl646::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor,
2455141cc406Sopenharmony_ci                                           Genesys_Register_Set* local_reg) const
2456141cc406Sopenharmony_ci{
2457141cc406Sopenharmony_ci    DBG_HELPER(dbg);
2458141cc406Sopenharmony_ci    (void) sensor;
2459141cc406Sopenharmony_ci
2460141cc406Sopenharmony_ci  dev->frontend = dev->frontend_initial;
2461141cc406Sopenharmony_ci
2462141cc406Sopenharmony_ci    unsigned resolution = 300;
2463141cc406Sopenharmony_ci    const auto& local_sensor = sanei_genesys_find_sensor(dev, resolution, 1,
2464141cc406Sopenharmony_ci                                                         dev->settings.scan_method);
2465141cc406Sopenharmony_ci
2466141cc406Sopenharmony_ci    // set up for a full width 2 lines gray scan without moving
2467141cc406Sopenharmony_ci    unsigned pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
2468141cc406Sopenharmony_ci
2469141cc406Sopenharmony_ci    ScanSession session;
2470141cc406Sopenharmony_ci    session.params.xres = resolution;
2471141cc406Sopenharmony_ci    session.params.yres = resolution;
2472141cc406Sopenharmony_ci    session.params.startx = 0;
2473141cc406Sopenharmony_ci    session.params.starty = 0;
2474141cc406Sopenharmony_ci    session.params.pixels = pixels;
2475141cc406Sopenharmony_ci    session.params.lines = 2;
2476141cc406Sopenharmony_ci    session.params.depth = dev->model->bpp_gray_values.front();
2477141cc406Sopenharmony_ci    session.params.channels = 1;
2478141cc406Sopenharmony_ci    session.params.scan_method = dev->settings.scan_method;
2479141cc406Sopenharmony_ci    session.params.scan_mode = ScanColorMode::GRAY;
2480141cc406Sopenharmony_ci    session.params.color_filter =  ColorFilter::RED;
2481141cc406Sopenharmony_ci    session.params.contrast_adjustment = 0;
2482141cc406Sopenharmony_ci    session.params.brightness_adjustment = 0;
2483141cc406Sopenharmony_ci    session.params.flags = ScanFlag::DISABLE_SHADING |
2484141cc406Sopenharmony_ci                           ScanFlag::DISABLE_GAMMA;
2485141cc406Sopenharmony_ci    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) {
2486141cc406Sopenharmony_ci        session.params.flags |= ScanFlag::USE_XPA;
2487141cc406Sopenharmony_ci    }
2488141cc406Sopenharmony_ci    compute_session(dev, session, local_sensor);
2489141cc406Sopenharmony_ci
2490141cc406Sopenharmony_ci    dev->cmd_set->init_regs_for_scan_session(dev, local_sensor, &dev->reg, session);
2491141cc406Sopenharmony_ci
2492141cc406Sopenharmony_ci  /* we are not going to move, so clear these bits */
2493141cc406Sopenharmony_ci    dev->reg.find_reg(0x02).value &= ~REG_0x02_FASTFED;
2494141cc406Sopenharmony_ci
2495141cc406Sopenharmony_ci  /* copy to local_reg */
2496141cc406Sopenharmony_ci  *local_reg = dev->reg;
2497141cc406Sopenharmony_ci
2498141cc406Sopenharmony_ci  /* turn off motor during this scan */
2499141cc406Sopenharmony_ci  sanei_genesys_set_motor_power(*local_reg, false);
2500141cc406Sopenharmony_ci
2501141cc406Sopenharmony_ci    // now registers are ok, write them to scanner
2502141cc406Sopenharmony_ci    gl646_set_fe(dev, local_sensor, AFE_SET, session.params.xres);
2503141cc406Sopenharmony_ci}
2504141cc406Sopenharmony_ci
2505141cc406Sopenharmony_ci/* *
2506141cc406Sopenharmony_ci * initialize ASIC : registers, motor tables, and gamma tables
2507141cc406Sopenharmony_ci * then ensure scanner's head is at home
2508141cc406Sopenharmony_ci * @param dev device description of the scanner to initialize
2509141cc406Sopenharmony_ci */
2510141cc406Sopenharmony_civoid CommandSetGl646::init(Genesys_Device* dev) const
2511141cc406Sopenharmony_ci{
2512141cc406Sopenharmony_ci    DBG_INIT();
2513141cc406Sopenharmony_ci    DBG_HELPER(dbg);
2514141cc406Sopenharmony_ci
2515141cc406Sopenharmony_ci    std::uint8_t val = 0;
2516141cc406Sopenharmony_ci    std::uint32_t addr = 0xdead;
2517141cc406Sopenharmony_ci  size_t len;
2518141cc406Sopenharmony_ci
2519141cc406Sopenharmony_ci    // to detect real power up condition, we write to REG_0x41 with pwrbit set, then read it back.
2520141cc406Sopenharmony_ci    // When scanner is cold (just replugged) PWRBIT will be set in the returned value
2521141cc406Sopenharmony_ci    auto status = scanner_read_status(*dev);
2522141cc406Sopenharmony_ci    if (status.is_replugged) {
2523141cc406Sopenharmony_ci      DBG(DBG_info, "%s: device is cold\n", __func__);
2524141cc406Sopenharmony_ci    } else {
2525141cc406Sopenharmony_ci      DBG(DBG_info, "%s: device is hot\n", __func__);
2526141cc406Sopenharmony_ci    }
2527141cc406Sopenharmony_ci
2528141cc406Sopenharmony_ci  const auto& sensor = sanei_genesys_find_sensor_any(dev);
2529141cc406Sopenharmony_ci
2530141cc406Sopenharmony_ci  /* if scanning session hasn't been initialized, set it up */
2531141cc406Sopenharmony_ci  if (!dev->already_initialized)
2532141cc406Sopenharmony_ci    {
2533141cc406Sopenharmony_ci      dev->dark_average_data.clear();
2534141cc406Sopenharmony_ci      dev->white_average_data.clear();
2535141cc406Sopenharmony_ci
2536141cc406Sopenharmony_ci      dev->settings.color_filter = ColorFilter::GREEN;
2537141cc406Sopenharmony_ci
2538141cc406Sopenharmony_ci      /* Set default values for registers */
2539141cc406Sopenharmony_ci      gl646_init_regs (dev);
2540141cc406Sopenharmony_ci
2541141cc406Sopenharmony_ci        // Init shading data
2542141cc406Sopenharmony_ci        sanei_genesys_init_shading_data(dev, sensor,
2543141cc406Sopenharmony_ci                                        dev->model->x_size_calib_mm * sensor.full_resolution /
2544141cc406Sopenharmony_ci                                            MM_PER_INCH);
2545141cc406Sopenharmony_ci
2546141cc406Sopenharmony_ci        dev->initial_regs = dev->reg;
2547141cc406Sopenharmony_ci    }
2548141cc406Sopenharmony_ci
2549141cc406Sopenharmony_ci    // execute physical unit init only if cold
2550141cc406Sopenharmony_ci    if (status.is_replugged)
2551141cc406Sopenharmony_ci    {
2552141cc406Sopenharmony_ci      DBG(DBG_info, "%s: device is cold\n", __func__);
2553141cc406Sopenharmony_ci
2554141cc406Sopenharmony_ci        val = 0x04;
2555141cc406Sopenharmony_ci        dev->interface->get_usb_device().control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER,
2556141cc406Sopenharmony_ci                                                     VALUE_INIT, INDEX, 1, &val);
2557141cc406Sopenharmony_ci
2558141cc406Sopenharmony_ci        // ASIC reset
2559141cc406Sopenharmony_ci        dev->interface->write_register(0x0e, 0x00);
2560141cc406Sopenharmony_ci        dev->interface->sleep_ms(100);
2561141cc406Sopenharmony_ci
2562141cc406Sopenharmony_ci        // Write initial registers
2563141cc406Sopenharmony_ci        dev->interface->write_registers(dev->reg);
2564141cc406Sopenharmony_ci
2565141cc406Sopenharmony_ci        // send gamma tables if needed
2566141cc406Sopenharmony_ci        dev->cmd_set->send_gamma_table(dev, sensor);
2567141cc406Sopenharmony_ci
2568141cc406Sopenharmony_ci        // Set powersaving(default = 15 minutes)
2569141cc406Sopenharmony_ci        dev->cmd_set->set_powersaving(dev, 15);
2570141cc406Sopenharmony_ci    }
2571141cc406Sopenharmony_ci
2572141cc406Sopenharmony_ci    // Set analog frontend
2573141cc406Sopenharmony_ci    gl646_set_fe(dev, sensor, AFE_INIT, 0);
2574141cc406Sopenharmony_ci
2575141cc406Sopenharmony_ci  /* GPO enabling for XP200 */
2576141cc406Sopenharmony_ci    if (dev->model->sensor_id == SensorId::CIS_XP200) {
2577141cc406Sopenharmony_ci        dev->interface->write_register(0x68, dev->gpo.regs.get_value(0x68));
2578141cc406Sopenharmony_ci        dev->interface->write_register(0x69, dev->gpo.regs.get_value(0x69));
2579141cc406Sopenharmony_ci
2580141cc406Sopenharmony_ci        // enable GPIO
2581141cc406Sopenharmony_ci        gl646_gpio_output_enable(dev->interface->get_usb_device(), 6);
2582141cc406Sopenharmony_ci
2583141cc406Sopenharmony_ci        // writes 0 to GPIO
2584141cc406Sopenharmony_ci        gl646_gpio_write(dev->interface->get_usb_device(), 0);
2585141cc406Sopenharmony_ci
2586141cc406Sopenharmony_ci        // clear GPIO enable
2587141cc406Sopenharmony_ci        gl646_gpio_output_enable(dev->interface->get_usb_device(), 0);
2588141cc406Sopenharmony_ci
2589141cc406Sopenharmony_ci        dev->interface->write_register(0x66, 0x10);
2590141cc406Sopenharmony_ci        dev->interface->write_register(0x66, 0x00);
2591141cc406Sopenharmony_ci        dev->interface->write_register(0x66, 0x10);
2592141cc406Sopenharmony_ci    }
2593141cc406Sopenharmony_ci
2594141cc406Sopenharmony_ci  /* MD6471/G2410 and XP200 read/write data from an undocumented memory area which
2595141cc406Sopenharmony_ci   * is after the second slope table */
2596141cc406Sopenharmony_ci    if (dev->model->gpio_id != GpioId::HP3670 &&
2597141cc406Sopenharmony_ci        dev->model->gpio_id != GpioId::HP2400)
2598141cc406Sopenharmony_ci    {
2599141cc406Sopenharmony_ci      switch (sensor.full_resolution)
2600141cc406Sopenharmony_ci	{
2601141cc406Sopenharmony_ci	case 600:
2602141cc406Sopenharmony_ci	  addr = 0x08200;
2603141cc406Sopenharmony_ci	  break;
2604141cc406Sopenharmony_ci	case 1200:
2605141cc406Sopenharmony_ci	  addr = 0x10200;
2606141cc406Sopenharmony_ci	  break;
2607141cc406Sopenharmony_ci	case 2400:
2608141cc406Sopenharmony_ci	  addr = 0x1fa00;
2609141cc406Sopenharmony_ci	  break;
2610141cc406Sopenharmony_ci	}
2611141cc406Sopenharmony_ci    sanei_genesys_set_buffer_address(dev, addr);
2612141cc406Sopenharmony_ci
2613141cc406Sopenharmony_ci      sanei_usb_set_timeout (2 * 1000);
2614141cc406Sopenharmony_ci      len = 6;
2615141cc406Sopenharmony_ci        // for some reason, read fails here for MD6471, HP2300 and XP200 one time out of
2616141cc406Sopenharmony_ci        // 2 scanimage launches
2617141cc406Sopenharmony_ci        try {
2618141cc406Sopenharmony_ci            dev->interface->bulk_read_data(0x45, dev->control, len);
2619141cc406Sopenharmony_ci        } catch (...) {
2620141cc406Sopenharmony_ci            dev->interface->bulk_read_data(0x45, dev->control, len);
2621141cc406Sopenharmony_ci        }
2622141cc406Sopenharmony_ci      sanei_usb_set_timeout (30 * 1000);
2623141cc406Sopenharmony_ci    }
2624141cc406Sopenharmony_ci  else
2625141cc406Sopenharmony_ci    /* HP2400 and HP3670 case */
2626141cc406Sopenharmony_ci    {
2627141cc406Sopenharmony_ci      dev->control[0] = 0x00;
2628141cc406Sopenharmony_ci      dev->control[1] = 0x00;
2629141cc406Sopenharmony_ci      dev->control[2] = 0x01;
2630141cc406Sopenharmony_ci      dev->control[3] = 0x00;
2631141cc406Sopenharmony_ci      dev->control[4] = 0x00;
2632141cc406Sopenharmony_ci      dev->control[5] = 0x00;
2633141cc406Sopenharmony_ci    }
2634141cc406Sopenharmony_ci
2635141cc406Sopenharmony_ci  /* ensure head is correctly parked, and check lock */
2636141cc406Sopenharmony_ci    if (!dev->model->is_sheetfed) {
2637141cc406Sopenharmony_ci        move_back_home(dev, true);
2638141cc406Sopenharmony_ci    }
2639141cc406Sopenharmony_ci
2640141cc406Sopenharmony_ci  /* here session and device are initialized */
2641141cc406Sopenharmony_ci    dev->already_initialized = true;
2642141cc406Sopenharmony_ci}
2643141cc406Sopenharmony_ci
2644141cc406Sopenharmony_cistatic void simple_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
2645141cc406Sopenharmony_ci                        const ScanSession& session, bool move,
2646141cc406Sopenharmony_ci                        std::vector<std::uint8_t>& data, const char* scan_identifier)
2647141cc406Sopenharmony_ci{
2648141cc406Sopenharmony_ci    unsigned lines = session.output_line_count;
2649141cc406Sopenharmony_ci    if (!dev->model->is_cis) {
2650141cc406Sopenharmony_ci        lines++;
2651141cc406Sopenharmony_ci    }
2652141cc406Sopenharmony_ci
2653141cc406Sopenharmony_ci    std::size_t size = lines * session.params.pixels;
2654141cc406Sopenharmony_ci    unsigned bpp = session.params.depth == 16 ? 2 : 1;
2655141cc406Sopenharmony_ci
2656141cc406Sopenharmony_ci    size *= bpp * session.params.channels;
2657141cc406Sopenharmony_ci  data.clear();
2658141cc406Sopenharmony_ci  data.resize(size);
2659141cc406Sopenharmony_ci
2660141cc406Sopenharmony_ci    // initialize frontend
2661141cc406Sopenharmony_ci    gl646_set_fe(dev, sensor, AFE_SET, session.params.xres);
2662141cc406Sopenharmony_ci
2663141cc406Sopenharmony_ci    // no watch dog for simple scan
2664141cc406Sopenharmony_ci    dev->reg.find_reg(0x01).value &= ~REG_0x01_DOGENB;
2665141cc406Sopenharmony_ci
2666141cc406Sopenharmony_ci  /* one table movement for simple scan */
2667141cc406Sopenharmony_ci    dev->reg.find_reg(0x02).value &= ~REG_0x02_FASTFED;
2668141cc406Sopenharmony_ci
2669141cc406Sopenharmony_ci    if (!move) {
2670141cc406Sopenharmony_ci        sanei_genesys_set_motor_power(dev->reg, false);
2671141cc406Sopenharmony_ci    }
2672141cc406Sopenharmony_ci
2673141cc406Sopenharmony_ci  /* no automatic go home when using XPA */
2674141cc406Sopenharmony_ci    if (session.params.scan_method == ScanMethod::TRANSPARENCY) {
2675141cc406Sopenharmony_ci        dev->reg.find_reg(0x02).value &= ~REG_0x02_AGOHOME;
2676141cc406Sopenharmony_ci    }
2677141cc406Sopenharmony_ci
2678141cc406Sopenharmony_ci    // write scan registers
2679141cc406Sopenharmony_ci    dev->interface->write_registers(dev->reg);
2680141cc406Sopenharmony_ci
2681141cc406Sopenharmony_ci    // starts scan
2682141cc406Sopenharmony_ci    dev->cmd_set->begin_scan(dev, sensor, &dev->reg, move);
2683141cc406Sopenharmony_ci
2684141cc406Sopenharmony_ci    if (is_testing_mode()) {
2685141cc406Sopenharmony_ci        dev->interface->test_checkpoint(scan_identifier);
2686141cc406Sopenharmony_ci        return;
2687141cc406Sopenharmony_ci    }
2688141cc406Sopenharmony_ci
2689141cc406Sopenharmony_ci    wait_until_buffer_non_empty(dev, true);
2690141cc406Sopenharmony_ci
2691141cc406Sopenharmony_ci    // now we're on target, we can read data
2692141cc406Sopenharmony_ci    sanei_genesys_read_data_from_scanner(dev, data.data(), size);
2693141cc406Sopenharmony_ci
2694141cc406Sopenharmony_ci  /* in case of CIS scanner, we must reorder data */
2695141cc406Sopenharmony_ci    if (dev->model->is_cis && session.params.scan_mode == ScanColorMode::COLOR_SINGLE_PASS) {
2696141cc406Sopenharmony_ci        auto pixels_count = session.params.pixels;
2697141cc406Sopenharmony_ci
2698141cc406Sopenharmony_ci        std::vector<std::uint8_t> buffer(pixels_count * 3 * bpp);
2699141cc406Sopenharmony_ci
2700141cc406Sopenharmony_ci        if (bpp == 1) {
2701141cc406Sopenharmony_ci            for (unsigned y = 0; y < lines; y++) {
2702141cc406Sopenharmony_ci                // reorder line
2703141cc406Sopenharmony_ci                for (unsigned x = 0; x < pixels_count; x++) {
2704141cc406Sopenharmony_ci                    buffer[x * 3] = data[y * pixels_count * 3 + x];
2705141cc406Sopenharmony_ci                    buffer[x * 3 + 1] = data[y * pixels_count * 3 + pixels_count + x];
2706141cc406Sopenharmony_ci                    buffer[x * 3 + 2] = data[y * pixels_count * 3 + 2 * pixels_count + x];
2707141cc406Sopenharmony_ci                }
2708141cc406Sopenharmony_ci                // copy line back
2709141cc406Sopenharmony_ci                std::memcpy(data.data() + pixels_count * 3 * y, buffer.data(), pixels_count * 3);
2710141cc406Sopenharmony_ci            }
2711141cc406Sopenharmony_ci        } else {
2712141cc406Sopenharmony_ci            for (unsigned y = 0; y < lines; y++) {
2713141cc406Sopenharmony_ci                // reorder line
2714141cc406Sopenharmony_ci                auto pixels_count = session.params.pixels;
2715141cc406Sopenharmony_ci                for (unsigned x = 0; x < pixels_count; x++) {
2716141cc406Sopenharmony_ci                    buffer[x * 6] = data[y * pixels_count * 6 + x * 2];
2717141cc406Sopenharmony_ci                    buffer[x * 6 + 1] = data[y * pixels_count * 6 + x * 2 + 1];
2718141cc406Sopenharmony_ci                    buffer[x * 6 + 2] = data[y * pixels_count * 6 + 2 * pixels_count + x * 2];
2719141cc406Sopenharmony_ci                    buffer[x * 6 + 3] = data[y * pixels_count * 6 + 2 * pixels_count + x * 2 + 1];
2720141cc406Sopenharmony_ci                    buffer[x * 6 + 4] = data[y * pixels_count * 6 + 4 * pixels_count + x * 2];
2721141cc406Sopenharmony_ci                    buffer[x * 6 + 5] = data[y * pixels_count * 6 + 4 * pixels_count + x * 2 + 1];
2722141cc406Sopenharmony_ci                }
2723141cc406Sopenharmony_ci                // copy line back
2724141cc406Sopenharmony_ci                std::memcpy(data.data() + pixels_count * 6 * y, buffer.data(),pixels_count * 6);
2725141cc406Sopenharmony_ci            }
2726141cc406Sopenharmony_ci        }
2727141cc406Sopenharmony_ci    }
2728141cc406Sopenharmony_ci
2729141cc406Sopenharmony_ci    // end scan , waiting the motor to stop if needed (if moving), but without ejecting doc
2730141cc406Sopenharmony_ci    end_scan_impl(dev, &dev->reg, true, false);
2731141cc406Sopenharmony_ci}
2732141cc406Sopenharmony_ci
2733141cc406Sopenharmony_ci/**
2734141cc406Sopenharmony_ci * update the status of the required sensor in the scanner session
2735141cc406Sopenharmony_ci * the button fields are used to make events 'sticky'
2736141cc406Sopenharmony_ci */
2737141cc406Sopenharmony_civoid CommandSetGl646::update_hardware_sensors(Genesys_Scanner* session) const
2738141cc406Sopenharmony_ci{
2739141cc406Sopenharmony_ci    DBG_HELPER(dbg);
2740141cc406Sopenharmony_ci  Genesys_Device *dev = session->dev;
2741141cc406Sopenharmony_ci    std::uint8_t value;
2742141cc406Sopenharmony_ci
2743141cc406Sopenharmony_ci    // do what is needed to get a new set of events, but try to not loose any of them.
2744141cc406Sopenharmony_ci    gl646_gpio_read(dev->interface->get_usb_device(), &value);
2745141cc406Sopenharmony_ci    DBG(DBG_io, "%s: GPIO=0x%02x\n", __func__, value);
2746141cc406Sopenharmony_ci
2747141cc406Sopenharmony_ci    // scan button
2748141cc406Sopenharmony_ci    if (dev->model->buttons & GENESYS_HAS_SCAN_SW) {
2749141cc406Sopenharmony_ci        switch (dev->model->gpio_id) {
2750141cc406Sopenharmony_ci        case GpioId::XP200:
2751141cc406Sopenharmony_ci            session->buttons[BUTTON_SCAN_SW].write((value & 0x02) != 0);
2752141cc406Sopenharmony_ci            break;
2753141cc406Sopenharmony_ci        case GpioId::MD_5345:
2754141cc406Sopenharmony_ci            session->buttons[BUTTON_SCAN_SW].write(value == 0x16);
2755141cc406Sopenharmony_ci            break;
2756141cc406Sopenharmony_ci        case GpioId::HP2300:
2757141cc406Sopenharmony_ci            session->buttons[BUTTON_SCAN_SW].write(value == 0x6c);
2758141cc406Sopenharmony_ci            break;
2759141cc406Sopenharmony_ci        case GpioId::HP3670:
2760141cc406Sopenharmony_ci        case GpioId::HP2400:
2761141cc406Sopenharmony_ci            session->buttons[BUTTON_SCAN_SW].write((value & 0x20) == 0);
2762141cc406Sopenharmony_ci            break;
2763141cc406Sopenharmony_ci        default:
2764141cc406Sopenharmony_ci                throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
2765141cc406Sopenharmony_ci	}
2766141cc406Sopenharmony_ci    }
2767141cc406Sopenharmony_ci
2768141cc406Sopenharmony_ci    // email button
2769141cc406Sopenharmony_ci    if (dev->model->buttons & GENESYS_HAS_EMAIL_SW) {
2770141cc406Sopenharmony_ci        switch (dev->model->gpio_id) {
2771141cc406Sopenharmony_ci        case GpioId::MD_5345:
2772141cc406Sopenharmony_ci            session->buttons[BUTTON_EMAIL_SW].write(value == 0x12);
2773141cc406Sopenharmony_ci            break;
2774141cc406Sopenharmony_ci        case GpioId::HP3670:
2775141cc406Sopenharmony_ci        case GpioId::HP2400:
2776141cc406Sopenharmony_ci            session->buttons[BUTTON_EMAIL_SW].write((value & 0x08) == 0);
2777141cc406Sopenharmony_ci            break;
2778141cc406Sopenharmony_ci        default:
2779141cc406Sopenharmony_ci                throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
2780141cc406Sopenharmony_ci    }
2781141cc406Sopenharmony_ci    }
2782141cc406Sopenharmony_ci
2783141cc406Sopenharmony_ci    // copy button
2784141cc406Sopenharmony_ci    if (dev->model->buttons & GENESYS_HAS_COPY_SW) {
2785141cc406Sopenharmony_ci        switch (dev->model->gpio_id) {
2786141cc406Sopenharmony_ci        case GpioId::MD_5345:
2787141cc406Sopenharmony_ci            session->buttons[BUTTON_COPY_SW].write(value == 0x11);
2788141cc406Sopenharmony_ci            break;
2789141cc406Sopenharmony_ci        case GpioId::HP2300:
2790141cc406Sopenharmony_ci            session->buttons[BUTTON_COPY_SW].write(value == 0x5c);
2791141cc406Sopenharmony_ci            break;
2792141cc406Sopenharmony_ci        case GpioId::HP3670:
2793141cc406Sopenharmony_ci        case GpioId::HP2400:
2794141cc406Sopenharmony_ci            session->buttons[BUTTON_COPY_SW].write((value & 0x10) == 0);
2795141cc406Sopenharmony_ci            break;
2796141cc406Sopenharmony_ci        default:
2797141cc406Sopenharmony_ci                throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
2798141cc406Sopenharmony_ci    }
2799141cc406Sopenharmony_ci    }
2800141cc406Sopenharmony_ci
2801141cc406Sopenharmony_ci    // power button
2802141cc406Sopenharmony_ci    if (dev->model->buttons & GENESYS_HAS_POWER_SW) {
2803141cc406Sopenharmony_ci        switch (dev->model->gpio_id) {
2804141cc406Sopenharmony_ci        case GpioId::MD_5345:
2805141cc406Sopenharmony_ci            session->buttons[BUTTON_POWER_SW].write(value == 0x14);
2806141cc406Sopenharmony_ci            break;
2807141cc406Sopenharmony_ci        default:
2808141cc406Sopenharmony_ci                throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
2809141cc406Sopenharmony_ci    }
2810141cc406Sopenharmony_ci    }
2811141cc406Sopenharmony_ci
2812141cc406Sopenharmony_ci    // ocr button
2813141cc406Sopenharmony_ci    if (dev->model->buttons & GENESYS_HAS_OCR_SW) {
2814141cc406Sopenharmony_ci        switch (dev->model->gpio_id) {
2815141cc406Sopenharmony_ci    case GpioId::MD_5345:
2816141cc406Sopenharmony_ci            session->buttons[BUTTON_OCR_SW].write(value == 0x13);
2817141cc406Sopenharmony_ci            break;
2818141cc406Sopenharmony_ci	default:
2819141cc406Sopenharmony_ci                throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
2820141cc406Sopenharmony_ci    }
2821141cc406Sopenharmony_ci    }
2822141cc406Sopenharmony_ci
2823141cc406Sopenharmony_ci    // document detection
2824141cc406Sopenharmony_ci    if (dev->model->buttons & GENESYS_HAS_PAGE_LOADED_SW) {
2825141cc406Sopenharmony_ci        switch (dev->model->gpio_id) {
2826141cc406Sopenharmony_ci        case GpioId::XP200:
2827141cc406Sopenharmony_ci            session->buttons[BUTTON_PAGE_LOADED_SW].write((value & 0x04) != 0);
2828141cc406Sopenharmony_ci            break;
2829141cc406Sopenharmony_ci        default:
2830141cc406Sopenharmony_ci                throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
2831141cc406Sopenharmony_ci    }
2832141cc406Sopenharmony_ci    }
2833141cc406Sopenharmony_ci
2834141cc406Sopenharmony_ci  /* XPA detection */
2835141cc406Sopenharmony_ci    if (dev->model->has_method(ScanMethod::TRANSPARENCY)) {
2836141cc406Sopenharmony_ci        switch (dev->model->gpio_id) {
2837141cc406Sopenharmony_ci            case GpioId::HP3670:
2838141cc406Sopenharmony_ci            case GpioId::HP2400:
2839141cc406Sopenharmony_ci	  /* test if XPA is plugged-in */
2840141cc406Sopenharmony_ci            if ((value & 0x40) == 0) {
2841141cc406Sopenharmony_ci                session->opt[OPT_SOURCE].cap &= ~SANE_CAP_INACTIVE;
2842141cc406Sopenharmony_ci            } else {
2843141cc406Sopenharmony_ci                session->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE;
2844141cc406Sopenharmony_ci            }
2845141cc406Sopenharmony_ci      break;
2846141cc406Sopenharmony_ci            default:
2847141cc406Sopenharmony_ci                throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type");
2848141cc406Sopenharmony_ci    }
2849141cc406Sopenharmony_ci    }
2850141cc406Sopenharmony_ci}
2851141cc406Sopenharmony_ci
2852141cc406Sopenharmony_civoid CommandSetGl646::update_home_sensor_gpio(Genesys_Device& dev) const
2853141cc406Sopenharmony_ci{
2854141cc406Sopenharmony_ci    DBG_HELPER(dbg);
2855141cc406Sopenharmony_ci    (void) dev;
2856141cc406Sopenharmony_ci}
2857141cc406Sopenharmony_ci
2858141cc406Sopenharmony_cistatic void write_control(Genesys_Device* dev, const Genesys_Sensor& sensor, int resolution)
2859141cc406Sopenharmony_ci{
2860141cc406Sopenharmony_ci    DBG_HELPER(dbg);
2861141cc406Sopenharmony_ci    std::uint8_t control[4];
2862141cc406Sopenharmony_ci    std::uint32_t addr = 0xdead;
2863141cc406Sopenharmony_ci
2864141cc406Sopenharmony_ci  /* 2300 does not write to 'control' */
2865141cc406Sopenharmony_ci    if (dev->model->motor_id == MotorId::HP2300) {
2866141cc406Sopenharmony_ci        return;
2867141cc406Sopenharmony_ci    }
2868141cc406Sopenharmony_ci
2869141cc406Sopenharmony_ci  /* MD6471/G2410/HP2300 and XP200 read/write data from an undocumented memory area which
2870141cc406Sopenharmony_ci   * is after the second slope table */
2871141cc406Sopenharmony_ci  switch (sensor.full_resolution)
2872141cc406Sopenharmony_ci    {
2873141cc406Sopenharmony_ci    case 600:
2874141cc406Sopenharmony_ci      addr = 0x08200;
2875141cc406Sopenharmony_ci      break;
2876141cc406Sopenharmony_ci    case 1200:
2877141cc406Sopenharmony_ci      addr = 0x10200;
2878141cc406Sopenharmony_ci      break;
2879141cc406Sopenharmony_ci    case 2400:
2880141cc406Sopenharmony_ci      addr = 0x1fa00;
2881141cc406Sopenharmony_ci      break;
2882141cc406Sopenharmony_ci    default:
2883141cc406Sopenharmony_ci        throw SaneException("failed to compute control address");
2884141cc406Sopenharmony_ci    }
2885141cc406Sopenharmony_ci
2886141cc406Sopenharmony_ci  /* XP200 sets dpi, what other scanner put is unknown yet */
2887141cc406Sopenharmony_ci  switch (dev->model->motor_id)
2888141cc406Sopenharmony_ci    {
2889141cc406Sopenharmony_ci        case MotorId::XP200:
2890141cc406Sopenharmony_ci      /* we put scan's dpi, not motor one */
2891141cc406Sopenharmony_ci            control[0] = resolution & 0xff;
2892141cc406Sopenharmony_ci            control[1] = (resolution >> 8) & 0xff;
2893141cc406Sopenharmony_ci      control[2] = dev->control[4];
2894141cc406Sopenharmony_ci      control[3] = dev->control[5];
2895141cc406Sopenharmony_ci      break;
2896141cc406Sopenharmony_ci        case MotorId::HP3670:
2897141cc406Sopenharmony_ci        case MotorId::HP2400:
2898141cc406Sopenharmony_ci        case MotorId::MD_5345:
2899141cc406Sopenharmony_ci        default:
2900141cc406Sopenharmony_ci      control[0] = dev->control[2];
2901141cc406Sopenharmony_ci      control[1] = dev->control[3];
2902141cc406Sopenharmony_ci      control[2] = dev->control[4];
2903141cc406Sopenharmony_ci      control[3] = dev->control[5];
2904141cc406Sopenharmony_ci      break;
2905141cc406Sopenharmony_ci    }
2906141cc406Sopenharmony_ci
2907141cc406Sopenharmony_ci    dev->interface->write_buffer(0x3c, addr, control, 4);
2908141cc406Sopenharmony_ci}
2909141cc406Sopenharmony_ci
2910141cc406Sopenharmony_civoid CommandSetGl646::wait_for_motor_stop(Genesys_Device* dev) const
2911141cc406Sopenharmony_ci{
2912141cc406Sopenharmony_ci    (void) dev;
2913141cc406Sopenharmony_ci}
2914141cc406Sopenharmony_ci
2915141cc406Sopenharmony_civoid CommandSetGl646::send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor,
2916141cc406Sopenharmony_ci                                        std::uint8_t* data, int size) const
2917141cc406Sopenharmony_ci{
2918141cc406Sopenharmony_ci    (void) dev;
2919141cc406Sopenharmony_ci    (void) sensor;
2920141cc406Sopenharmony_ci    (void) data;
2921141cc406Sopenharmony_ci    (void) size;
2922141cc406Sopenharmony_ci    throw SaneException("not implemented");
2923141cc406Sopenharmony_ci}
2924141cc406Sopenharmony_ci
2925141cc406Sopenharmony_ciScanSession CommandSetGl646::calculate_scan_session(const Genesys_Device* dev,
2926141cc406Sopenharmony_ci                                                    const Genesys_Sensor& sensor,
2927141cc406Sopenharmony_ci                                                    const Genesys_Settings& settings) const
2928141cc406Sopenharmony_ci{
2929141cc406Sopenharmony_ci    // compute distance to move
2930141cc406Sopenharmony_ci    float move = 0;
2931141cc406Sopenharmony_ci    if (!dev->model->is_sheetfed) {
2932141cc406Sopenharmony_ci        move = dev->model->y_offset;
2933141cc406Sopenharmony_ci        // add tl_y to base movement
2934141cc406Sopenharmony_ci    }
2935141cc406Sopenharmony_ci    move += settings.tl_y;
2936141cc406Sopenharmony_ci
2937141cc406Sopenharmony_ci    if (move < 0) {
2938141cc406Sopenharmony_ci        DBG(DBG_error, "%s: overriding negative move value %f\n", __func__, move);
2939141cc406Sopenharmony_ci        move = 0;
2940141cc406Sopenharmony_ci    }
2941141cc406Sopenharmony_ci
2942141cc406Sopenharmony_ci    move = static_cast<float>((move * dev->motor.base_ydpi) / MM_PER_INCH);
2943141cc406Sopenharmony_ci    float start = settings.tl_x;
2944141cc406Sopenharmony_ci    if (settings.scan_method == ScanMethod::FLATBED) {
2945141cc406Sopenharmony_ci        start += dev->model->x_offset;
2946141cc406Sopenharmony_ci    } else {
2947141cc406Sopenharmony_ci        start += dev->model->x_offset_ta;
2948141cc406Sopenharmony_ci    }
2949141cc406Sopenharmony_ci    start = static_cast<float>((start * settings.xres) / MM_PER_INCH);
2950141cc406Sopenharmony_ci
2951141cc406Sopenharmony_ci    ScanSession session;
2952141cc406Sopenharmony_ci    session.params.xres = settings.xres;
2953141cc406Sopenharmony_ci    session.params.yres = settings.yres;
2954141cc406Sopenharmony_ci    session.params.startx = static_cast<unsigned>(start);
2955141cc406Sopenharmony_ci    session.params.starty = static_cast<unsigned>(move);
2956141cc406Sopenharmony_ci    session.params.pixels = settings.pixels;
2957141cc406Sopenharmony_ci    session.params.requested_pixels = settings.requested_pixels;
2958141cc406Sopenharmony_ci    session.params.lines = settings.lines;
2959141cc406Sopenharmony_ci    session.params.depth = settings.depth;
2960141cc406Sopenharmony_ci    session.params.channels = settings.get_channels();
2961141cc406Sopenharmony_ci    session.params.scan_method = dev->settings.scan_method;
2962141cc406Sopenharmony_ci    session.params.scan_mode = settings.scan_mode;
2963141cc406Sopenharmony_ci    session.params.color_filter = settings.color_filter;
2964141cc406Sopenharmony_ci    session.params.contrast_adjustment = settings.contrast;
2965141cc406Sopenharmony_ci    session.params.brightness_adjustment = settings.brightness;
2966141cc406Sopenharmony_ci    session.params.flags = ScanFlag::AUTO_GO_HOME;
2967141cc406Sopenharmony_ci    if (settings.scan_method == ScanMethod::TRANSPARENCY) {
2968141cc406Sopenharmony_ci        session.params.flags |= ScanFlag::USE_XPA;
2969141cc406Sopenharmony_ci    }
2970141cc406Sopenharmony_ci    compute_session(dev, session, sensor);
2971141cc406Sopenharmony_ci
2972141cc406Sopenharmony_ci    return session;
2973141cc406Sopenharmony_ci}
2974141cc406Sopenharmony_ci
2975141cc406Sopenharmony_civoid CommandSetGl646::asic_boot(Genesys_Device *dev, bool cold) const
2976141cc406Sopenharmony_ci{
2977141cc406Sopenharmony_ci    (void) dev;
2978141cc406Sopenharmony_ci    (void) cold;
2979141cc406Sopenharmony_ci    throw SaneException("not implemented");
2980141cc406Sopenharmony_ci}
2981141cc406Sopenharmony_ci
2982141cc406Sopenharmony_ci} // namespace gl646
2983141cc406Sopenharmony_ci} // namespace genesys
2984