1 /*  sane - Scanner Access Now Easy.
2 
3     Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr>
4     Copyright (C) 2020 Povilas Kanapickas <povilas@radix.lt>
5 
6     This file is part of the SANE package.
7 
8     This program is free software; you can redistribute it and/or
9     modify it under the terms of the GNU General Public License as
10     published by the Free Software Foundation; either version 2 of the
11     License, or (at your option) any later version.
12 
13     This program is distributed in the hope that it will be useful, but
14     WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16     General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License
19     along with this program.  If not, see <https://www.gnu.org/licenses/>.
20 */
21 
22 #define DEBUG_DECLARE_ONLY
23 
24 #include "gl842_registers.h"
25 #include "gl842.h"
26 #include "test_settings.h"
27 
28 #include <string>
29 #include <vector>
30 
31 namespace genesys {
32 namespace gl842 {
33 
gl842_init_registers(Genesys_Device& dev)34 static void gl842_init_registers(Genesys_Device& dev)
35 {
36     // Within this function SENSOR_DEF marker documents that a register is part
37     // of the sensors definition and the actual value is set in
38     // gl842_setup_sensor().
39 
40     DBG_HELPER(dbg);
41 
42     dev.reg.clear();
43 
44     if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) {
45         dev.reg.init_reg(0x01, 0x00);
46         dev.reg.init_reg(0x02, 0x78);
47         dev.reg.init_reg(0x03, 0xbf);
48         dev.reg.init_reg(0x04, 0x22);
49         dev.reg.init_reg(0x05, 0x48);
50 
51         dev.reg.init_reg(0x06, 0xb8);
52 
53         dev.reg.init_reg(0x07, 0x00);
54         dev.reg.init_reg(0x08, 0x00);
55         dev.reg.init_reg(0x09, 0x00);
56         dev.reg.init_reg(0x0a, 0x00);
57         dev.reg.init_reg(0x0d, 0x01);
58     } else if (dev.model->model_id == ModelId::CANON_LIDE_90) {
59         dev.reg.init_reg(0x01, 0x82);
60         dev.reg.init_reg(0x02, 0x10);
61         dev.reg.init_reg(0x03, 0x60);
62         dev.reg.init_reg(0x04, 0x10);
63         dev.reg.init_reg(0x05, 0x8c);
64 
65         dev.reg.init_reg(0x06, 0x18);
66 
67         //dev.reg.init_reg(0x07, 0x00);
68         dev.reg.init_reg(0x08, 0x00);
69         dev.reg.init_reg(0x09, 0x21);
70         dev.reg.init_reg(0x0a, 0x00);
71         dev.reg.init_reg(0x0d, 0x00);
72     }
73 
74     dev.reg.init_reg(0x10, 0x00); // exposure, overwritten in scanner_setup_sensor() below
75     dev.reg.init_reg(0x11, 0x00); // exposure, overwritten in scanner_setup_sensor() below
76     dev.reg.init_reg(0x12, 0x00); // exposure, overwritten in scanner_setup_sensor() below
77     dev.reg.init_reg(0x13, 0x00); // exposure, overwritten in scanner_setup_sensor() below
78     dev.reg.init_reg(0x14, 0x00); // exposure, overwritten in scanner_setup_sensor() below
79     dev.reg.init_reg(0x15, 0x00); // exposure, overwritten in scanner_setup_sensor() below
80 
81     // CCD signal settings.
82     dev.reg.init_reg(0x16, 0x00); // SENSOR_DEF
83     dev.reg.init_reg(0x17, 0x00); // SENSOR_DEF
84     dev.reg.init_reg(0x18, 0x00); // SENSOR_DEF
85 
86     // EXPDMY[0:7]: Exposure time of dummy lines.
87     dev.reg.init_reg(0x19, 0x00); // SENSOR_DEF
88 
89     // Various CCD clock settings.
90     dev.reg.init_reg(0x1a, 0x00); // SENSOR_DEF
91     if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) {
92         dev.reg.init_reg(0x1b, 0x00); // SENSOR_DEF
93     }
94     dev.reg.init_reg(0x1c, 0x00); // SENSOR_DEF
95     dev.reg.init_reg(0x1d, 0x00); // SENSOR_DEF
96     dev.reg.init_reg(0x1e, 0x10); // WDTIME, LINESEL: setup during sensor and motor setup
97 
98     if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) {
99         dev.reg.init_reg(0x1f, 0x01);
100         dev.reg.init_reg(0x20, 0x27); // BUFSEL: buffer full condition
101     } else if (dev.model->model_id == ModelId::CANON_LIDE_90) {
102         dev.reg.init_reg(0x1f, 0x02);
103         dev.reg.init_reg(0x20, 0x02); // BUFSEL: buffer full condition
104     }
105 
106     dev.reg.init_reg(0x21, 0x10); // STEPNO: set during motor setup
107     dev.reg.init_reg(0x22, 0x10); // FWDSTEP: set during motor setup
108     dev.reg.init_reg(0x23, 0x10); // BWDSTEP: set during motor setup
109     dev.reg.init_reg(0x24, 0x10); // FASTNO: set during motor setup
110     dev.reg.init_reg(0x25, 0x00); // LINCNT: set during motor setup
111     dev.reg.init_reg(0x26, 0x00); // LINCNT: set during motor setup
112     dev.reg.init_reg(0x27, 0x00); // LINCNT: set during motor setup
113 
114     dev.reg.init_reg(0x29, 0xff); // LAMPPWM
115 
116     dev.reg.init_reg(0x2c, 0x02); // DPISET: set during sensor setup
117     dev.reg.init_reg(0x2d, 0x58); // DPISET: set during sensor setup
118 
119     dev.reg.init_reg(0x2e, 0x80); // BWHI: black/white low threshdold
120     dev.reg.init_reg(0x2f, 0x80); // BWLOW: black/white low threshold
121 
122     dev.reg.init_reg(0x30, 0x00); // STRPIXEL: set during sensor setup
123     dev.reg.init_reg(0x31, 0x49); // STRPIXEL: set during sensor setup
124     dev.reg.init_reg(0x32, 0x53); // ENDPIXEL: set during sensor setup
125     dev.reg.init_reg(0x33, 0xb9); // ENDPIXEL: set during sensor setup
126 
127     dev.reg.init_reg(0x34, 0x13); // DUMMY: SENSOR_DEF
128     dev.reg.init_reg(0x35, 0x00); // MAXWD: set during scan setup
129     dev.reg.init_reg(0x36, 0x40); // MAXWD: set during scan setup
130     dev.reg.init_reg(0x37, 0x00); // MAXWD: set during scan setup
131     dev.reg.init_reg(0x38, 0x2a); // LPERIOD: SENSOR_DEF
132     dev.reg.init_reg(0x39, 0xf8); // LPERIOD: SENSOR_DEF
133     dev.reg.init_reg(0x3d, 0x00); // FEEDL: set during motor setup
134     dev.reg.init_reg(0x3e, 0x00); // FEEDL: set during motor setup
135     dev.reg.init_reg(0x3f, 0x01); // FEEDL: set during motor setup
136 
137     dev.reg.init_reg(0x52, 0x00); // SENSOR_DEF
138     dev.reg.init_reg(0x53, 0x00); // SENSOR_DEF
139     dev.reg.init_reg(0x54, 0x00); // SENSOR_DEF
140     dev.reg.init_reg(0x55, 0x00); // SENSOR_DEF
141     dev.reg.init_reg(0x56, 0x00); // SENSOR_DEF
142     dev.reg.init_reg(0x57, 0x00); // SENSOR_DEF
143     dev.reg.init_reg(0x58, 0x00); // SENSOR_DEF
144     dev.reg.init_reg(0x59, 0x00); // SENSOR_DEF
145     dev.reg.init_reg(0x5a, 0x00); // SENSOR_DEF
146 
147     if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) {
148         dev.reg.init_reg(0x5e, 0x01); // DECSEL, STOPTIM
149     } else if (dev.model->model_id == ModelId::CANON_LIDE_90) {
150         dev.reg.init_reg(0x5e, 0x41); // DECSEL, STOPTIM
151         dev.reg.init_reg(0x5d, 0x20);
152     }
153     dev.reg.init_reg(0x5f, 0x10); // FMOVDEC: set during motor setup
154 
155     dev.reg.init_reg(0x60, 0x00); // Z1MOD: overwritten during motor setup
156     dev.reg.init_reg(0x61, 0x00); // Z1MOD: overwritten during motor setup
157     dev.reg.init_reg(0x62, 0x00); // Z1MOD: overwritten during motor setup
158     dev.reg.init_reg(0x63, 0x00); // Z2MOD: overwritten during motor setup
159     dev.reg.init_reg(0x64, 0x00); // Z2MOD: overwritten during motor setup
160     dev.reg.init_reg(0x65, 0x00); // Z2MOD: overwritten during motor setup
161 
162     if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) {
163         dev.reg.init_reg(0x67, 0x7f); // STEPSEL, MTRPWM: partially overwritten during motor setup
164         dev.reg.init_reg(0x68, 0x7f); // FSTPSEL, FASTPWM: partially overwritten during motor setup
165     } else if (dev.model->model_id == ModelId::CANON_LIDE_90) {
166         dev.reg.init_reg(0x66, 0x00); // PHFREQ
167         dev.reg.init_reg(0x67, 0x40); // STEPSEL, MTRPWM: partially overwritten during motor setup
168         dev.reg.init_reg(0x68, 0x40); // FSTPSEL, FASTPWM: partially overwritten during motor setup
169     }
170     dev.reg.init_reg(0x69, 0x10); // FSHDEC: overwritten during motor setup
171     dev.reg.init_reg(0x6a, 0x10); // FMOVNO: overwritten during motor setup
172 
173     // 0x6b, 0x6c, 0x6d, 0x6e, 0x6f - set according to gpio tables. See gl842_init_gpio.
174 
175     dev.reg.init_reg(0x70, 0x00); // SENSOR_DEF
176     dev.reg.init_reg(0x71, 0x00); // SENSOR_DEF
177     dev.reg.init_reg(0x72, 0x00); // SENSOR_DEF
178     dev.reg.init_reg(0x73, 0x00); // SENSOR_DEF
179     dev.reg.init_reg(0x74, 0x00); // SENSOR_DEF
180     dev.reg.init_reg(0x75, 0x00); // SENSOR_DEF
181     dev.reg.init_reg(0x76, 0x00); // SENSOR_DEF
182     dev.reg.init_reg(0x77, 0x00); // SENSOR_DEF
183     dev.reg.init_reg(0x78, 0x00); // SENSOR_DEF
184     dev.reg.init_reg(0x79, 0x00); // SENSOR_DEF
185     dev.reg.init_reg(0x7a, 0x00); // SENSOR_DEF
186     dev.reg.init_reg(0x7b, 0x00); // SENSOR_DEF
187     dev.reg.init_reg(0x7c, 0x00); // SENSOR_DEF
188     dev.reg.init_reg(0x7d, 0x00); // SENSOR_DEF
189 
190     // 0x7e - set according to gpio tables. See gl842_init_gpio.
191 
192     dev.reg.init_reg(0x7f, 0x00); // SENSOR_DEF
193 
194     // VRHOME, VRMOVE, VRBACK, VRSCAN: Vref settings of the motor driver IC for
195     // moving in various situations.
196     dev.reg.init_reg(0x80, 0x00); // MOTOR_PROFILE
197 
198     if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) {
199         dev.reg.init_reg(0x81, 0x00);
200         dev.reg.init_reg(0x82, 0x00);
201         dev.reg.init_reg(0x83, 0x00);
202         dev.reg.init_reg(0x84, 0x00);
203         dev.reg.init_reg(0x85, 0x00);
204         dev.reg.init_reg(0x86, 0x00);
205         dev.reg.init_reg(0x87, 0x00);
206     } else if (dev.model->model_id == ModelId::CANON_LIDE_90) {
207         dev.reg.init_reg(0x7e, 0x00);
208         dev.reg.init_reg(0x81, 0x00);
209         dev.reg.init_reg(0x82, 0x0f);
210         dev.reg.init_reg(0x83, 0x00);
211         dev.reg.init_reg(0x84, 0x0e);
212         dev.reg.init_reg(0x85, 0x00);
213         dev.reg.init_reg(0x86, 0x0d);
214         dev.reg.init_reg(0x87, 0x00);
215         dev.reg.init_reg(0x88, 0x00);
216         dev.reg.init_reg(0x89, 0x00);
217     }
218 
219     const auto& sensor = sanei_genesys_find_sensor_any(&dev);
220     sanei_genesys_set_dpihw(dev.reg, sensor.register_dpihw);
221 
222     scanner_setup_sensor(dev, sensor, dev.reg);
223 }
224 
225 // Set values of analog frontend
set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t set) const226 void CommandSetGl842::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor,
227                              std::uint8_t set) const
228 {
229     DBG_HELPER_ARGS(dbg, "%s", set == AFE_INIT ? "init" :
230                                set == AFE_SET ? "set" :
231                                set == AFE_POWER_SAVE ? "powersave" : "huh?");
232     (void) sensor;
233 
234     if (set == AFE_INIT) {
235         dev->frontend = dev->frontend_initial;
236     }
237 
238     // check analog frontend type
239     // FIXME: looks like we write to that register with initial data
240     std::uint8_t fe_type = dev->interface->read_register(REG_0x04) & REG_0x04_FESET;
241     if (fe_type == 2 || dev->model->model_id == ModelId::CANON_LIDE_90) {
242         for (const auto& reg : dev->frontend.regs) {
243             dev->interface->write_fe_register(reg.address, reg.value);
244         }
245         return;
246     }
247     if (fe_type != 0) {
248         throw SaneException(SANE_STATUS_UNSUPPORTED, "unsupported frontend type %d", fe_type);
249     }
250 
251     for (unsigned i = 1; i <= 3; i++) {
252         dev->interface->write_fe_register(i, dev->frontend.regs.get_value(0x00 + i));
253     }
254     for (const auto& reg : sensor.custom_fe_regs) {
255         dev->interface->write_fe_register(reg.address, reg.value);
256     }
257 
258     for (unsigned i = 0; i < 3; i++) {
259         dev->interface->write_fe_register(0x20 + i, dev->frontend.get_offset(i));
260     }
261 
262     for (unsigned i = 0; i < 3; i++) {
263         dev->interface->write_fe_register(0x28 + i, dev->frontend.get_gain(i));
264     }
265 }
266 
gl842_init_motor_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, const ScanSession& session, Genesys_Register_Set* reg, const MotorProfile& motor_profile, unsigned int exposure, unsigned scan_yres, unsigned int scan_lines, unsigned int scan_dummy, unsigned int feed_steps, ScanFlag flags)267 static void gl842_init_motor_regs_scan(Genesys_Device* dev,
268                                        const Genesys_Sensor& sensor,
269                                        const ScanSession& session,
270                                        Genesys_Register_Set* reg,
271                                        const MotorProfile& motor_profile,
272                                        unsigned int exposure,
273                                        unsigned scan_yres,
274                                        unsigned int scan_lines,
275                                        unsigned int scan_dummy,
276                                        unsigned int feed_steps,
277                                        ScanFlag flags)
278 {
279     DBG_HELPER_ARGS(dbg, "exposure=%d, scan_yres=%d, step_type=%d, scan_lines=%d, scan_dummy=%d, "
280                          "feed_steps=%d, flags=%x",
281                     exposure, scan_yres, static_cast<unsigned>(motor_profile.step_type),
282                     scan_lines, scan_dummy, feed_steps, static_cast<unsigned>(flags));
283 
284     unsigned step_multiplier = 2;
285     bool use_fast_fed = false;
286 
287     if ((scan_yres >= 300 && feed_steps > 900) || (has_flag(flags, ScanFlag::FEEDING))) {
288         use_fast_fed = true;
289     }
290     if (has_flag(dev->model->flags, ModelFlag::DISABLE_FAST_FEEDING)) {
291         use_fast_fed = false;
292     }
293 
294     reg->set24(REG_LINCNT, scan_lines);
295 
296     reg->set8(REG_0x02, 0);
297     sanei_genesys_set_motor_power(*reg, true);
298 
299     std::uint8_t reg02 = reg->get8(REG_0x02);
300     if (use_fast_fed) {
301         reg02 |= REG_0x02_FASTFED;
302     } else {
303         reg02 &= ~REG_0x02_FASTFED;
304     }
305 
306     // in case of automatic go home, move until home sensor
307     if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) {
308         reg02 |= REG_0x02_AGOHOME | REG_0x02_NOTHOME;
309     }
310 
311     // disable backtracking if needed
312     if (has_flag(flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE) ||
313         (scan_yres >= 2400) ||
314         (scan_yres >= sensor.full_resolution))
315     {
316         reg02 |= REG_0x02_ACDCDIS;
317     }
318 
319     if (has_flag(flags, ScanFlag::REVERSE)) {
320         reg02 |= REG_0x02_MTRREV;
321     } else {
322         reg02 &= ~REG_0x02_MTRREV;
323     }
324     reg->set8(REG_0x02, reg02);
325 
326     // scan and backtracking slope table
327     auto scan_table = create_slope_table(dev->model->asic_type, dev->motor, scan_yres, exposure,
328                                          step_multiplier, motor_profile);
329 
330     scanner_send_slope_table(dev, sensor, SCAN_TABLE, scan_table.table);
331     scanner_send_slope_table(dev, sensor, BACKTRACK_TABLE, scan_table.table);
332     scanner_send_slope_table(dev, sensor, STOP_TABLE, scan_table.table);
333 
334     reg->set8(REG_STEPNO, scan_table.table.size() / step_multiplier);
335     reg->set8(REG_FASTNO, scan_table.table.size() / step_multiplier);
336     reg->set8(REG_FSHDEC, scan_table.table.size() / step_multiplier);
337 
338     // fast table
339     const auto* fast_profile = get_motor_profile_ptr(dev->motor.fast_profiles, 0, session);
340     if (fast_profile == nullptr) {
341         fast_profile = &motor_profile;
342     }
343 
344     auto fast_table = create_slope_table_fastest(dev->model->asic_type, step_multiplier,
345                                                  *fast_profile);
346 
347     scanner_send_slope_table(dev, sensor, FAST_TABLE, fast_table.table);
348     scanner_send_slope_table(dev, sensor, HOME_TABLE, fast_table.table);
349 
350     reg->set8(REG_FMOVNO, fast_table.table.size() / step_multiplier);
351 
352     if (motor_profile.motor_vref != -1 && fast_profile->motor_vref != 1) {
353         std::uint8_t vref = 0;
354         vref |= (motor_profile.motor_vref << REG_0x80S_TABLE1_NORMAL) & REG_0x80_TABLE1_NORMAL;
355         vref |= (motor_profile.motor_vref << REG_0x80S_TABLE2_BACK) & REG_0x80_TABLE2_BACK;
356         vref |= (fast_profile->motor_vref << REG_0x80S_TABLE4_FAST) & REG_0x80_TABLE4_FAST;
357         vref |= (fast_profile->motor_vref << REG_0x80S_TABLE5_GO_HOME) & REG_0x80_TABLE5_GO_HOME;
358         reg->set8(REG_0x80, vref);
359     }
360 
361     // subtract acceleration distance from feedl
362     unsigned feedl = feed_steps;
363     feedl <<= static_cast<unsigned>(motor_profile.step_type);
364 
365     unsigned dist = scan_table.table.size() / step_multiplier;
366 
367     if (use_fast_fed) {
368         dist += (fast_table.table.size() / step_multiplier) * 2;
369     }
370 
371     // make sure when don't insane value : XXX STEF XXX in this case we should
372     // fall back to single table move
373     if (dist < feedl) {
374         feedl -= dist;
375     } else {
376         feedl = 1;
377     }
378 
379     reg->set24(REG_FEEDL, feedl);
380 
381     // doesn't seem to matter that much
382     std::uint32_t z1, z2;
383     sanei_genesys_calculate_zmod(use_fast_fed,
384                                  exposure,
385                                  scan_table.table,
386                                  scan_table.table.size() / step_multiplier,
387                                  feedl,
388                                  scan_table.table.size() / step_multiplier,
389                                  &z1,
390                                  &z2);
391     if (scan_yres > 600) {
392         z1 = 0;
393         z2 = 0;
394     }
395 
396     reg->set24(REG_Z1MOD, z1);
397     reg->set24(REG_Z2MOD, z2);
398 
399     reg->set8_mask(REG_0x1E, scan_dummy, 0x0f);
400 
401     reg->set8_mask(REG_0x67, static_cast<unsigned>(motor_profile.step_type) << REG_0x67S_STEPSEL,
402                    REG_0x67_STEPSEL);
403     reg->set8_mask(REG_0x68, static_cast<unsigned>(fast_profile->step_type) << REG_0x68S_FSTPSEL,
404                    REG_0x68_FSTPSEL);
405 
406     // steps for STOP table
407     reg->set8(REG_FMOVDEC, fast_table.table.size() / step_multiplier);
408 }
409 
gl842_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, unsigned int exposure, const ScanSession& session)410 static void gl842_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
411                                          Genesys_Register_Set* reg, unsigned int exposure,
412                                          const ScanSession& session)
413 {
414     DBG_HELPER(dbg);
415 
416     scanner_setup_sensor(*dev, sensor, *reg);
417 
418     dev->cmd_set->set_fe(dev, sensor, AFE_SET);
419 
420     // enable shading
421     regs_set_optical_off(dev->model->asic_type, *reg);
422     if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) ||
423         has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) ||
424         session.use_host_side_calib)
425     {
426         reg->find_reg(REG_0x01).value &= ~REG_0x01_DVDSET;
427 
428     } else {
429         reg->find_reg(REG_0x01).value |= REG_0x01_DVDSET;
430     }
431 
432     bool use_shdarea = true;
433 
434     if (use_shdarea) {
435         reg->find_reg(REG_0x01).value |= REG_0x01_SHDAREA;
436     } else {
437         reg->find_reg(REG_0x01).value &= ~REG_0x01_SHDAREA;
438     }
439 
440     if (dev->model->model_id == ModelId::CANON_8600F) {
441         reg->find_reg(REG_0x03).value |= REG_0x03_AVEENB;
442     } else {
443         reg->find_reg(REG_0x03).value &= ~REG_0x03_AVEENB;
444     }
445 
446     // FIXME: we probably don't need to set exposure to registers at this point. It was this way
447     // before a refactor.
448     sanei_genesys_set_lamp_power(dev, sensor, *reg,
449                                  !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP));
450 
451     // select XPA
452     reg->find_reg(REG_0x03).value &= ~REG_0x03_XPASEL;
453     if (has_flag(session.params.flags, ScanFlag::USE_XPA)) {
454         reg->find_reg(REG_0x03).value |= REG_0x03_XPASEL;
455     }
456     reg->state.is_xpa_on = has_flag(session.params.flags, ScanFlag::USE_XPA);
457 
458     // BW threshold
459     reg->set8(REG_0x2E, 0x7f);
460     reg->set8(REG_0x2F, 0x7f);
461 
462     // monochrome / color scan parameters
463     std::uint8_t reg04 = reg->get8(REG_0x04);
464     reg04 = reg04 & REG_0x04_FESET;
465 
466     switch (session.params.depth) {
467         case 8:
468             break;
469         case 16:
470             reg04 |= REG_0x04_BITSET;
471             break;
472     }
473 
474     if (session.params.channels == 1) {
475         switch (session.params.color_filter) {
476             case ColorFilter::RED: reg04 |= 0x14; break;
477             case ColorFilter::BLUE: reg04 |= 0x1c; break;
478             case ColorFilter::GREEN: reg04 |= 0x18; break;
479             default:
480                 break; // should not happen
481         }
482     } else {
483         switch (dev->frontend.layout.type) {
484             case FrontendType::WOLFSON:
485                 // pixel by pixel
486                 reg04 |= 0x10;
487                 break;
488             case FrontendType::ANALOG_DEVICES:
489                 // slow color pixel by pixel
490                 reg04 |= 0x20;
491                 break;
492             default:
493                 throw SaneException("Invalid frontend type %d",
494                                     static_cast<unsigned>(dev->frontend.layout.type));
495         }
496     }
497 
498     reg->set8(REG_0x04, reg04);
499 
500     const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, session.output_resolution,
501                                                          session.params.channels,
502                                                          session.params.scan_method);
503     sanei_genesys_set_dpihw(*reg, dpihw_sensor.register_dpihw);
504 
505     if (should_enable_gamma(session, sensor)) {
506         reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB;
507     } else {
508         reg->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB;
509     }
510 
511     reg->set16(REG_DPISET, sensor.register_dpiset);
512 
513     reg->set16(REG_STRPIXEL, session.pixel_startx);
514     reg->set16(REG_ENDPIXEL, session.pixel_endx);
515 
516     if (dev->model->is_cis) {
517         reg->set24(REG_MAXWD, session.output_line_bytes_raw * session.params.channels);
518     } else {
519         reg->set24(REG_MAXWD, session.output_line_bytes_raw);
520     }
521 
522     unsigned tgtime = exposure / 65536 + 1;
523     reg->set16(REG_LPERIOD, exposure / tgtime);
524 
525     reg->set8(REG_DUMMY, sensor.dummy_pixel);
526 }
527 
init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, const ScanSession& session) const528 void CommandSetGl842::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,
529                                                  Genesys_Register_Set* reg,
530                                                  const ScanSession& session) const
531 {
532     DBG_HELPER(dbg);
533     session.assert_computed();
534 
535     // we enable true gray for cis scanners only, and just when doing scan since color calibration
536     // is OK for this mode
537 
538     int dummy = 0;
539 
540   /* slope_dpi */
541   /* cis color scan is effectively a gray scan with 3 gray lines per color line and a FILTER of 0 */
542     int slope_dpi = 0;
543     if (dev->model->is_cis) {
544         slope_dpi = session.params.yres * session.params.channels;
545     } else {
546         slope_dpi = session.params.yres;
547     }
548     slope_dpi = slope_dpi * (1 + dummy);
549 
550     int exposure = sensor.exposure_lperiod;
551     if (exposure < 0) {
552         throw std::runtime_error("Exposure not defined in sensor definition");
553     }
554     if (dev->model->model_id == ModelId::CANON_LIDE_90) {
555         exposure *= 2;
556     }
557     const auto& motor_profile = get_motor_profile(dev->motor.profiles, exposure, session);
558 
559     // now _LOGICAL_ optical values used are known, setup registers
560     gl842_init_optical_regs_scan(dev, sensor, reg, exposure, session);
561     gl842_init_motor_regs_scan(dev, sensor, session, reg, motor_profile, exposure, slope_dpi,
562                                session.optical_line_count, dummy, session.params.starty,
563                                session.params.flags);
564 
565     setup_image_pipeline(*dev, session);
566 
567     dev->read_active = true;
568 
569     dev->session = session;
570 
571     dev->total_bytes_read = 0;
572     dev->total_bytes_to_read = (size_t)session.output_line_bytes_requested * (size_t)session.params.lines;
573 }
574 
calculate_scan_session(const Genesys_Device* dev, const Genesys_Sensor& sensor, const Genesys_Settings& settings) const575 ScanSession CommandSetGl842::calculate_scan_session(const Genesys_Device* dev,
576                                                     const Genesys_Sensor& sensor,
577                                                     const Genesys_Settings& settings) const
578 {
579     DBG_HELPER(dbg);
580     debug_dump(DBG_info, settings);
581 
582     ScanFlag flags = ScanFlag::NONE;
583 
584     float move = 0.0f;
585     if (settings.scan_method == ScanMethod::TRANSPARENCY ||
586         settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
587     {
588         // note: scanner_move_to_ta() function has already been called and the sensor is at the
589         // transparency adapter
590         if (!dev->ignore_offsets) {
591             move = dev->model->y_offset_ta - dev->model->y_offset_sensor_to_ta;
592         }
593         flags |= ScanFlag::USE_XPA;
594     } else {
595         if (!dev->ignore_offsets) {
596             move = dev->model->y_offset;
597         }
598     }
599 
600     move += settings.tl_y;
601 
602     int move_dpi = dev->motor.base_ydpi;
603     move = static_cast<float>((move * move_dpi) / MM_PER_INCH);
604 
605     float start = 0.0f;
606     if (settings.scan_method==ScanMethod::TRANSPARENCY ||
607         settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
608     {
609         start = dev->model->x_offset_ta;
610     } else {
611         start = dev->model->x_offset;
612     }
613     start = start + settings.tl_x;
614 
615     start = static_cast<float>((start * settings.xres) / MM_PER_INCH);
616 
617     ScanSession session;
618     session.params.xres = settings.xres;
619     session.params.yres = settings.yres;
620     session.params.startx = static_cast<unsigned>(start);
621     session.params.starty = static_cast<unsigned>(move);
622     session.params.pixels = settings.pixels;
623     session.params.requested_pixels = settings.requested_pixels;
624     session.params.lines = settings.lines;
625     session.params.depth = settings.depth;
626     session.params.channels = settings.get_channels();
627     session.params.scan_method = settings.scan_method;
628     session.params.scan_mode = settings.scan_mode;
629     session.params.color_filter = settings.color_filter;
630     session.params.contrast_adjustment = settings.contrast;
631     session.params.brightness_adjustment = settings.brightness;
632     session.params.flags = flags;
633     compute_session(dev, session, sensor);
634 
635     return session;
636 }
637 
save_power(Genesys_Device* dev, bool enable) const638 void CommandSetGl842::save_power(Genesys_Device* dev, bool enable) const
639 {
640     (void) dev;
641     DBG_HELPER_ARGS(dbg, "enable = %d", enable);
642 }
643 
set_powersaving(Genesys_Device* dev, int delay ) const644 void CommandSetGl842::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const
645 {
646     (void) dev;
647     DBG_HELPER_ARGS(dbg, "delay = %d", delay);
648 }
649 
eject_document(Genesys_Device* dev) const650 void CommandSetGl842::eject_document(Genesys_Device* dev) const
651 {
652     (void) dev;
653     DBG_HELPER(dbg);
654 }
655 
656 
load_document(Genesys_Device* dev) const657 void CommandSetGl842::load_document(Genesys_Device* dev) const
658 {
659     DBG_HELPER(dbg);
660     (void) dev;
661 }
662 
detect_document_end(Genesys_Device* dev) const663 void CommandSetGl842::detect_document_end(Genesys_Device* dev) const
664 {
665     DBG_HELPER(dbg);
666     (void) dev;
667     throw SaneException(SANE_STATUS_UNSUPPORTED);
668 }
669 
670 // Send the low-level scan command
begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg, bool start_motor) const671 void CommandSetGl842::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,
672                                  Genesys_Register_Set* reg, bool start_motor) const
673 {
674     DBG_HELPER(dbg);
675     (void) sensor;
676 
677     if (reg->state.is_xpa_on && reg->state.is_lamp_on &&
678         !has_flag(dev->model->flags, ModelFlag::TA_NO_SECONDARY_LAMP))
679     {
680         dev->cmd_set->set_xpa_lamp_power(*dev, true);
681     }
682     if (reg->state.is_xpa_on && !has_flag(dev->model->flags, ModelFlag::UTA_NO_SECONDARY_MOTOR)) {
683         dev->cmd_set->set_motor_mode(*dev, *reg, MotorMode::PRIMARY_AND_SECONDARY);
684     }
685 
686     if (dev->model->model_id == ModelId::CANON_LIDE_90) {
687         if (has_flag(dev->session.params.flags, ScanFlag::REVERSE)) {
688             dev->interface->write_register(REG_0x6B, 0x01);
689             dev->interface->write_register(REG_0x6C, 0x02);
690         } else {
691             dev->interface->write_register(REG_0x6B, 0x03);
692             switch (dev->session.params.xres) {
693                 case 150: dev->interface->write_register(REG_0x6C, 0x74); break;
694                 case 300: dev->interface->write_register(REG_0x6C, 0x38); break;
695                 case 600: dev->interface->write_register(REG_0x6C, 0x1c); break;
696                 case 1200: dev->interface->write_register(REG_0x6C, 0x2c); break;
697                 case 2400: dev->interface->write_register(REG_0x6C, 0x0c); break;
698                 default:
699                     break;
700             }
701         }
702         dev->interface->sleep_ms(100);
703     }
704 
705     scanner_clear_scan_and_feed_counts(*dev);
706 
707     // enable scan and motor
708     std::uint8_t val = dev->interface->read_register(REG_0x01);
709     val |= REG_0x01_SCAN;
710     dev->interface->write_register(REG_0x01, val);
711 
712     scanner_start_action(*dev, start_motor);
713 
714     switch (reg->state.motor_mode) {
715         case MotorMode::PRIMARY: {
716             if (reg->state.is_motor_on) {
717                 dev->advance_head_pos_by_session(ScanHeadId::PRIMARY);
718             }
719             break;
720         }
721         case MotorMode::PRIMARY_AND_SECONDARY: {
722             if (reg->state.is_motor_on) {
723                 dev->advance_head_pos_by_session(ScanHeadId::PRIMARY);
724                 dev->advance_head_pos_by_session(ScanHeadId::SECONDARY);
725             }
726             break;
727         }
728         case MotorMode::SECONDARY: {
729             if (reg->state.is_motor_on) {
730                 dev->advance_head_pos_by_session(ScanHeadId::SECONDARY);
731             }
732             break;
733         }
734     }
735 }
736 
end_scan(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop) const737 void CommandSetGl842::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg,
738                                bool check_stop) const
739 {
740     DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop);
741 
742     if (reg->state.is_xpa_on) {
743         dev->cmd_set->set_xpa_lamp_power(*dev, false);
744     }
745 
746     if (!dev->model->is_sheetfed) {
747         scanner_stop_action(*dev);
748     }
749 }
750 
move_back_home(Genesys_Device* dev, bool wait_until_home) const751 void CommandSetGl842::move_back_home(Genesys_Device* dev, bool wait_until_home) const
752 {
753     scanner_move_back_home(*dev, wait_until_home);
754 }
755 
init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const756 void CommandSetGl842::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
757                                             Genesys_Register_Set& regs) const
758 {
759     DBG_HELPER(dbg);
760     int move;
761 
762     float calib_size_mm = 0;
763     if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
764         dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
765     {
766         calib_size_mm = dev->model->y_size_calib_ta_mm;
767     } else {
768         calib_size_mm = dev->model->y_size_calib_mm;
769     }
770 
771     unsigned resolution = sensor.shading_resolution;
772 
773     unsigned channels = 3;
774     const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
775                                                          dev->settings.scan_method);
776 
777     unsigned calib_pixels = 0;
778     unsigned calib_pixels_offset = 0;
779 
780     if (should_calibrate_only_active_area(*dev, dev->settings)) {
781         float offset = dev->model->x_offset_ta;
782         // FIXME: we should use resolution here
783         offset = static_cast<float>((offset * dev->settings.xres) / MM_PER_INCH);
784 
785         float size = dev->model->x_size_ta;
786         size = static_cast<float>((size * dev->settings.xres) / MM_PER_INCH);
787 
788         calib_pixels_offset = static_cast<std::size_t>(offset);
789         calib_pixels = static_cast<std::size_t>(size);
790     } else {
791         calib_pixels_offset = 0;
792         calib_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
793     }
794 
795     ScanFlag flags = ScanFlag::DISABLE_SHADING |
796                      ScanFlag::DISABLE_GAMMA |
797                      ScanFlag::DISABLE_BUFFER_FULL_MOVE;
798 
799     if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
800         dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
801     {
802         // note: scanner_move_to_ta() function has already been called and the sensor is at the
803         // transparency adapter
804         move = static_cast<int>(dev->model->y_offset_calib_white_ta -
805                                 dev->model->y_offset_sensor_to_ta);
806         flags |= ScanFlag::USE_XPA;
807     } else {
808         move = static_cast<int>(dev->model->y_offset_calib_white);
809     }
810 
811     move = static_cast<int>((move * resolution) / MM_PER_INCH);
812     unsigned calib_lines = static_cast<unsigned>(calib_size_mm * resolution / MM_PER_INCH);
813 
814     ScanSession session;
815     session.params.xres = resolution;
816     session.params.yres = resolution;
817     session.params.startx = calib_pixels_offset;
818     session.params.starty = move;
819     session.params.pixels = calib_pixels;
820     session.params.lines = calib_lines;
821     session.params.depth = 16;
822     session.params.channels = channels;
823     session.params.scan_method = dev->settings.scan_method;
824     session.params.scan_mode = dev->settings.scan_mode;
825     session.params.color_filter = dev->settings.color_filter;
826     session.params.contrast_adjustment = dev->settings.contrast;
827     session.params.brightness_adjustment = dev->settings.brightness;
828     session.params.flags = flags;
829     compute_session(dev, session, calib_sensor);
830 
831     init_regs_for_scan_session(dev, calib_sensor, &regs, session);
832 
833     dev->calib_session = session;
834 }
835 
send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const836 void CommandSetGl842::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const
837 {
838     DBG_HELPER(dbg);
839 
840     if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200)
841         return; // No gamma on this model
842 
843     unsigned size = 256;
844 
845     std::vector<std::uint8_t> gamma(size * 2 * 3);
846 
847     std::vector<std::uint16_t> rgamma = get_gamma_table(dev, sensor, GENESYS_RED);
848     std::vector<std::uint16_t> ggamma = get_gamma_table(dev, sensor, GENESYS_GREEN);
849     std::vector<std::uint16_t> bgamma = get_gamma_table(dev, sensor, GENESYS_BLUE);
850 
851     // copy sensor specific's gamma tables
852     for (unsigned i = 0; i < size; i++) {
853         gamma[i * 2 + size * 0 + 0] = rgamma[i] & 0xff;
854         gamma[i * 2 + size * 0 + 1] = (rgamma[i] >> 8) & 0xff;
855         gamma[i * 2 + size * 2 + 0] = ggamma[i] & 0xff;
856         gamma[i * 2 + size * 2 + 1] = (ggamma[i] >> 8) & 0xff;
857         gamma[i * 2 + size * 4 + 0] = bgamma[i] & 0xff;
858         gamma[i * 2 + size * 4 + 1] = (bgamma[i] >> 8) & 0xff;
859     }
860 
861     dev->interface->write_gamma(0x28, 0x0000, gamma.data(), size * 2 * 3);
862 }
863 
led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const864 SensorExposure CommandSetGl842::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
865                                                 Genesys_Register_Set& regs) const
866 {
867     return scanner_led_calibration(*dev, sensor, regs);
868 }
869 
offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs) const870 void CommandSetGl842::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
871                                          Genesys_Register_Set& regs) const
872 {
873     scanner_offset_calibration(*dev, sensor, regs);
874 }
875 
coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set& regs, int dpi) const876 void CommandSetGl842::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
877                                               Genesys_Register_Set& regs, int dpi) const
878 {
879     scanner_coarse_gain_calibration(*dev, sensor, regs, dpi);
880 }
881 
init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, Genesys_Register_Set* reg) const882 void CommandSetGl842::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor,
883                                            Genesys_Register_Set* reg) const
884 {
885     DBG_HELPER(dbg);
886     (void) sensor;
887 
888     unsigned channels = 3;
889     unsigned resolution = dev->model->get_resolution_settings(dev->settings.scan_method)
890                                      .get_nearest_resolution_x(600);
891 
892     const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
893                                                          dev->settings.scan_method);
894     unsigned num_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH / 2;
895 
896     *reg = dev->reg;
897 
898     auto flags = ScanFlag::DISABLE_SHADING |
899                  ScanFlag::DISABLE_GAMMA |
900                  ScanFlag::SINGLE_LINE |
901                  ScanFlag::IGNORE_STAGGER_OFFSET |
902                  ScanFlag::IGNORE_COLOR_OFFSET;
903     if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
904         dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
905     {
906         flags |= ScanFlag::USE_XPA;
907     }
908 
909     ScanSession session;
910     session.params.xres = resolution;
911     session.params.yres = resolution;
912     session.params.startx = (num_pixels / 2) * resolution / calib_sensor.full_resolution;
913     session.params.starty = 0;
914     session.params.pixels = num_pixels;
915     session.params.lines = 1;
916     session.params.depth = dev->model->bpp_color_values.front();
917     session.params.channels = channels;
918     session.params.scan_method = dev->settings.scan_method;
919     session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
920     session.params.color_filter = dev->settings.color_filter;
921     session.params.contrast_adjustment = 0;
922     session.params.brightness_adjustment = 0;
923     session.params.flags = flags;
924 
925     compute_session(dev, session, calib_sensor);
926 
927     init_regs_for_scan_session(dev, calib_sensor, reg, session);
928 
929     sanei_genesys_set_motor_power(*reg, false);
930 }
931 
gl842_init_gpio(Genesys_Device* dev)932 static void gl842_init_gpio(Genesys_Device* dev)
933 {
934     DBG_HELPER(dbg);
935     apply_registers_ordered(dev->gpo.regs, { 0x6e, 0x6f }, [&](const GenesysRegisterSetting& reg)
936     {
937         dev->interface->write_register(reg.address, reg.value);
938     });
939 }
940 
asic_boot(Genesys_Device* dev, bool cold) const941 void CommandSetGl842::asic_boot(Genesys_Device* dev, bool cold) const
942 {
943     DBG_HELPER(dbg);
944 
945     if (cold) {
946         dev->interface->write_register(0x0e, 0x01);
947         dev->interface->write_register(0x0e, 0x00);
948     }
949 
950     // setup initial register values
951     gl842_init_registers(*dev);
952     dev->interface->write_registers(dev->reg);
953 
954     if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) {
955         std::uint8_t data[32] = {
956             0xd0, 0x38, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00,
957             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
958             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
959             0x6a, 0x73, 0x63, 0x68, 0x69, 0x65, 0x6e, 0x00,
960         };
961 
962         dev->interface->write_buffer(0x3c, 0x010a00, data, 32);
963     }
964 
965     if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) {
966         dev->interface->write_0x8c(0x10, 0x94);
967     }
968     if (dev->model->model_id == ModelId::CANON_LIDE_90) {
969         dev->interface->write_0x8c(0x10, 0xd4);
970     }
971 
972     // set RAM read address
973     dev->interface->write_register(REG_0x2A, 0x00);
974     dev->interface->write_register(REG_0x2B, 0x00);
975 
976     // setup gpio
977     gl842_init_gpio(dev);
978     dev->interface->sleep_ms(100);
979 }
980 
init(Genesys_Device* dev) const981 void CommandSetGl842::init(Genesys_Device* dev) const
982 {
983     DBG_INIT();
984     DBG_HELPER(dbg);
985 
986     sanei_genesys_asic_init(dev);
987 }
988 
update_hardware_sensors(Genesys_Scanner* s) const989 void CommandSetGl842::update_hardware_sensors(Genesys_Scanner* s) const
990 {
991     DBG_HELPER(dbg);
992     (void) s;
993 }
994 
update_home_sensor_gpio(Genesys_Device& dev) const995 void CommandSetGl842::update_home_sensor_gpio(Genesys_Device& dev) const
996 {
997     DBG_HELPER(dbg);
998     if (dev.model->model_id == ModelId::CANON_LIDE_90) {
999         std::uint8_t val = dev.interface->read_register(REG_0x6C);
1000         val |= 0x02;
1001         dev.interface->write_register(REG_0x6C, val);
1002     }
1003 }
1004 
1005 /**
1006  * Send shading calibration data. The buffer is considered to always hold values
1007  * for all the channels.
1008  */
send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, std::uint8_t* data, int size) const1009 void CommandSetGl842::send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor,
1010                                         std::uint8_t* data, int size) const
1011 {
1012     DBG_HELPER(dbg);
1013 
1014     int offset = 0;
1015     unsigned length = size;
1016 
1017     if (dev->reg.get8(REG_0x01) & REG_0x01_SHDAREA) {
1018         offset = dev->session.params.startx * sensor.shading_resolution /
1019                  dev->session.params.xres;
1020 
1021         length = dev->session.output_pixels * sensor.shading_resolution /
1022                  dev->session.params.xres;
1023 
1024         offset += sensor.shading_pixel_offset;
1025 
1026         // 16 bit words, 2 words per color, 3 color channels
1027         length *= 2 * 2 * 3;
1028         offset *= 2 * 2 * 3;
1029     } else {
1030         offset += sensor.shading_pixel_offset * 2 * 2 * 3;
1031     }
1032 
1033     dev->interface->record_key_value("shading_offset", std::to_string(offset));
1034     dev->interface->record_key_value("shading_length", std::to_string(length));
1035 
1036     std::vector<std::uint8_t> final_data(length, 0);
1037 
1038     unsigned count = 0;
1039     if (offset < 0) {
1040         count += (-offset);
1041         length -= (-offset);
1042         offset = 0;
1043     }
1044     if (static_cast<int>(length) + offset > static_cast<int>(size)) {
1045         length = size - offset;
1046     }
1047 
1048     for (unsigned i = 0; i < length; i++) {
1049         final_data[count++] = data[offset + i];
1050         count++;
1051     }
1052 
1053     dev->interface->write_buffer(0x3c, 0, final_data.data(), count);
1054 }
1055 
needs_home_before_init_regs_for_scan(Genesys_Device* dev) const1056 bool CommandSetGl842::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const
1057 {
1058     (void) dev;
1059     return true;
1060 }
1061 
wait_for_motor_stop(Genesys_Device* dev) const1062 void CommandSetGl842::wait_for_motor_stop(Genesys_Device* dev) const
1063 {
1064     (void) dev;
1065 }
1066 
1067 } // namespace gl842
1068 } // namespace genesys
1069