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, ®s, 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